Merge "Revert "Remove Bitmap#getSkBitmap""
diff --git a/Android.mk b/Android.mk
index d7e8e4e..bd8b16a 100644
--- a/Android.mk
+++ b/Android.mk
@@ -74,6 +74,7 @@
 	core/java/android/app/IBackupAgent.aidl \
 	core/java/android/app/IInstrumentationWatcher.aidl \
 	core/java/android/app/INotificationManager.aidl \
+	core/java/android/app/INotificationManagerCallback.aidl \
 	core/java/android/app/IProcessObserver.aidl \
 	core/java/android/app/ISearchManager.aidl \
 	core/java/android/app/ISearchManagerCallback.aidl \
@@ -112,6 +113,7 @@
 	core/java/android/bluetooth/IBluetoothManagerCallback.aidl \
 	core/java/android/bluetooth/IBluetoothPbap.aidl \
 	core/java/android/bluetooth/IBluetoothMap.aidl \
+	core/java/android/bluetooth/IBluetoothSap.aidl \
 	core/java/android/bluetooth/IBluetoothStateChangeCallback.aidl \
 	core/java/android/bluetooth/IBluetoothHeadsetClient.aidl \
 	core/java/android/bluetooth/IBluetoothGatt.aidl \
@@ -207,6 +209,7 @@
 	core/java/android/os/IUserManager.aidl \
 	core/java/android/os/IVibratorService.aidl \
 	core/java/android/security/IKeystoreService.aidl \
+	core/java/android/service/carrier/ICarrierConfigService.aidl \
 	core/java/android/service/carrier/ICarrierMessagingCallback.aidl \
 	core/java/android/service/carrier/ICarrierMessagingService.aidl \
 	core/java/android/service/gatekeeper/IGateKeeperService.aidl \
@@ -388,15 +391,16 @@
 	telephony/java/com/android/ims/internal/IImsVideoCallCallback.aidl \
 	telephony/java/com/android/ims/internal/IImsVideoCallProvider.aidl \
 	telephony/java/com/android/ims/ImsConfigListener.aidl \
+	telephony/java/com/android/internal/telephony/ICarrierConfigLoader.aidl \
+	telephony/java/com/android/internal/telephony/IMms.aidl \
+	telephony/java/com/android/internal/telephony/IOnSubscriptionsChangedListener.aidl \
 	telephony/java/com/android/internal/telephony/IPhoneStateListener.aidl \
 	telephony/java/com/android/internal/telephony/IPhoneSubInfo.aidl \
+	telephony/java/com/android/internal/telephony/ISms.aidl \
+	telephony/java/com/android/internal/telephony/ISub.aidl \
 	telephony/java/com/android/internal/telephony/ITelephony.aidl \
 	telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl \
-	telephony/java/com/android/internal/telephony/IOnSubscriptionsChangedListener.aidl \
-	telephony/java/com/android/internal/telephony/ISms.aidl \
 	telephony/java/com/android/internal/telephony/IWapPushManager.aidl \
-	telephony/java/com/android/internal/telephony/ISub.aidl \
-	telephony/java/com/android/internal/telephony/IMms.aidl \
 	wifi/java/android/net/wifi/IWifiManager.aidl \
 	wifi/java/android/net/wifi/passpoint/IWifiPasspointManager.aidl \
 	wifi/java/android/net/wifi/p2p/IWifiP2pManager.aidl \
@@ -547,6 +551,7 @@
 	frameworks/base/core/java/android/view/textservice/SpellCheckerInfo.aidl \
 	frameworks/base/core/java/android/view/textservice/SentenceSuggestionsInfo.aidl \
 	frameworks/base/core/java/android/view/textservice/SuggestionsInfo.aidl \
+	frameworks/base/core/java/android/service/carrier/CarrierIdentifier.aidl \
 	frameworks/base/core/java/android/service/carrier/MessagePdu.aidl \
 	frameworks/base/core/java/android/service/notification/StatusBarNotification.aidl \
 	frameworks/base/core/java/android/service/chooser/ChooserTarget.aidl \
@@ -555,6 +560,7 @@
 	frameworks/base/core/java/android/app/AssistStructure.aidl \
 	frameworks/base/core/java/android/app/AssistContent.aidl \
 	frameworks/base/core/java/android/app/Notification.aidl \
+	frameworks/base/core/java/android/app/NotificationManager.aidl \
 	frameworks/base/core/java/android/app/WallpaperInfo.aidl \
 	frameworks/base/core/java/android/app/AppOpsManager.aidl \
 	frameworks/base/core/java/android/app/ActivityManager.aidl \
diff --git a/api/current.txt b/api/current.txt
index 9b47519..7016cf9 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -20,6 +20,7 @@
     field public static final java.lang.String BATTERY_STATS = "android.permission.BATTERY_STATS";
     field public static final java.lang.String BIND_ACCESSIBILITY_SERVICE = "android.permission.BIND_ACCESSIBILITY_SERVICE";
     field public static final java.lang.String BIND_APPWIDGET = "android.permission.BIND_APPWIDGET";
+    field public static final java.lang.String BIND_CARRIER_CONFIG_SERVICE = "android.permission.BIND_CARRIER_CONFIG_SERVICE";
     field public static final java.lang.String BIND_CARRIER_MESSAGING_SERVICE = "android.permission.BIND_CARRIER_MESSAGING_SERVICE";
     field public static final java.lang.String BIND_CHOOSER_TARGET_SERVICE = "android.permission.BIND_CHOOSER_TARGET_SERVICE";
     field public static final java.lang.String BIND_DEVICE_ADMIN = "android.permission.BIND_DEVICE_ADMIN";
@@ -793,6 +794,7 @@
     field public static final int layout_x = 16843135; // 0x101017f
     field public static final int layout_y = 16843136; // 0x1010180
     field public static final int left = 16843181; // 0x10101ad
+    field public static final int leftIndents = 16844016; // 0x10104f0
     field public static final int letterSpacing = 16843958; // 0x10104b6
     field public static final int lineSpacingExtra = 16843287; // 0x1010217
     field public static final int lineSpacingMultiplier = 16843288; // 0x1010218
@@ -815,6 +817,7 @@
     field public static final int listSeparatorTextViewStyle = 16843272; // 0x1010208
     field public static final int listViewStyle = 16842868; // 0x1010074
     field public static final int listViewWhiteStyle = 16842869; // 0x1010075
+    field public static final int lockTaskMode = 16844015; // 0x10104ef
     field public static final int logo = 16843454; // 0x10102be
     field public static final int longClickable = 16842982; // 0x10100e6
     field public static final int loopViews = 16843527; // 0x1010307
@@ -1020,6 +1023,7 @@
     field public static final int reversible = 16843851; // 0x101044b
     field public static final int revisionCode = 16843989; // 0x10104d5
     field public static final int right = 16843183; // 0x10101af
+    field public static final int rightIndents = 16844017; // 0x10104f1
     field public static final int ringtonePreferenceStyle = 16842899; // 0x1010093
     field public static final int ringtoneType = 16843257; // 0x10101f9
     field public static final int rotation = 16843558; // 0x1010326
@@ -1095,7 +1099,8 @@
     field public static final int showAsAction = 16843481; // 0x10102d9
     field public static final int showDefault = 16843258; // 0x10101fa
     field public static final int showDividers = 16843561; // 0x1010329
-    field public static final int showOnLockScreen = 16843721; // 0x10103c9
+    field public static final int showForAllUsers = 16844018; // 0x10104f2
+    field public static final deprecated int showOnLockScreen = 16843721; // 0x10103c9
     field public static final int showSilent = 16843259; // 0x10101fb
     field public static final int showText = 16843949; // 0x10104ad
     field public static final deprecated int showWeekNumber = 16843582; // 0x101033e
@@ -1975,6 +1980,7 @@
     field public static final int TextAppearance_Material_Widget_ActionMode_Title = 16974355; // 0x1030213
     field public static final int TextAppearance_Material_Widget_ActionMode_Title_Inverse = 16974356; // 0x1030214
     field public static final int TextAppearance_Material_Widget_Button = 16974357; // 0x1030215
+    field public static final int TextAppearance_Material_Widget_Button_Inverse = 16974565; // 0x10302e5
     field public static final int TextAppearance_Material_Widget_DropDownHint = 16974358; // 0x1030216
     field public static final int TextAppearance_Material_Widget_DropDownItem = 16974359; // 0x1030217
     field public static final int TextAppearance_Material_Widget_EditText = 16974360; // 0x1030218
@@ -3630,12 +3636,15 @@
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.app.ActivityManager.RecentTaskInfo> CREATOR;
     field public int affiliatedTaskId;
+    field public android.content.ComponentName baseActivity;
     field public android.content.Intent baseIntent;
     field public java.lang.CharSequence description;
     field public int id;
+    field public int numActivities;
     field public android.content.ComponentName origActivity;
     field public int persistentId;
     field public android.app.ActivityManager.TaskDescription taskDescription;
+    field public android.content.ComponentName topActivity;
   }
 
   public static class ActivityManager.RunningAppProcessInfo implements android.os.Parcelable {
@@ -5107,8 +5116,44 @@
     method public void cancel(int);
     method public void cancel(java.lang.String, int);
     method public void cancelAll();
+    method public android.app.NotificationManager.Policy getNotificationPolicy(android.app.NotificationManager.Policy.Token);
+    method public boolean isNotificationPolicyTokenValid(android.app.NotificationManager.Policy.Token);
     method public void notify(int, android.app.Notification);
     method public void notify(java.lang.String, int, android.app.Notification);
+    method public void requestNotificationPolicyToken(android.app.NotificationManager.Policy.Token.RequestCallback, android.os.Handler);
+    method public void setNotificationPolicy(android.app.NotificationManager.Policy.Token, android.app.NotificationManager.Policy);
+    field public static final java.lang.String ACTION_NOTIFICATION_POLICY_CHANGED = "android.app.action.NOTIFICATION_POLICY_CHANGED";
+  }
+
+  public static class NotificationManager.Policy implements android.os.Parcelable {
+    ctor public NotificationManager.Policy(int, int);
+    method public int describeContents();
+    method public static java.lang.String priorityCategoriesToString(int);
+    method public static java.lang.String prioritySendersToString(int);
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.app.NotificationManager.Policy> CREATOR;
+    field public static final int PRIORITY_CATEGORY_CALLS = 8; // 0x8
+    field public static final int PRIORITY_CATEGORY_EVENTS = 2; // 0x2
+    field public static final int PRIORITY_CATEGORY_MESSAGES = 4; // 0x4
+    field public static final int PRIORITY_CATEGORY_REMINDERS = 1; // 0x1
+    field public static final int PRIORITY_CATEGORY_REPEAT_CALLERS = 16; // 0x10
+    field public static final int PRIORITY_SENDERS_ANY = 0; // 0x0
+    field public static final int PRIORITY_SENDERS_CONTACTS = 1; // 0x1
+    field public static final int PRIORITY_SENDERS_STARRED = 2; // 0x2
+    field public final int priorityCategories;
+    field public final int prioritySenders;
+  }
+
+  public static class NotificationManager.Policy.Token implements android.os.Parcelable {
+    method public int describeContents();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.app.NotificationManager.Policy.Token> CREATOR;
+  }
+
+  public static abstract class NotificationManager.Policy.Token.RequestCallback {
+    ctor public NotificationManager.Policy.Token.RequestCallback();
+    method public abstract void onTokenDenied();
+    method public abstract void onTokenGranted(android.app.NotificationManager.Policy.Token);
   }
 
   public final class PendingIntent implements android.os.Parcelable {
@@ -5740,6 +5785,7 @@
     method public void setRestrictionsProvider(android.content.ComponentName, android.content.ComponentName);
     method public void setScreenCaptureDisabled(android.content.ComponentName, boolean);
     method public void setSecureSetting(android.content.ComponentName, java.lang.String, java.lang.String);
+    method public void setStatusBarEnabledState(android.content.ComponentName, boolean);
     method public int setStorageEncryption(android.content.ComponentName, boolean);
     method public void setTrustAgentConfiguration(android.content.ComponentName, android.content.ComponentName, android.os.PersistableBundle);
     method public void setUninstallBlocked(android.content.ComponentName, java.lang.String, boolean);
@@ -6911,6 +6957,7 @@
     field public static final int GATT_SERVER = 8; // 0x8
     field public static final int HEADSET = 1; // 0x1
     field public static final int HEALTH = 3; // 0x3
+    field public static final int SAP = 10; // 0xa
     field public static final int STATE_CONNECTED = 2; // 0x2
     field public static final int STATE_CONNECTING = 1; // 0x1
     field public static final int STATE_DISCONNECTED = 0; // 0x0
@@ -6922,6 +6969,25 @@
     method public abstract void onServiceDisconnected(int);
   }
 
+  public final class BluetoothSap implements android.bluetooth.BluetoothProfile {
+    method public synchronized void close();
+    method public boolean connect(android.bluetooth.BluetoothDevice);
+    method public boolean disconnect(android.bluetooth.BluetoothDevice);
+    method public android.bluetooth.BluetoothDevice getClient();
+    method public java.util.List<android.bluetooth.BluetoothDevice> getConnectedDevices();
+    method public int getConnectionState(android.bluetooth.BluetoothDevice);
+    method public java.util.List<android.bluetooth.BluetoothDevice> getDevicesMatchingConnectionStates(int[]);
+    method public int getPriority(android.bluetooth.BluetoothDevice);
+    method public int getState();
+    method public boolean isConnected(android.bluetooth.BluetoothDevice);
+    method public boolean setPriority(android.bluetooth.BluetoothDevice, int);
+    field public static final java.lang.String ACTION_CONNECTION_STATE_CHANGED = "android.bluetooth.sap.profile.action.CONNECTION_STATE_CHANGED";
+    field public static final int RESULT_CANCELED = 2; // 0x2
+    field public static final int RESULT_FAILURE = 0; // 0x0
+    field public static final int RESULT_SUCCESS = 1; // 0x1
+    field public static final int STATE_ERROR = -1; // 0xffffffff
+  }
+
   public final class BluetoothServerSocket implements java.io.Closeable {
     method public android.bluetooth.BluetoothSocket accept() throws java.io.IOException;
     method public android.bluetooth.BluetoothSocket accept(int) throws java.io.IOException;
@@ -6931,10 +6997,16 @@
   public final class BluetoothSocket implements java.io.Closeable {
     method public void close() throws java.io.IOException;
     method public void connect() throws java.io.IOException;
+    method public int getConnectionType();
     method public java.io.InputStream getInputStream() throws java.io.IOException;
+    method public int getMaxReceivePacketSize();
+    method public int getMaxTransmitPacketSize();
     method public java.io.OutputStream getOutputStream() throws java.io.IOException;
     method public android.bluetooth.BluetoothDevice getRemoteDevice();
     method public boolean isConnected();
+    field public static final int TYPE_L2CAP = 3; // 0x3
+    field public static final int TYPE_RFCOMM = 1; // 0x1
+    field public static final int TYPE_SCO = 2; // 0x2
   }
 
 }
@@ -7660,6 +7732,7 @@
     field public static final java.lang.String ALARM_SERVICE = "alarm";
     field public static final java.lang.String APPWIDGET_SERVICE = "appwidget";
     field public static final java.lang.String APP_OPS_SERVICE = "appops";
+    field public static final java.lang.String AUDIO_DEVICES_SERVICE = "audio_devices_manager";
     field public static final java.lang.String AUDIO_SERVICE = "audio";
     field public static final java.lang.String BATTERY_SERVICE = "batterymanager";
     field public static final int BIND_ABOVE_CLIENT = 8; // 0x8
@@ -7673,6 +7746,7 @@
     field public static final java.lang.String BLUETOOTH_SERVICE = "bluetooth";
     field public static final java.lang.String CAMERA_SERVICE = "camera";
     field public static final java.lang.String CAPTIONING_SERVICE = "captioning";
+    field public static final java.lang.String CARRIER_CONFIG_SERVICE = "carrier_config";
     field public static final java.lang.String CLIPBOARD_SERVICE = "clipboard";
     field public static final java.lang.String CONNECTIVITY_SERVICE = "connectivity";
     field public static final java.lang.String CONSUMER_IR_SERVICE = "consumer_ir";
@@ -12607,6 +12681,7 @@
     method public final void unlock();
     field public static final java.lang.String ACTION_NEW_PICTURE = "android.hardware.action.NEW_PICTURE";
     field public static final java.lang.String ACTION_NEW_VIDEO = "android.hardware.action.NEW_VIDEO";
+    field public static final int CAMERA_ERROR_EVICTED = 2; // 0x2
     field public static final int CAMERA_ERROR_SERVER_DIED = 100; // 0x64
     field public static final int CAMERA_ERROR_UNKNOWN = 1; // 0x1
   }
@@ -13055,6 +13130,7 @@
     method public abstract android.hardware.camera2.CameraDevice getDevice();
     method public abstract android.view.Surface getInputSurface();
     method public abstract boolean isReprocessible();
+    method public abstract void prepare(android.view.Surface) throws android.hardware.camera2.CameraAccessException;
     method public abstract int setRepeatingBurst(java.util.List<android.hardware.camera2.CaptureRequest>, android.hardware.camera2.CameraCaptureSession.CaptureCallback, android.os.Handler) throws android.hardware.camera2.CameraAccessException;
     method public abstract int setRepeatingRequest(android.hardware.camera2.CaptureRequest, android.hardware.camera2.CameraCaptureSession.CaptureCallback, android.os.Handler) throws android.hardware.camera2.CameraAccessException;
     method public abstract void stopRepeating() throws android.hardware.camera2.CameraAccessException;
@@ -13077,6 +13153,7 @@
     method public abstract void onConfigureFailed(android.hardware.camera2.CameraCaptureSession);
     method public abstract void onConfigured(android.hardware.camera2.CameraCaptureSession);
     method public void onReady(android.hardware.camera2.CameraCaptureSession);
+    method public void onSurfacePrepared(android.hardware.camera2.CameraCaptureSession, android.view.Surface);
   }
 
   public final class CameraCharacteristics extends android.hardware.camera2.CameraMetadata {
@@ -13100,6 +13177,7 @@
     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;
+    field public static final android.hardware.camera2.CameraCharacteristics.Key<java.lang.Boolean> DEPTH_DEPTH_IS_EXCLUSIVE;
     field public static final android.hardware.camera2.CameraCharacteristics.Key<int[]> EDGE_AVAILABLE_EDGE_MODES;
     field public static final android.hardware.camera2.CameraCharacteristics.Key<java.lang.Boolean> FLASH_INFO_AVAILABLE;
     field public static final android.hardware.camera2.CameraCharacteristics.Key<int[]> HOT_PIXEL_AVAILABLE_HOT_PIXEL_MODES;
@@ -13328,6 +13406,7 @@
     field public static final int HOT_PIXEL_MODE_HIGH_QUALITY = 2; // 0x2
     field public static final int HOT_PIXEL_MODE_OFF = 0; // 0x0
     field public static final int INFO_SUPPORTED_HARDWARE_LEVEL_FULL = 1; // 0x1
+    field public static final int INFO_SUPPORTED_HARDWARE_LEVEL_HIGH_RESOLUTION = 3; // 0x3
     field public static final int INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY = 2; // 0x2
     field public static final int INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED = 0; // 0x0
     field public static final int LENS_FACING_BACK = 1; // 0x1
@@ -13346,6 +13425,7 @@
     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_DEPTH_OUTPUT = 8; // 0x8
     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
@@ -13765,6 +13845,8 @@
 
   public class FingerprintManager {
     method public void authenticate(android.hardware.fingerprint.FingerprintManager.CryptoObject, android.os.CancellationSignal, android.hardware.fingerprint.FingerprintManager.AuthenticationCallback, int);
+    method public boolean hasEnrolledFingerprints();
+    method public boolean isHardwareDetected();
     field public static final int FINGERPRINT_ACQUIRED_GOOD = 0; // 0x0
     field public static final int FINGERPRINT_ACQUIRED_IMAGER_DIRTY = 3; // 0x3
     field public static final int FINGERPRINT_ACQUIRED_INSUFFICIENT = 2; // 0x2
@@ -13795,6 +13877,8 @@
   }
 
   public static class FingerprintManager.CryptoObject {
+    ctor public FingerprintManager.CryptoObject(java.security.Signature);
+    ctor public FingerprintManager.CryptoObject(javax.crypto.Cipher);
     method public javax.crypto.Cipher getCipher();
     method public java.security.Signature getSignature();
   }
@@ -14563,6 +14647,47 @@
     method public android.media.AudioAttributes.Builder setUsage(int);
   }
 
+  public class AudioDeviceInfo {
+    method public java.lang.String getAddress();
+    method public int[] getChannelCounts();
+    method public int[] getChannelMasks();
+    method public int[] getFormats();
+    method public java.lang.String getName();
+    method public int[] getSampleRates();
+    method public int getType();
+    method public boolean isSink();
+    method public boolean isSource();
+    field public static final int TYPE_AUX_LINE = 19; // 0x13
+    field public static final int TYPE_BLUETOOTH_A2DP = 8; // 0x8
+    field public static final int TYPE_BLUETOOTH_SCO = 7; // 0x7
+    field public static final int TYPE_BUILTIN_EARPIECE = 1; // 0x1
+    field public static final int TYPE_BUILTIN_MIC = 15; // 0xf
+    field public static final int TYPE_BUILTIN_SPEAKER = 2; // 0x2
+    field public static final int TYPE_DOCK = 13; // 0xd
+    field public static final int TYPE_FM = 14; // 0xe
+    field public static final int TYPE_FM_TUNER = 16; // 0x10
+    field public static final int TYPE_HDMI = 9; // 0x9
+    field public static final int TYPE_HDMI_ARC = 10; // 0xa
+    field public static final int TYPE_LINE_ANALOG = 5; // 0x5
+    field public static final int TYPE_LINE_DIGITAL = 6; // 0x6
+    field public static final int TYPE_TELEPHONY = 18; // 0x12
+    field public static final int TYPE_TV_TUNER = 17; // 0x11
+    field public static final int TYPE_UNKNOWN = 0; // 0x0
+    field public static final int TYPE_USB_ACCESSORY = 12; // 0xc
+    field public static final int TYPE_USB_DEVICE = 11; // 0xb
+    field public static final int TYPE_WIRED_HEADPHONES = 4; // 0x4
+    field public static final int TYPE_WIRED_HEADSET = 3; // 0x3
+  }
+
+  public class AudioDevicesManager {
+    method public void addOnAudioDeviceConnectionListener(android.media.OnAudioDeviceConnectionListener, android.os.Handler);
+    method public android.media.AudioDeviceInfo[] listDevices(int);
+    method public void removeOnAudioDeviceConnectionListener(android.media.OnAudioDeviceConnectionListener);
+    field public static final int LIST_DEVICES_ALL = 3; // 0x3
+    field public static final int LIST_DEVICES_INPUTS = 1; // 0x1
+    field public static final int LIST_DEVICES_OUTPUTS = 2; // 0x2
+  }
+
   public class AudioFormat {
     method public int getChannelCount();
     method public int getChannelIndexMask();
@@ -14788,9 +14913,12 @@
     method public int getSampleRate();
     method public int getState();
     method public int read(byte[], int, int);
+    method public int read(byte[], int, int, int);
     method public int read(short[], int, int);
+    method public int read(short[], int, int, int);
     method public int read(float[], int, int, int);
     method public int read(java.nio.ByteBuffer, int);
+    method public int read(java.nio.ByteBuffer, int, int);
     method public void release();
     method public int setNotificationMarkerPosition(int);
     method public int setPositionNotificationPeriod(int);
@@ -14850,6 +14978,7 @@
     method public int getPlaybackHeadPosition();
     method public int getPlaybackRate();
     method public int getPositionNotificationPeriod();
+    method public android.media.AudioDeviceInfo getPreferredOutputDevice();
     method public int getSampleRate();
     method public int getState();
     method public int getStreamType();
@@ -14866,12 +14995,15 @@
     method public void setPlaybackPositionUpdateListener(android.media.AudioTrack.OnPlaybackPositionUpdateListener, android.os.Handler);
     method public int setPlaybackRate(int);
     method public int setPositionNotificationPeriod(int);
+    method public boolean setPreferredOutputDevice(android.media.AudioDeviceInfo);
     method protected deprecated void setState(int);
     method public deprecated int setStereoVolume(float, float);
     method public int setVolume(float);
     method public void stop() throws java.lang.IllegalStateException;
     method public int write(byte[], int, int);
+    method public int write(byte[], int, int, int);
     method public int write(short[], int, int);
+    method public int write(short[], int, int, int);
     method public int write(float[], int, int, int);
     method public int write(java.nio.ByteBuffer, int, int);
     field public static final int ERROR = -1; // 0xffffffff
@@ -15449,6 +15581,11 @@
     ctor public MediaCryptoException(java.lang.String);
   }
 
+  public abstract interface MediaDataSource implements java.io.Closeable {
+    method public abstract long getSize();
+    method public abstract int readAt(long, byte[], int);
+  }
+
   public class MediaDescription implements android.os.Parcelable {
     method public int describeContents();
     method public java.lang.CharSequence getDescription();
@@ -15584,6 +15721,7 @@
     method public final void release();
     method public void seekTo(long, int);
     method public void selectTrack(int);
+    method public final void setDataSource(android.media.MediaDataSource) throws java.io.IOException, java.lang.IllegalArgumentException;
     method public final void setDataSource(android.content.Context, android.net.Uri, java.util.Map<java.lang.String, java.lang.String>) throws java.io.IOException;
     method public final void setDataSource(java.lang.String, java.util.Map<java.lang.String, java.lang.String>) throws java.io.IOException;
     method public final void setDataSource(java.lang.String) throws java.io.IOException;
@@ -15763,6 +15901,7 @@
     method public void setDataSource(java.io.FileDescriptor, long, long) throws java.lang.IllegalArgumentException;
     method public void setDataSource(java.io.FileDescriptor) throws java.lang.IllegalArgumentException;
     method public void setDataSource(android.content.Context, android.net.Uri) throws java.lang.IllegalArgumentException, java.lang.SecurityException;
+    method public void setDataSource(android.media.MediaDataSource) throws java.lang.IllegalArgumentException;
     field public static final int METADATA_KEY_ALBUM = 1; // 0x1
     field public static final int METADATA_KEY_ALBUMARTIST = 13; // 0xd
     field public static final int METADATA_KEY_ARTIST = 2; // 0x2
@@ -15847,6 +15986,7 @@
     method public void setDataSource(java.lang.String) throws java.io.IOException, java.lang.IllegalArgumentException, java.lang.IllegalStateException, java.lang.SecurityException;
     method public void setDataSource(java.io.FileDescriptor) throws java.io.IOException, java.lang.IllegalArgumentException, java.lang.IllegalStateException;
     method public void setDataSource(java.io.FileDescriptor, long, long) throws java.io.IOException, java.lang.IllegalArgumentException, java.lang.IllegalStateException;
+    method public void setDataSource(android.media.MediaDataSource) throws java.lang.IllegalArgumentException, java.lang.IllegalStateException;
     method public void setDisplay(android.view.SurfaceHolder);
     method public void setLooping(boolean);
     method public void setNextMediaPlayer(android.media.MediaPlayer);
@@ -16169,7 +16309,7 @@
 
   public final class MediaSync {
     ctor public MediaSync();
-    method public void configureAudioTrack(android.media.AudioTrack, int);
+    method public void configureAudioTrack(android.media.AudioTrack);
     method public void configureSurface(android.view.Surface);
     method public final android.view.Surface createInputSurface();
     method public boolean getTimestamp(android.media.MediaTimestamp);
@@ -16205,6 +16345,10 @@
     ctor public NotProvisionedException(java.lang.String);
   }
 
+  public abstract interface OnAudioDeviceConnectionListener {
+    method public abstract void onAudioDeviceConnection();
+  }
+
   public final class Rating implements android.os.Parcelable {
     method public int describeContents();
     method public float getPercentRating();
@@ -18015,6 +18159,7 @@
   public class ConnectivityManager {
     method public void addDefaultNetworkActiveListener(android.net.ConnectivityManager.OnNetworkActiveListener);
     method public boolean bindProcessToNetwork(android.net.Network);
+    method public android.net.Network getActiveNetwork();
     method public android.net.NetworkInfo getActiveNetworkInfo();
     method public android.net.NetworkInfo[] getAllNetworkInfo();
     method public android.net.Network[] getAllNetworks();
@@ -18033,7 +18178,8 @@
     method public void registerNetworkCallback(android.net.NetworkRequest, android.net.ConnectivityManager.NetworkCallback);
     method public void releaseNetworkRequest(android.app.PendingIntent);
     method public void removeDefaultNetworkActiveListener(android.net.ConnectivityManager.OnNetworkActiveListener);
-    method public void reportBadNetwork(android.net.Network);
+    method public deprecated void reportBadNetwork(android.net.Network);
+    method public void reportNetworkConnectivity(android.net.Network, boolean);
     method public void requestNetwork(android.net.NetworkRequest, android.net.ConnectivityManager.NetworkCallback);
     method public void requestNetwork(android.net.NetworkRequest, android.app.PendingIntent);
     method public deprecated boolean requestRouteToHost(int, int);
@@ -19549,6 +19695,7 @@
 
   public final class NfcEvent {
     field public final android.nfc.NfcAdapter nfcAdapter;
+    field public final byte peerLlcpVersion;
   }
 
   public final class NfcManager {
@@ -22499,7 +22646,6 @@
   public class Binder implements android.os.IBinder {
     ctor public Binder();
     method public void attachInterface(android.os.IInterface, java.lang.String);
-    method public static final void blockUntilThreadAvailable();
     method public static final long clearCallingIdentity();
     method public void dump(java.io.FileDescriptor, java.lang.String[]);
     method protected void dump(java.io.FileDescriptor, java.io.PrintWriter, java.lang.String[]);
@@ -22713,6 +22859,8 @@
     method public static long getNativeHeapFreeSize();
     method public static long getNativeHeapSize();
     method public static long getPss();
+    method public static java.lang.String getRuntimeStat(java.lang.String);
+    method public static java.util.Map<java.lang.String, java.lang.String> getRuntimeStats();
     method public static deprecated int getThreadAllocCount();
     method public static deprecated int getThreadAllocSize();
     method public static deprecated int getThreadExternalAllocCount();
@@ -26463,6 +26611,7 @@
     field public static final java.lang.String DEBUG_APP = "debug_app";
     field public static final java.lang.String DEVELOPMENT_SETTINGS_ENABLED = "development_settings_enabled";
     field public static final java.lang.String DEVICE_PROVISIONED = "device_provisioned";
+    field public static final java.lang.String HIDE_CARRIER_NETWORK_SETTINGS = "hide_carrier_network_settings";
     field public static final java.lang.String HTTP_PROXY = "http_proxy";
     field public static final deprecated java.lang.String INSTALL_NON_MARKET_APPS = "install_non_market_apps";
     field public static final java.lang.String MODE_RINGER = "mode_ringer";
@@ -26628,6 +26777,7 @@
     field public static final android.net.Uri DEFAULT_RINGTONE_URI;
     field public static final deprecated java.lang.String DEVICE_PROVISIONED = "device_provisioned";
     field public static final deprecated java.lang.String DIM_SCREEN = "dim_screen";
+    field public static final java.lang.String DTMF_TONE_TYPE_WHEN_DIALING = "dtmf_tone_type";
     field public static final java.lang.String DTMF_TONE_WHEN_DIALING = "dtmf_tone";
     field public static final java.lang.String END_BUTTON_BEHAVIOR = "end_button_behavior";
     field public static final java.lang.String FONT_SCALE = "font_scale";
@@ -26676,6 +26826,7 @@
     field public static final java.lang.String USER_ROTATION = "user_rotation";
     field public static final deprecated java.lang.String USE_GOOGLE_MAIL = "use_google_mail";
     field public static final java.lang.String VIBRATE_ON = "vibrate_on";
+    field public static final java.lang.String VIBRATE_WHEN_RINGING = "vibrate_when_ringing";
     field public static final deprecated java.lang.String WAIT_FOR_DEBUGGER = "wait_for_debugger";
     field public static final deprecated java.lang.String WALLPAPER_ACTIVITY = "wallpaper_activity";
     field public static final deprecated java.lang.String WIFI_MAX_DHCP_RETRY_COUNT = "wifi_max_dhcp_retry_count";
@@ -27207,7 +27358,6 @@
     method public static android.renderscript.AllocationAdapter create1D(android.renderscript.RenderScript, android.renderscript.Allocation);
     method public static android.renderscript.AllocationAdapter create2D(android.renderscript.RenderScript, android.renderscript.Allocation);
     method public static android.renderscript.AllocationAdapter createTyped(android.renderscript.RenderScript, android.renderscript.Allocation, android.renderscript.Type);
-    method public void setArray(int, int);
     method public void setFace(android.renderscript.Type.CubemapFace);
     method public void setLOD(int);
     method public void setX(int);
@@ -27868,6 +28018,62 @@
     method public void setLUT(android.renderscript.Allocation);
   }
 
+  public final class ScriptIntrinsicBLAS extends android.renderscript.ScriptIntrinsic {
+    method public void BNNM(android.renderscript.Allocation, int, android.renderscript.Allocation, int, android.renderscript.Allocation, int, int);
+    method public void CGEMM(int, int, android.renderscript.Float2, android.renderscript.Allocation, android.renderscript.Allocation, android.renderscript.Float2, android.renderscript.Allocation);
+    method public void CHEMM(int, int, float, android.renderscript.Allocation, android.renderscript.Allocation, float, android.renderscript.Allocation);
+    method public void CHER2K(int, int, android.renderscript.Float2, android.renderscript.Allocation, android.renderscript.Allocation, float, android.renderscript.Allocation);
+    method public void CHERK(int, int, float, android.renderscript.Allocation, float, android.renderscript.Allocation);
+    method public void CSYMM(int, int, android.renderscript.Float2, android.renderscript.Allocation, android.renderscript.Allocation, android.renderscript.Float2, android.renderscript.Allocation);
+    method public void CSYR2K(int, int, android.renderscript.Float2, android.renderscript.Allocation, android.renderscript.Allocation, android.renderscript.Float2, android.renderscript.Allocation);
+    method public void CSYRK(int, int, float, float, android.renderscript.Allocation, float, float, android.renderscript.Allocation);
+    method public void CTRMM(int, int, int, int, android.renderscript.Float2, android.renderscript.Allocation, android.renderscript.Allocation);
+    method public void CTRSM(int, int, int, int, android.renderscript.Float2, android.renderscript.Allocation, android.renderscript.Allocation);
+    method public void DGEMM(int, int, double, android.renderscript.Allocation, android.renderscript.Allocation, double, android.renderscript.Allocation);
+    method public void DSYMM(int, int, double, android.renderscript.Allocation, android.renderscript.Allocation, double, android.renderscript.Allocation);
+    method public void DSYR2K(int, int, double, android.renderscript.Allocation, android.renderscript.Allocation, double, android.renderscript.Allocation);
+    method public void DSYRK(int, int, double, android.renderscript.Allocation, double, android.renderscript.Allocation);
+    method public void DTRMM(int, int, int, int, double, android.renderscript.Allocation, android.renderscript.Allocation);
+    method public void DTRSM(int, int, int, int, double, android.renderscript.Allocation, android.renderscript.Allocation);
+    method public void SGEMM(int, int, float, android.renderscript.Allocation, android.renderscript.Allocation, float, android.renderscript.Allocation);
+    method public void SSYMM(int, int, float, android.renderscript.Allocation, android.renderscript.Allocation, float, android.renderscript.Allocation);
+    method public void SSYR2K(int, int, float, android.renderscript.Allocation, android.renderscript.Allocation, float, android.renderscript.Allocation);
+    method public void SSYRK(int, int, float, android.renderscript.Allocation, float, android.renderscript.Allocation);
+    method public void STRMM(int, int, int, int, float, android.renderscript.Allocation, android.renderscript.Allocation);
+    method public void STRSM(int, int, int, int, float, android.renderscript.Allocation, android.renderscript.Allocation);
+    method public void ZGEMM(int, int, android.renderscript.Double2, android.renderscript.Allocation, android.renderscript.Allocation, android.renderscript.Double2, android.renderscript.Allocation);
+    method public void ZHEMM(int, int, double, android.renderscript.Allocation, android.renderscript.Allocation, double, android.renderscript.Allocation);
+    method public void ZHER2K(int, int, android.renderscript.Double2, android.renderscript.Allocation, android.renderscript.Allocation, double, android.renderscript.Allocation);
+    method public void ZHERK(int, int, double, android.renderscript.Allocation, double, android.renderscript.Allocation);
+    method public void ZSYMM(int, int, android.renderscript.Double2, android.renderscript.Allocation, android.renderscript.Allocation, android.renderscript.Double2, android.renderscript.Allocation);
+    method public void ZSYR2K(int, int, android.renderscript.Double2, android.renderscript.Allocation, android.renderscript.Allocation, android.renderscript.Double2, android.renderscript.Allocation);
+    method public void ZSYRK(int, int, double, double, android.renderscript.Allocation, double, double, android.renderscript.Allocation);
+    method public void ZTRMM(int, int, int, int, android.renderscript.Double2, android.renderscript.Allocation, android.renderscript.Allocation);
+    method public void ZTRSM(int, int, int, int, android.renderscript.Double2, android.renderscript.Allocation, android.renderscript.Allocation);
+    method public static android.renderscript.ScriptIntrinsicBLAS create(android.renderscript.RenderScript);
+    field public static final int CONJ_TRANSPOSE = 113; // 0x71
+    field public static final int LEFT = 141; // 0x8d
+    field public static final int LOWER = 122; // 0x7a
+    field public static final int NON_UNIT = 131; // 0x83
+    field public static final int NO_TRANSPOSE = 111; // 0x6f
+    field public static final int RIGHT = 142; // 0x8e
+    field public static final int TRANSPOSE = 112; // 0x70
+    field public static final int UNIT = 132; // 0x84
+    field public static final int UPPER = 121; // 0x79
+  }
+
+  public static abstract class ScriptIntrinsicBLAS.Diag implements java.lang.annotation.Annotation {
+  }
+
+  public static abstract class ScriptIntrinsicBLAS.Side implements java.lang.annotation.Annotation {
+  }
+
+  public static abstract class ScriptIntrinsicBLAS.Transpose implements java.lang.annotation.Annotation {
+  }
+
+  public static abstract class ScriptIntrinsicBLAS.Uplo implements java.lang.annotation.Annotation {
+  }
+
   public class ScriptIntrinsicBlend extends android.renderscript.ScriptIntrinsic {
     method public static android.renderscript.ScriptIntrinsicBlend create(android.renderscript.RenderScript, android.renderscript.Element);
     method public void forEachAdd(android.renderscript.Allocation, android.renderscript.Allocation);
@@ -28030,8 +28236,6 @@
     method public static android.renderscript.Type createX(android.renderscript.RenderScript, android.renderscript.Element, int);
     method public static android.renderscript.Type createXY(android.renderscript.RenderScript, android.renderscript.Element, int, int);
     method public static android.renderscript.Type createXYZ(android.renderscript.RenderScript, android.renderscript.Element, int, int, int);
-    method public int getArray(int);
-    method public int getArrayCount();
     method public int getCount();
     method public android.renderscript.Element getElement();
     method public int getX();
@@ -28045,7 +28249,6 @@
   public static class Type.Builder {
     ctor public Type.Builder(android.renderscript.RenderScript, android.renderscript.Element);
     method public android.renderscript.Type create();
-    method public android.renderscript.Type.Builder setArray(int, int);
     method public android.renderscript.Type.Builder setFaces(boolean);
     method public android.renderscript.Type.Builder setMipmaps(boolean);
     method public android.renderscript.Type.Builder setX(int);
@@ -28281,6 +28484,7 @@
   public static abstract class KeyStoreKeyProperties.Origin {
     field public static final int GENERATED = 1; // 0x1
     field public static final int IMPORTED = 2; // 0x2
+    field public static final int UNKNOWN = 4; // 0x4
   }
 
   public static abstract class KeyStoreKeyProperties.OriginEnum implements java.lang.annotation.Annotation {
@@ -28379,6 +28583,26 @@
 
 package android.service.carrier {
 
+  public abstract class CarrierConfigService extends android.app.Service {
+    ctor public CarrierConfigService();
+    method public final android.os.IBinder onBind(android.content.Intent);
+    method public abstract android.os.Bundle onLoadConfig(android.service.carrier.CarrierIdentifier);
+    field public static final java.lang.String SERVICE_INTERFACE = "android.service.carrier.CarrierConfigService";
+  }
+
+  public class CarrierIdentifier implements android.os.Parcelable {
+    ctor public CarrierIdentifier(java.lang.String, java.lang.String, java.lang.String, java.lang.String, java.lang.String, java.lang.String);
+    method public int describeContents();
+    method public java.lang.String getGid1();
+    method public java.lang.String getGid2();
+    method public java.lang.String getImsi();
+    method public java.lang.String getMcc();
+    method public java.lang.String getMnc();
+    method public java.lang.String getSpn();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.service.carrier.CarrierIdentifier> CREATOR;
+  }
+
   public abstract class CarrierMessagingService extends android.app.Service {
     ctor public CarrierMessagingService();
     method public android.os.IBinder onBind(android.content.Intent);
@@ -28550,6 +28774,7 @@
     method public final int getCurrentInterruptionFilter();
     method public final int getCurrentListenerHints();
     method public android.service.notification.NotificationListenerService.RankingMap getCurrentRanking();
+    method public final android.app.NotificationManager.Policy.Token getNotificationPolicyToken();
     method public android.os.IBinder onBind(android.content.Intent);
     method public void onInterruptionFilterChanged(int);
     method public void onListenerConnected();
@@ -29733,6 +29958,382 @@
 
 package android.telecom {
 
+  public final class AudioState implements android.os.Parcelable {
+    ctor public AudioState(boolean, int, int);
+    ctor public AudioState(android.telecom.AudioState);
+    method public static java.lang.String audioRouteToString(int);
+    method public int describeContents();
+    method public int getRoute();
+    method public int getSupportedRouteMask();
+    method public boolean isMuted();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.telecom.AudioState> CREATOR;
+    field public static final int ROUTE_BLUETOOTH = 2; // 0x2
+    field public static final int ROUTE_EARPIECE = 1; // 0x1
+    field public static final int ROUTE_SPEAKER = 8; // 0x8
+    field public static final int ROUTE_WIRED_HEADSET = 4; // 0x4
+    field public static final int ROUTE_WIRED_OR_EARPIECE = 5; // 0x5
+  }
+
+  public final class Call {
+    method public void answer(int);
+    method public void conference(android.telecom.Call);
+    method public void disconnect();
+    method public java.util.List<java.lang.String> getCannedTextResponses();
+    method public java.util.List<android.telecom.Call> getChildren();
+    method public java.util.List<android.telecom.Call> getConferenceableCalls();
+    method public android.telecom.Call.Details getDetails();
+    method public android.telecom.Call getParent();
+    method public java.lang.String getRemainingPostDialSequence();
+    method public int getState();
+    method public android.telecom.InCallService.VideoCall getVideoCall();
+    method public void hold();
+    method public void mergeConference();
+    method public void phoneAccountSelected(android.telecom.PhoneAccountHandle, boolean);
+    method public void playDtmfTone(char);
+    method public void postDialContinue(boolean);
+    method public void registerCallback(android.telecom.Call.Callback);
+    method public void reject(boolean, java.lang.String);
+    method public void splitFromConference();
+    method public void stopDtmfTone();
+    method public void swapConference();
+    method public void unhold();
+    method public void unregisterCallback(android.telecom.Call.Callback);
+    field public static final java.lang.String AVAILABLE_PHONE_ACCOUNTS = "selectPhoneAccountAccounts";
+    field public static final int STATE_ACTIVE = 4; // 0x4
+    field public static final int STATE_CONNECTING = 9; // 0x9
+    field public static final int STATE_DIALING = 1; // 0x1
+    field public static final int STATE_DISCONNECTED = 7; // 0x7
+    field public static final int STATE_DISCONNECTING = 10; // 0xa
+    field public static final int STATE_HOLDING = 3; // 0x3
+    field public static final int STATE_NEW = 0; // 0x0
+    field public static final int STATE_PRE_DIAL_WAIT = 8; // 0x8
+    field public static final int STATE_RINGING = 2; // 0x2
+  }
+
+  public static abstract class Call.Callback {
+    ctor public Call.Callback();
+    method public void onCallDestroyed(android.telecom.Call);
+    method public void onCannedTextResponsesLoaded(android.telecom.Call, java.util.List<java.lang.String>);
+    method public void onChildrenChanged(android.telecom.Call, java.util.List<android.telecom.Call>);
+    method public void onConferenceableCallsChanged(android.telecom.Call, java.util.List<android.telecom.Call>);
+    method public void onDetailsChanged(android.telecom.Call, android.telecom.Call.Details);
+    method public void onParentChanged(android.telecom.Call, android.telecom.Call);
+    method public void onPostDialWait(android.telecom.Call, java.lang.String);
+    method public void onStateChanged(android.telecom.Call, int);
+    method public void onVideoCallChanged(android.telecom.Call, android.telecom.InCallService.VideoCall);
+  }
+
+  public static class Call.Details {
+    method public static boolean can(int, int);
+    method public boolean can(int);
+    method public static java.lang.String capabilitiesToString(int);
+    method public android.telecom.PhoneAccountHandle getAccountHandle();
+    method public int getCallCapabilities();
+    method public int getCallProperties();
+    method public java.lang.String getCallerDisplayName();
+    method public int getCallerDisplayNamePresentation();
+    method public final long getConnectTimeMillis();
+    method public android.telecom.DisconnectCause getDisconnectCause();
+    method public android.os.Bundle getExtras();
+    method public android.telecom.GatewayInfo getGatewayInfo();
+    method public android.net.Uri getHandle();
+    method public int getHandlePresentation();
+    method public android.telecom.StatusHints getStatusHints();
+    method public int getVideoState();
+    field public static final int CAPABILITY_DISCONNECT_FROM_CONFERENCE = 8192; // 0x2000
+    field public static final int CAPABILITY_GENERIC_CONFERENCE = 16384; // 0x4000
+    field public static final int CAPABILITY_HIGH_DEF_AUDIO = 32768; // 0x8000
+    field public static final int CAPABILITY_HOLD = 1; // 0x1
+    field public static final int CAPABILITY_MANAGE_CONFERENCE = 128; // 0x80
+    field public static final int CAPABILITY_MERGE_CONFERENCE = 4; // 0x4
+    field public static final int CAPABILITY_MUTE = 64; // 0x40
+    field public static final int CAPABILITY_RESPOND_VIA_TEXT = 32; // 0x20
+    field public static final int CAPABILITY_SEPARATE_FROM_CONFERENCE = 4096; // 0x1000
+    field public static final int CAPABILITY_SHOW_CALLBACK_NUMBER = 131072; // 0x20000
+    field public static final int CAPABILITY_SUPPORTS_VT_LOCAL_BIDIRECTIONAL = 768; // 0x300
+    field public static final int CAPABILITY_SUPPORTS_VT_LOCAL_RX = 256; // 0x100
+    field public static final int CAPABILITY_SUPPORTS_VT_LOCAL_TX = 512; // 0x200
+    field public static final int CAPABILITY_SUPPORTS_VT_REMOTE_BIDIRECTIONAL = 3072; // 0xc00
+    field public static final int CAPABILITY_SUPPORTS_VT_REMOTE_RX = 1024; // 0x400
+    field public static final int CAPABILITY_SUPPORTS_VT_REMOTE_TX = 2048; // 0x800
+    field public static final int CAPABILITY_SUPPORT_HOLD = 2; // 0x2
+    field public static final int CAPABILITY_SWAP_CONFERENCE = 8; // 0x8
+    field public static final int CAPABILITY_WIFI = 65536; // 0x10000
+  }
+
+  public class CallProperties {
+    ctor public CallProperties();
+    field public static final int CONFERENCE = 1; // 0x1
+  }
+
+  public final class CallState {
+    method public static java.lang.String toString(int);
+    field public static final int ABORTED = 8; // 0x8
+    field public static final int ACTIVE = 5; // 0x5
+    field public static final int CONNECTING = 1; // 0x1
+    field public static final int DIALING = 3; // 0x3
+    field public static final int DISCONNECTED = 7; // 0x7
+    field public static final int DISCONNECTING = 9; // 0x9
+    field public static final int NEW = 0; // 0x0
+    field public static final int ON_HOLD = 6; // 0x6
+    field public static final int PRE_DIAL_WAIT = 2; // 0x2
+    field public static final int RINGING = 4; // 0x4
+  }
+
+  public final class CameraCapabilities implements android.os.Parcelable {
+    ctor public CameraCapabilities(int, int);
+    method public int describeContents();
+    method public int getHeight();
+    method public int getWidth();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.telecom.CameraCapabilities> CREATOR;
+  }
+
+  public abstract class Conference implements android.telecom.IConferenceable {
+    ctor public Conference(android.telecom.PhoneAccountHandle);
+    method public final boolean addConnection(android.telecom.Connection);
+    method public final void destroy();
+    method public final android.telecom.AudioState getAudioState();
+    method public final java.util.List<android.telecom.Connection> getConferenceableConnections();
+    method public final long getConnectTimeMillis();
+    method public final int getConnectionCapabilities();
+    method public final java.util.List<android.telecom.Connection> getConnections();
+    method public final android.telecom.DisconnectCause getDisconnectCause();
+    method public final android.telecom.PhoneAccountHandle getPhoneAccountHandle();
+    method public android.telecom.Connection getPrimaryConnection();
+    method public final int getState();
+    method public void onAudioStateChanged(android.telecom.AudioState);
+    method public void onConnectionAdded(android.telecom.Connection);
+    method public void onDisconnect();
+    method public void onHold();
+    method public void onMerge(android.telecom.Connection);
+    method public void onMerge();
+    method public void onPlayDtmfTone(char);
+    method public void onSeparate(android.telecom.Connection);
+    method public void onStopDtmfTone();
+    method public void onSwap();
+    method public void onUnhold();
+    method public final void removeConnection(android.telecom.Connection);
+    method public final void setActive();
+    method public final void setConferenceableConnections(java.util.List<android.telecom.Connection>);
+    method public void setConnectTimeMillis(long);
+    method public final void setConnectionCapabilities(int);
+    method public final void setDisconnected(android.telecom.DisconnectCause);
+    method public final void setOnHold();
+    field public static final long CONNECT_TIME_NOT_SPECIFIED = 0L; // 0x0L
+  }
+
+  public abstract class Connection implements android.telecom.IConferenceable {
+    ctor public Connection();
+    method public static java.lang.String capabilitiesToString(int);
+    method public static android.telecom.Connection createCanceledConnection();
+    method public static android.telecom.Connection createFailedConnection(android.telecom.DisconnectCause);
+    method public final void destroy();
+    method public final android.net.Uri getAddress();
+    method public final int getAddressPresentation();
+    method public final boolean getAudioModeIsVoip();
+    method public final android.telecom.AudioState getAudioState();
+    method public final java.lang.String getCallerDisplayName();
+    method public final int getCallerDisplayNamePresentation();
+    method public final android.telecom.Conference getConference();
+    method public final java.util.List<android.telecom.IConferenceable> getConferenceables();
+    method public final int getConnectionCapabilities();
+    method public final android.telecom.DisconnectCause getDisconnectCause();
+    method public final int getState();
+    method public final android.telecom.StatusHints getStatusHints();
+    method public final android.telecom.Connection.VideoProvider getVideoProvider();
+    method public final boolean isRingbackRequested();
+    method public void onAbort();
+    method public void onAnswer();
+    method public void onAudioStateChanged(android.telecom.AudioState);
+    method public void onDisconnect();
+    method public void onHold();
+    method public void onPlayDtmfTone(char);
+    method public void onPostDialContinue(boolean);
+    method public void onReject();
+    method public void onSeparate();
+    method public void onStateChanged(int);
+    method public void onStopDtmfTone();
+    method public void onUnhold();
+    method public final void setActive();
+    method public final void setAddress(android.net.Uri, int);
+    method public final void setAudioModeIsVoip(boolean);
+    method public final void setCallerDisplayName(java.lang.String, int);
+    method public final void setConferenceableConnections(java.util.List<android.telecom.Connection>);
+    method public final void setConferenceables(java.util.List<android.telecom.IConferenceable>);
+    method public final void setConnectionCapabilities(int);
+    method public final void setConnectionService(android.telecom.ConnectionService);
+    method public final void setDialing();
+    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);
+    method public final void setRinging();
+    method public final void setStatusHints(android.telecom.StatusHints);
+    method public static java.lang.String stateToString(int);
+    field public static final int CAPABILITY_DISCONNECT_FROM_CONFERENCE = 8192; // 0x2000
+    field public static final int CAPABILITY_HOLD = 1; // 0x1
+    field public static final int CAPABILITY_MANAGE_CONFERENCE = 128; // 0x80
+    field public static final int CAPABILITY_MERGE_CONFERENCE = 4; // 0x4
+    field public static final int CAPABILITY_MUTE = 64; // 0x40
+    field public static final int CAPABILITY_RESPOND_VIA_TEXT = 32; // 0x20
+    field public static final int CAPABILITY_SEPARATE_FROM_CONFERENCE = 4096; // 0x1000
+    field public static final int CAPABILITY_SUPPORT_HOLD = 2; // 0x2
+    field public static final int CAPABILITY_SWAP_CONFERENCE = 8; // 0x8
+    field public static final int STATE_ACTIVE = 4; // 0x4
+    field public static final int STATE_DIALING = 3; // 0x3
+    field public static final int STATE_DISCONNECTED = 6; // 0x6
+    field public static final int STATE_HOLDING = 5; // 0x5
+    field public static final int STATE_INITIALIZING = 0; // 0x0
+    field public static final int STATE_NEW = 1; // 0x1
+    field public static final int STATE_RINGING = 2; // 0x2
+  }
+
+  public static abstract class Connection.VideoProvider {
+    ctor public Connection.VideoProvider();
+    method public void changeCallDataUsage(long);
+    method public void changeCameraCapabilities(android.telecom.CameraCapabilities);
+    method public void changePeerDimensions(int, int);
+    method public void changeVideoQuality(int);
+    method public void handleCallSessionEvent(int);
+    method public abstract void onRequestCameraCapabilities();
+    method public abstract void onRequestConnectionDataUsage();
+    method public abstract void onSendSessionModifyRequest(android.telecom.VideoProfile);
+    method public abstract void onSendSessionModifyResponse(android.telecom.VideoProfile);
+    method public abstract void onSetCamera(java.lang.String);
+    method public abstract void onSetDeviceOrientation(int);
+    method public abstract void onSetDisplaySurface(android.view.Surface);
+    method public abstract void onSetPauseImage(java.lang.String);
+    method public abstract void onSetPreviewSurface(android.view.Surface);
+    method public abstract void onSetZoom(float);
+    method public void receiveSessionModifyRequest(android.telecom.VideoProfile);
+    method public void receiveSessionModifyResponse(int, android.telecom.VideoProfile, android.telecom.VideoProfile);
+    field public static final int SESSION_EVENT_CAMERA_FAILURE = 5; // 0x5
+    field public static final int SESSION_EVENT_CAMERA_READY = 6; // 0x6
+    field public static final int SESSION_EVENT_RX_PAUSE = 1; // 0x1
+    field public static final int SESSION_EVENT_RX_RESUME = 2; // 0x2
+    field public static final int SESSION_EVENT_TX_START = 3; // 0x3
+    field public static final int SESSION_EVENT_TX_STOP = 4; // 0x4
+    field public static final int SESSION_MODIFY_REQUEST_FAIL = 2; // 0x2
+    field public static final int SESSION_MODIFY_REQUEST_INVALID = 3; // 0x3
+    field public static final int SESSION_MODIFY_REQUEST_REJECTED_BY_REMOTE = 5; // 0x5
+    field public static final int SESSION_MODIFY_REQUEST_SUCCESS = 1; // 0x1
+    field public static final int SESSION_MODIFY_REQUEST_TIMED_OUT = 4; // 0x4
+  }
+
+  public final class ConnectionRequest implements android.os.Parcelable {
+    ctor public ConnectionRequest(android.telecom.PhoneAccountHandle, android.net.Uri, android.os.Bundle);
+    method public int describeContents();
+    method public android.telecom.PhoneAccountHandle getAccountHandle();
+    method public android.net.Uri getAddress();
+    method public android.os.Bundle getExtras();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.telecom.ConnectionRequest> CREATOR;
+  }
+
+  public abstract class ConnectionService extends android.app.Service {
+    ctor public ConnectionService();
+    method public final void addConference(android.telecom.Conference);
+    method public final void addExistingConnection(android.telecom.PhoneAccountHandle, android.telecom.Connection);
+    method public final void conferenceRemoteConnections(android.telecom.RemoteConnection, android.telecom.RemoteConnection);
+    method public final android.telecom.RemoteConnection createRemoteIncomingConnection(android.telecom.PhoneAccountHandle, android.telecom.ConnectionRequest);
+    method public final android.telecom.RemoteConnection createRemoteOutgoingConnection(android.telecom.PhoneAccountHandle, android.telecom.ConnectionRequest);
+    method public final java.util.Collection<android.telecom.Connection> getAllConnections();
+    method public final android.os.IBinder onBind(android.content.Intent);
+    method public void onConference(android.telecom.Connection, android.telecom.Connection);
+    method public android.telecom.Connection onCreateIncomingConnection(android.telecom.PhoneAccountHandle, android.telecom.ConnectionRequest);
+    method public android.telecom.Connection onCreateOutgoingConnection(android.telecom.PhoneAccountHandle, android.telecom.ConnectionRequest);
+    method public void onRemoteConferenceAdded(android.telecom.RemoteConference);
+    method public void onRemoteExistingConnectionAdded(android.telecom.RemoteConnection);
+    field public static final java.lang.String SERVICE_INTERFACE = "android.telecom.ConnectionService";
+  }
+
+  public final class DisconnectCause implements android.os.Parcelable {
+    ctor public DisconnectCause(int);
+    ctor public DisconnectCause(int, java.lang.String);
+    ctor public DisconnectCause(int, java.lang.CharSequence, java.lang.CharSequence, java.lang.String);
+    ctor public DisconnectCause(int, java.lang.CharSequence, java.lang.CharSequence, java.lang.String, int);
+    method public int describeContents();
+    method public int getCode();
+    method public java.lang.CharSequence getDescription();
+    method public java.lang.CharSequence getLabel();
+    method public java.lang.String getReason();
+    method public int getTone();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final int BUSY = 7; // 0x7
+    field public static final int CANCELED = 4; // 0x4
+    field public static final int CONNECTION_MANAGER_NOT_SUPPORTED = 10; // 0xa
+    field public static final android.os.Parcelable.Creator<android.telecom.DisconnectCause> CREATOR;
+    field public static final int ERROR = 1; // 0x1
+    field public static final int LOCAL = 2; // 0x2
+    field public static final int MISSED = 5; // 0x5
+    field public static final int OTHER = 9; // 0x9
+    field public static final int REJECTED = 6; // 0x6
+    field public static final int REMOTE = 3; // 0x3
+    field public static final int RESTRICTED = 8; // 0x8
+    field public static final int UNKNOWN = 0; // 0x0
+  }
+
+  public class GatewayInfo implements android.os.Parcelable {
+    ctor public GatewayInfo(java.lang.String, android.net.Uri, android.net.Uri);
+    method public int describeContents();
+    method public android.net.Uri getGatewayAddress();
+    method public java.lang.String getGatewayProviderPackageName();
+    method public android.net.Uri getOriginalAddress();
+    method public boolean isEmpty();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.telecom.GatewayInfo> CREATOR;
+  }
+
+  public abstract interface IConferenceable {
+  }
+
+  public abstract class InCallService extends android.app.Service {
+    ctor public InCallService();
+    method public final boolean canAddCall();
+    method public final android.telecom.AudioState getAudioState();
+    method public final java.util.List<android.telecom.Call> getCalls();
+    method public void onAudioStateChanged(android.telecom.AudioState);
+    method public android.os.IBinder onBind(android.content.Intent);
+    method public void onBringToForeground(boolean);
+    method public void onCallAdded(android.telecom.Call);
+    method public void onCallRemoved(android.telecom.Call);
+    method public void onCanAddCallChanged(boolean);
+    method public final void setAudioRoute(int);
+    method public final void setMuted(boolean);
+    field public static final java.lang.String SERVICE_INTERFACE = "android.telecom.InCallService";
+  }
+
+  public static abstract class InCallService.VideoCall {
+    ctor public InCallService.VideoCall();
+    method public abstract void registerCallback(android.telecom.InCallService.VideoCall.Callback);
+    method public abstract void requestCallDataUsage();
+    method public abstract void requestCameraCapabilities();
+    method public abstract void sendSessionModifyRequest(android.telecom.VideoProfile);
+    method public abstract void sendSessionModifyResponse(android.telecom.VideoProfile);
+    method public abstract void setCamera(java.lang.String);
+    method public abstract void setDeviceOrientation(int);
+    method public abstract void setDisplaySurface(android.view.Surface);
+    method public abstract void setPauseImage(java.lang.String);
+    method public abstract void setPreviewSurface(android.view.Surface);
+    method public abstract void setZoom(float);
+  }
+
+  public static abstract class InCallService.VideoCall.Callback {
+    ctor public InCallService.VideoCall.Callback();
+    method public abstract void onCallDataUsageChanged(long);
+    method public abstract void onCallSessionEvent(int);
+    method public abstract void onCameraCapabilitiesChanged(android.telecom.CameraCapabilities);
+    method public abstract void onPeerDimensionsChanged(int, int);
+    method public abstract void onSessionModifyRequestReceived(android.telecom.VideoProfile);
+    method public abstract void onSessionModifyResponseReceived(int, android.telecom.VideoProfile, android.telecom.VideoProfile);
+    method public abstract void onVideoQualityChanged(int);
+  }
+
   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);
@@ -29751,7 +30352,10 @@
     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_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
@@ -29767,6 +30371,7 @@
   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 setAddress(android.net.Uri);
     method public android.telecom.PhoneAccount.Builder setCapabilities(int);
@@ -29783,27 +30388,130 @@
 
   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;
   }
 
+  public final class RemoteConference {
+    method public void disconnect();
+    method public java.util.List<android.telecom.RemoteConnection> getConferenceableConnections();
+    method public final int getConnectionCapabilities();
+    method public final java.util.List<android.telecom.RemoteConnection> getConnections();
+    method public android.telecom.DisconnectCause getDisconnectCause();
+    method public final int getState();
+    method public void hold();
+    method public void merge();
+    method public void playDtmfTone(char);
+    method public final void registerCallback(android.telecom.RemoteConference.Callback);
+    method public void separate(android.telecom.RemoteConnection);
+    method public void setAudioState(android.telecom.AudioState);
+    method public void stopDtmfTone();
+    method public void swap();
+    method public void unhold();
+    method public final void unregisterCallback(android.telecom.RemoteConference.Callback);
+  }
+
+  public static abstract class RemoteConference.Callback {
+    ctor public RemoteConference.Callback();
+    method public void onConferenceableConnectionsChanged(android.telecom.RemoteConference, java.util.List<android.telecom.RemoteConnection>);
+    method public void onConnectionAdded(android.telecom.RemoteConference, android.telecom.RemoteConnection);
+    method public void onConnectionCapabilitiesChanged(android.telecom.RemoteConference, int);
+    method public void onConnectionRemoved(android.telecom.RemoteConference, android.telecom.RemoteConnection);
+    method public void onDestroyed(android.telecom.RemoteConference);
+    method public void onDisconnected(android.telecom.RemoteConference, android.telecom.DisconnectCause);
+    method public void onStateChanged(android.telecom.RemoteConference, int, int);
+  }
+
+  public final class RemoteConnection {
+    method public void abort();
+    method public void answer();
+    method public void disconnect();
+    method public android.net.Uri getAddress();
+    method public int getAddressPresentation();
+    method public java.lang.CharSequence getCallerDisplayName();
+    method public int getCallerDisplayNamePresentation();
+    method public android.telecom.RemoteConference getConference();
+    method public java.util.List<android.telecom.RemoteConnection> getConferenceableConnections();
+    method public int getConnectionCapabilities();
+    method public android.telecom.DisconnectCause getDisconnectCause();
+    method public int getState();
+    method public android.telecom.StatusHints getStatusHints();
+    method public void hold();
+    method public boolean isRingbackRequested();
+    method public boolean isVoipAudioMode();
+    method public void playDtmfTone(char);
+    method public void postDialContinue(boolean);
+    method public void registerCallback(android.telecom.RemoteConnection.Callback);
+    method public void reject();
+    method public void setAudioState(android.telecom.AudioState);
+    method public void stopDtmfTone();
+    method public void unhold();
+    method public void unregisterCallback(android.telecom.RemoteConnection.Callback);
+  }
+
+  public static abstract class RemoteConnection.Callback {
+    ctor public RemoteConnection.Callback();
+    method public void onAddressChanged(android.telecom.RemoteConnection, android.net.Uri, int);
+    method public void onCallerDisplayNameChanged(android.telecom.RemoteConnection, java.lang.String, int);
+    method public void onConferenceChanged(android.telecom.RemoteConnection, android.telecom.RemoteConference);
+    method public void onConferenceableConnectionsChanged(android.telecom.RemoteConnection, java.util.List<android.telecom.RemoteConnection>);
+    method public void onConnectionCapabilitiesChanged(android.telecom.RemoteConnection, int);
+    method public void onDestroyed(android.telecom.RemoteConnection);
+    method public void onDisconnected(android.telecom.RemoteConnection, android.telecom.DisconnectCause);
+    method public void onPostDialChar(android.telecom.RemoteConnection, char);
+    method public void onPostDialWait(android.telecom.RemoteConnection, java.lang.String);
+    method public void onRingbackRequested(android.telecom.RemoteConnection, boolean);
+    method public void onStateChanged(android.telecom.RemoteConnection, int);
+    method public void onStatusHintsChanged(android.telecom.RemoteConnection, android.telecom.StatusHints);
+    method public void onVoipAudioChanged(android.telecom.RemoteConnection, boolean);
+  }
+
+  public final class StatusHints implements android.os.Parcelable {
+    ctor public StatusHints(android.content.ComponentName, java.lang.CharSequence, int, android.os.Bundle);
+    method public int describeContents();
+    method public android.os.Bundle getExtras();
+    method public android.graphics.drawable.Drawable getIcon(android.content.Context);
+    method public int getIconResId();
+    method public java.lang.CharSequence getLabel();
+    method public android.content.ComponentName getPackageName();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.telecom.StatusHints> CREATOR;
+  }
+
   public class TelecomManager {
+    method public void addNewIncomingCall(android.telecom.PhoneAccountHandle, android.os.Bundle);
     method public void cancelMissedCallsNotification();
+    method public android.net.Uri getAdnUriForPhoneAccount(android.telecom.PhoneAccountHandle);
     method public java.util.List<android.telecom.PhoneAccountHandle> getCallCapablePhoneAccounts();
+    method public android.telecom.PhoneAccountHandle getDefaultOutgoingPhoneAccount(java.lang.String);
+    method public java.lang.String getLine1Number(android.telecom.PhoneAccountHandle);
     method public android.telecom.PhoneAccount getPhoneAccount(android.telecom.PhoneAccountHandle);
+    method public android.telecom.PhoneAccountHandle getSimCallManager();
+    method public java.lang.String getVoiceMailNumber(android.telecom.PhoneAccountHandle);
     method public boolean handleMmi(java.lang.String);
+    method public boolean handleMmi(java.lang.String, android.telecom.PhoneAccountHandle);
     method public boolean isInCall();
+    method public boolean isVoiceMailNumber(android.telecom.PhoneAccountHandle, java.lang.String);
+    method public void registerPhoneAccount(android.telecom.PhoneAccount);
     method public void showInCallScreen(boolean);
+    method public void silenceRinger();
+    method public void unregisterPhoneAccount(android.telecom.PhoneAccountHandle);
+    field public static final java.lang.String ACTION_CHANGE_PHONE_ACCOUNTS = "android.telecom.action.CHANGE_PHONE_ACCOUNTS";
+    field public static final java.lang.String ACTION_INCOMING_CALL = "android.telecom.action.INCOMING_CALL";
     field public static final java.lang.String ACTION_SHOW_CALL_ACCESSIBILITY_SETTINGS = "android.telecom.action.SHOW_CALL_ACCESSIBILITY_SETTINGS";
     field public static final java.lang.String ACTION_SHOW_CALL_SETTINGS = "android.telecom.action.SHOW_CALL_SETTINGS";
     field public static final java.lang.String ACTION_SHOW_RESPOND_VIA_SMS_SETTINGS = "android.telecom.action.SHOW_RESPOND_VIA_SMS_SETTINGS";
     field public static final char DTMF_CHARACTER_PAUSE = 44; // 0x002c ','
     field public static final char DTMF_CHARACTER_WAIT = 59; // 0x003b ';'
+    field public static final java.lang.String EXTRA_CALL_BACK_NUMBER = "android.telecom.extra.CALL_BACK_NUMBER";
     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_INCOMING_CALL_EXTRAS = "android.telecom.extra.INCOMING_CALL_EXTRAS";
     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";
@@ -29848,6 +30556,18 @@
 
 package android.telephony {
 
+  public class CarrierConfigManager {
+    method public android.os.Bundle getConfig();
+    method public android.os.Bundle getConfigForSubId(int);
+    method public void reloadCarrierConfigForSubId(int);
+    field public static final java.lang.String ACTION_CARRIER_CONFIG_CHANGED = "android.telephony.action.CARRIER_CONFIG_CHANGED";
+    field public static final java.lang.String BOOL_CARRIER_VOLTE_AVAILABLE = "bool_carrier_volte_available";
+    field public static final java.lang.String BOOL_CARRIER_VOLTE_PROVISIONED = "bool_carrier_volte_provisioned";
+    field public static final java.lang.String BOOL_CARRIER_VOLTE_TTY_SUPPORTED = "bool_carrier_volte_tty_supported";
+    field public static final java.lang.String BOOL_SHOW_APN_SETTING_CDMA = "bool_show_apn_setting_cdma";
+    field public static final java.lang.String INT_VOLTE_REPLACEMENT_RAT = "int_volte_replacement_rat";
+  }
+
   public final class CellIdentityCdma implements android.os.Parcelable {
     method public int describeContents();
     method public int getBasestationId();
@@ -30320,6 +31040,7 @@
     method public int getDataActivity();
     method public int getDataState();
     method public java.lang.String getDeviceId();
+    method public java.lang.String getDeviceId(int);
     method public java.lang.String getDeviceSoftwareVersion();
     method public java.lang.String getGroupIdLevel1();
     method public java.lang.String getLine1Number();
@@ -30330,6 +31051,7 @@
     method public java.lang.String getNetworkOperator();
     method public java.lang.String getNetworkOperatorName();
     method public int getNetworkType();
+    method public int getPhoneCount();
     method public int getPhoneType();
     method public java.lang.String getSimCountryIso();
     method public java.lang.String getSimOperator();
@@ -30353,6 +31075,7 @@
     method public boolean isVoiceCapable();
     method public boolean isWorldPhone();
     method public void listen(android.telephony.PhoneStateListener, int);
+    method public void notifyCarrierNetworkChange(boolean);
     method public java.lang.String sendEnvelopeWithStatus(java.lang.String);
     method public boolean setLine1NumberForDisplay(java.lang.String, java.lang.String);
     method public boolean setOperatorBrandOverride(java.lang.String);
@@ -33850,6 +34573,7 @@
     method public boolean onTouchEvent(android.view.MotionEvent);
     method public void setIsLongpressEnabled(boolean);
     method public void setOnDoubleTapListener(android.view.GestureDetector.OnDoubleTapListener);
+    method public void setOnStylusButtonPressListener(android.view.GestureDetector.OnStylusButtonPressListener);
   }
 
   public static abstract interface GestureDetector.OnDoubleTapListener {
@@ -33867,7 +34591,11 @@
     method public abstract boolean onSingleTapUp(android.view.MotionEvent);
   }
 
-  public static class GestureDetector.SimpleOnGestureListener implements android.view.GestureDetector.OnDoubleTapListener android.view.GestureDetector.OnGestureListener {
+  public static abstract interface GestureDetector.OnStylusButtonPressListener {
+    method public abstract boolean onStylusButtonPress(android.view.MotionEvent);
+  }
+
+  public static class GestureDetector.SimpleOnGestureListener implements android.view.GestureDetector.OnDoubleTapListener android.view.GestureDetector.OnGestureListener android.view.GestureDetector.OnStylusButtonPressListener {
     ctor public GestureDetector.SimpleOnGestureListener();
     method public boolean onDoubleTap(android.view.MotionEvent);
     method public boolean onDoubleTapEvent(android.view.MotionEvent);
@@ -33878,6 +34606,7 @@
     method public void onShowPress(android.view.MotionEvent);
     method public boolean onSingleTapConfirmed(android.view.MotionEvent);
     method public boolean onSingleTapUp(android.view.MotionEvent);
+    method public boolean onStylusButtonPress(android.view.MotionEvent);
   }
 
   public class Gravity {
@@ -36525,6 +37254,7 @@
     field public static final int TITLE_CHANGED = 64; // 0x40
     field public static final int TYPE_ACCESSIBILITY_OVERLAY = 2032; // 0x7f0
     field public static final int TYPE_APPLICATION = 2; // 0x2
+    field public static final int TYPE_APPLICATION_ABOVE_SUB_PANEL = 1005; // 0x3ed
     field public static final int TYPE_APPLICATION_ATTACHED_DIALOG = 1003; // 0x3eb
     field public static final int TYPE_APPLICATION_MEDIA = 1001; // 0x3e9
     field public static final int TYPE_APPLICATION_PANEL = 1000; // 0x3e8
@@ -40637,6 +41367,7 @@
     method public int getInputType();
     method public final android.text.method.KeyListener getKeyListener();
     method public final android.text.Layout getLayout();
+    method public int[] getLeftIndents();
     method public float getLetterSpacing();
     method public int getLineBounds(int, android.graphics.Rect);
     method public int getLineCount();
@@ -40659,6 +41390,7 @@
     method public android.text.TextPaint getPaint();
     method public int getPaintFlags();
     method public java.lang.String getPrivateImeOptions();
+    method public int[] getRightIndents();
     method public int getSelectionEnd();
     method public int getSelectionStart();
     method public int getShadowColor();
@@ -40736,6 +41468,7 @@
     method public void setImeActionLabel(java.lang.CharSequence, int);
     method public void setImeOptions(int);
     method public void setIncludeFontPadding(boolean);
+    method public void setIndents(int[], int[]);
     method public void setInputExtras(int) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
     method public void setInputType(int);
     method public void setKeyListener(android.text.method.KeyListener);
diff --git a/api/system-current.txt b/api/system-current.txt
index 3fd3517..34a9905 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -28,6 +28,7 @@
     field public static final java.lang.String BATTERY_STATS = "android.permission.BATTERY_STATS";
     field public static final java.lang.String BIND_ACCESSIBILITY_SERVICE = "android.permission.BIND_ACCESSIBILITY_SERVICE";
     field public static final java.lang.String BIND_APPWIDGET = "android.permission.BIND_APPWIDGET";
+    field public static final java.lang.String BIND_CARRIER_CONFIG_SERVICE = "android.permission.BIND_CARRIER_CONFIG_SERVICE";
     field public static final java.lang.String BIND_CARRIER_MESSAGING_SERVICE = "android.permission.BIND_CARRIER_MESSAGING_SERVICE";
     field public static final java.lang.String BIND_CHOOSER_TARGET_SERVICE = "android.permission.BIND_CHOOSER_TARGET_SERVICE";
     field public static final java.lang.String BIND_CONDITION_PROVIDER_SERVICE = "android.permission.BIND_CONDITION_PROVIDER_SERVICE";
@@ -866,6 +867,7 @@
     field public static final int layout_x = 16843135; // 0x101017f
     field public static final int layout_y = 16843136; // 0x1010180
     field public static final int left = 16843181; // 0x10101ad
+    field public static final int leftIndents = 16844016; // 0x10104f0
     field public static final int letterSpacing = 16843958; // 0x10104b6
     field public static final int lineSpacingExtra = 16843287; // 0x1010217
     field public static final int lineSpacingMultiplier = 16843288; // 0x1010218
@@ -888,6 +890,7 @@
     field public static final int listSeparatorTextViewStyle = 16843272; // 0x1010208
     field public static final int listViewStyle = 16842868; // 0x1010074
     field public static final int listViewWhiteStyle = 16842869; // 0x1010075
+    field public static final int lockTaskMode = 16844015; // 0x10104ef
     field public static final int logo = 16843454; // 0x10102be
     field public static final int longClickable = 16842982; // 0x10100e6
     field public static final int loopViews = 16843527; // 0x1010307
@@ -1093,6 +1096,7 @@
     field public static final int reversible = 16843851; // 0x101044b
     field public static final int revisionCode = 16843989; // 0x10104d5
     field public static final int right = 16843183; // 0x10101af
+    field public static final int rightIndents = 16844017; // 0x10104f1
     field public static final int ringtonePreferenceStyle = 16842899; // 0x1010093
     field public static final int ringtoneType = 16843257; // 0x10101f9
     field public static final int rotation = 16843558; // 0x1010326
@@ -1172,7 +1176,8 @@
     field public static final int showAsAction = 16843481; // 0x10102d9
     field public static final int showDefault = 16843258; // 0x10101fa
     field public static final int showDividers = 16843561; // 0x1010329
-    field public static final int showOnLockScreen = 16843721; // 0x10103c9
+    field public static final int showForAllUsers = 16844018; // 0x10104f2
+    field public static final deprecated int showOnLockScreen = 16843721; // 0x10103c9
     field public static final int showSilent = 16843259; // 0x10101fb
     field public static final int showText = 16843949; // 0x10104ad
     field public static final deprecated int showWeekNumber = 16843582; // 0x101033e
@@ -2054,6 +2059,7 @@
     field public static final int TextAppearance_Material_Widget_ActionMode_Title = 16974355; // 0x1030213
     field public static final int TextAppearance_Material_Widget_ActionMode_Title_Inverse = 16974356; // 0x1030214
     field public static final int TextAppearance_Material_Widget_Button = 16974357; // 0x1030215
+    field public static final int TextAppearance_Material_Widget_Button_Inverse = 16974565; // 0x10302e5
     field public static final int TextAppearance_Material_Widget_DropDownHint = 16974358; // 0x1030216
     field public static final int TextAppearance_Material_Widget_DropDownItem = 16974359; // 0x1030217
     field public static final int TextAppearance_Material_Widget_EditText = 16974360; // 0x1030218
@@ -3719,12 +3725,15 @@
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.app.ActivityManager.RecentTaskInfo> CREATOR;
     field public int affiliatedTaskId;
+    field public android.content.ComponentName baseActivity;
     field public android.content.Intent baseIntent;
     field public java.lang.CharSequence description;
     field public int id;
+    field public int numActivities;
     field public android.content.ComponentName origActivity;
     field public int persistentId;
     field public android.app.ActivityManager.TaskDescription taskDescription;
+    field public android.content.ComponentName topActivity;
   }
 
   public static class ActivityManager.RunningAppProcessInfo implements android.os.Parcelable {
@@ -5198,8 +5207,44 @@
     method public void cancel(int);
     method public void cancel(java.lang.String, int);
     method public void cancelAll();
+    method public android.app.NotificationManager.Policy getNotificationPolicy(android.app.NotificationManager.Policy.Token);
+    method public boolean isNotificationPolicyTokenValid(android.app.NotificationManager.Policy.Token);
     method public void notify(int, android.app.Notification);
     method public void notify(java.lang.String, int, android.app.Notification);
+    method public void requestNotificationPolicyToken(android.app.NotificationManager.Policy.Token.RequestCallback, android.os.Handler);
+    method public void setNotificationPolicy(android.app.NotificationManager.Policy.Token, android.app.NotificationManager.Policy);
+    field public static final java.lang.String ACTION_NOTIFICATION_POLICY_CHANGED = "android.app.action.NOTIFICATION_POLICY_CHANGED";
+  }
+
+  public static class NotificationManager.Policy implements android.os.Parcelable {
+    ctor public NotificationManager.Policy(int, int);
+    method public int describeContents();
+    method public static java.lang.String priorityCategoriesToString(int);
+    method public static java.lang.String prioritySendersToString(int);
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.app.NotificationManager.Policy> CREATOR;
+    field public static final int PRIORITY_CATEGORY_CALLS = 8; // 0x8
+    field public static final int PRIORITY_CATEGORY_EVENTS = 2; // 0x2
+    field public static final int PRIORITY_CATEGORY_MESSAGES = 4; // 0x4
+    field public static final int PRIORITY_CATEGORY_REMINDERS = 1; // 0x1
+    field public static final int PRIORITY_CATEGORY_REPEAT_CALLERS = 16; // 0x10
+    field public static final int PRIORITY_SENDERS_ANY = 0; // 0x0
+    field public static final int PRIORITY_SENDERS_CONTACTS = 1; // 0x1
+    field public static final int PRIORITY_SENDERS_STARRED = 2; // 0x2
+    field public final int priorityCategories;
+    field public final int prioritySenders;
+  }
+
+  public static class NotificationManager.Policy.Token implements android.os.Parcelable {
+    method public int describeContents();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.app.NotificationManager.Policy.Token> CREATOR;
+  }
+
+  public static abstract class NotificationManager.Policy.Token.RequestCallback {
+    ctor public NotificationManager.Policy.Token.RequestCallback();
+    method public abstract void onTokenDenied();
+    method public abstract void onTokenGranted(android.app.NotificationManager.Policy.Token);
   }
 
   public final class PendingIntent implements android.os.Parcelable {
@@ -5844,6 +5889,7 @@
     method public void setRestrictionsProvider(android.content.ComponentName, android.content.ComponentName);
     method public void setScreenCaptureDisabled(android.content.ComponentName, boolean);
     method public void setSecureSetting(android.content.ComponentName, java.lang.String, java.lang.String);
+    method public void setStatusBarEnabledState(android.content.ComponentName, boolean);
     method public int setStorageEncryption(android.content.ComponentName, boolean);
     method public void setTrustAgentConfiguration(android.content.ComponentName, android.content.ComponentName, android.os.PersistableBundle);
     method public void setUninstallBlocked(android.content.ComponentName, java.lang.String, boolean);
@@ -7102,6 +7148,7 @@
     field public static final int GATT_SERVER = 8; // 0x8
     field public static final int HEADSET = 1; // 0x1
     field public static final int HEALTH = 3; // 0x3
+    field public static final int SAP = 10; // 0xa
     field public static final int STATE_CONNECTED = 2; // 0x2
     field public static final int STATE_CONNECTING = 1; // 0x1
     field public static final int STATE_DISCONNECTED = 0; // 0x0
@@ -7113,6 +7160,25 @@
     method public abstract void onServiceDisconnected(int);
   }
 
+  public final class BluetoothSap implements android.bluetooth.BluetoothProfile {
+    method public synchronized void close();
+    method public boolean connect(android.bluetooth.BluetoothDevice);
+    method public boolean disconnect(android.bluetooth.BluetoothDevice);
+    method public android.bluetooth.BluetoothDevice getClient();
+    method public java.util.List<android.bluetooth.BluetoothDevice> getConnectedDevices();
+    method public int getConnectionState(android.bluetooth.BluetoothDevice);
+    method public java.util.List<android.bluetooth.BluetoothDevice> getDevicesMatchingConnectionStates(int[]);
+    method public int getPriority(android.bluetooth.BluetoothDevice);
+    method public int getState();
+    method public boolean isConnected(android.bluetooth.BluetoothDevice);
+    method public boolean setPriority(android.bluetooth.BluetoothDevice, int);
+    field public static final java.lang.String ACTION_CONNECTION_STATE_CHANGED = "android.bluetooth.sap.profile.action.CONNECTION_STATE_CHANGED";
+    field public static final int RESULT_CANCELED = 2; // 0x2
+    field public static final int RESULT_FAILURE = 0; // 0x0
+    field public static final int RESULT_SUCCESS = 1; // 0x1
+    field public static final int STATE_ERROR = -1; // 0xffffffff
+  }
+
   public final class BluetoothServerSocket implements java.io.Closeable {
     method public android.bluetooth.BluetoothSocket accept() throws java.io.IOException;
     method public android.bluetooth.BluetoothSocket accept(int) throws java.io.IOException;
@@ -7122,10 +7188,16 @@
   public final class BluetoothSocket implements java.io.Closeable {
     method public void close() throws java.io.IOException;
     method public void connect() throws java.io.IOException;
+    method public int getConnectionType();
     method public java.io.InputStream getInputStream() throws java.io.IOException;
+    method public int getMaxReceivePacketSize();
+    method public int getMaxTransmitPacketSize();
     method public java.io.OutputStream getOutputStream() throws java.io.IOException;
     method public android.bluetooth.BluetoothDevice getRemoteDevice();
     method public boolean isConnected();
+    field public static final int TYPE_L2CAP = 3; // 0x3
+    field public static final int TYPE_RFCOMM = 1; // 0x1
+    field public static final int TYPE_SCO = 2; // 0x2
   }
 
 }
@@ -7875,6 +7947,7 @@
     field public static final java.lang.String ALARM_SERVICE = "alarm";
     field public static final java.lang.String APPWIDGET_SERVICE = "appwidget";
     field public static final java.lang.String APP_OPS_SERVICE = "appops";
+    field public static final java.lang.String AUDIO_DEVICES_SERVICE = "audio_devices_manager";
     field public static final java.lang.String AUDIO_SERVICE = "audio";
     field public static final java.lang.String BACKUP_SERVICE = "backup";
     field public static final java.lang.String BATTERY_SERVICE = "batterymanager";
@@ -7889,6 +7962,7 @@
     field public static final java.lang.String BLUETOOTH_SERVICE = "bluetooth";
     field public static final java.lang.String CAMERA_SERVICE = "camera";
     field public static final java.lang.String CAPTIONING_SERVICE = "captioning";
+    field public static final java.lang.String CARRIER_CONFIG_SERVICE = "carrier_config";
     field public static final java.lang.String CLIPBOARD_SERVICE = "clipboard";
     field public static final java.lang.String CONNECTIVITY_SERVICE = "connectivity";
     field public static final java.lang.String CONSUMER_IR_SERVICE = "consumer_ir";
@@ -12901,6 +12975,7 @@
     method public final void unlock();
     field public static final java.lang.String ACTION_NEW_PICTURE = "android.hardware.action.NEW_PICTURE";
     field public static final java.lang.String ACTION_NEW_VIDEO = "android.hardware.action.NEW_VIDEO";
+    field public static final int CAMERA_ERROR_EVICTED = 2; // 0x2
     field public static final int CAMERA_ERROR_SERVER_DIED = 100; // 0x64
     field public static final int CAMERA_ERROR_UNKNOWN = 1; // 0x1
   }
@@ -13351,6 +13426,7 @@
     method public abstract android.hardware.camera2.CameraDevice getDevice();
     method public abstract android.view.Surface getInputSurface();
     method public abstract boolean isReprocessible();
+    method public abstract void prepare(android.view.Surface) throws android.hardware.camera2.CameraAccessException;
     method public abstract int setRepeatingBurst(java.util.List<android.hardware.camera2.CaptureRequest>, android.hardware.camera2.CameraCaptureSession.CaptureCallback, android.os.Handler) throws android.hardware.camera2.CameraAccessException;
     method public abstract int setRepeatingRequest(android.hardware.camera2.CaptureRequest, android.hardware.camera2.CameraCaptureSession.CaptureCallback, android.os.Handler) throws android.hardware.camera2.CameraAccessException;
     method public abstract void stopRepeating() throws android.hardware.camera2.CameraAccessException;
@@ -13373,6 +13449,7 @@
     method public abstract void onConfigureFailed(android.hardware.camera2.CameraCaptureSession);
     method public abstract void onConfigured(android.hardware.camera2.CameraCaptureSession);
     method public void onReady(android.hardware.camera2.CameraCaptureSession);
+    method public void onSurfacePrepared(android.hardware.camera2.CameraCaptureSession, android.view.Surface);
   }
 
   public final class CameraCharacteristics extends android.hardware.camera2.CameraMetadata {
@@ -13396,6 +13473,7 @@
     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;
+    field public static final android.hardware.camera2.CameraCharacteristics.Key<java.lang.Boolean> DEPTH_DEPTH_IS_EXCLUSIVE;
     field public static final android.hardware.camera2.CameraCharacteristics.Key<int[]> EDGE_AVAILABLE_EDGE_MODES;
     field public static final android.hardware.camera2.CameraCharacteristics.Key<java.lang.Boolean> FLASH_INFO_AVAILABLE;
     field public static final android.hardware.camera2.CameraCharacteristics.Key<int[]> HOT_PIXEL_AVAILABLE_HOT_PIXEL_MODES;
@@ -13624,6 +13702,7 @@
     field public static final int HOT_PIXEL_MODE_HIGH_QUALITY = 2; // 0x2
     field public static final int HOT_PIXEL_MODE_OFF = 0; // 0x0
     field public static final int INFO_SUPPORTED_HARDWARE_LEVEL_FULL = 1; // 0x1
+    field public static final int INFO_SUPPORTED_HARDWARE_LEVEL_HIGH_RESOLUTION = 3; // 0x3
     field public static final int INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY = 2; // 0x2
     field public static final int INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED = 0; // 0x0
     field public static final int LENS_FACING_BACK = 1; // 0x1
@@ -13642,6 +13721,7 @@
     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_DEPTH_OUTPUT = 8; // 0x8
     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
@@ -14061,6 +14141,8 @@
 
   public class FingerprintManager {
     method public void authenticate(android.hardware.fingerprint.FingerprintManager.CryptoObject, android.os.CancellationSignal, android.hardware.fingerprint.FingerprintManager.AuthenticationCallback, int);
+    method public boolean hasEnrolledFingerprints();
+    method public boolean isHardwareDetected();
     field public static final int FINGERPRINT_ACQUIRED_GOOD = 0; // 0x0
     field public static final int FINGERPRINT_ACQUIRED_IMAGER_DIRTY = 3; // 0x3
     field public static final int FINGERPRINT_ACQUIRED_INSUFFICIENT = 2; // 0x2
@@ -14091,6 +14173,8 @@
   }
 
   public static class FingerprintManager.CryptoObject {
+    ctor public FingerprintManager.CryptoObject(java.security.Signature);
+    ctor public FingerprintManager.CryptoObject(javax.crypto.Cipher);
     method public javax.crypto.Cipher getCipher();
     method public java.security.Signature getSignature();
   }
@@ -15751,6 +15835,47 @@
     method public android.media.AudioAttributes.Builder setUsage(int);
   }
 
+  public class AudioDeviceInfo {
+    method public java.lang.String getAddress();
+    method public int[] getChannelCounts();
+    method public int[] getChannelMasks();
+    method public int[] getFormats();
+    method public java.lang.String getName();
+    method public int[] getSampleRates();
+    method public int getType();
+    method public boolean isSink();
+    method public boolean isSource();
+    field public static final int TYPE_AUX_LINE = 19; // 0x13
+    field public static final int TYPE_BLUETOOTH_A2DP = 8; // 0x8
+    field public static final int TYPE_BLUETOOTH_SCO = 7; // 0x7
+    field public static final int TYPE_BUILTIN_EARPIECE = 1; // 0x1
+    field public static final int TYPE_BUILTIN_MIC = 15; // 0xf
+    field public static final int TYPE_BUILTIN_SPEAKER = 2; // 0x2
+    field public static final int TYPE_DOCK = 13; // 0xd
+    field public static final int TYPE_FM = 14; // 0xe
+    field public static final int TYPE_FM_TUNER = 16; // 0x10
+    field public static final int TYPE_HDMI = 9; // 0x9
+    field public static final int TYPE_HDMI_ARC = 10; // 0xa
+    field public static final int TYPE_LINE_ANALOG = 5; // 0x5
+    field public static final int TYPE_LINE_DIGITAL = 6; // 0x6
+    field public static final int TYPE_TELEPHONY = 18; // 0x12
+    field public static final int TYPE_TV_TUNER = 17; // 0x11
+    field public static final int TYPE_UNKNOWN = 0; // 0x0
+    field public static final int TYPE_USB_ACCESSORY = 12; // 0xc
+    field public static final int TYPE_USB_DEVICE = 11; // 0xb
+    field public static final int TYPE_WIRED_HEADPHONES = 4; // 0x4
+    field public static final int TYPE_WIRED_HEADSET = 3; // 0x3
+  }
+
+  public class AudioDevicesManager {
+    method public void addOnAudioDeviceConnectionListener(android.media.OnAudioDeviceConnectionListener, android.os.Handler);
+    method public android.media.AudioDeviceInfo[] listDevices(int);
+    method public void removeOnAudioDeviceConnectionListener(android.media.OnAudioDeviceConnectionListener);
+    field public static final int LIST_DEVICES_ALL = 3; // 0x3
+    field public static final int LIST_DEVICES_INPUTS = 1; // 0x1
+    field public static final int LIST_DEVICES_OUTPUTS = 2; // 0x2
+  }
+
   public final class AudioFocusInfo implements android.os.Parcelable {
     method public int describeContents();
     method public android.media.AudioAttributes getAttributes();
@@ -15998,9 +16123,12 @@
     method public int getSampleRate();
     method public int getState();
     method public int read(byte[], int, int);
+    method public int read(byte[], int, int, int);
     method public int read(short[], int, int);
+    method public int read(short[], int, int, int);
     method public int read(float[], int, int, int);
     method public int read(java.nio.ByteBuffer, int);
+    method public int read(java.nio.ByteBuffer, int, int);
     method public void release();
     method public int setNotificationMarkerPosition(int);
     method public int setPositionNotificationPeriod(int);
@@ -16062,6 +16190,7 @@
     method public int getPlaybackHeadPosition();
     method public int getPlaybackRate();
     method public int getPositionNotificationPeriod();
+    method public android.media.AudioDeviceInfo getPreferredOutputDevice();
     method public int getSampleRate();
     method public int getState();
     method public int getStreamType();
@@ -16078,12 +16207,15 @@
     method public void setPlaybackPositionUpdateListener(android.media.AudioTrack.OnPlaybackPositionUpdateListener, android.os.Handler);
     method public int setPlaybackRate(int);
     method public int setPositionNotificationPeriod(int);
+    method public boolean setPreferredOutputDevice(android.media.AudioDeviceInfo);
     method protected deprecated void setState(int);
     method public deprecated int setStereoVolume(float, float);
     method public int setVolume(float);
     method public void stop() throws java.lang.IllegalStateException;
     method public int write(byte[], int, int);
+    method public int write(byte[], int, int, int);
     method public int write(short[], int, int);
+    method public int write(short[], int, int, int);
     method public int write(float[], int, int, int);
     method public int write(java.nio.ByteBuffer, int, int);
     field public static final int ERROR = -1; // 0xffffffff
@@ -16661,6 +16793,11 @@
     ctor public MediaCryptoException(java.lang.String);
   }
 
+  public abstract interface MediaDataSource implements java.io.Closeable {
+    method public abstract long getSize();
+    method public abstract int readAt(long, byte[], int);
+  }
+
   public class MediaDescription implements android.os.Parcelable {
     method public int describeContents();
     method public java.lang.CharSequence getDescription();
@@ -16797,6 +16934,7 @@
     method public final void release();
     method public void seekTo(long, int);
     method public void selectTrack(int);
+    method public final void setDataSource(android.media.MediaDataSource) throws java.io.IOException, java.lang.IllegalArgumentException;
     method public final void setDataSource(android.content.Context, android.net.Uri, java.util.Map<java.lang.String, java.lang.String>) throws java.io.IOException;
     method public final void setDataSource(java.lang.String, java.util.Map<java.lang.String, java.lang.String>) throws java.io.IOException;
     method public final void setDataSource(java.lang.String) throws java.io.IOException;
@@ -16976,6 +17114,7 @@
     method public void setDataSource(java.io.FileDescriptor, long, long) throws java.lang.IllegalArgumentException;
     method public void setDataSource(java.io.FileDescriptor) throws java.lang.IllegalArgumentException;
     method public void setDataSource(android.content.Context, android.net.Uri) throws java.lang.IllegalArgumentException, java.lang.SecurityException;
+    method public void setDataSource(android.media.MediaDataSource) throws java.lang.IllegalArgumentException;
     field public static final int METADATA_KEY_ALBUM = 1; // 0x1
     field public static final int METADATA_KEY_ALBUMARTIST = 13; // 0xd
     field public static final int METADATA_KEY_ARTIST = 2; // 0x2
@@ -17060,6 +17199,7 @@
     method public void setDataSource(java.lang.String) throws java.io.IOException, java.lang.IllegalArgumentException, java.lang.IllegalStateException, java.lang.SecurityException;
     method public void setDataSource(java.io.FileDescriptor) throws java.io.IOException, java.lang.IllegalArgumentException, java.lang.IllegalStateException;
     method public void setDataSource(java.io.FileDescriptor, long, long) throws java.io.IOException, java.lang.IllegalArgumentException, java.lang.IllegalStateException;
+    method public void setDataSource(android.media.MediaDataSource) throws java.lang.IllegalArgumentException, java.lang.IllegalStateException;
     method public void setDisplay(android.view.SurfaceHolder);
     method public void setLooping(boolean);
     method public void setNextMediaPlayer(android.media.MediaPlayer);
@@ -17384,7 +17524,7 @@
 
   public final class MediaSync {
     ctor public MediaSync();
-    method public void configureAudioTrack(android.media.AudioTrack, int);
+    method public void configureAudioTrack(android.media.AudioTrack);
     method public void configureSurface(android.view.Surface);
     method public final android.view.Surface createInputSurface();
     method public boolean getTimestamp(android.media.MediaTimestamp);
@@ -17420,6 +17560,10 @@
     ctor public NotProvisionedException(java.lang.String);
   }
 
+  public abstract interface OnAudioDeviceConnectionListener {
+    method public abstract void onAudioDeviceConnection();
+  }
+
   public final class Rating implements android.os.Parcelable {
     method public int describeContents();
     method public float getPercentRating();
@@ -19473,6 +19617,7 @@
   public class ConnectivityManager {
     method public void addDefaultNetworkActiveListener(android.net.ConnectivityManager.OnNetworkActiveListener);
     method public boolean bindProcessToNetwork(android.net.Network);
+    method public android.net.Network getActiveNetwork();
     method public android.net.NetworkInfo getActiveNetworkInfo();
     method public android.net.NetworkInfo[] getAllNetworkInfo();
     method public android.net.Network[] getAllNetworks();
@@ -19491,7 +19636,8 @@
     method public void registerNetworkCallback(android.net.NetworkRequest, android.net.ConnectivityManager.NetworkCallback);
     method public void releaseNetworkRequest(android.app.PendingIntent);
     method public void removeDefaultNetworkActiveListener(android.net.ConnectivityManager.OnNetworkActiveListener);
-    method public void reportBadNetwork(android.net.Network);
+    method public deprecated void reportBadNetwork(android.net.Network);
+    method public void reportNetworkConnectivity(android.net.Network, boolean);
     method public void requestNetwork(android.net.NetworkRequest, android.net.ConnectivityManager.NetworkCallback);
     method public void requestNetwork(android.net.NetworkRequest, android.app.PendingIntent);
     method public deprecated boolean requestRouteToHost(int, int);
@@ -21437,6 +21583,7 @@
 
   public final class NfcEvent {
     field public final android.nfc.NfcAdapter nfcAdapter;
+    field public final byte peerLlcpVersion;
   }
 
   public final class NfcManager {
@@ -24387,7 +24534,6 @@
   public class Binder implements android.os.IBinder {
     ctor public Binder();
     method public void attachInterface(android.os.IInterface, java.lang.String);
-    method public static final void blockUntilThreadAvailable();
     method public static final long clearCallingIdentity();
     method public void dump(java.io.FileDescriptor, java.lang.String[]);
     method protected void dump(java.io.FileDescriptor, java.io.PrintWriter, java.lang.String[]);
@@ -24601,6 +24747,8 @@
     method public static long getNativeHeapFreeSize();
     method public static long getNativeHeapSize();
     method public static long getPss();
+    method public static java.lang.String getRuntimeStat(java.lang.String);
+    method public static java.util.Map<java.lang.String, java.lang.String> getRuntimeStats();
     method public static deprecated int getThreadAllocCount();
     method public static deprecated int getThreadAllocSize();
     method public static deprecated int getThreadExternalAllocCount();
@@ -28465,6 +28613,7 @@
     field public static final java.lang.String DEBUG_APP = "debug_app";
     field public static final java.lang.String DEVELOPMENT_SETTINGS_ENABLED = "development_settings_enabled";
     field public static final java.lang.String DEVICE_PROVISIONED = "device_provisioned";
+    field public static final java.lang.String HIDE_CARRIER_NETWORK_SETTINGS = "hide_carrier_network_settings";
     field public static final java.lang.String HTTP_PROXY = "http_proxy";
     field public static final deprecated java.lang.String INSTALL_NON_MARKET_APPS = "install_non_market_apps";
     field public static final java.lang.String MODE_RINGER = "mode_ringer";
@@ -28631,6 +28780,7 @@
     field public static final android.net.Uri DEFAULT_RINGTONE_URI;
     field public static final deprecated java.lang.String DEVICE_PROVISIONED = "device_provisioned";
     field public static final deprecated java.lang.String DIM_SCREEN = "dim_screen";
+    field public static final java.lang.String DTMF_TONE_TYPE_WHEN_DIALING = "dtmf_tone_type";
     field public static final java.lang.String DTMF_TONE_WHEN_DIALING = "dtmf_tone";
     field public static final java.lang.String END_BUTTON_BEHAVIOR = "end_button_behavior";
     field public static final java.lang.String FONT_SCALE = "font_scale";
@@ -28679,6 +28829,7 @@
     field public static final java.lang.String USER_ROTATION = "user_rotation";
     field public static final deprecated java.lang.String USE_GOOGLE_MAIL = "use_google_mail";
     field public static final java.lang.String VIBRATE_ON = "vibrate_on";
+    field public static final java.lang.String VIBRATE_WHEN_RINGING = "vibrate_when_ringing";
     field public static final deprecated java.lang.String WAIT_FOR_DEBUGGER = "wait_for_debugger";
     field public static final deprecated java.lang.String WALLPAPER_ACTIVITY = "wallpaper_activity";
     field public static final deprecated java.lang.String WIFI_MAX_DHCP_RETRY_COUNT = "wifi_max_dhcp_retry_count";
@@ -29210,7 +29361,6 @@
     method public static android.renderscript.AllocationAdapter create1D(android.renderscript.RenderScript, android.renderscript.Allocation);
     method public static android.renderscript.AllocationAdapter create2D(android.renderscript.RenderScript, android.renderscript.Allocation);
     method public static android.renderscript.AllocationAdapter createTyped(android.renderscript.RenderScript, android.renderscript.Allocation, android.renderscript.Type);
-    method public void setArray(int, int);
     method public void setFace(android.renderscript.Type.CubemapFace);
     method public void setLOD(int);
     method public void setX(int);
@@ -29871,6 +30021,62 @@
     method public void setLUT(android.renderscript.Allocation);
   }
 
+  public final class ScriptIntrinsicBLAS extends android.renderscript.ScriptIntrinsic {
+    method public void BNNM(android.renderscript.Allocation, int, android.renderscript.Allocation, int, android.renderscript.Allocation, int, int);
+    method public void CGEMM(int, int, android.renderscript.Float2, android.renderscript.Allocation, android.renderscript.Allocation, android.renderscript.Float2, android.renderscript.Allocation);
+    method public void CHEMM(int, int, float, android.renderscript.Allocation, android.renderscript.Allocation, float, android.renderscript.Allocation);
+    method public void CHER2K(int, int, android.renderscript.Float2, android.renderscript.Allocation, android.renderscript.Allocation, float, android.renderscript.Allocation);
+    method public void CHERK(int, int, float, android.renderscript.Allocation, float, android.renderscript.Allocation);
+    method public void CSYMM(int, int, android.renderscript.Float2, android.renderscript.Allocation, android.renderscript.Allocation, android.renderscript.Float2, android.renderscript.Allocation);
+    method public void CSYR2K(int, int, android.renderscript.Float2, android.renderscript.Allocation, android.renderscript.Allocation, android.renderscript.Float2, android.renderscript.Allocation);
+    method public void CSYRK(int, int, float, float, android.renderscript.Allocation, float, float, android.renderscript.Allocation);
+    method public void CTRMM(int, int, int, int, android.renderscript.Float2, android.renderscript.Allocation, android.renderscript.Allocation);
+    method public void CTRSM(int, int, int, int, android.renderscript.Float2, android.renderscript.Allocation, android.renderscript.Allocation);
+    method public void DGEMM(int, int, double, android.renderscript.Allocation, android.renderscript.Allocation, double, android.renderscript.Allocation);
+    method public void DSYMM(int, int, double, android.renderscript.Allocation, android.renderscript.Allocation, double, android.renderscript.Allocation);
+    method public void DSYR2K(int, int, double, android.renderscript.Allocation, android.renderscript.Allocation, double, android.renderscript.Allocation);
+    method public void DSYRK(int, int, double, android.renderscript.Allocation, double, android.renderscript.Allocation);
+    method public void DTRMM(int, int, int, int, double, android.renderscript.Allocation, android.renderscript.Allocation);
+    method public void DTRSM(int, int, int, int, double, android.renderscript.Allocation, android.renderscript.Allocation);
+    method public void SGEMM(int, int, float, android.renderscript.Allocation, android.renderscript.Allocation, float, android.renderscript.Allocation);
+    method public void SSYMM(int, int, float, android.renderscript.Allocation, android.renderscript.Allocation, float, android.renderscript.Allocation);
+    method public void SSYR2K(int, int, float, android.renderscript.Allocation, android.renderscript.Allocation, float, android.renderscript.Allocation);
+    method public void SSYRK(int, int, float, android.renderscript.Allocation, float, android.renderscript.Allocation);
+    method public void STRMM(int, int, int, int, float, android.renderscript.Allocation, android.renderscript.Allocation);
+    method public void STRSM(int, int, int, int, float, android.renderscript.Allocation, android.renderscript.Allocation);
+    method public void ZGEMM(int, int, android.renderscript.Double2, android.renderscript.Allocation, android.renderscript.Allocation, android.renderscript.Double2, android.renderscript.Allocation);
+    method public void ZHEMM(int, int, double, android.renderscript.Allocation, android.renderscript.Allocation, double, android.renderscript.Allocation);
+    method public void ZHER2K(int, int, android.renderscript.Double2, android.renderscript.Allocation, android.renderscript.Allocation, double, android.renderscript.Allocation);
+    method public void ZHERK(int, int, double, android.renderscript.Allocation, double, android.renderscript.Allocation);
+    method public void ZSYMM(int, int, android.renderscript.Double2, android.renderscript.Allocation, android.renderscript.Allocation, android.renderscript.Double2, android.renderscript.Allocation);
+    method public void ZSYR2K(int, int, android.renderscript.Double2, android.renderscript.Allocation, android.renderscript.Allocation, android.renderscript.Double2, android.renderscript.Allocation);
+    method public void ZSYRK(int, int, double, double, android.renderscript.Allocation, double, double, android.renderscript.Allocation);
+    method public void ZTRMM(int, int, int, int, android.renderscript.Double2, android.renderscript.Allocation, android.renderscript.Allocation);
+    method public void ZTRSM(int, int, int, int, android.renderscript.Double2, android.renderscript.Allocation, android.renderscript.Allocation);
+    method public static android.renderscript.ScriptIntrinsicBLAS create(android.renderscript.RenderScript);
+    field public static final int CONJ_TRANSPOSE = 113; // 0x71
+    field public static final int LEFT = 141; // 0x8d
+    field public static final int LOWER = 122; // 0x7a
+    field public static final int NON_UNIT = 131; // 0x83
+    field public static final int NO_TRANSPOSE = 111; // 0x6f
+    field public static final int RIGHT = 142; // 0x8e
+    field public static final int TRANSPOSE = 112; // 0x70
+    field public static final int UNIT = 132; // 0x84
+    field public static final int UPPER = 121; // 0x79
+  }
+
+  public static abstract class ScriptIntrinsicBLAS.Diag implements java.lang.annotation.Annotation {
+  }
+
+  public static abstract class ScriptIntrinsicBLAS.Side implements java.lang.annotation.Annotation {
+  }
+
+  public static abstract class ScriptIntrinsicBLAS.Transpose implements java.lang.annotation.Annotation {
+  }
+
+  public static abstract class ScriptIntrinsicBLAS.Uplo implements java.lang.annotation.Annotation {
+  }
+
   public class ScriptIntrinsicBlend extends android.renderscript.ScriptIntrinsic {
     method public static android.renderscript.ScriptIntrinsicBlend create(android.renderscript.RenderScript, android.renderscript.Element);
     method public void forEachAdd(android.renderscript.Allocation, android.renderscript.Allocation);
@@ -30033,8 +30239,6 @@
     method public static android.renderscript.Type createX(android.renderscript.RenderScript, android.renderscript.Element, int);
     method public static android.renderscript.Type createXY(android.renderscript.RenderScript, android.renderscript.Element, int, int);
     method public static android.renderscript.Type createXYZ(android.renderscript.RenderScript, android.renderscript.Element, int, int, int);
-    method public int getArray(int);
-    method public int getArrayCount();
     method public int getCount();
     method public android.renderscript.Element getElement();
     method public int getX();
@@ -30048,7 +30252,6 @@
   public static class Type.Builder {
     ctor public Type.Builder(android.renderscript.RenderScript, android.renderscript.Element);
     method public android.renderscript.Type create();
-    method public android.renderscript.Type.Builder setArray(int, int);
     method public android.renderscript.Type.Builder setFaces(boolean);
     method public android.renderscript.Type.Builder setMipmaps(boolean);
     method public android.renderscript.Type.Builder setX(int);
@@ -30284,6 +30487,7 @@
   public static abstract class KeyStoreKeyProperties.Origin {
     field public static final int GENERATED = 1; // 0x1
     field public static final int IMPORTED = 2; // 0x2
+    field public static final int UNKNOWN = 4; // 0x4
   }
 
   public static abstract class KeyStoreKeyProperties.OriginEnum implements java.lang.annotation.Annotation {
@@ -30382,6 +30586,26 @@
 
 package android.service.carrier {
 
+  public abstract class CarrierConfigService extends android.app.Service {
+    ctor public CarrierConfigService();
+    method public final android.os.IBinder onBind(android.content.Intent);
+    method public abstract android.os.Bundle onLoadConfig(android.service.carrier.CarrierIdentifier);
+    field public static final java.lang.String SERVICE_INTERFACE = "android.service.carrier.CarrierConfigService";
+  }
+
+  public class CarrierIdentifier implements android.os.Parcelable {
+    ctor public CarrierIdentifier(java.lang.String, java.lang.String, java.lang.String, java.lang.String, java.lang.String, java.lang.String);
+    method public int describeContents();
+    method public java.lang.String getGid1();
+    method public java.lang.String getGid2();
+    method public java.lang.String getImsi();
+    method public java.lang.String getMcc();
+    method public java.lang.String getMnc();
+    method public java.lang.String getSpn();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.service.carrier.CarrierIdentifier> CREATOR;
+  }
+
   public abstract class CarrierMessagingService extends android.app.Service {
     ctor public CarrierMessagingService();
     method public android.os.IBinder onBind(android.content.Intent);
@@ -30593,6 +30817,7 @@
     method public final int getCurrentInterruptionFilter();
     method public final int getCurrentListenerHints();
     method public android.service.notification.NotificationListenerService.RankingMap getCurrentRanking();
+    method public final android.app.NotificationManager.Policy.Token getNotificationPolicyToken();
     method public android.os.IBinder onBind(android.content.Intent);
     method public void onInterruptionFilterChanged(int);
     method public void onListenerConnected();
@@ -30726,7 +30951,8 @@
 
   public class TrustAgentService extends android.app.Service {
     ctor public TrustAgentService();
-    method public final void grantTrust(java.lang.CharSequence, long, boolean);
+    method public final deprecated void grantTrust(java.lang.CharSequence, long, boolean);
+    method public final void grantTrust(java.lang.CharSequence, long, int);
     method public final android.os.IBinder onBind(android.content.Intent);
     method public boolean onConfigure(java.util.List<android.os.PersistableBundle>);
     method public void onDeviceLocked();
@@ -30735,6 +30961,8 @@
     method public void onUnlockAttempt(boolean);
     method public final void revokeTrust();
     method public final void setManagingTrust(boolean);
+    field public static final int FLAG_GRANT_TRUST_DISMISS_KEYGUARD = 2; // 0x2
+    field public static final int FLAG_GRANT_TRUST_INITIATED_BY_USER = 1; // 0x1
     field public static final java.lang.String SERVICE_INTERFACE = "android.service.trust.TrustAgentService";
     field public static final java.lang.String TRUST_AGENT_META_DATA = "android.service.trust.trustagent";
   }
@@ -31836,6 +32064,7 @@
   public final class AudioState implements android.os.Parcelable {
     ctor public AudioState(boolean, int, int);
     ctor public AudioState(android.telecom.AudioState);
+    method public static java.lang.String audioRouteToString(int);
     method public int describeContents();
     method public int getRoute();
     method public int getSupportedRouteMask();
@@ -31847,29 +32076,10 @@
     field public static final int ROUTE_SPEAKER = 8; // 0x8
     field public static final int ROUTE_WIRED_HEADSET = 4; // 0x4
     field public static final int ROUTE_WIRED_OR_EARPIECE = 5; // 0x5
-    field public final boolean isMuted;
-    field public final int route;
-    field public final int supportedRouteMask;
-  }
-
-  public class AuthenticatorService extends android.app.Service {
-    ctor public AuthenticatorService();
-    method public android.os.IBinder onBind(android.content.Intent);
-  }
-
-  public class AuthenticatorService.Authenticator extends android.accounts.AbstractAccountAuthenticator {
-    ctor public AuthenticatorService.Authenticator(android.content.Context);
-    method public android.os.Bundle addAccount(android.accounts.AccountAuthenticatorResponse, java.lang.String, java.lang.String, java.lang.String[], android.os.Bundle) throws android.accounts.NetworkErrorException;
-    method public android.os.Bundle confirmCredentials(android.accounts.AccountAuthenticatorResponse, android.accounts.Account, android.os.Bundle) throws android.accounts.NetworkErrorException;
-    method public android.os.Bundle editProperties(android.accounts.AccountAuthenticatorResponse, java.lang.String);
-    method public android.os.Bundle getAuthToken(android.accounts.AccountAuthenticatorResponse, android.accounts.Account, java.lang.String, android.os.Bundle) throws android.accounts.NetworkErrorException;
-    method public java.lang.String getAuthTokenLabel(java.lang.String);
-    method public android.os.Bundle hasFeatures(android.accounts.AccountAuthenticatorResponse, android.accounts.Account, java.lang.String[]) throws android.accounts.NetworkErrorException;
-    method public android.os.Bundle updateCredentials(android.accounts.AccountAuthenticatorResponse, android.accounts.Account, java.lang.String, android.os.Bundle) throws android.accounts.NetworkErrorException;
   }
 
   public final class Call {
-    method public void addListener(android.telecom.Call.Listener);
+    method public deprecated void addListener(android.telecom.Call.Listener);
     method public void answer(int);
     method public void conference(android.telecom.Call);
     method public void disconnect();
@@ -31880,17 +32090,21 @@
     method public android.telecom.Call getParent();
     method public java.lang.String getRemainingPostDialSequence();
     method public int getState();
+    method public android.telecom.InCallService.VideoCall getVideoCall();
     method public void hold();
     method public void mergeConference();
     method public void phoneAccountSelected(android.telecom.PhoneAccountHandle, boolean);
     method public void playDtmfTone(char);
     method public void postDialContinue(boolean);
+    method public void registerCallback(android.telecom.Call.Callback);
     method public void reject(boolean, java.lang.String);
-    method public void removeListener(android.telecom.Call.Listener);
+    method public deprecated void removeListener(android.telecom.Call.Listener);
     method public void splitFromConference();
     method public void stopDtmfTone();
     method public void swapConference();
     method public void unhold();
+    method public void unregisterCallback(android.telecom.Call.Callback);
+    field public static final java.lang.String AVAILABLE_PHONE_ACCOUNTS = "selectPhoneAccountAccounts";
     field public static final int STATE_ACTIVE = 4; // 0x4
     field public static final int STATE_CONNECTING = 9; // 0x9
     field public static final int STATE_DIALING = 1; // 0x1
@@ -31902,34 +32116,8 @@
     field public static final int STATE_RINGING = 2; // 0x2
   }
 
-  public static class Call.Details {
-    method public static java.lang.String capabilitiesToString(int);
-    method public android.telecom.PhoneAccountHandle getAccountHandle();
-    method public int getCallCapabilities();
-    method public int getCallProperties();
-    method public java.lang.String getCallerDisplayName();
-    method public int getCallerDisplayNamePresentation();
-    method public long getConnectTimeMillis();
-    method public android.telecom.DisconnectCause getDisconnectCause();
-    method public android.os.Bundle getExtras();
-    method public android.telecom.GatewayInfo getGatewayInfo();
-    method public android.net.Uri getHandle();
-    method public int getHandlePresentation();
-    method public android.telecom.StatusHints getStatusHints();
-    method public int getVideoState();
-    field public static final int CAPABILITY_DISCONNECT_FROM_CONFERENCE = 8192; // 0x2000
-    field public static final int CAPABILITY_HOLD = 1; // 0x1
-    field public static final int CAPABILITY_MANAGE_CONFERENCE = 128; // 0x80
-    field public static final int CAPABILITY_MERGE_CONFERENCE = 4; // 0x4
-    field public static final int CAPABILITY_MUTE = 64; // 0x40
-    field public static final int CAPABILITY_RESPOND_VIA_TEXT = 32; // 0x20
-    field public static final int CAPABILITY_SEPARATE_FROM_CONFERENCE = 4096; // 0x1000
-    field public static final int CAPABILITY_SUPPORT_HOLD = 2; // 0x2
-    field public static final int CAPABILITY_SWAP_CONFERENCE = 8; // 0x8
-  }
-
-  public static abstract class Call.Listener {
-    ctor public Call.Listener();
+  public static abstract class Call.Callback {
+    ctor public Call.Callback();
     method public void onCallDestroyed(android.telecom.Call);
     method public void onCannedTextResponsesLoaded(android.telecom.Call, java.util.List<java.lang.String>);
     method public void onChildrenChanged(android.telecom.Call, java.util.List<android.telecom.Call>);
@@ -31938,6 +32126,54 @@
     method public void onParentChanged(android.telecom.Call, android.telecom.Call);
     method public void onPostDialWait(android.telecom.Call, java.lang.String);
     method public void onStateChanged(android.telecom.Call, int);
+    method public void onVideoCallChanged(android.telecom.Call, android.telecom.InCallService.VideoCall);
+  }
+
+  public static class Call.Details {
+    method public static boolean can(int, int);
+    method public boolean can(int);
+    method public static java.lang.String capabilitiesToString(int);
+    method public android.telecom.PhoneAccountHandle getAccountHandle();
+    method public int getCallCapabilities();
+    method public int getCallProperties();
+    method public java.lang.String getCallerDisplayName();
+    method public int getCallerDisplayNamePresentation();
+    method public final long getConnectTimeMillis();
+    method public android.telecom.DisconnectCause getDisconnectCause();
+    method public android.os.Bundle getExtras();
+    method public android.telecom.GatewayInfo getGatewayInfo();
+    method public android.net.Uri getHandle();
+    method public int getHandlePresentation();
+    method public android.telecom.StatusHints getStatusHints();
+    method public int getVideoState();
+    field public static final int CAPABILITY_DISCONNECT_FROM_CONFERENCE = 8192; // 0x2000
+    field public static final int CAPABILITY_GENERIC_CONFERENCE = 16384; // 0x4000
+    field public static final int CAPABILITY_HIGH_DEF_AUDIO = 32768; // 0x8000
+    field public static final int CAPABILITY_HOLD = 1; // 0x1
+    field public static final int CAPABILITY_MANAGE_CONFERENCE = 128; // 0x80
+    field public static final int CAPABILITY_MERGE_CONFERENCE = 4; // 0x4
+    field public static final int CAPABILITY_MUTE = 64; // 0x40
+    field public static final int CAPABILITY_RESPOND_VIA_TEXT = 32; // 0x20
+    field public static final int CAPABILITY_SEPARATE_FROM_CONFERENCE = 4096; // 0x1000
+    field public static final int CAPABILITY_SHOW_CALLBACK_NUMBER = 131072; // 0x20000
+    field public static final int CAPABILITY_SUPPORTS_VT_LOCAL_BIDIRECTIONAL = 768; // 0x300
+    field public static final int CAPABILITY_SUPPORTS_VT_LOCAL_RX = 256; // 0x100
+    field public static final int CAPABILITY_SUPPORTS_VT_LOCAL_TX = 512; // 0x200
+    field public static final int CAPABILITY_SUPPORTS_VT_REMOTE_BIDIRECTIONAL = 3072; // 0xc00
+    field public static final int CAPABILITY_SUPPORTS_VT_REMOTE_RX = 1024; // 0x400
+    field public static final int CAPABILITY_SUPPORTS_VT_REMOTE_TX = 2048; // 0x800
+    field public static final int CAPABILITY_SUPPORT_HOLD = 2; // 0x2
+    field public static final int CAPABILITY_SWAP_CONFERENCE = 8; // 0x8
+    field public static final int CAPABILITY_WIFI = 65536; // 0x10000
+  }
+
+  public static abstract deprecated class Call.Listener extends android.telecom.Call.Callback {
+    ctor public Call.Listener();
+  }
+
+  public class CallProperties {
+    ctor public CallProperties();
+    field public static final int CONFERENCE = 1; // 0x1
   }
 
   public final class CallState {
@@ -31954,13 +32190,22 @@
     field public static final int RINGING = 4; // 0x4
   }
 
+  public final class CameraCapabilities implements android.os.Parcelable {
+    ctor public CameraCapabilities(int, int);
+    method public int describeContents();
+    method public int getHeight();
+    method public int getWidth();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.telecom.CameraCapabilities> CREATOR;
+  }
+
   public abstract class Conference implements android.telecom.IConferenceable {
     ctor public Conference(android.telecom.PhoneAccountHandle);
     method public final boolean addConnection(android.telecom.Connection);
     method public final void destroy();
     method public final android.telecom.AudioState getAudioState();
     method public final java.util.List<android.telecom.Connection> getConferenceableConnections();
-    method public long getConnectTimeMillis();
+    method public final long getConnectTimeMillis();
     method public final int getConnectionCapabilities();
     method public final java.util.List<android.telecom.Connection> getConnections();
     method public final android.telecom.DisconnectCause getDisconnectCause();
@@ -31985,8 +32230,7 @@
     method public final void setConnectionCapabilities(int);
     method public final void setDisconnected(android.telecom.DisconnectCause);
     method public final void setOnHold();
-    field public static long CONNECT_TIME_NOT_SPECIFIED;
-    field protected android.telecom.PhoneAccountHandle mPhoneAccount;
+    field public static final long CONNECT_TIME_NOT_SPECIFIED = 0L; // 0x0L
   }
 
   public abstract class Connection implements android.telecom.IConferenceable {
@@ -31999,7 +32243,6 @@
     method public final int getAddressPresentation();
     method public final boolean getAudioModeIsVoip();
     method public final android.telecom.AudioState getAudioState();
-    method public final deprecated int getCallCapabilities();
     method public final java.lang.String getCallerDisplayName();
     method public final int getCallerDisplayNamePresentation();
     method public final android.telecom.Conference getConference();
@@ -32008,8 +32251,8 @@
     method public final android.telecom.DisconnectCause getDisconnectCause();
     method public final int getState();
     method public final android.telecom.StatusHints getStatusHints();
+    method public final android.telecom.Connection.VideoProvider getVideoProvider();
     method public final boolean isRingbackRequested();
-    method protected void notifyConferenceStarted();
     method public void onAbort();
     method public void onAnswer();
     method public void onAudioStateChanged(android.telecom.AudioState);
@@ -32025,7 +32268,6 @@
     method public final void setActive();
     method public final void setAddress(android.net.Uri, int);
     method public final void setAudioModeIsVoip(boolean);
-    method public final deprecated void setCallCapabilities(int);
     method public final void setCallerDisplayName(java.lang.String, int);
     method public final void setConferenceableConnections(java.util.List<android.telecom.Connection>);
     method public final void setConferenceables(java.util.List<android.telecom.IConferenceable>);
@@ -32060,6 +32302,38 @@
     field public static final int STATE_RINGING = 2; // 0x2
   }
 
+  public static abstract class Connection.VideoProvider {
+    ctor public Connection.VideoProvider();
+    method public void changeCallDataUsage(long);
+    method public void changeCameraCapabilities(android.telecom.CameraCapabilities);
+    method public void changePeerDimensions(int, int);
+    method public void changeVideoQuality(int);
+    method public void handleCallSessionEvent(int);
+    method public abstract void onRequestCameraCapabilities();
+    method public abstract void onRequestConnectionDataUsage();
+    method public abstract void onSendSessionModifyRequest(android.telecom.VideoProfile);
+    method public abstract void onSendSessionModifyResponse(android.telecom.VideoProfile);
+    method public abstract void onSetCamera(java.lang.String);
+    method public abstract void onSetDeviceOrientation(int);
+    method public abstract void onSetDisplaySurface(android.view.Surface);
+    method public abstract void onSetPauseImage(java.lang.String);
+    method public abstract void onSetPreviewSurface(android.view.Surface);
+    method public abstract void onSetZoom(float);
+    method public void receiveSessionModifyRequest(android.telecom.VideoProfile);
+    method public void receiveSessionModifyResponse(int, android.telecom.VideoProfile, android.telecom.VideoProfile);
+    field public static final int SESSION_EVENT_CAMERA_FAILURE = 5; // 0x5
+    field public static final int SESSION_EVENT_CAMERA_READY = 6; // 0x6
+    field public static final int SESSION_EVENT_RX_PAUSE = 1; // 0x1
+    field public static final int SESSION_EVENT_RX_RESUME = 2; // 0x2
+    field public static final int SESSION_EVENT_TX_START = 3; // 0x3
+    field public static final int SESSION_EVENT_TX_STOP = 4; // 0x4
+    field public static final int SESSION_MODIFY_REQUEST_FAIL = 2; // 0x2
+    field public static final int SESSION_MODIFY_REQUEST_INVALID = 3; // 0x3
+    field public static final int SESSION_MODIFY_REQUEST_REJECTED_BY_REMOTE = 5; // 0x5
+    field public static final int SESSION_MODIFY_REQUEST_SUCCESS = 1; // 0x1
+    field public static final int SESSION_MODIFY_REQUEST_TIMED_OUT = 4; // 0x4
+  }
+
   public final class ConnectionRequest implements android.os.Parcelable {
     ctor public ConnectionRequest(android.telecom.PhoneAccountHandle, android.net.Uri, android.os.Bundle);
     method public int describeContents();
@@ -32129,14 +32403,50 @@
 
   public abstract class InCallService extends android.app.Service {
     ctor public InCallService();
-    method public android.telecom.Phone getPhone();
+    method public final boolean canAddCall();
+    method public final android.telecom.AudioState getAudioState();
+    method public final java.util.List<android.telecom.Call> getCalls();
+    method public deprecated android.telecom.Phone getPhone();
+    method public void onAudioStateChanged(android.telecom.AudioState);
     method public android.os.IBinder onBind(android.content.Intent);
-    method public void onPhoneCreated(android.telecom.Phone);
-    method public void onPhoneDestroyed(android.telecom.Phone);
+    method public void onBringToForeground(boolean);
+    method public void onCallAdded(android.telecom.Call);
+    method public void onCallRemoved(android.telecom.Call);
+    method public void onCanAddCallChanged(boolean);
+    method public deprecated void onPhoneCreated(android.telecom.Phone);
+    method public deprecated void onPhoneDestroyed(android.telecom.Phone);
+    method public final void setAudioRoute(int);
+    method public final void setMuted(boolean);
     field public static final java.lang.String SERVICE_INTERFACE = "android.telecom.InCallService";
   }
 
-  public final class Phone {
+  public static abstract class InCallService.VideoCall {
+    ctor public InCallService.VideoCall();
+    method public abstract void registerCallback(android.telecom.InCallService.VideoCall.Callback);
+    method public abstract void requestCallDataUsage();
+    method public abstract void requestCameraCapabilities();
+    method public abstract void sendSessionModifyRequest(android.telecom.VideoProfile);
+    method public abstract void sendSessionModifyResponse(android.telecom.VideoProfile);
+    method public abstract void setCamera(java.lang.String);
+    method public abstract void setDeviceOrientation(int);
+    method public abstract void setDisplaySurface(android.view.Surface);
+    method public abstract void setPauseImage(java.lang.String);
+    method public abstract void setPreviewSurface(android.view.Surface);
+    method public abstract void setZoom(float);
+  }
+
+  public static abstract class InCallService.VideoCall.Callback {
+    ctor public InCallService.VideoCall.Callback();
+    method public abstract void onCallDataUsageChanged(long);
+    method public abstract void onCallSessionEvent(int);
+    method public abstract void onCameraCapabilitiesChanged(android.telecom.CameraCapabilities);
+    method public abstract void onPeerDimensionsChanged(int, int);
+    method public abstract void onSessionModifyRequestReceived(android.telecom.VideoProfile);
+    method public abstract void onSessionModifyResponseReceived(int, android.telecom.VideoProfile, android.telecom.VideoProfile);
+    method public abstract void onVideoQualityChanged(int);
+  }
+
+  public final deprecated class Phone {
     method public final void addListener(android.telecom.Phone.Listener);
     method public final boolean canAddCall();
     method public final android.telecom.AudioState getAudioState();
@@ -32195,7 +32505,6 @@
     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);
@@ -32311,7 +32620,8 @@
     method public void addNewIncomingCall(android.telecom.PhoneAccountHandle, android.os.Bundle);
     method public void addNewUnknownCall(android.telecom.PhoneAccountHandle, android.os.Bundle);
     method public void cancelMissedCallsNotification();
-    method public void clearAccounts();
+    method public deprecated void clearAccounts();
+    method public void clearPhoneAccounts();
     method public boolean endCall();
     method public android.net.Uri getAdnUriForPhoneAccount(android.telecom.PhoneAccountHandle);
     method public java.util.List<android.telecom.PhoneAccountHandle> getAllPhoneAccountHandles();
@@ -32327,10 +32637,10 @@
     method public java.util.List<android.telecom.PhoneAccountHandle> getPhoneAccountsForPackage();
     method public java.util.List<android.telecom.PhoneAccountHandle> getPhoneAccountsSupportingScheme(java.lang.String);
     method public java.util.List<android.telecom.PhoneAccountHandle> getRegisteredConnectionManagers();
+    method public android.telecom.PhoneAccountHandle getSimCallManager();
+    method public java.lang.String getVoiceMailNumber(android.telecom.PhoneAccountHandle);
     method public boolean handleMmi(java.lang.String);
-    method public boolean handleMmi(android.telecom.PhoneAccountHandle, java.lang.String);
-    method public boolean hasMultipleCallCapableAccounts();
-    method public boolean hasVoiceMailNumber(android.telecom.PhoneAccountHandle);
+    method public boolean handleMmi(java.lang.String, android.telecom.PhoneAccountHandle);
     method public boolean isInCall();
     method public boolean isRinging();
     method public boolean isTtySupported();
@@ -32341,6 +32651,7 @@
     method public void unregisterPhoneAccount(android.telecom.PhoneAccountHandle);
     field public static final java.lang.String ACTION_CHANGE_PHONE_ACCOUNTS = "android.telecom.action.CHANGE_PHONE_ACCOUNTS";
     field public static final java.lang.String ACTION_CONNECTION_SERVICE_CONFIGURE = "android.telecom.action.CONNECTION_SERVICE_CONFIGURE";
+    field public static final java.lang.String ACTION_INCOMING_CALL = "android.telecom.action.INCOMING_CALL";
     field public static final java.lang.String ACTION_PHONE_ACCOUNT_REGISTERED = "android.telecom.action.PHONE_ACCOUNT_REGISTERED";
     field public static final java.lang.String ACTION_SHOW_CALL_ACCESSIBILITY_SETTINGS = "android.telecom.action.SHOW_CALL_ACCESSIBILITY_SETTINGS";
     field public static final java.lang.String ACTION_SHOW_CALL_SETTINGS = "android.telecom.action.SHOW_CALL_SETTINGS";
@@ -32392,40 +32703,28 @@
     field public static final int TX_ENABLED = 1; // 0x1
   }
 
-  public class Voicemail implements android.os.Parcelable {
-    method public static android.telecom.Voicemail.Builder createForInsertion(long, java.lang.String);
-    method public static android.telecom.Voicemail.Builder createForUpdate(long, java.lang.String);
-    method public int describeContents();
-    method public long getDuration();
-    method public long getId();
-    method public java.lang.String getNumber();
-    method public java.lang.String getSourceData();
-    method public java.lang.String getSourcePackage();
-    method public long getTimestampMillis();
-    method public android.net.Uri getUri();
-    method public boolean hasContent();
-    method public boolean isRead();
-    method public void writeToParcel(android.os.Parcel, int);
-    field public static final android.os.Parcelable.Creator<android.telecom.Voicemail> CREATOR;
-  }
-
-  public static class Voicemail.Builder {
-    method public android.telecom.Voicemail build();
-    method public android.telecom.Voicemail.Builder setDuration(long);
-    method public android.telecom.Voicemail.Builder setHasContent(boolean);
-    method public android.telecom.Voicemail.Builder setId(long);
-    method public android.telecom.Voicemail.Builder setIsRead(boolean);
-    method public android.telecom.Voicemail.Builder setNumber(java.lang.String);
-    method public android.telecom.Voicemail.Builder setSourceData(java.lang.String);
-    method public android.telecom.Voicemail.Builder setSourcePackage(java.lang.String);
-    method public android.telecom.Voicemail.Builder setTimestamp(long);
-    method public android.telecom.Voicemail.Builder setUri(android.net.Uri);
-  }
-
 }
 
 package android.telephony {
 
+  public class CarrierConfigManager {
+    method public android.os.Bundle getConfig();
+    method public android.os.Bundle getConfigForSubId(int);
+    method public static android.os.Bundle getDefaultConfig();
+    method public void reloadCarrierConfigForSubId(int);
+    method public void updateConfigForPhoneId(int, java.lang.String);
+    field public static final java.lang.String ACTION_CARRIER_CONFIG_CHANGED = "android.telephony.action.CARRIER_CONFIG_CHANGED";
+    field public static final java.lang.String BOOL_CARRIER_VOLTE_AVAILABLE = "bool_carrier_volte_available";
+    field public static final java.lang.String BOOL_CARRIER_VOLTE_PROVISIONED = "bool_carrier_volte_provisioned";
+    field public static final java.lang.String BOOL_CARRIER_VOLTE_TTY_SUPPORTED = "bool_carrier_volte_tty_supported";
+    field public static final java.lang.String BOOL_SHOW_APN_SETTING_CDMA = "bool_show_apn_setting_cdma";
+    field public static final java.lang.String INT_VOLTE_REPLACEMENT_RAT = "int_volte_replacement_rat";
+    field public static final java.lang.String SHORT_VVM_PORT_NUMBER = "string_vvm_port_number";
+    field public static final java.lang.String STRING_VVM_DESTINATION_NUMBER = "string_vvm_destination_number";
+    field public static final java.lang.String STRING_VVM_TYPE = "string_vvm_type";
+    field public static final java.lang.String VVM_TYPE_OMTP = "vvm_type_omtp";
+  }
+
   public final class CellIdentityCdma implements android.os.Parcelable {
     method public int describeContents();
     method public int getBasestationId();
@@ -32915,6 +33214,7 @@
     method public boolean getDataEnabled(int);
     method public int getDataState();
     method public java.lang.String getDeviceId();
+    method public java.lang.String getDeviceId(int);
     method public java.lang.String getDeviceSoftwareVersion();
     method public java.lang.String getGroupIdLevel1();
     method public java.lang.String getLine1Number();
@@ -32925,6 +33225,7 @@
     method public java.lang.String getNetworkOperator();
     method public java.lang.String getNetworkOperatorName();
     method public int getNetworkType();
+    method public int getPhoneCount();
     method public int getPhoneType();
     method public java.lang.String getSimCountryIso();
     method public java.lang.String getSimOperator();
@@ -32958,6 +33259,7 @@
     method public boolean isWorldPhone();
     method public void listen(android.telephony.PhoneStateListener, int);
     method public boolean needsOtaServiceProvisioning();
+    method public void notifyCarrierNetworkChange(boolean);
     method public java.lang.String sendEnvelopeWithStatus(java.lang.String);
     method public void setDataEnabled(boolean);
     method public void setDataEnabled(int, boolean);
@@ -36473,6 +36775,7 @@
     method public boolean onTouchEvent(android.view.MotionEvent);
     method public void setIsLongpressEnabled(boolean);
     method public void setOnDoubleTapListener(android.view.GestureDetector.OnDoubleTapListener);
+    method public void setOnStylusButtonPressListener(android.view.GestureDetector.OnStylusButtonPressListener);
   }
 
   public static abstract interface GestureDetector.OnDoubleTapListener {
@@ -36490,7 +36793,11 @@
     method public abstract boolean onSingleTapUp(android.view.MotionEvent);
   }
 
-  public static class GestureDetector.SimpleOnGestureListener implements android.view.GestureDetector.OnDoubleTapListener android.view.GestureDetector.OnGestureListener {
+  public static abstract interface GestureDetector.OnStylusButtonPressListener {
+    method public abstract boolean onStylusButtonPress(android.view.MotionEvent);
+  }
+
+  public static class GestureDetector.SimpleOnGestureListener implements android.view.GestureDetector.OnDoubleTapListener android.view.GestureDetector.OnGestureListener android.view.GestureDetector.OnStylusButtonPressListener {
     ctor public GestureDetector.SimpleOnGestureListener();
     method public boolean onDoubleTap(android.view.MotionEvent);
     method public boolean onDoubleTapEvent(android.view.MotionEvent);
@@ -36501,6 +36808,7 @@
     method public void onShowPress(android.view.MotionEvent);
     method public boolean onSingleTapConfirmed(android.view.MotionEvent);
     method public boolean onSingleTapUp(android.view.MotionEvent);
+    method public boolean onStylusButtonPress(android.view.MotionEvent);
   }
 
   public class Gravity {
@@ -39151,6 +39459,7 @@
     field public static final int TITLE_CHANGED = 64; // 0x40
     field public static final int TYPE_ACCESSIBILITY_OVERLAY = 2032; // 0x7f0
     field public static final int TYPE_APPLICATION = 2; // 0x2
+    field public static final int TYPE_APPLICATION_ABOVE_SUB_PANEL = 1005; // 0x3ed
     field public static final int TYPE_APPLICATION_ATTACHED_DIALOG = 1003; // 0x3eb
     field public static final int TYPE_APPLICATION_MEDIA = 1001; // 0x3e9
     field public static final int TYPE_APPLICATION_PANEL = 1000; // 0x3e8
@@ -43562,6 +43871,7 @@
     method public int getInputType();
     method public final android.text.method.KeyListener getKeyListener();
     method public final android.text.Layout getLayout();
+    method public int[] getLeftIndents();
     method public float getLetterSpacing();
     method public int getLineBounds(int, android.graphics.Rect);
     method public int getLineCount();
@@ -43584,6 +43894,7 @@
     method public android.text.TextPaint getPaint();
     method public int getPaintFlags();
     method public java.lang.String getPrivateImeOptions();
+    method public int[] getRightIndents();
     method public int getSelectionEnd();
     method public int getSelectionStart();
     method public int getShadowColor();
@@ -43661,6 +43972,7 @@
     method public void setImeActionLabel(java.lang.CharSequence, int);
     method public void setImeOptions(int);
     method public void setIncludeFontPadding(boolean);
+    method public void setIndents(int[], int[]);
     method public void setInputExtras(int) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
     method public void setInputType(int);
     method public void setKeyListener(android.text.method.KeyListener);
diff --git a/cmds/am/src/com/android/commands/am/Am.java b/cmds/am/src/com/android/commands/am/Am.java
index fa28143..8ba2a5a 100644
--- a/cmds/am/src/com/android/commands/am/Am.java
+++ b/cmds/am/src/com/android/commands/am/Am.java
@@ -857,6 +857,11 @@
                             "Error: Activity not started, voice control not allowed for: "
                                     + intent);
                     break;
+                case ActivityManager.START_NOT_CURRENT_USER_ACTIVITY:
+                    out.println(
+                            "Error: Not allowed to start background user activity"
+                            + " that shouldn't be displayed for all users.");
+                    break;
                 default:
                     out.println(
                             "Error: Activity not started, unknown error code " + res);
diff --git a/cmds/app_process/Android.mk b/cmds/app_process/Android.mk
index dd5e0ea..ce6d7b5 100644
--- a/cmds/app_process/Android.mk
+++ b/cmds/app_process/Android.mk
@@ -56,6 +56,7 @@
 LOCAL_MODULE_PATH := $(TARGET_OUT_EXECUTABLES)/asan
 LOCAL_MODULE_STEM := app_process
 LOCAL_ADDRESS_SANITIZER := true
+LOCAL_CLANG := true
 
 LOCAL_CFLAGS += -Wall -Werror -Wunused -Wunreachable-code
 
diff --git a/cmds/wm/src/com/android/commands/wm/Wm.java b/cmds/wm/src/com/android/commands/wm/Wm.java
index 815a0ac..64f023f 100644
--- a/cmds/wm/src/com/android/commands/wm/Wm.java
+++ b/cmds/wm/src/com/android/commands/wm/Wm.java
@@ -24,6 +24,7 @@
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.util.AndroidException;
+import android.util.DisplayMetrics;
 import android.view.Display;
 import android.view.IWindowManager;
 import com.android.internal.os.BaseCommand;
@@ -45,21 +46,27 @@
         (new Wm()).run(args);
     }
 
+    @Override
     public void onShowUsage(PrintStream out) {
         out.println(
                 "usage: wm [subcommand] [options]\n" +
-                "       wm size [reset|WxH]\n" +
+                "       wm size [reset|WxH|WdpxHdp]\n" +
                 "       wm density [reset|DENSITY]\n" +
                 "       wm overscan [reset|LEFT,TOP,RIGHT,BOTTOM]\n" +
+                "       wm scaling [off|auto]\n" +
                 "\n" +
                 "wm size: return or override display size.\n" +
+                "         width and height in pixels unless suffixed with 'dp'.\n" +
                 "\n" +
                 "wm density: override display density.\n" +
                 "\n" +
-                "wm overscan: set overscan area for display.\n"
+                "wm overscan: set overscan area for display.\n" +
+                "\n" +
+                "wm scaling: set display scaling mode.\n"
                 );
     }
 
+    @Override
     public void onRun() throws Exception {
         mWm = IWindowManager.Stub.asInterface(ServiceManager.checkService(
                         Context.WINDOW_SERVICE));
@@ -76,6 +83,8 @@
             runDisplayDensity();
         } else if (op.equals("overscan")) {
             runDisplayOverscan();
+        } else if (op.equals("scaling")) {
+            runDisplayScaling();
         } else {
             showError("Error: unknown command '" + op + "'");
             return;
@@ -85,6 +94,7 @@
     private void runDisplaySize() throws Exception {
         String size = nextArg();
         int w, h;
+        boolean scale = true;
         if (size == null) {
             Point initialSize = new Point();
             Point baseSize = new Point();
@@ -109,8 +119,8 @@
             String wstr = size.substring(0, div);
             String hstr = size.substring(div+1);
             try {
-                w = Integer.parseInt(wstr);
-                h = Integer.parseInt(hstr);
+                w = parseDimension(wstr);
+                h = parseDimension(hstr);
             } catch (NumberFormatException e) {
                 System.err.println("Error: bad number " + e);
                 return;
@@ -193,4 +203,32 @@
         } catch (RemoteException e) {
         }
     }
+
+    private void runDisplayScaling() throws Exception {
+        String scalingStr = nextArgRequired();
+        if ("auto".equals(scalingStr)) {
+            mWm.setForcedDisplayScalingMode(Display.DEFAULT_DISPLAY, 0);
+        } else if ("off".equals(scalingStr)) {
+            mWm.setForcedDisplayScalingMode(Display.DEFAULT_DISPLAY, 1);
+        } else {
+            System.err.println("Error: scaling must be 'auto' or 'off'");
+        }
+    }
+
+    private int parseDimension(String s) throws NumberFormatException {
+        if (s.endsWith("px")) {
+            return Integer.parseInt(s.substring(0, s.length() - 2));
+        }
+        if (s.endsWith("dp")) {
+            int density;
+            try {
+                density = mWm.getBaseDisplayDensity(Display.DEFAULT_DISPLAY);
+            } catch (RemoteException e) {
+                density = DisplayMetrics.DENSITY_DEFAULT;
+            }
+            return Integer.parseInt(s.substring(0, s.length() - 2)) * density /
+                    DisplayMetrics.DENSITY_DEFAULT;
+        }
+        return Integer.parseInt(s);
+    }
 }
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 6cf6481..4ccde1c 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -6434,18 +6434,22 @@
      * Request to put this Activity in a mode where the user is locked to the
      * current task.
      *
-     * This will prevent the user from launching other apps, going to settings,
-     * or reaching the home screen.
+     * This will prevent the user from launching other apps, going to settings, or reaching the
+     * home screen. This does not include those apps whose {@link android.R.attr#lockTaskMode}
+     * values permit launching while locked.
      *
-     * If {@link DevicePolicyManager#isLockTaskPermitted(String)} returns true
-     * for this component then the app will go directly into Lock Task mode.  The user
-     * will not be able to exit this mode until {@link Activity#stopLockTask()} is called.
+     * If {@link DevicePolicyManager#isLockTaskPermitted(String)} returns true or
+     * lockTaskMode=lockTaskModeAlways for this component then the app will go directly into
+     * Lock Task mode. The user will not be able to exit this mode until
+     * {@link Activity#stopLockTask()} is called.
      *
      * If {@link DevicePolicyManager#isLockTaskPermitted(String)} returns false
      * then the system will prompt the user with a dialog requesting permission to enter
      * this mode.  When entered through this method the user can exit at any time through
      * an action described by the request dialog.  Calling stopLockTask will also exit the
      * mode.
+     *
+     * @see android.R.attr#lockTaskMode
      */
     public void startLockTask() {
         try {
@@ -6462,6 +6466,14 @@
      * startLockTask previously.
      *
      * This will allow the user to exit this app and move onto other activities.
+     * <p>Note: This method should only be called when the activity is user-facing. That is,
+     * between onResume() and onPause().
+     * <p>Note: If there are other tasks below this one that are also locked then calling this
+     * method will immediately finish this task and resume the previous locked one, remaining in
+     * lockTask mode.
+     *
+     * @see android.R.attr#lockTaskMode
+     * @see ActivityManager#getLockTaskModeState()
      */
     public void stopLockTask() {
         try {
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index 51ececc..576a046 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -86,6 +86,13 @@
     public static final String META_HOME_ALTERNATE = "android.app.home.alternate";
 
     /**
+     * Result for IActivityManager.startActivity: trying to start a background user
+     * activity that shouldn't be displayed for all users.
+     * @hide
+     */
+    public static final int START_NOT_CURRENT_USER_ACTIVITY = -8;
+
+    /**
      * Result for IActivityManager.startActivity: trying to start an activity under voice
      * control when that activity does not support the VOICE category.
      * @hide
@@ -862,6 +869,23 @@
          */
         public int affiliatedTaskColor;
 
+        /**
+         * The component launched as the first activity in the task.
+         * This can be considered the "application" of this task.
+         */
+        public ComponentName baseActivity;
+
+        /**
+         * The activity component at the top of the history stack of the task.
+         * This is what the user is currently doing.
+         */
+        public ComponentName topActivity;
+
+        /**
+         * Number of activities in this task.
+         */
+        public int numActivities;
+
         public RecentTaskInfo() {
         }
 
@@ -895,6 +919,9 @@
             dest.writeLong(lastActiveTime);
             dest.writeInt(affiliatedTaskId);
             dest.writeInt(affiliatedTaskColor);
+            ComponentName.writeToParcel(baseActivity, dest);
+            ComponentName.writeToParcel(topActivity, dest);
+            dest.writeInt(numActivities);
         }
 
         public void readFromParcel(Parcel source) {
@@ -911,6 +938,9 @@
             lastActiveTime = source.readLong();
             affiliatedTaskId = source.readInt();
             affiliatedTaskColor = source.readInt();
+            baseActivity = ComponentName.readFromParcel(source);
+            topActivity = ComponentName.readFromParcel(source);
+            numActivities = source.readInt();
         }
 
         public static final Creator<RecentTaskInfo> CREATOR
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index 907ae26..b9ddff0 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -67,6 +67,8 @@
 import android.os.SystemProperties;
 import android.os.UserHandle;
 import android.os.UserManager;
+import android.os.storage.StorageManager;
+import android.os.storage.VolumeInfo;
 import android.util.ArrayMap;
 import android.util.Log;
 import android.view.Display;
@@ -771,10 +773,12 @@
                                     .getCompatibilityInfo().applicationScale,
                             e);
                 }
-                if (DEBUG_ICONS)
+                if (DEBUG_ICONS) {
                     Log.v(TAG, "Getting drawable 0x"
                             + Integer.toHexString(resId) + " from " + r
                             + ": " + dr);
+                }
+                return dr;
             } catch (NameNotFoundException e) {
                 Log.w("PackageManager", "Failure retrieving resources for "
                         + appInfo.packageName);
@@ -1425,6 +1429,61 @@
     }
 
     @Override
+    public @NonNull VolumeInfo getApplicationCurrentVolume(ApplicationInfo app) {
+        final StorageManager storage = mContext.getSystemService(StorageManager.class);
+        if (app.isInternal()) {
+            return Preconditions.checkNotNull(
+                    storage.findVolumeById(VolumeInfo.ID_PRIVATE_INTERNAL));
+        } else if (app.isExternalAsec()) {
+            final List<VolumeInfo> vols = storage.getVolumes();
+            for (VolumeInfo vol : vols) {
+                if ((vol.getType() == VolumeInfo.TYPE_PUBLIC) && vol.isPrimary()) {
+                    return vol;
+                }
+            }
+            throw new IllegalStateException("Failed to find primary public volume");
+        } else {
+            return Preconditions.checkNotNull(storage.findVolumeByUuid(app.volumeUuid));
+        }
+    }
+
+    @Override
+    public @NonNull List<VolumeInfo> getApplicationCandidateVolumes(ApplicationInfo app) {
+        final StorageManager storage = mContext.getSystemService(StorageManager.class);
+        final List<VolumeInfo> vols = storage.getVolumes();
+        final List<VolumeInfo> candidates = new ArrayList<>();
+        for (VolumeInfo vol : vols) {
+            if (isCandidateVolume(app, vol)) {
+                candidates.add(vol);
+            }
+        }
+        return candidates;
+    }
+
+    private static boolean isCandidateVolume(ApplicationInfo app, VolumeInfo vol) {
+        // Private internal is always an option
+        if (VolumeInfo.ID_PRIVATE_INTERNAL.equals(vol.getId())) {
+            return true;
+        }
+
+        // System apps and apps demanding internal storage can't be moved
+        // anywhere else
+        if (app.isSystemApp()
+                || app.installLocation == PackageInfo.INSTALL_LOCATION_INTERNAL_ONLY) {
+            return false;
+        }
+
+        // Moving into an ASEC on public primary is only an option when app is
+        // internal, or already in ASEC
+        if ((vol.getType() == VolumeInfo.TYPE_PUBLIC) && vol.isPrimary()) {
+            return app.isInternal() || app.isExternalAsec();
+        }
+
+        // Otherwise we can move to any private volume
+        return (vol.getType() == VolumeInfo.TYPE_PRIVATE);
+    }
+
+    @Override
     public String getInstallerPackageName(String packageName) {
         try {
             return mPM.getInstallerPackageName(packageName);
diff --git a/core/java/android/app/INotificationManager.aidl b/core/java/android/app/INotificationManager.aidl
index e2230da..913159a 100644
--- a/core/java/android/app/INotificationManager.aidl
+++ b/core/java/android/app/INotificationManager.aidl
@@ -17,8 +17,10 @@
 
 package android.app;
 
+import android.app.INotificationManagerCallback;
 import android.app.ITransientNotification;
 import android.app.Notification;
+import android.app.NotificationManager;
 import android.content.ComponentName;
 import android.content.Intent;
 import android.content.pm.ParceledListSlice;
@@ -71,6 +73,7 @@
     void requestInterruptionFilterFromListener(in INotificationListener token, int interruptionFilter);
     int getInterruptionFilterFromListener(in INotificationListener token);
     void setOnNotificationPostedTrimFromListener(in INotificationListener token, int trim);
+    NotificationManager.Policy.Token getPolicyTokenFromListener(in INotificationListener listener);
 
     ComponentName getEffectsSuppressor();
     boolean matchesCallFilter(in Bundle extras);
@@ -82,4 +85,8 @@
     oneway void setZenMode(int mode, in Uri conditionId, String reason);
     oneway void notifyConditions(String pkg, in IConditionProvider provider, in Condition[] conditions);
     oneway void requestZenModeConditions(in IConditionListener callback, int relevance);
+    oneway void requestNotificationPolicyToken(String pkg, in INotificationManagerCallback callback);
+    boolean isNotificationPolicyTokenValid(String pkg, in NotificationManager.Policy.Token token);
+    NotificationManager.Policy getNotificationPolicy(in NotificationManager.Policy.Token token);
+    void setNotificationPolicy(in NotificationManager.Policy.Token token, in NotificationManager.Policy policy);
 }
diff --git a/packages/SystemUI/res/drawable/ic_audio_phone.xml b/core/java/android/app/INotificationManagerCallback.aidl
similarity index 65%
copy from packages/SystemUI/res/drawable/ic_audio_phone.xml
copy to core/java/android/app/INotificationManagerCallback.aidl
index 64147f2b..b9414ca 100644
--- a/packages/SystemUI/res/drawable/ic_audio_phone.xml
+++ b/core/java/android/app/INotificationManagerCallback.aidl
@@ -1,7 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
- * Copyright 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.
@@ -15,9 +13,12 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
--->
 
-<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
-    android:src="@*android:drawable/ic_audio_phone_am_alpha"
-    android:autoMirrored="true"
-    android:tint="#ffffffff" />
\ No newline at end of file
+package android.app;
+
+import android.app.NotificationManager;
+
+/** @hide */
+oneway interface INotificationManagerCallback {
+    void onPolicyToken(in NotificationManager.Policy.Token token);
+}
diff --git a/core/java/android/app/Instrumentation.java b/core/java/android/app/Instrumentation.java
index fc96464..b77dec5 100644
--- a/core/java/android/app/Instrumentation.java
+++ b/core/java/android/app/Instrumentation.java
@@ -1797,6 +1797,10 @@
             case ActivityManager.START_NOT_VOICE_COMPATIBLE:
                 throw new SecurityException(
                         "Starting under voice control not allowed for: " + intent);
+            case ActivityManager.START_NOT_CURRENT_USER_ACTIVITY:
+                throw new SecurityException(
+                        "Not allowed to start background user activity that shouldn't be"
+                        + " displayed for all users.");
             default:
                 throw new AndroidRuntimeException("Unknown error code "
                         + res + " when starting " + intent);
diff --git a/packages/SystemUI/res/drawable/ic_audio_phone.xml b/core/java/android/app/NotificationManager.aidl
similarity index 65%
copy from packages/SystemUI/res/drawable/ic_audio_phone.xml
copy to core/java/android/app/NotificationManager.aidl
index 64147f2b..8380b8d 100644
--- a/packages/SystemUI/res/drawable/ic_audio_phone.xml
+++ b/core/java/android/app/NotificationManager.aidl
@@ -1,7 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
- * Copyright 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.
@@ -15,9 +13,8 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
--->
 
-<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
-    android:src="@*android:drawable/ic_audio_phone_am_alpha"
-    android:autoMirrored="true"
-    android:tint="#ffffffff" />
\ No newline at end of file
+package android.app;
+
+parcelable NotificationManager.Policy;
+parcelable NotificationManager.Policy.Token;
\ No newline at end of file
diff --git a/core/java/android/app/NotificationManager.java b/core/java/android/app/NotificationManager.java
index fa61e18..7133dce 100644
--- a/core/java/android/app/NotificationManager.java
+++ b/core/java/android/app/NotificationManager.java
@@ -16,14 +16,19 @@
 
 package android.app;
 
+import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.annotation.SdkConstant;
 import android.app.Notification.Builder;
+import android.app.NotificationManager.Policy.Token;
 import android.content.ComponentName;
 import android.content.Context;
 import android.net.Uri;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.IBinder;
+import android.os.Parcel;
+import android.os.Parcelable;
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.os.StrictMode;
@@ -33,6 +38,8 @@
 import android.service.notification.ZenModeConfig;
 import android.util.Log;
 
+import java.util.Objects;
+
 /**
  * Class to notify the user of events that happen.  This is how you tell
  * the user that something has happened in the background. {@more}
@@ -89,6 +96,14 @@
     public static final String ACTION_EFFECTS_SUPPRESSOR_CHANGED
             = "android.os.action.ACTION_EFFECTS_SUPPRESSOR_CHANGED";
 
+    /**
+     * Intent that is broadcast when the state of getNotificationPolicy() changes.
+     * This broadcast is only sent to registered receivers.
+     */
+    @SdkConstant(SdkConstant.SdkConstantType.BROADCAST_INTENT_ACTION)
+    public static final String ACTION_NOTIFICATION_POLICY_CHANGED
+            = "android.app.action.NOTIFICATION_POLICY_CHANGED";
+
     private static INotificationManager sService;
 
     /** @hide */
@@ -338,5 +353,293 @@
         return null;
     }
 
+    /**
+     * Requests a notification policy token for the calling package.
+     *
+     * @param callback required, used to receive the granted token or the deny signal.
+     * @param handler The handler used when receiving the result.
+     *                If null, the current thread is used.
+     */
+    public void requestNotificationPolicyToken(@NonNull final Policy.Token.RequestCallback callback,
+            @Nullable Handler handler) {
+        checkRequired("callback", callback);
+        final Handler h = handler != null ? handler : new Handler();
+        INotificationManager service = getService();
+        try {
+            service.requestNotificationPolicyToken(mContext.getOpPackageName(),
+                    new INotificationManagerCallback.Stub() {
+                @Override
+                public void onPolicyToken(final Token token) throws RemoteException {
+                    h.post(new Runnable() {
+                        @Override
+                        public void run() {
+                            if (token != null) {
+                                callback.onTokenGranted(token);
+                            } else {
+                                callback.onTokenDenied();
+                            }
+                        }
+                    });
+                }
+            });
+        } catch (RemoteException e) {
+        }
+    }
+
+    /**
+     * Checks a given notification policy token.
+     *
+     * Returns true if the token is still valid for managing policy.
+     */
+    public boolean isNotificationPolicyTokenValid(@NonNull Policy.Token token) {
+        if (token == null) return false;
+        INotificationManager service = getService();
+        try {
+            return service.isNotificationPolicyTokenValid(mContext.getOpPackageName(), token);
+        } catch (RemoteException e) {
+        }
+        return false;
+    }
+
+    /**
+     * Gets the current notification policy.
+     *
+     * @param token A valid notification policy token is required to access the current policy.
+     */
+    public Policy getNotificationPolicy(@NonNull Policy.Token token) {
+        checkRequired("token", token);
+        INotificationManager service = getService();
+        try {
+            return service.getNotificationPolicy(token);
+        } catch (RemoteException e) {
+        }
+        return null;
+    }
+
+    /**
+     * Sets the current notification policy.
+     *
+     * @param token  A valid notification policy token is required to modify the current policy.
+     * @param policy The new desired policy.
+     */
+    public void setNotificationPolicy(@NonNull Policy.Token token, @NonNull Policy policy) {
+        checkRequired("token", token);
+        checkRequired("policy", policy);
+        INotificationManager service = getService();
+        try {
+            service.setNotificationPolicy(token, policy);
+        } catch (RemoteException e) {
+        }
+    }
+
     private Context mContext;
+
+    private static void checkRequired(String name, Object value) {
+        if (value == null) {
+            throw new IllegalArgumentException(name + " is required");
+        }
+    }
+
+    /**
+     * Notification policy configuration.  Represents user-preferences for notification
+     * filtering and prioritization.
+     */
+    public static class Policy implements android.os.Parcelable {
+        /** Reminder notifications are prioritized. */
+        public static final int PRIORITY_CATEGORY_REMINDERS = 1 << 0;
+        /** Event notifications are prioritized. */
+        public static final int PRIORITY_CATEGORY_EVENTS = 1 << 1;
+        /** Message notifications are prioritized. */
+        public static final int PRIORITY_CATEGORY_MESSAGES = 1 << 2;
+        /** Calls are prioritized. */
+        public static final int PRIORITY_CATEGORY_CALLS = 1 << 3;
+        /** Calls from repeat callers are prioritized. */
+        public static final int PRIORITY_CATEGORY_REPEAT_CALLERS = 1 << 4;
+
+        private static final int[] ALL_PRIORITY_CATEGORIES = {
+            PRIORITY_CATEGORY_REMINDERS,
+            PRIORITY_CATEGORY_EVENTS,
+            PRIORITY_CATEGORY_MESSAGES,
+            PRIORITY_CATEGORY_CALLS,
+            PRIORITY_CATEGORY_REPEAT_CALLERS,
+        };
+
+        /** Any sender is prioritized. */
+        public static final int PRIORITY_SENDERS_ANY = 0;
+        /** Saved contacts are prioritized. */
+        public static final int PRIORITY_SENDERS_CONTACTS = 1;
+        /** Only starred contacts are prioritized. */
+        public static final int PRIORITY_SENDERS_STARRED = 2;
+
+        /** Notification categories to prioritize. Bitmask of PRIORITY_CATEGORY_* constants. */
+        public final int priorityCategories;
+
+        /** Notification senders to prioritize. One of:
+         * PRIORITY_SENDERS_ANY, PRIORITY_SENDERS_CONTACTS, PRIORITY_SENDERS_STARRED */
+        public final int prioritySenders;
+
+        public Policy(int priorityCategories, int prioritySenders) {
+            this.priorityCategories = priorityCategories;
+            this.prioritySenders = prioritySenders;
+        }
+
+        /** @hide */
+        public Policy(Parcel source) {
+            this(source.readInt(), source.readInt());
+        }
+
+        @Override
+        public void writeToParcel(Parcel dest, int flags) {
+            dest.writeInt(priorityCategories);
+            dest.writeInt(prioritySenders);
+        }
+
+        @Override
+        public int describeContents() {
+            return 0;
+        }
+
+        @Override
+        public int hashCode() {
+            return Objects.hash(priorityCategories, prioritySenders);
+        }
+
+        @Override
+        public boolean equals(Object o) {
+            if (!(o instanceof Policy)) return false;
+            if (o == this) return true;
+            final Policy other = (Policy) o;
+            return other.priorityCategories == priorityCategories
+                    && other.prioritySenders == prioritySenders;
+        }
+
+        @Override
+        public String toString() {
+            return "NotificationManager.Policy["
+                    + "priorityCategories=" + priorityCategoriesToString(priorityCategories)
+                    + ",prioritySenders=" + prioritySendersToString(prioritySenders)
+                    + "]";
+        }
+
+        public static String priorityCategoriesToString(int priorityCategories) {
+            if (priorityCategories == 0) return "";
+            final StringBuilder sb = new StringBuilder();
+            for (int i = 0; i < ALL_PRIORITY_CATEGORIES.length; i++) {
+                final int priorityCategory = ALL_PRIORITY_CATEGORIES[i];
+                if ((priorityCategories & priorityCategory) != 0) {
+                    if (sb.length() > 0) sb.append(',');
+                    sb.append(priorityCategoryToString(priorityCategory));
+                }
+                priorityCategories &= ~priorityCategory;
+            }
+            if (priorityCategories != 0) {
+                if (sb.length() > 0) sb.append(',');
+                sb.append("PRIORITY_CATEGORY_UNKNOWN_").append(priorityCategories);
+            }
+            return sb.toString();
+        }
+
+        private static String priorityCategoryToString(int priorityCategory) {
+            switch (priorityCategory) {
+                case PRIORITY_CATEGORY_REMINDERS: return "PRIORITY_CATEGORY_REMINDERS";
+                case PRIORITY_CATEGORY_EVENTS: return "PRIORITY_CATEGORY_EVENTS";
+                case PRIORITY_CATEGORY_MESSAGES: return "PRIORITY_CATEGORY_MESSAGES";
+                case PRIORITY_CATEGORY_CALLS: return "PRIORITY_CATEGORY_CALLS";
+                case PRIORITY_CATEGORY_REPEAT_CALLERS: return "PRIORITY_CATEGORY_REPEAT_CALLERS";
+                default: return "PRIORITY_CATEGORY_UNKNOWN_" + priorityCategory;
+            }
+        }
+
+        public static String prioritySendersToString(int prioritySenders) {
+            switch (prioritySenders) {
+                case PRIORITY_SENDERS_ANY: return "PRIORITY_SENDERS_ANY";
+                case PRIORITY_SENDERS_CONTACTS: return "PRIORITY_SENDERS_CONTACTS";
+                case PRIORITY_SENDERS_STARRED: return "PRIORITY_SENDERS_STARRED";
+                default: return "PRIORITY_SENDERS_UNKNOWN_" + prioritySenders;
+            }
+        }
+
+        public static final Parcelable.Creator<Policy> CREATOR = new Parcelable.Creator<Policy>() {
+            @Override
+            public Policy createFromParcel(Parcel in) {
+                return new Policy(in);
+            }
+
+            @Override
+            public Policy[] newArray(int size) {
+                return new Policy[size];
+            }
+        };
+
+        /**
+         * Represents a client-specific token required to manage notification policy.
+         */
+        public static class Token implements Parcelable {
+            private final IBinder mBinder;
+
+            /** @hide */
+            public Token(IBinder binder) {
+                if (binder == null) throw new IllegalArgumentException("Binder required for token");
+                mBinder = binder;
+            }
+
+            @Override
+            public int describeContents() {
+                return 0;
+            }
+
+            @Override
+            public int hashCode() {
+                return Objects.hash(mBinder);
+            }
+
+            @Override
+            public boolean equals(Object o) {
+                if (!(o instanceof Token)) return false;
+                if (o == this) return true;
+                final Token other = (Token) o;
+                return Objects.equals(other.mBinder, mBinder);
+            }
+
+            @Override
+            public String toString() {
+                return String.format("NotificationManager.Token[0x%08x]",
+                        System.identityHashCode(mBinder));
+            }
+
+            @Override
+            public void writeToParcel(Parcel dest, int flags) {
+                dest.writeStrongBinder(mBinder);
+            }
+
+            public static final Parcelable.Creator<Token> CREATOR
+                    = new Parcelable.Creator<Token>() {
+                @Override
+                public Token createFromParcel(Parcel in) {
+                    return new Token(in.readStrongBinder());
+                }
+
+                @Override
+                public Token[] newArray(int size) {
+                    return new Token[size];
+                }
+            };
+
+            /** Callback for receiving the result of a token request. */
+            public static abstract class RequestCallback {
+                /**
+                 * Received if the request was granted for this package.
+                 *
+                 * @param token can be used to manage notification policy.
+                 */
+                public abstract void onTokenGranted(Policy.Token token);
+
+                /**
+                 * Received if the request was denied for this package.
+                 */
+                public abstract void onTokenDenied();
+            }
+        }
+    }
+
 }
diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index b3aa6be..e446700 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -55,6 +55,7 @@
 import android.location.ICountryDetector;
 import android.location.ILocationManager;
 import android.location.LocationManager;
+import android.media.AudioDevicesManager;
 import android.media.AudioManager;
 import android.media.MediaRouter;
 import android.media.midi.IMidiManager;
@@ -104,6 +105,7 @@
 import android.service.persistentdata.IPersistentDataBlockService;
 import android.service.persistentdata.PersistentDataBlockManager;
 import android.telecom.TelecomManager;
+import android.telephony.CarrierConfigManager;
 import android.telephony.SubscriptionManager;
 import android.telephony.TelephonyManager;
 import android.util.Log;
@@ -419,6 +421,13 @@
                 return new SubscriptionManager(ctx.getOuterContext());
             }});
 
+        registerService(Context.CARRIER_CONFIG_SERVICE, CarrierConfigManager.class,
+                new CachedServiceFetcher<CarrierConfigManager>() {
+            @Override
+            public CarrierConfigManager createService(ContextImpl ctx) {
+                return new CarrierConfigManager();
+            }});
+
         registerService(Context.TELECOM_SERVICE, TelecomManager.class,
                 new CachedServiceFetcher<TelecomManager>() {
             @Override
@@ -693,6 +702,13 @@
             public RadioManager createService(ContextImpl ctx) {
                 return new RadioManager(ctx);
             }});
+
+        registerService(Context.AUDIO_DEVICES_SERVICE, AudioDevicesManager.class,
+                new CachedServiceFetcher<AudioDevicesManager>() {
+            @Override
+            public AudioDevicesManager createService(ContextImpl ctx) {
+                return new AudioDevicesManager(ctx);
+            }});
     }
 
     /**
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index a0a6c4c..44760ce 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -4210,4 +4210,20 @@
             return false;
         }
     }
+
+    /**
+     * Called by device owner to set the enabled state of the status bar. Disabling the status
+     * bar blocks notifications, quick settings and other screen overlays that allow escaping from
+     * a single use device.
+     *
+     * @param admin Which {@link DeviceAdminReceiver} this request is associated with.
+     * @param enabled New state of the status bar.
+     */
+    public void setStatusBarEnabledState(ComponentName admin, boolean enabled) {
+        try {
+            mService.setStatusBarEnabledState(admin, enabled);
+        } catch (RemoteException re) {
+            Log.w(TAG, "Failed talking with device policy service", re);
+        }
+    }
 }
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index 131b99c..7502e1d 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -223,4 +223,5 @@
     PersistableBundle getOtaPolicy();
 
     boolean setKeyguardEnabledState(in ComponentName admin, boolean enabled);
+    void setStatusBarEnabledState(in ComponentName who, boolean enabled);
 }
diff --git a/core/java/android/app/backup/BackupAgent.java b/core/java/android/app/backup/BackupAgent.java
index 2bf267a..d8556a2 100644
--- a/core/java/android/app/backup/BackupAgent.java
+++ b/core/java/android/app/backup/BackupAgent.java
@@ -283,6 +283,7 @@
         // all of the ones we will be traversing
         String rootDir = new File(appInfo.dataDir).getCanonicalPath();
         String filesDir = getFilesDir().getCanonicalPath();
+        String nobackupDir = getNoBackupFilesDir().getCanonicalPath();
         String databaseDir = getDatabasePath("foo").getParentFile().getCanonicalPath();
         String sharedPrefsDir = getSharedPrefsFile("foo").getParentFile().getCanonicalPath();
         String cacheDir = getCacheDir().getCanonicalPath();
@@ -304,6 +305,7 @@
         filterSet.add(databaseDir);
         filterSet.add(sharedPrefsDir);
         filterSet.add(filesDir);
+        filterSet.add(nobackupDir);
         fullBackupFileTree(packageName, FullBackup.ROOT_TREE_TOKEN, rootDir, filterSet, data);
 
         // Now do the same for the files dir, db dir, and shared prefs dir
diff --git a/core/java/android/app/trust/ITrustListener.aidl b/core/java/android/app/trust/ITrustListener.aidl
index d80f58c..506dd12 100644
--- a/core/java/android/app/trust/ITrustListener.aidl
+++ b/core/java/android/app/trust/ITrustListener.aidl
@@ -22,6 +22,6 @@
  * {@hide}
  */
 oneway interface ITrustListener {
-    void onTrustChanged(boolean enabled, int userId, boolean initiatedByUser);
+    void onTrustChanged(boolean enabled, int userId, int flags);
     void onTrustManagedChanged(boolean managed, int userId);
 }
\ No newline at end of file
diff --git a/core/java/android/app/trust/TrustManager.java b/core/java/android/app/trust/TrustManager.java
index 705a144..b5c5317 100644
--- a/core/java/android/app/trust/TrustManager.java
+++ b/core/java/android/app/trust/TrustManager.java
@@ -34,7 +34,7 @@
     private static final int MSG_TRUST_MANAGED_CHANGED = 2;
 
     private static final String TAG = "TrustManager";
-    private static final String DATA_INITIATED_BY_USER = "initiatedByUser";
+    private static final String DATA_FLAGS = "initiatedByUser";
 
     private final ITrustManager mService;
     private final ArrayMap<TrustListener, ITrustListener> mTrustListeners;
@@ -109,11 +109,11 @@
         try {
             ITrustListener.Stub iTrustListener = new ITrustListener.Stub() {
                 @Override
-                public void onTrustChanged(boolean enabled, int userId, boolean initiatedByUser) {
+                public void onTrustChanged(boolean enabled, int userId, int flags) {
                     Message m = mHandler.obtainMessage(MSG_TRUST_CHANGED, (enabled ? 1 : 0), userId,
                             trustListener);
-                    if (initiatedByUser) {
-                        m.getData().putBoolean(DATA_INITIATED_BY_USER, initiatedByUser);
+                    if (flags != 0) {
+                        m.getData().putInt(DATA_FLAGS, flags);
                     }
                     m.sendToTarget();
                 }
@@ -156,11 +156,8 @@
         public void handleMessage(Message msg) {
             switch(msg.what) {
                 case MSG_TRUST_CHANGED:
-                    boolean initiatedByUser = msg.peekData() != null &&
-                            msg.peekData().getBoolean(DATA_INITIATED_BY_USER);
-                    ((TrustListener)msg.obj).onTrustChanged(
-                            msg.arg1 != 0, msg.arg2, initiatedByUser);
-
+                    int flags = msg.peekData() != null ? msg.peekData().getInt(DATA_FLAGS) : 0;
+                    ((TrustListener)msg.obj).onTrustChanged(msg.arg1 != 0, msg.arg2, flags);
                     break;
                 case MSG_TRUST_MANAGED_CHANGED:
                     ((TrustListener)msg.obj).onTrustManagedChanged(msg.arg1 != 0, msg.arg2);
@@ -174,10 +171,11 @@
          * Reports that the trust state has changed.
          * @param enabled if true, the system believes the environment to be trusted.
          * @param userId the user, for which the trust changed.
-         * @param initiatedByUser indicates that the user has explicitly initiated an action that
-         *                        proves the user is about to use the device.
+         * @param flags flags specified by the trust agent when granting trust. See
+         *     {@link android.service.trust.TrustAgentService#grantTrust(CharSequence, long, int)
+         *                 TrustAgentService.grantTrust(CharSequence, long, int)}.
          */
-        void onTrustChanged(boolean enabled, int userId, boolean initiatedByUser);
+        void onTrustChanged(boolean enabled, int userId, int flags);
 
         /**
          * Reports that whether trust is managed has changed
diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java
index 2b3cf34..2418e82 100644
--- a/core/java/android/bluetooth/BluetoothAdapter.java
+++ b/core/java/android/bluetooth/BluetoothAdapter.java
@@ -1,5 +1,6 @@
 /*
- * Copyright (C) 2009-2014 The Android Open Source Project
+ * Copyright (C) 2009-2015 The Android Open Source Project
+ * Copyright (C) 2015 Samsung LSI
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -31,6 +32,9 @@
 import android.os.ParcelUuid;
 import android.os.RemoteException;
 import android.os.ServiceManager;
+import android.app.ActivityThread;
+import android.os.SystemProperties;
+import android.os.Binder;
 import android.util.Log;
 import android.util.Pair;
 
@@ -152,6 +156,24 @@
     public static final int STATE_TURNING_OFF = 13;
 
     /**
+     * Indicates the local Bluetooth adapter is turning Bluetooth LE mode on.
+     * @hide
+     */
+    public static final int STATE_BLE_TURNING_ON = 14;
+
+    /**
+     * Indicates the local Bluetooth adapter is in LE only mode.
+     * @hide
+     */
+    public static final int STATE_BLE_ON = 15;
+
+    /**
+     * Indicates the local Bluetooth adapter is turning off LE only mode.
+     * @hide
+     */
+    public static final int STATE_BLE_TURNING_OFF = 16;
+
+    /**
      * Activity Action: Show a system activity that requests discoverable mode.
      * This activity will also request the user to turn on Bluetooth if it
      * is not currently enabled.
@@ -362,6 +384,39 @@
     public static final String EXTRA_PREVIOUS_CONNECTION_STATE =
           "android.bluetooth.adapter.extra.PREVIOUS_CONNECTION_STATE";
 
+    /**
+     * Broadcast Action: The Bluetooth adapter state has changed in LE only mode.
+     * @hide
+     */
+    public static final String ACTION_BLE_STATE_CHANGED =
+        "anrdoid.bluetooth.adapter.action.BLE_STATE_CHANGED";
+
+    /**
+     * Broadcast Action: The notifys Bluetooth ACL connected event. This will be
+     * by BLE Always on enabled application to know the ACL_CONNECTED event
+     * when Bluetooth state in STATE_BLE_ON. This denotes GATT connection
+     * as Bluetooth LE is the only feature available in STATE_BLE_ON
+     *
+     * This is counterpart of {@link BluetoothDevice#ACTION_ACL_CONNECTED} which
+     * works in Bluetooth state STATE_ON
+     * @hide
+     */
+    public static final String ACTION_BLE_ACL_CONNECTED =
+        "android.bluetooth.adapter.action.BLE_ACL_CONNECTED";
+
+    /**
+     * Broadcast Action: The notifys Bluetooth ACL connected event. This will be
+     * by BLE Always on enabled application to know the ACL_DISCONNECTED event
+     * when Bluetooth state in STATE_BLE_ON. This denotes GATT disconnection as Bluetooth
+     * LE is the only feature available in STATE_BLE_ON
+     *
+     * This is counterpart of {@link BluetoothDevice#ACTION_ACL_DISCONNECTED} which
+     * works in Bluetooth state STATE_ON
+     * @hide
+     */
+    public static final String ACTION_BLE_ACL_DISCONNECTED =
+        "android.bluetooth.adapter.action.BLE_ACL_DISCONNECTED";
+
     /** The profile is in disconnected state */
     public static final int STATE_DISCONNECTED  = 0;
     /** The profile is in connecting state */
@@ -373,6 +428,19 @@
 
     /** @hide */
     public static final String BLUETOOTH_MANAGER_SERVICE = "bluetooth_manager";
+    private final IBinder mToken;
+
+
+    /** When creating a ServerSocket using listenUsingRfcommOn() or
+     *  listenUsingL2capOn() use SOCKET_CHANNEL_AUTO_STATIC to create
+     *  a ServerSocket that auto assigns a channel number to the first
+     *  bluetooth socket.
+     *  The channel number assigned to this first Bluetooth Socket will
+     *  be stored in the ServerSocket, and reused for subsequent Bluetooth
+     *  sockets.
+     * @hide */
+    public static final int SOCKET_CHANNEL_AUTO_STATIC_NO_SDP = -2;
+
 
     private static final int ADDRESS_LENGTH = 17;
 
@@ -431,6 +499,7 @@
         } catch (RemoteException e) {Log.e(TAG, "", e);}
         mManagerService = managerService;
         mLeScanClients = new HashMap<LeScanCallback, ScanCallback>();
+        mToken = new Binder();
     }
 
     /**
@@ -477,11 +546,9 @@
      * on this device before calling this method.
      */
     public BluetoothLeAdvertiser getBluetoothLeAdvertiser() {
-        if (getState() != STATE_ON) {
-            return null;
-        }
+        if (!getLeAccess()) return null;
         if (!isMultipleAdvertisementSupported() && !isPeripheralModeSupported()) {
-            Log.e(TAG, "bluetooth le advertising not supported");
+            Log.e(TAG, "Bluetooth LE advertising not supported");
             return null;
         }
         synchronized(mLock) {
@@ -496,9 +563,7 @@
      * Returns a {@link BluetoothLeScanner} object for Bluetooth LE scan operations.
      */
     public BluetoothLeScanner getBluetoothLeScanner() {
-        if (getState() != STATE_ON) {
-            return null;
-        }
+        if (!getLeAccess()) return null;
         synchronized(mLock) {
             if (sBluetoothLeScanner == null) {
                 sBluetoothLeScanner = new BluetoothLeScanner(mManagerService);
@@ -516,7 +581,6 @@
      * @return true if the local adapter is turned on
      */
     public boolean isEnabled() {
-
         try {
             synchronized(mManagerCallback) {
                 if (mService != null) return mService.isEnabled();
@@ -526,6 +590,178 @@
     }
 
     /**
+     * Return true if Bluetooth LE(Always BLE On feature) is currently
+     * enabled and ready for use
+     * <p>This returns true if current state is either STATE_ON or STATE_BLE_ON
+     *
+     * @return true if the local Bluetooth LE adapter is turned on
+     * @hide
+     */
+     public boolean isLeEnabled() {
+        final int state = getLeState();
+        if (state == BluetoothAdapter.STATE_ON) {
+            if (DBG) Log.d (TAG, "STATE_ON");
+        } else if (state == BluetoothAdapter.STATE_BLE_ON) {
+            if (DBG) Log.d (TAG, "STATE_BLE_ON");
+        } else {
+            if (DBG) Log.d (TAG, "STATE_OFF");
+            return false;
+        }
+        return true;
+    }
+
+    /**
+     * Performs action based on user action to turn BT ON
+     * or OFF if BT is in BLE_ON state
+     */
+    private void notifyUserAction(boolean enable) {
+        if (mService == null) {
+            Log.e(TAG, "mService is null");
+            return;
+        }
+
+        try {
+            if (enable) {
+                mService.onLeServiceUp(); //NA:TODO implementation pending
+            } else {
+                mService.onBrEdrDown(); //NA:TODO implementation pending
+            }
+        } catch (RemoteException e) {
+            Log.e(TAG, "", e);
+        }
+    }
+
+    /**
+     * Returns true if LE only mode is enabled, that is apps
+     * have authorization to turn only BT ON and the calling
+     * app has privilage to do so
+     */
+    private boolean isLEAlwaysOnEnabled() {
+        boolean ret = false;
+        if (SystemProperties.getBoolean("ro.bluetooth.blealwayson", true) == true) {
+            Log.v(TAG, "LE always on mode is enabled");
+            // TODO: System API authorization check
+            ret = true;
+        } else {
+            Log.v(TAG, "LE always on mode is disabled");
+            ret = false;
+        }
+        return ret;
+    }
+
+    /**
+     * Turns off Bluetooth LE which was earlier turned on by calling EnableBLE().
+     *
+     * <p> If the internal Adapter state is STATE_BLE_ON, this would trigger the transition
+     * to STATE_OFF and completely shut-down Bluetooth
+     *
+     * <p> If the Adapter state is STATE_ON, This would unregister the existance of
+     * special Bluetooth LE application and hence the further turning off of Bluetooth
+     * from UI would ensure the complete turn-off of Bluetooth rather than staying back
+     * BLE only state
+     *
+     * <p>This is an asynchronous call: it will return immediately, and
+     * clients should listen for {@link #ACTION_BLE_STATE_CHANGED}
+     * to be notified of subsequent adapter state changes If this call returns
+     * true, then the adapter state will immediately transition from {@link
+     * #STATE_ON} to {@link #STATE_TURNING_OFF}, and some time
+     * later transition to either {@link #STATE_BLE_ON} or {@link
+     * #STATE_OFF} based on the existance of the further Always BLE ON enabled applications
+     * If this call returns false then there was an
+     * immediate problem that will prevent the QAdapter from being turned off -
+     * such as the QAadapter already being turned off.
+     *
+     * @return true to indicate success, or false on
+     *         immediate error
+     * @hide
+     */
+    public boolean disableBLE() {
+        if (isLEAlwaysOnEnabled() != true) return false;
+
+        int state = getLeState();
+        if (state == BluetoothAdapter.STATE_ON) {
+            if (DBG) Log.d (TAG, "STATE_ON: shouldn't disable");
+            try {
+                mManagerService.updateBleAppCount(mToken, false);
+            } catch (RemoteException e) {
+                Log.e(TAG, "", e);
+            }
+            return true;
+
+        } else if (state == BluetoothAdapter.STATE_BLE_ON) {
+            if (DBG) Log.d (TAG, "STATE_BLE_ON");
+            int bleAppCnt = 0;
+            try {
+                bleAppCnt = mManagerService.updateBleAppCount(mToken, false);
+            } catch (RemoteException e) {
+                Log.e(TAG, "", e);
+            }
+            if (bleAppCnt == 0) {
+                // Disable only if there are no other clients
+                notifyUserAction(false);
+            }
+            return true;
+        }
+
+        if (DBG) Log.d (TAG, "STATE_OFF: Already disabled");
+        return false;
+    }
+
+    /**
+     * Special Applications who want to only turn on Bluetooth Low Energy (BLE) would
+     * EnableBLE, EnableBLE brings-up Bluetooth so that application can access
+     * only LE related feature (Bluetooth GATT layers interfaces using the respective class)
+     * EnableBLE in turn registers the existance of a special App which wants to
+     * turn on Bluetooth Low enrgy part without making it visible at the settings UI
+     * as Bluetooth ON.
+     * <p>Invoking EnableBLE when Bluetooth is already in ON state, would just registers
+     * the existance of special Application and doesn't do anything to current BT state.
+     * when user turn OFF Bluetooth from UI, if there is an existance of special app, Bluetooth
+     * would stay in BLE_ON state so that LE features are still acessible to the special
+     * Applications.
+     *
+     * <p>This is an asynchronous call: it will return immediately, and
+     * clients should listen for {@link #ACTION_BLE_STATE_CHANGED}
+     * to be notified of subsequent adapter state changes. If this call returns
+     * true, then the adapter state will immediately transition from {@link
+     * #STATE_OFF} to {@link #STATE_BLE_TURNING_ON}, and some time
+     * later transition to either {@link #STATE_OFF} or {@link
+     * #STATE_BLE_ON}. If this call returns false then there was an
+     * immediate problem that will prevent the adapter from being turned on -
+     * such as Airplane mode, or the adapter is already turned on.
+     * (@link #ACTION_BLE_STATE_CHANGED) returns the Bluetooth Adapter's various
+     * states, It includes all the classic Bluetooth Adapter states along with
+     * internal BLE only states
+     *
+     * @return true to indicate Bluetooth LE start-up has begun, or false on
+     *         immediate error
+     * @hide
+     */
+    public boolean enableBLE() {
+        if (isLEAlwaysOnEnabled() != true) return false;
+
+        if (isLeEnabled() == true) {
+            if (DBG) Log.d(TAG, "enableBLE(): BT is already enabled..!");
+            try {
+                mManagerService.updateBleAppCount(mToken, true);
+            } catch (RemoteException e) {
+                Log.e(TAG, "", e);
+            }
+            return true;
+        }
+
+        try {
+            if (DBG) Log.d(TAG, "Calling enableBLE");
+            mManagerService.updateBleAppCount(mToken, true);
+            return mManagerService.enable();
+        } catch (RemoteException e) {
+            Log.e(TAG, "", e);
+        }
+
+        return false;
+    }
+
+    /**
      * Get the current state of the local Bluetooth adapter.
      * <p>Possible return values are
      * {@link #STATE_OFF},
@@ -543,6 +779,13 @@
                 {
                     int state=  mService.getState();
                     if (VDBG) Log.d(TAG, "" + hashCode() + ": getState(). Returning " + state);
+                    //consider all internal states as OFF
+                    if (state == BluetoothAdapter.STATE_BLE_ON
+                        || state == BluetoothAdapter.STATE_BLE_TURNING_ON
+                        || state == BluetoothAdapter.STATE_BLE_TURNING_OFF) {
+                        if (VDBG) Log.d(TAG, "Consider internal state as OFF");
+                        state = BluetoothAdapter.STATE_OFF;
+                    }
                     return state;
                 }
                 // TODO(BT) there might be a small gap during STATE_TURNING_ON that
@@ -554,6 +797,49 @@
     }
 
     /**
+     * Get the current state of the local Bluetooth adapter
+     * <p>This returns current internal state of Adapter including LE ON/OFF
+     *
+     * <p>Possible return values are
+     * {@link #STATE_OFF},
+     * {@link #STATE_BLE_TURNING_ON},
+     * {@link #STATE_BLE_ON},
+     * {@link #STATE_TURNING_ON},
+     * {@link #STATE_ON},
+     * {@link #STATE_TURNING_OFF},
+     * {@link #STATE_BLE_TURNING_OFF}.
+     * <p>Requires {@link android.Manifest.permission#BLUETOOTH}
+     *
+     * @return current state of Bluetooth adapter
+     * @hide
+     */
+    public int getLeState() {
+        try {
+            synchronized(mManagerCallback) {
+                if (mService != null)
+                {
+                    int state=  mService.getState();
+                    if (VDBG) Log.d(TAG,"getLeState() returning " + state);
+                    return state;
+                }
+            }
+        } catch (RemoteException e) {
+            Log.e(TAG, "", e);
+        }
+        return BluetoothAdapter.STATE_OFF;
+    }
+
+    boolean getLeAccess() {
+        if(getLeState() == STATE_ON)
+            return true;
+
+        else if (getLeState() == STATE_BLE_ON)
+            return true; // TODO: FILTER SYSTEM APPS HERE <--
+
+        return false;
+    }
+
+    /**
      * Turn on the local Bluetooth adapter&mdash;do not use without explicit
      * user action to turn on Bluetooth.
      * <p>This powers on the underlying Bluetooth hardware, and starts all
@@ -581,10 +867,23 @@
      *         immediate error
      */
     public boolean enable() {
+        int state = STATE_OFF;
         if (isEnabled() == true){
             if (DBG) Log.d(TAG, "enable(): BT is already enabled..!");
             return true;
         }
+        //Use service interface to get the exact state
+        if (mService != null) {
+            try {
+               state = mService.getState();
+            } catch (RemoteException e) {Log.e(TAG, "", e);}
+        }
+
+        if (state == BluetoothAdapter.STATE_BLE_ON) {
+                Log.e(TAG, "BT is in BLE_ON State");
+                notifyUserAction(true);
+                return true;
+        }
         try {
             return mManagerService.enable();
         } catch (RemoteException e) {Log.e(TAG, "", e);}
@@ -1141,6 +1440,9 @@
         BluetoothServerSocket socket = new BluetoothServerSocket(
                 BluetoothSocket.TYPE_RFCOMM, true, true, channel);
         int errno = socket.mSocket.bindListen();
+        if(channel == SOCKET_CHANNEL_AUTO_STATIC_NO_SDP) {
+            socket.setChannel(socket.mSocket.getPort());
+        }
         if (errno != 0) {
             //TODO(BT): Throw the same exception error code
             // that the previous code was using.
@@ -1275,6 +1577,9 @@
         BluetoothServerSocket socket = new BluetoothServerSocket(
                 BluetoothSocket.TYPE_RFCOMM, false, false, port);
         int errno = socket.mSocket.bindListen();
+        if(port == SOCKET_CHANNEL_AUTO_STATIC_NO_SDP) {
+            socket.setChannel(socket.mSocket.getPort());
+        }
         if (errno != 0) {
             //TODO(BT): Throw the same exception error code
             // that the previous code was using.
@@ -1297,6 +1602,9 @@
         BluetoothServerSocket socket = new BluetoothServerSocket(
                 BluetoothSocket.TYPE_RFCOMM, false, true, port);
         int errno = socket.mSocket.bindListen();
+        if(port == SOCKET_CHANNEL_AUTO_STATIC_NO_SDP) {
+            socket.setChannel(socket.mSocket.getPort());
+        }
         if (errno < 0) {
             //TODO(BT): Throw the same exception error code
             // that the previous code was using.
@@ -1327,6 +1635,30 @@
     }
 
     /**
+     * Construct an encrypted, authenticated, L2CAP server socket.
+     * Call #accept to retrieve connections to this socket.
+     * @return An L2CAP BluetoothServerSocket
+     * @throws IOException On error, for example Bluetooth not available, or
+     *                     insufficient permissions.
+     * @hide
+     */
+    public BluetoothServerSocket listenUsingL2capOn(int port) throws IOException {
+        BluetoothServerSocket socket = new BluetoothServerSocket(
+                BluetoothSocket.TYPE_L2CAP, true, true, port);
+        int errno = socket.mSocket.bindListen();
+        if(port == SOCKET_CHANNEL_AUTO_STATIC_NO_SDP) {
+            socket.setChannel(socket.mSocket.getPort());
+        }
+        if (errno != 0) {
+            //TODO(BT): Throw the same exception error code
+            // that the previous code was using.
+            //socket.mSocket.throwErrnoNative(errno);
+            throw new IOException("Error: " + errno);
+        }
+        return socket;
+    }
+
+    /**
      * Read the local Out of Band Pairing Data
      * <p>Requires {@link android.Manifest.permission#BLUETOOTH}
      *
@@ -1405,6 +1737,9 @@
         } else if (profile == BluetoothProfile.HEADSET_CLIENT) {
             BluetoothHeadsetClient headsetClient = new BluetoothHeadsetClient(context, listener);
             return true;
+        } else if (profile == BluetoothProfile.SAP) {
+            BluetoothSap sap = new BluetoothSap(context, listener);
+            return true;
         } else {
             return false;
         }
@@ -1469,6 +1804,10 @@
                 BluetoothHeadsetClient headsetClient = (BluetoothHeadsetClient)proxy;
                 headsetClient.close();
                 break;
+            case BluetoothProfile.SAP:
+                BluetoothSap sap = (BluetoothSap)proxy;
+                sap.close();
+                break;
         }
     }
 
@@ -1512,6 +1851,10 @@
                     }
                 }
             }
+
+            public void onBrEdrDown() {
+                if (VDBG) Log.i(TAG, "on QBrEdrDown: ");
+            }
     };
 
     /**
diff --git a/core/java/android/bluetooth/BluetoothDevice.java b/core/java/android/bluetooth/BluetoothDevice.java
index bb0d0a3..bfc374fb 100644
--- a/core/java/android/bluetooth/BluetoothDevice.java
+++ b/core/java/android/bluetooth/BluetoothDevice.java
@@ -302,6 +302,12 @@
      */
     public static final int DEVICE_TYPE_DUAL = 3;
 
+
+    /** @hide */
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    public static final String ACTION_SDP_RECORD =
+            "android.bluetooth.device.action.SDP_RECORD";
+
     /**
      * Broadcast Action: This intent is used to broadcast the {@link UUID}
      * wrapped as a {@link android.os.ParcelUuid} of the remote device after it
@@ -376,6 +382,9 @@
     /**@hide*/
     public static final int REQUEST_TYPE_MESSAGE_ACCESS = 3;
 
+    /**@hide*/
+    public static final int REQUEST_TYPE_SIM_ACCESS = 4;
+
     /**
      * Used as an extra field in {@link #ACTION_CONNECTION_ACCESS_REQUEST} intents,
      * Contains package name to return reply intent to.
@@ -526,6 +535,13 @@
      */
     public static final String EXTRA_UUID = "android.bluetooth.device.extra.UUID";
 
+    /** @hide */
+    public static final String EXTRA_SDP_RECORD =
+        "android.bluetooth.device.extra.SDP_RECORD";
+
+    /** @hide */
+    public static final String EXTRA_SDP_SEARCH_STATUS =
+            "android.bluetooth.device.extra.SDP_SEARCH_STATUS";
     /**
      * For {@link #getPhonebookAccessPermission}, {@link #setPhonebookAccessPermission},
      * {@link #getMessageAccessPermission} and {@link #setMessageAccessPermission}.
@@ -593,7 +609,9 @@
         public void onBluetoothServiceUp(IBluetooth bluetoothService)
                 throws RemoteException {
             synchronized (BluetoothDevice.class) {
-                sService = bluetoothService;
+                if (sService == null) {
+                    sService = bluetoothService;
+                }
             }
         }
 
@@ -603,6 +621,11 @@
                 sService = null;
             }
         }
+
+        public void onBrEdrDown()
+        {
+            if (DBG) Log.d(TAG, "onBrEdrDown: reached BLE ON state");
+        }
     };
     /**
      * Create a new BluetoothDevice
@@ -1017,7 +1040,7 @@
      *         or null on error
      */
      public ParcelUuid[] getUuids() {
-         if (sService == null) {
+         if (sService == null || isBluetoothEnabled() == false) {
             Log.e(TAG, "BT not enabled. Cannot get remote device Uuids");
              return null;
          }
@@ -1044,7 +1067,7 @@
       */
      public boolean fetchUuidsWithSdp() {
         IBluetooth service = sService;
-        if (service == null) {
+        if (service == null || isBluetoothEnabled() == false) {
             Log.e(TAG, "BT not enabled. Cannot fetchUuidsWithSdp");
             return false;
         }
@@ -1054,28 +1077,38 @@
             return false;
     }
 
+     /**
+      * Perform a service discovery on the remote device to get the SDP records associated
+      * with the specified UUID.
+      *
+      * <p>This API is asynchronous and {@link #ACTION_SDP_RECORD} intent is sent,
+      * with the SDP records found on the remote end. If there is an error
+      * in getting the SDP records or if the process takes a long time,
+      * {@link #ACTION_SDP_RECORD} intent is sent with an status value in
+      * {@link #EXTRA_SDP_SEARCH_STATUS} different from 0.
+      * Detailed status error codes can be found by members of the Bluetooth package in
+      * the AbstractionLayer class.
+      * <p>Requires {@link android.Manifest.permission#BLUETOOTH}.
+      * The SDP record data will be stored in the intent as {@link #EXTRA_SDP_RECORD}.
+      * The object type will match one of the SdpXxxRecord types, depending on the UUID searched
+      * for.
+      *
+      * @return False if the sanity check fails, True if the process
+      *               of initiating an ACL connection to the remote device
+      *               was started.
+      */
      /** @hide */
-     public boolean fetchMasInstances() {
+     public boolean sdpSearch(ParcelUuid uuid) {
          if (sService == null) {
-             Log.e(TAG, "BT not enabled. Cannot query remote device for MAS instances");
+             Log.e(TAG, "BT not enabled. Cannot query remote device sdp records");
              return false;
          }
          try {
-             return sService.fetchRemoteMasInstances(this);
+             return sService.sdpSearch(this,uuid);
          } catch (RemoteException e) {Log.e(TAG, "", e);}
          return false;
      }
 
-    /** @hide */
-    public int getServiceChannel(ParcelUuid uuid) {
-        //TODO(BT)
-        /*
-         try {
-             return sService.getRemoteServiceChannel(this, uuid);
-         } catch (RemoteException e) {Log.e(TAG, "", e);}*/
-         return BluetoothDevice.ERROR;
-    }
-
     /**
      * Set the pin during pairing when the pairing method is {@link #PAIRING_VARIANT_PIN}
      * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}.
@@ -1154,6 +1187,15 @@
         return false;
     }
 
+     boolean isBluetoothEnabled() {
+         boolean ret = false;
+         BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
+         if (adapter != null && adapter.isEnabled() == true) {
+             ret = true;
+         }
+         return ret;
+     }
+
     /**
      * Requires {@link android.Manifest.permission#BLUETOOTH}.
      * @return Whether the phonebook access is allowed to this device. Can be
@@ -1231,6 +1273,44 @@
     }
 
     /**
+     * Requires {@link android.Manifest.permission#BLUETOOTH}.
+     * @return Whether the Sim access is allowed to this device. Can be
+     *         {@link #ACCESS_UNKNOWN}, {@link #ACCESS_ALLOWED} or {@link #ACCESS_REJECTED}.
+     * @hide
+     */
+    public int getSimAccessPermission() {
+        if (sService == null) {
+            return ACCESS_UNKNOWN;
+        }
+        try {
+            return sService.getSimAccessPermission(this);
+        } catch (RemoteException e) {
+            Log.e(TAG, "", e);
+        }
+        return ACCESS_UNKNOWN;
+    }
+
+    /**
+     * Sets whether the Sim access is allowed to this device.
+     * <p>Requires {@link android.Manifest.permission#BLUETOOTH_PRIVILEGED}.
+     * @param value Can be {@link #ACCESS_UNKNOWN}, {@link #ACCESS_ALLOWED} or
+     *              {@link #ACCESS_REJECTED}.
+     * @return Whether the value has been successfully set.
+     * @hide
+     */
+    public boolean setSimAccessPermission(int value) {
+        if (sService == null) {
+            return false;
+        }
+        try {
+            return sService.setSimAccessPermission(this, value);
+        } catch (RemoteException e) {
+            Log.e(TAG, "", e);
+        }
+        return false;
+    }    
+    
+    /**
      * Create an RFCOMM {@link BluetoothSocket} ready to start a secure
      * outgoing connection to this remote device on given channel.
      * <p>The remote device will be authenticated and communication on this
@@ -1256,11 +1336,45 @@
      * @hide
      */
     public BluetoothSocket createRfcommSocket(int channel) throws IOException {
+        if (isBluetoothEnabled() == false) {
+            Log.e(TAG, "Bluetooth is not enabled");
+            throw new IOException();
+        }
         return new BluetoothSocket(BluetoothSocket.TYPE_RFCOMM, -1, true, true, this, channel,
                 null);
     }
 
     /**
+     * Create an L2cap {@link BluetoothSocket} ready to start a secure
+     * outgoing connection to this remote device on given channel.
+     * <p>The remote device will be authenticated and communication on this
+     * socket will be encrypted.
+     * <p> Use this socket only if an authenticated socket link is possible.
+     * Authentication refers to the authentication of the link key to
+     * prevent man-in-the-middle type of attacks.
+     * For example, for Bluetooth 2.1 devices, if any of the devices does not
+     * have an input and output capability or just has the ability to
+     * display a numeric key, a secure socket connection is not possible.
+     * In such a case, use {#link createInsecureRfcommSocket}.
+     * For more details, refer to the Security Model section 5.2 (vol 3) of
+     * Bluetooth Core Specification version 2.1 + EDR.
+     * <p>Use {@link BluetoothSocket#connect} to initiate the outgoing
+     * connection.
+     * <p>Valid L2CAP PSM channels are in range 1 to 2^16.
+     * <p>Requires {@link android.Manifest.permission#BLUETOOTH}
+     *
+     * @param channel L2cap PSM/channel to connect to
+     * @return a RFCOMM BluetoothServerSocket ready for an outgoing connection
+     * @throws IOException on error, for example Bluetooth not available, or
+     *                     insufficient permissions
+     * @hide
+     */
+    public BluetoothSocket createL2capSocket(int channel) throws IOException {
+        return new BluetoothSocket(BluetoothSocket.TYPE_L2CAP, -1, true, true, this, channel,
+                null);
+    }
+
+    /**
      * Create an RFCOMM {@link BluetoothSocket} ready to start a secure
      * outgoing connection to this remote device using SDP lookup of uuid.
      * <p>This is designed to be used with {@link
@@ -1292,6 +1406,11 @@
      *                     insufficient permissions
      */
     public BluetoothSocket createRfcommSocketToServiceRecord(UUID uuid) throws IOException {
+        if (isBluetoothEnabled() == false) {
+            Log.e(TAG, "Bluetooth is not enabled");
+            throw new IOException();
+        }
+
         return new BluetoothSocket(BluetoothSocket.TYPE_RFCOMM, -1, true, true, this, -1,
                 new ParcelUuid(uuid));
     }
@@ -1325,6 +1444,10 @@
      *                     insufficient permissions
      */
     public BluetoothSocket createInsecureRfcommSocketToServiceRecord(UUID uuid) throws IOException {
+        if (isBluetoothEnabled() == false) {
+            Log.e(TAG, "Bluetooth is not enabled");
+            throw new IOException();
+        }
         return new BluetoothSocket(BluetoothSocket.TYPE_RFCOMM, -1, false, false, this, -1,
                 new ParcelUuid(uuid));
     }
@@ -1344,6 +1467,11 @@
      * @hide
      */
     public BluetoothSocket createInsecureRfcommSocket(int port) throws IOException {
+
+        if (isBluetoothEnabled() == false) {
+            Log.e(TAG, "Bluetooth is not enabled");
+            throw new IOException();
+        }
         return new BluetoothSocket(BluetoothSocket.TYPE_RFCOMM, -1, false, false, this, port,
                 null);
     }
@@ -1359,6 +1487,11 @@
      * @hide
      */
     public BluetoothSocket createScoSocket() throws IOException {
+
+        if (isBluetoothEnabled() == false) {
+            Log.e(TAG, "Bluetooth is not enabled");
+            throw new IOException();
+        }
         return new BluetoothSocket(BluetoothSocket.TYPE_SCO, -1, true, true, this, -1, null);
     }
 
diff --git a/core/java/android/bluetooth/BluetoothProfile.java b/core/java/android/bluetooth/BluetoothProfile.java
index 1367405..eecb073 100644
--- a/core/java/android/bluetooth/BluetoothProfile.java
+++ b/core/java/android/bluetooth/BluetoothProfile.java
@@ -103,17 +103,23 @@
      */
     public static final int MAP = 9;
 
+    /*
+     * SAP Profile
+     * @hide
+     */
+    public static final int SAP = 10;
+
     /**
      * A2DP Sink Profile
      * @hide
      */
-    public static final int A2DP_SINK = 10;
+    public static final int A2DP_SINK = 11;
 
     /**
      * AVRCP Controller Profile
      * @hide
      */
-    public static final int AVRCP_CONTROLLER = 11;
+    public static final int AVRCP_CONTROLLER = 12;
 
     /**
      * Headset Client - HFP HF Role
diff --git a/core/java/android/bluetooth/BluetoothSap.java b/core/java/android/bluetooth/BluetoothSap.java
new file mode 100644
index 0000000..7b4c6f9
--- /dev/null
+++ b/core/java/android/bluetooth/BluetoothSap.java
@@ -0,0 +1,383 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.bluetooth;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.os.RemoteException;
+import android.os.IBinder;
+import android.os.ServiceManager;
+import android.util.Log;
+
+
+public final class BluetoothSap implements BluetoothProfile {
+
+    private static final String TAG = "BluetoothSap";
+    private static final boolean DBG = true;
+    private static final boolean VDBG = false;
+
+    public static final String ACTION_CONNECTION_STATE_CHANGED =
+        "android.bluetooth.sap.profile.action.CONNECTION_STATE_CHANGED";
+
+    private IBluetoothSap mService;
+    private final Context mContext;
+    private ServiceListener mServiceListener;
+    private BluetoothAdapter mAdapter;
+
+    /** There was an error trying to obtain the state */
+    public static final int STATE_ERROR        = -1;
+
+    public static final int RESULT_FAILURE = 0;
+    public static final int RESULT_SUCCESS = 1;
+    /** Connection canceled before completion. */
+    public static final int RESULT_CANCELED = 2;
+
+    final private IBluetoothStateChangeCallback mBluetoothStateChangeCallback =
+            new IBluetoothStateChangeCallback.Stub() {
+                public void onBluetoothStateChange(boolean up) {
+                    if (DBG) Log.d(TAG, "onBluetoothStateChange: up=" + up);
+                    if (!up) {
+                        if (VDBG) Log.d(TAG,"Unbinding service...");
+                        synchronized (mConnection) {
+                            try {
+                                mService = null;
+                                mContext.unbindService(mConnection);
+                            } catch (Exception re) {
+                                Log.e(TAG,"",re);
+                            }
+                        }
+                    } else {
+                        synchronized (mConnection) {
+                            try {
+                                if (mService == null) {
+                                    if (VDBG) Log.d(TAG,"Binding service...");
+                                    doBind();
+                                }
+                            } catch (Exception re) {
+                                Log.e(TAG,"",re);
+                            }
+                        }
+                    }
+                }
+        };
+
+    /**
+     * Create a BluetoothSap proxy object.
+     */
+    /*package*/ BluetoothSap(Context context, ServiceListener l) {
+        if (DBG) Log.d(TAG, "Create BluetoothSap proxy object");
+        mContext = context;
+        mServiceListener = l;
+        mAdapter = BluetoothAdapter.getDefaultAdapter();
+        IBluetoothManager mgr = mAdapter.getBluetoothManager();
+        if (mgr != null) {
+            try {
+                mgr.registerStateChangeCallback(mBluetoothStateChangeCallback);
+            } catch (RemoteException e) {
+                Log.e(TAG,"",e);
+            }
+        }
+        doBind();
+    }
+
+    boolean doBind() {
+        Intent intent = new Intent(IBluetoothMap.class.getName());
+        ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0);
+        intent.setComponent(comp);
+        if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0,
+                android.os.Process.myUserHandle())) {
+            Log.e(TAG, "Could not bind to Bluetooth SAP Service with " + intent);
+            return false;
+        }
+        return true;
+    }
+
+    protected void finalize() throws Throwable {
+        try {
+            close();
+        } finally {
+            super.finalize();
+        }
+    }
+
+    /**
+     * Close the connection to the backing service.
+     * Other public functions of BluetoothSap will return default error
+     * results once close() has been called. Multiple invocations of close()
+     * are ok.
+     */
+    public synchronized void close() {
+        IBluetoothManager mgr = mAdapter.getBluetoothManager();
+        if (mgr != null) {
+            try {
+                mgr.unregisterStateChangeCallback(mBluetoothStateChangeCallback);
+            } catch (Exception e) {
+                Log.e(TAG,"",e);
+            }
+        }
+
+        synchronized (mConnection) {
+            if (mService != null) {
+                try {
+                    mService = null;
+                    mContext.unbindService(mConnection);
+                } catch (Exception re) {
+                    Log.e(TAG,"",re);
+                }
+            }
+        }
+        mServiceListener = null;
+    }
+
+    /**
+     * Get the current state of the BluetoothSap service.
+     * @return One of the STATE_ return codes, or STATE_ERROR if this proxy
+     *         object is currently not connected to the Sap service.
+     */
+    public int getState() {
+        if (VDBG) log("getState()");
+        if (mService != null) {
+            try {
+                return mService.getState();
+            } catch (RemoteException e) {Log.e(TAG, e.toString());}
+        } else {
+            Log.w(TAG, "Proxy not attached to service");
+            if (DBG) log(Log.getStackTraceString(new Throwable()));
+        }
+        return BluetoothSap.STATE_ERROR;
+    }
+
+    /**
+     * Get the currently connected remote Bluetooth device (PCE).
+     * @return The remote Bluetooth device, or null if not in connected or
+     *         connecting state, or if this proxy object is not connected to
+     *         the Sap service.
+     */
+    public BluetoothDevice getClient() {
+        if (VDBG) log("getClient()");
+        if (mService != null) {
+            try {
+                return mService.getClient();
+            } catch (RemoteException e) {Log.e(TAG, e.toString());}
+        } else {
+            Log.w(TAG, "Proxy not attached to service");
+            if (DBG) log(Log.getStackTraceString(new Throwable()));
+        }
+        return null;
+    }
+
+    /**
+     * Returns true if the specified Bluetooth device is connected.
+     * Returns false if not connected, or if this proxy object is not
+     * currently connected to the Sap service.
+     */
+    public boolean isConnected(BluetoothDevice device) {
+        if (VDBG) log("isConnected(" + device + ")");
+        if (mService != null) {
+            try {
+                return mService.isConnected(device);
+            } catch (RemoteException e) {Log.e(TAG, e.toString());}
+        } else {
+            Log.w(TAG, "Proxy not attached to service");
+            if (DBG) log(Log.getStackTraceString(new Throwable()));
+        }
+        return false;
+    }
+
+    /**
+     * Initiate connection. Initiation of outgoing connections is not
+     * supported for SAP server.
+     */
+    public boolean connect(BluetoothDevice device) {
+        if (DBG) log("connect(" + device + ")" + "not supported for SAPS");
+        return false;
+    }
+
+    /**
+     * Initiate disconnect.
+     *
+     * @param device Remote Bluetooth Device
+     * @return false on error,
+     *               true otherwise
+     */
+    public boolean disconnect(BluetoothDevice device) {
+        if (DBG) log("disconnect(" + device + ")");
+        if (mService != null && isEnabled() &&
+            isValidDevice(device)) {
+            try {
+                return mService.disconnect(device);
+            } catch (RemoteException e) {
+              Log.e(TAG, Log.getStackTraceString(new Throwable()));
+              return false;
+            }
+        }
+        if (mService == null) Log.w(TAG, "Proxy not attached to service");
+        return false;
+    }
+
+    /**
+     * Get the list of connected devices. Currently at most one.
+     *
+     * @return list of connected devices
+     */
+    public List<BluetoothDevice> getConnectedDevices() {
+        if (DBG) log("getConnectedDevices()");
+        if (mService != null && isEnabled()) {
+            try {
+                return mService.getConnectedDevices();
+            } catch (RemoteException e) {
+                Log.e(TAG, Log.getStackTraceString(new Throwable()));
+                return new ArrayList<BluetoothDevice>();
+            }
+        }
+        if (mService == null) Log.w(TAG, "Proxy not attached to service");
+        return new ArrayList<BluetoothDevice>();
+    }
+
+    /**
+     * Get the list of devices matching specified states. Currently at most one.
+     *
+     * @return list of matching devices
+     */
+    public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
+        if (DBG) log("getDevicesMatchingStates()");
+        if (mService != null && isEnabled()) {
+            try {
+                return mService.getDevicesMatchingConnectionStates(states);
+            } catch (RemoteException e) {
+                Log.e(TAG, Log.getStackTraceString(new Throwable()));
+                return new ArrayList<BluetoothDevice>();
+            }
+        }
+        if (mService == null) Log.w(TAG, "Proxy not attached to service");
+        return new ArrayList<BluetoothDevice>();
+    }
+
+    /**
+     * Get connection state of device
+     *
+     * @return device connection state
+     */
+    public int getConnectionState(BluetoothDevice device) {
+        if (DBG) log("getConnectionState(" + device + ")");
+        if (mService != null && isEnabled() &&
+            isValidDevice(device)) {
+            try {
+                return mService.getConnectionState(device);
+            } catch (RemoteException e) {
+                Log.e(TAG, Log.getStackTraceString(new Throwable()));
+                return BluetoothProfile.STATE_DISCONNECTED;
+            }
+        }
+        if (mService == null) Log.w(TAG, "Proxy not attached to service");
+        return BluetoothProfile.STATE_DISCONNECTED;
+    }
+
+    /**
+     * Set priority of the profile
+     *
+     * <p> The device should already be paired.
+     *
+     * @param device Paired bluetooth device
+     * @param priority
+     * @return true if priority is set, false on error
+     */
+    public boolean setPriority(BluetoothDevice device, int priority) {
+        if (DBG) log("setPriority(" + device + ", " + priority + ")");
+        if (mService != null && isEnabled() &&
+            isValidDevice(device)) {
+            if (priority != BluetoothProfile.PRIORITY_OFF &&
+                priority != BluetoothProfile.PRIORITY_ON) {
+              return false;
+            }
+            try {
+                return mService.setPriority(device, priority);
+            } catch (RemoteException e) {
+                Log.e(TAG, Log.getStackTraceString(new Throwable()));
+                return false;
+            }
+        }
+        if (mService == null) Log.w(TAG, "Proxy not attached to service");
+        return false;
+    }
+
+    /**
+     * Get the priority of the profile.
+     *
+     * @param device Bluetooth device
+     * @return priority of the device
+     */
+    public int getPriority(BluetoothDevice device) {
+        if (VDBG) log("getPriority(" + device + ")");
+        if (mService != null && isEnabled() &&
+            isValidDevice(device)) {
+            try {
+                return mService.getPriority(device);
+            } catch (RemoteException e) {
+                Log.e(TAG, Log.getStackTraceString(new Throwable()));
+                return PRIORITY_OFF;
+            }
+        }
+        if (mService == null) Log.w(TAG, "Proxy not attached to service");
+        return PRIORITY_OFF;
+    }
+
+    private ServiceConnection mConnection = new ServiceConnection() {
+        public void onServiceConnected(ComponentName className, IBinder service) {
+            if (DBG) log("Proxy object connected");
+            mService = IBluetoothSap.Stub.asInterface(service);
+            if (mServiceListener != null) {
+                mServiceListener.onServiceConnected(BluetoothProfile.SAP, BluetoothSap.this);
+            }
+        }
+        public void onServiceDisconnected(ComponentName className) {
+            if (DBG) log("Proxy object disconnected");
+            mService = null;
+            if (mServiceListener != null) {
+                mServiceListener.onServiceDisconnected(BluetoothProfile.SAP);
+            }
+        }
+    };
+
+    private static void log(String msg) {
+        Log.d(TAG, msg);
+    }
+
+    private boolean isEnabled() {
+        BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
+
+        if (adapter != null && adapter.getState() == BluetoothAdapter.STATE_ON)
+            return true;
+        log("Bluetooth is Not enabled");
+        return false;
+    }
+
+    private boolean isValidDevice(BluetoothDevice device) {
+       if (device == null)
+           return false;
+
+       if (BluetoothAdapter.checkBluetoothAddress(device.getAddress()))
+           return true;
+       return false;
+    }
+
+}
diff --git a/core/java/android/bluetooth/BluetoothServerSocket.java b/core/java/android/bluetooth/BluetoothServerSocket.java
index bc56e55..21024a6 100644
--- a/core/java/android/bluetooth/BluetoothServerSocket.java
+++ b/core/java/android/bluetooth/BluetoothServerSocket.java
@@ -18,6 +18,7 @@
 
 import android.os.Handler;
 import android.os.ParcelUuid;
+import android.util.Log;
 
 import java.io.Closeable;
 import java.io.IOException;
@@ -66,10 +67,11 @@
  */
 public final class BluetoothServerSocket implements Closeable {
 
+    private static final String TAG = "BluetoothServerSocket";
     /*package*/ final BluetoothSocket mSocket;
     private Handler mHandler;
     private int mMessage;
-    private final int mChannel;
+    private int mChannel;
 
     /**
      * Construct a socket for incoming connections.
@@ -84,6 +86,9 @@
             throws IOException {
         mChannel = port;
         mSocket = new BluetoothSocket(type, -1, auth, encrypt, null, port, null);
+        if(port == BluetoothAdapter.SOCKET_CHANNEL_AUTO_STATIC_NO_SDP) {
+            mSocket.setExcludeSdp(true);
+        }
     }
 
     /**
@@ -98,6 +103,7 @@
     /*package*/ BluetoothServerSocket(int type, boolean auth, boolean encrypt, ParcelUuid uuid)
             throws IOException {
         mSocket = new BluetoothSocket(type, -1, auth, encrypt, null, -1, uuid);
+        // TODO: This is the same as mChannel = -1 - is this intentional?
         mChannel = mSocket.getPort();
     }
 
@@ -153,6 +159,7 @@
     /*package*/ void setServiceName(String ServiceName) {
         mSocket.setServiceName(ServiceName);
     }
+
     /**
      * Returns the channel on which this socket is bound.
      * @hide
@@ -160,4 +167,47 @@
     public int getChannel() {
         return mChannel;
     }
+
+    /**
+     * Sets the channel on which future sockets are bound.
+     * Currently used only when a channel is auto generated.
+     */
+    /*package*/ void setChannel(int newChannel) {
+        /* TODO: From a design/architecture perspective this is wrong.
+         *       The bind operation should be conducted through this class
+         *       and the resulting port should be kept in mChannel, and
+         *       not set from BluetoothAdapter. */
+        if(mSocket != null) {
+            if(mSocket.getPort() != newChannel) {
+                Log.w(TAG,"The port set is different that the underlying port. mSocket.getPort(): "
+                            + mSocket.getPort() + " requested newChannel: " + newChannel);
+            }
+        }
+        mChannel = newChannel;
+    }
+
+    @Override
+    public String toString() {
+        StringBuilder sb = new StringBuilder();
+        sb.append("ServerSocket: Type: ");
+        switch(mSocket.getConnectionType()) {
+            case BluetoothSocket.TYPE_RFCOMM:
+            {
+                sb.append("TYPE_RFCOMM");
+                break;
+            }
+            case BluetoothSocket.TYPE_L2CAP:
+            {
+                sb.append("TYPE_L2CAP");
+                break;
+            }
+            case BluetoothSocket.TYPE_SCO:
+            {
+                sb.append("TYPE_SCO");
+                break;
+            }
+        }
+        sb.append(" Channel: ").append(mChannel);
+        return sb.toString();
+    }
 }
diff --git a/core/java/android/bluetooth/BluetoothSocket.java b/core/java/android/bluetooth/BluetoothSocket.java
index 36997e5..5702d11 100644
--- a/core/java/android/bluetooth/BluetoothSocket.java
+++ b/core/java/android/bluetooth/BluetoothSocket.java
@@ -21,6 +21,7 @@
 import android.os.RemoteException;
 import android.util.Log;
 
+import java.io.BufferedInputStream;
 import java.io.Closeable;
 import java.io.FileDescriptor;
 import java.io.IOException;
@@ -29,6 +30,8 @@
 import java.util.Locale;
 import java.util.UUID;
 import android.net.LocalSocket;
+
+import java.nio.Buffer;
 import java.nio.ByteOrder;
 import java.nio.ByteBuffer;
 /**
@@ -86,17 +89,19 @@
 
     /** @hide */
     public static final int MAX_RFCOMM_CHANNEL = 30;
+    /*package*/ static final int MAX_L2CAP_PACKAGE_SIZE = 0xFFFF;
 
     /** Keep TYPE_ fields in sync with BluetoothSocket.cpp */
-    /*package*/ static final int TYPE_RFCOMM = 1;
-    /*package*/ static final int TYPE_SCO = 2;
-    /*package*/ static final int TYPE_L2CAP = 3;
+    public static final int TYPE_RFCOMM = 1;
+    public static final int TYPE_SCO = 2;
+    public static final int TYPE_L2CAP = 3;
 
     /*package*/ static final int EBADFD = 77;
     /*package*/ static final int EADDRINUSE = 98;
 
     /*package*/ static final int SEC_FLAG_ENCRYPT = 1;
     /*package*/ static final int SEC_FLAG_AUTH = 1 << 1;
+    /*package*/ static final int BTSOCK_FLAG_NO_SDP  = 1 << 2;
 
     private final int mType;  /* one of TYPE_RFCOMM etc */
     private BluetoothDevice mDevice;    /* remote device */
@@ -106,6 +111,7 @@
     private final BluetoothInputStream mInputStream;
     private final BluetoothOutputStream mOutputStream;
     private final ParcelUuid mUuid;
+    private boolean mExcludeSdp = false;
     private ParcelFileDescriptor mPfd;
     private LocalSocket mSocket;
     private InputStream mSocketIS;
@@ -115,7 +121,11 @@
     private String mServiceName;
     private static int PROXY_CONNECTION_TIMEOUT = 5000;
 
-    private static int SOCK_SIGNAL_SIZE = 16;
+    private static int SOCK_SIGNAL_SIZE = 20;
+
+    private ByteBuffer mL2capBuffer = null;
+    private int mMaxTxPacketSize = 0; // The l2cap maximum packet size supported by the peer.
+    private int mMaxRxPacketSize = 0; // The l2cap maximum packet size that can be received.
 
     private enum SocketState {
         INIT,
@@ -144,12 +154,14 @@
      */
     /*package*/ BluetoothSocket(int type, int fd, boolean auth, boolean encrypt,
             BluetoothDevice device, int port, ParcelUuid uuid) throws IOException {
-        if (type == BluetoothSocket.TYPE_RFCOMM && uuid == null && fd == -1) {
+        if (VDBG) Log.d(TAG, "Creating new BluetoothSocket of type: " + type);
+        if (type == BluetoothSocket.TYPE_RFCOMM && uuid == null && fd == -1
+                && port != BluetoothAdapter.SOCKET_CHANNEL_AUTO_STATIC_NO_SDP) {
             if (port < 1 || port > MAX_RFCOMM_CHANNEL) {
                 throw new IOException("Invalid RFCOMM channel: " + port);
             }
         }
-        if(uuid != null)
+        if (uuid != null)
             mUuid = uuid;
         else mUuid = new ParcelUuid(new UUID(0, 0));
         mType = type;
@@ -172,6 +184,7 @@
         mOutputStream = new BluetoothOutputStream(this);
     }
     private BluetoothSocket(BluetoothSocket s) {
+        if (VDBG) Log.d(TAG, "Creating new Private BluetoothSocket of type: " + s.mType);
         mUuid = s.mUuid;
         mType = s.mType;
         mAuth = s.mAuth;
@@ -179,7 +192,11 @@
         mPort = s.mPort;
         mInputStream = new BluetoothInputStream(this);
         mOutputStream = new BluetoothOutputStream(this);
+        mMaxRxPacketSize = s.mMaxRxPacketSize;
+        mMaxTxPacketSize = s.mMaxTxPacketSize;
+
         mServiceName = s.mServiceName;
+        mExcludeSdp = s.mExcludeSdp;
     }
     private BluetoothSocket acceptSocket(String RemoteAddr) throws IOException {
         BluetoothSocket as = new BluetoothSocket(this);
@@ -229,6 +246,8 @@
             flags |= SEC_FLAG_AUTH;
         if(mEncrypt)
             flags |= SEC_FLAG_ENCRYPT;
+        if(mExcludeSdp)
+            flags |= BTSOCK_FLAG_NO_SDP;
         return flags;
     }
 
@@ -298,7 +317,8 @@
 
         try {
             if (mSocketState == SocketState.CLOSED) throw new IOException("socket closed");
-            IBluetooth bluetoothProxy = BluetoothAdapter.getDefaultAdapter().getBluetoothService(null);
+            IBluetooth bluetoothProxy =
+                    BluetoothAdapter.getDefaultAdapter().getBluetoothService(null);
             if (bluetoothProxy == null) throw new IOException("Bluetooth is off");
             mPfd = bluetoothProxy.connectSocket(mDevice, mType,
                     mUuid, mPort, getSecurityFlags());
@@ -370,7 +390,7 @@
                     mSocketState = SocketState.LISTENING;
             }
             if (DBG) Log.d(TAG, "channel: " + channel);
-            if (mPort == -1) {
+            if (mPort <= -1) {
                 mPort = channel;
             } // else ASSERT(mPort == channel)
             ret = 0;
@@ -391,7 +411,8 @@
 
     /*package*/ BluetoothSocket accept(int timeout) throws IOException {
         BluetoothSocket acceptedSocket;
-        if (mSocketState != SocketState.LISTENING) throw new IOException("bt socket is not in listen state");
+        if (mSocketState != SocketState.LISTENING)
+            throw new IOException("bt socket is not in listen state");
         if(timeout > 0) {
             Log.d(TAG, "accept() set timeout (ms):" + timeout);
            mSocket.setSoTimeout(timeout);
@@ -427,27 +448,80 @@
     }
 
     /*package*/ int read(byte[] b, int offset, int length) throws IOException {
-        if (mSocketIS == null) throw new IOException("read is called on null InputStream");
+        int ret = 0;
         if (VDBG) Log.d(TAG, "read in:  " + mSocketIS + " len: " + length);
-        int ret = mSocketIS.read(b, offset, length);
-        if(ret < 0)
+        if(mType == TYPE_L2CAP)
+        {
+            int bytesToRead = length;
+            if (VDBG) Log.v(TAG, "l2cap: read(): offset: " + offset + " length:" + length
+                    + "mL2capBuffer= " + mL2capBuffer);
+            if (mL2capBuffer == null) {
+                createL2capRxBuffer();
+            }
+            if (mL2capBuffer.remaining() == 0) {
+                if (VDBG) Log.v(TAG, "l2cap buffer empty, refilling...");
+                if (fillL2capRxBuffer() == -1) {
+                    return -1;
+                }
+            }
+            if (bytesToRead > mL2capBuffer.remaining()) {
+                bytesToRead = mL2capBuffer.remaining();
+            }
+            if(VDBG) Log.v(TAG, "get(): offset: " + offset
+                    + " bytesToRead: " + bytesToRead);
+            mL2capBuffer.get(b, offset, bytesToRead);
+            ret = bytesToRead;
+        }else {
+            if (VDBG) Log.v(TAG, "default: read(): offset: " + offset + " length:" + length);
+            ret = mSocketIS.read(b, offset, length);
+        }
+        if (ret < 0)
             throw new IOException("bt socket closed, read return: " + ret);
         if (VDBG) Log.d(TAG, "read out:  " + mSocketIS + " ret: " + ret);
         return ret;
     }
 
     /*package*/ int write(byte[] b, int offset, int length) throws IOException {
-        if (mSocketOS == null) throw new IOException("write is called on null OutputStream");
-        if (VDBG) Log.d(TAG, "write: " + mSocketOS + " length: " + length);
-        mSocketOS.write(b, offset, length);
-        // There is no good way to confirm since the entire process is asynchronous anyway
-        if (VDBG) Log.d(TAG, "write out: " + mSocketOS + " length: " + length);
-        return length;
+
+        //TODO: Since bindings can exist between the SDU size and the
+        //      protocol, we might need to throw an exception instead of just
+        //      splitting the write into multiple smaller writes.
+        //      Rfcomm uses dynamic allocation, and should not have any bindings
+        //      to the actual message length.
+            if (VDBG) Log.d(TAG, "write: " + mSocketOS + " length: " + length);
+            if (mType == TYPE_L2CAP) {
+                if(length <= mMaxTxPacketSize) {
+                    mSocketOS.write(b, offset, length);
+                } else {
+                    int tmpOffset = offset;
+                    int tmpLength = mMaxTxPacketSize;
+                    int endIndex = offset + length;
+                    boolean done = false;
+                    if(DBG) Log.w(TAG, "WARNING: Write buffer larger than L2CAP packet size!\n"
+                            + "Packet will be divided into SDU packets of size "
+                            + mMaxTxPacketSize);
+                    do{
+                        mSocketOS.write(b, tmpOffset, tmpLength);
+                        tmpOffset += mMaxTxPacketSize;
+                        if((tmpOffset + mMaxTxPacketSize) > endIndex) {
+                            tmpLength = endIndex - tmpOffset;
+                            done = true;
+                        }
+                    } while(!done);
+
+                }
+            } else {
+                mSocketOS.write(b, offset, length);
+            }
+            // There is no good way to confirm since the entire process is asynchronous anyway
+            if (VDBG) Log.d(TAG, "write out: " + mSocketOS + " length: " + length);
+            return length;
     }
 
     @Override
     public void close() throws IOException {
-        if (DBG) Log.d(TAG, "close() in, this: " + this + ", channel: " + mPort + ", state: " + mSocketState);
+        if (DBG) Log.d(TAG, "close() in, this: " + this + ", channel: " + mPort + ", state: "
+                + mSocketState);
         if(mSocketState == SocketState.CLOSED)
             return;
         else
@@ -457,8 +531,9 @@
                  if(mSocketState == SocketState.CLOSED)
                     return;
                  mSocketState = SocketState.CLOSED;
-                 if (DBG) Log.d(TAG, "close() this: " + this + ", channel: " + mPort + ", mSocketIS: " + mSocketIS +
-                        ", mSocketOS: " + mSocketOS + "mSocket: " + mSocket);
+                 if (DBG) Log.d(TAG, "close() this: " + this + ", channel: " + mPort +
+                         ", mSocketIS: " + mSocketIS + ", mSocketOS: " + mSocketOS +
+                         "mSocket: " + mSocket);
                  if(mSocket != null) {
                     if (DBG) Log.d(TAG, "Closing mSocket: " + mSocket);
                     mSocket.shutdownInput();
@@ -480,6 +555,47 @@
     /*package */ int getPort() {
         return mPort;
     }
+
+    /**
+     * Get the maximum supported Transmit packet size for the underlying transport.
+     * Use this to optimize the writes done to the output socket, to avoid sending
+     * half full packets.
+     * @return the maximum supported Transmit packet size for the underlying transport.
+     */
+    public int getMaxTransmitPacketSize(){
+        return mMaxTxPacketSize;
+    }
+
+    /**
+     * Get the maximum supported Receive packet size for the underlying transport.
+     * Use this to optimize the reads done on the input stream, as any call to read
+     * will return a maximum of this amount of bytes - or for some transports a
+     * multiple of this value.
+     * @return the maximum supported Receive packet size for the underlying transport.
+     */
+    public int getMaxReceivePacketSize(){
+        return mMaxRxPacketSize;
+    }
+
+    /**
+     * Get the type of the underlying connection
+     * @return one of TYPE_
+     */
+    public int getConnectionType() {
+        return mType;
+    }
+
+    /**
+     * Change if a SDP entry should be automatically created.
+     * Must be called before calling .bind, for the call to have any effect.
+     * @param mExcludeSdp <li>TRUE  - do not auto generate SDP record.
+     *                    <li>FALSE - default - auto generate SPP SDP record.
+     * @hide
+     */
+    public void setExcludeSdp(boolean excludeSdp) {
+        this.mExcludeSdp = excludeSdp;
+    }
+
     private String convertAddr(final byte[] addr)  {
         return String.format(Locale.US, "%02X:%02X:%02X:%02X:%02X:%02X",
                 addr[0] , addr[1], addr[2], addr[3] , addr[4], addr[5]);
@@ -487,8 +603,10 @@
     private String waitSocketSignal(InputStream is) throws IOException {
         byte [] sig = new byte[SOCK_SIGNAL_SIZE];
         int ret = readAll(is, sig);
-        if (VDBG) Log.d(TAG, "waitSocketSignal read 16 bytes signal ret: " + ret);
+        if (VDBG) Log.d(TAG, "waitSocketSignal read " + SOCK_SIGNAL_SIZE +
+                " bytes signal ret: " + ret);
         ByteBuffer bb = ByteBuffer.wrap(sig);
+        /* the struct in native is decorated with __attribute__((packed)), hence this is possible */
         bb.order(ByteOrder.nativeOrder());
         int size = bb.getShort();
         if(size != SOCK_SIGNAL_SIZE)
@@ -497,19 +615,36 @@
         bb.get(addr);
         int channel = bb.getInt();
         int status = bb.getInt();
+        mMaxTxPacketSize = (bb.getShort() & 0xffff); // Convert to unsigned value
+        mMaxRxPacketSize = (bb.getShort() & 0xffff); // Convert to unsigned value
         String RemoteAddr = convertAddr(addr);
         if (VDBG) Log.d(TAG, "waitSocketSignal: sig size: " + size + ", remote addr: "
-                + RemoteAddr + ", channel: " + channel + ", status: " + status);
+                + RemoteAddr + ", channel: " + channel + ", status: " + status
+                + " MaxRxPktSize: " + mMaxRxPacketSize + " MaxTxPktSize: " + mMaxTxPacketSize);
         if(status != 0)
             throw new IOException("Connection failure, status: " + status);
         return RemoteAddr;
     }
+
+    private void createL2capRxBuffer(){
+        if(mType == TYPE_L2CAP) {
+            // Allocate the buffer to use for reads.
+            if(VDBG) Log.v(TAG, "  Creating mL2capBuffer: mMaxPacketSize: " + mMaxRxPacketSize);
+            mL2capBuffer = ByteBuffer.wrap(new byte[mMaxRxPacketSize]);
+            if(VDBG) Log.v(TAG, "mL2capBuffer.remaining()" + mL2capBuffer.remaining());
+            mL2capBuffer.limit(0); // Ensure we do a real read at the first read-request
+            if(VDBG) Log.v(TAG, "mL2capBuffer.remaining() after limit(0):" +
+                    mL2capBuffer.remaining());
+        }
+    }
+
     private int readAll(InputStream is, byte[] b) throws IOException {
         int left = b.length;
         while(left > 0) {
             int ret = is.read(b, b.length - left, left);
             if(ret <= 0)
-                 throw new IOException("read failed, socket might closed or timeout, read ret: " + ret);
+                 throw new IOException("read failed, socket might closed or timeout, read ret: "
+                         + ret);
             left -= ret;
             if(left != 0)
                 Log.w(TAG, "readAll() looping, read partial size: " + (b.length - left) +
@@ -526,4 +661,18 @@
         bb.order(ByteOrder.nativeOrder());
         return bb.getInt();
     }
+
+    private int fillL2capRxBuffer() throws IOException {
+        mL2capBuffer.rewind();
+        int ret = mSocketIS.read(mL2capBuffer.array());
+        if(ret == -1) {
+            // reached end of stream - return -1
+            mL2capBuffer.limit(0);
+            return -1;
+        }
+        mL2capBuffer.limit(ret);
+        return ret;
+    }
+
+
 }
diff --git a/core/java/android/bluetooth/BluetoothUuid.java b/core/java/android/bluetooth/BluetoothUuid.java
index 194a53e..2ded4c8 100644
--- a/core/java/android/bluetooth/BluetoothUuid.java
+++ b/core/java/android/bluetooth/BluetoothUuid.java
@@ -76,7 +76,9 @@
             ParcelUuid.fromString("00001133-0000-1000-8000-00805F9B34FB");
     public static final ParcelUuid MAS =
             ParcelUuid.fromString("00001132-0000-1000-8000-00805F9B34FB");
-
+  public static final ParcelUuid SAP =
+            ParcelUuid.fromString("0000112D-0000-1000-8000-00805F9B34FB");
+			
     public static final ParcelUuid BASE_UUID =
             ParcelUuid.fromString("00000000-0000-1000-8000-00805F9B34FB");
 
@@ -89,7 +91,7 @@
 
     public static final ParcelUuid[] RESERVED_UUIDS = {
         AudioSink, AudioSource, AdvAudioDist, HSP, Handsfree, AvrcpController, AvrcpTarget,
-        ObexObjectPush, PANU, NAP, MAP, MNS, MAS};
+        ObexObjectPush, PANU, NAP, MAP, MNS, MAS, SAP};
 
     public static boolean isAudioSource(ParcelUuid uuid) {
         return uuid.equals(AudioSource);
@@ -143,6 +145,9 @@
     public static boolean isMas(ParcelUuid uuid) {
         return uuid.equals(MAS);
     }
+    public static boolean isSap(ParcelUuid uuid) {
+        return uuid.equals(SAP);
+    }
 
     /**
      * Returns true if ParcelUuid is present in uuidArray
diff --git a/core/java/android/bluetooth/IBluetooth.aidl b/core/java/android/bluetooth/IBluetooth.aidl
index 299f4c8..f6001bf 100644
--- a/core/java/android/bluetooth/IBluetooth.aidl
+++ b/core/java/android/bluetooth/IBluetooth.aidl
@@ -68,7 +68,7 @@
     int getRemoteClass(in BluetoothDevice device);
     ParcelUuid[] getRemoteUuids(in BluetoothDevice device);
     boolean fetchRemoteUuids(in BluetoothDevice device);
-    boolean fetchRemoteMasInstances(in BluetoothDevice device);
+    boolean sdpSearch(in BluetoothDevice device, in ParcelUuid uuid);
 
     boolean setPin(in BluetoothDevice device, boolean accept, int len, in byte[] pinCode);
     boolean setPasskey(in BluetoothDevice device, boolean accept, int len, in byte[]
@@ -79,6 +79,8 @@
     boolean setPhonebookAccessPermission(in BluetoothDevice device, int value);
     int getMessageAccessPermission(in BluetoothDevice device);
     boolean setMessageAccessPermission(in BluetoothDevice device, int value);
+    int getSimAccessPermission(in BluetoothDevice device);
+    boolean setSimAccessPermission(in BluetoothDevice device, int value);
 
     void sendConnectionStateChange(in BluetoothDevice device, int profile, int state, int prevState);
 
@@ -102,4 +104,6 @@
 
     // for dumpsys support
     String dump();
+    void onLeServiceUp();
+    void onBrEdrDown();
 }
diff --git a/core/java/android/bluetooth/IBluetoothGatt.aidl b/core/java/android/bluetooth/IBluetoothGatt.aidl
index 7070bae..4ca57f8 100644
--- a/core/java/android/bluetooth/IBluetoothGatt.aidl
+++ b/core/java/android/bluetooth/IBluetoothGatt.aidl
@@ -101,4 +101,6 @@
                             in int srvcInstanceId, in ParcelUuid srvcId,
                             in int charInstanceId, in ParcelUuid charId,
                             in boolean confirm, in byte[] value);
+    void disconnectAll();
+    void unregAll();
 }
diff --git a/core/java/android/bluetooth/IBluetoothManager.aidl b/core/java/android/bluetooth/IBluetoothManager.aidl
index 7411d3f..8d1ce99 100644
--- a/core/java/android/bluetooth/IBluetoothManager.aidl
+++ b/core/java/android/bluetooth/IBluetoothManager.aidl
@@ -44,4 +44,6 @@
 
     String getAddress();
     String getName();
+    int updateBleAppCount(IBinder b, boolean enable);
+    boolean isBleAppPresent();
 }
diff --git a/core/java/android/bluetooth/IBluetoothManagerCallback.aidl b/core/java/android/bluetooth/IBluetoothManagerCallback.aidl
index 9551086..1385daf 100644
--- a/core/java/android/bluetooth/IBluetoothManagerCallback.aidl
+++ b/core/java/android/bluetooth/IBluetoothManagerCallback.aidl
@@ -26,4 +26,5 @@
 interface IBluetoothManagerCallback {
     void onBluetoothServiceUp(in IBluetooth bluetoothService);
     void onBluetoothServiceDown();
-}
\ No newline at end of file
+    void onBrEdrDown();
+}
diff --git a/core/java/android/bluetooth/IBluetoothSap.aidl b/core/java/android/bluetooth/IBluetoothSap.aidl
new file mode 100644
index 0000000..8970639
--- /dev/null
+++ b/core/java/android/bluetooth/IBluetoothSap.aidl
@@ -0,0 +1,37 @@
+/*
+ * 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.
+ */
+
+package android.bluetooth;
+
+import android.bluetooth.BluetoothDevice;
+
+/**
+ * System private API for Bluetooth SAP service
+ *
+ * {@hide}
+ */
+interface IBluetoothSap {
+    int getState();
+    BluetoothDevice getClient();
+    boolean connect(in BluetoothDevice device);
+    boolean disconnect(in BluetoothDevice device);
+    boolean isConnected(in BluetoothDevice device);
+    List<BluetoothDevice> getConnectedDevices();
+    List<BluetoothDevice> getDevicesMatchingConnectionStates(in int[] states);
+    int getConnectionState(in BluetoothDevice device);
+    boolean setPriority(in BluetoothDevice device, int priority);
+    int getPriority(in BluetoothDevice device);
+}
diff --git a/core/java/android/bluetooth/SdpMasRecord.java b/core/java/android/bluetooth/SdpMasRecord.java
new file mode 100644
index 0000000..fa164c0
--- /dev/null
+++ b/core/java/android/bluetooth/SdpMasRecord.java
@@ -0,0 +1,147 @@
+/*
+* Copyright (C) 2015 Samsung System LSI
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*      http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package android.bluetooth;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/** @hide */
+public class SdpMasRecord implements Parcelable {
+    private final int mMasInstanceId;
+    private final int mL2capPsm;
+    private final int mRfcommChannelNumber;
+    private final int mProfileVersion;
+    private final int mSupportedFeatures;
+    private final int mSupportedMessageTypes;
+    private final String mServiceName;
+    public static final class MessageType {
+        public static final int EMAIL    = 0x01;
+        public static final int SMS_GSM  = 0x02;
+        public static final int SMS_CDMA = 0x04;
+        public static final int MMS      = 0x08;
+    }
+
+    public SdpMasRecord(int mas_instance_id,
+                                 int l2cap_psm,
+                                 int rfcomm_channel_number,
+                                 int profile_version,
+                                 int supported_features,
+                                 int supported_message_types,
+                                 String service_name){
+        this.mMasInstanceId = mas_instance_id;
+        this.mL2capPsm = l2cap_psm;
+        this.mRfcommChannelNumber = rfcomm_channel_number;
+        this.mProfileVersion = profile_version;
+        this.mSupportedFeatures = supported_features;
+        this.mSupportedMessageTypes = supported_message_types;
+        this.mServiceName = service_name;
+    }
+
+    public SdpMasRecord(Parcel in){
+        this.mMasInstanceId = in.readInt();
+        this.mL2capPsm = in.readInt();
+        this.mRfcommChannelNumber = in.readInt();
+        this.mProfileVersion = in.readInt();
+        this.mSupportedFeatures = in.readInt();
+        this.mSupportedMessageTypes = in.readInt();
+        this.mServiceName = in.readString();
+    }
+    @Override
+    public int describeContents() {
+        // TODO Auto-generated method stub
+        return 0;
+    }
+
+    public int getMasInstanceId() {
+        return mMasInstanceId;
+    }
+
+    public int getL2capPsm() {
+        return mL2capPsm;
+    }
+
+    public int getRfcommCannelNumber() {
+        return mRfcommChannelNumber;
+    }
+
+    public int getProfileVersion() {
+        return mProfileVersion;
+    }
+
+    public int getSupportedFeatures() {
+        return mSupportedFeatures;
+    }
+
+    public int getSupportedMessageTypes() {
+        return mSupportedMessageTypes;
+    }
+    
+    public boolean msgSupported(int msg) {
+        return (mSupportedMessageTypes & msg) != 0;
+    }
+    
+    public String getServiceName() {
+        return mServiceName;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+
+        dest.writeInt(this.mMasInstanceId);
+        dest.writeInt(this.mL2capPsm);
+        dest.writeInt(this.mRfcommChannelNumber);
+        dest.writeInt(this.mProfileVersion);
+        dest.writeInt(this.mSupportedFeatures);
+        dest.writeInt(this.mSupportedMessageTypes);
+        dest.writeString(this.mServiceName);
+
+    }
+    @Override
+    public String toString(){
+        String ret = "Bluetooth MAS SDP Record:\n";
+
+        if(mMasInstanceId != -1){
+            ret += "Mas Instance Id: " + mMasInstanceId + "\n";
+        }
+        if(mRfcommChannelNumber != -1){
+            ret += "RFCOMM Chan Number: " + mRfcommChannelNumber + "\n";
+        }
+        if(mL2capPsm != -1){
+            ret += "L2CAP PSM: " + mL2capPsm + "\n";
+        }
+        if(mServiceName != null){
+            ret += "Service Name: " + mServiceName + "\n";
+        }
+        if(mProfileVersion != -1){
+            ret += "Profile version: " + mProfileVersion + "\n";
+        }
+        if(mSupportedMessageTypes != -1){
+            ret += "Supported msg types: " + mSupportedMessageTypes + "\n";
+        }
+        if(mSupportedFeatures != -1){
+            ret += "Supported features: " + mSupportedFeatures + "\n";
+        }
+        return ret;
+    }
+
+    public static final Parcelable.Creator CREATOR = new Parcelable.Creator() {
+        public SdpMasRecord createFromParcel(Parcel in) {
+            return new SdpMasRecord(in);
+        }
+        public SdpRecord[] newArray(int size) {
+            return new SdpRecord[size];
+        }
+    };
+}
diff --git a/core/java/android/bluetooth/SdpMnsRecord.java b/core/java/android/bluetooth/SdpMnsRecord.java
new file mode 100644
index 0000000..c02bb5a
--- /dev/null
+++ b/core/java/android/bluetooth/SdpMnsRecord.java
@@ -0,0 +1,112 @@
+/*
+* Copyright (C) 2015 Samsung System LSI
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*      http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package android.bluetooth;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/** @hide */
+public class SdpMnsRecord implements Parcelable {
+    private final int mL2capPsm;
+    private final int mRfcommChannelNumber;
+    private final int mSupportedFeatures;
+    private final int mProfileVersion;
+    private final String mServiceName;
+
+    public SdpMnsRecord(int l2cap_psm,
+            int rfcomm_channel_number,
+            int profile_version,
+            int supported_features,
+            String service_name){
+        this.mL2capPsm = l2cap_psm;
+        this.mRfcommChannelNumber = rfcomm_channel_number;
+        this.mSupportedFeatures = supported_features;
+        this.mServiceName = service_name;
+        this.mProfileVersion = profile_version;
+    }
+
+    public SdpMnsRecord(Parcel in){
+           this.mRfcommChannelNumber = in.readInt();
+           this.mL2capPsm = in.readInt();
+           this.mServiceName = in.readString();
+           this.mSupportedFeatures = in.readInt();
+           this.mProfileVersion = in.readInt();
+    }
+    @Override
+    public int describeContents() {
+        // TODO Auto-generated method stub
+        return 0;
+    }
+
+
+    public int getL2capPsm() {
+        return mL2capPsm;
+    }
+
+    public int getRfcommChannelNumber() {
+        return mRfcommChannelNumber;
+    }
+
+    public int getSupportedFeatures() {
+        return mSupportedFeatures;
+    }
+
+    public String getServiceName() {
+        return mServiceName;
+    }
+
+    public int getProfileVersion() {
+        return mProfileVersion;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeInt(mRfcommChannelNumber);
+        dest.writeInt(mL2capPsm);
+        dest.writeString(mServiceName);
+        dest.writeInt(mSupportedFeatures);
+        dest.writeInt(mProfileVersion);
+    }
+
+    public String toString(){
+        String ret = "Bluetooth MNS SDP Record:\n";
+
+        if(mRfcommChannelNumber != -1){
+            ret += "RFCOMM Chan Number: " + mRfcommChannelNumber + "\n";
+        }
+        if(mL2capPsm != -1){
+            ret += "L2CAP PSM: " + mL2capPsm + "\n";
+        }
+        if(mServiceName != null){
+            ret += "Service Name: " + mServiceName + "\n";
+        }
+        if(mSupportedFeatures != -1){
+            ret += "Supported features: " + mSupportedFeatures + "\n";
+        }
+        if(mProfileVersion != -1){
+            ret += "Profile_version: " + mProfileVersion+"\n";
+        }
+        return ret;
+    }
+
+    public static final Parcelable.Creator CREATOR = new Parcelable.Creator() {
+        public SdpMnsRecord createFromParcel(Parcel in) {
+            return new SdpMnsRecord(in);
+        }
+        public SdpMnsRecord[] newArray(int size) {
+            return new SdpMnsRecord[size];
+        }
+    };
+}
diff --git a/core/java/android/bluetooth/SdpOppOpsRecord.java b/core/java/android/bluetooth/SdpOppOpsRecord.java
new file mode 100644
index 0000000..e0e4007
--- /dev/null
+++ b/core/java/android/bluetooth/SdpOppOpsRecord.java
@@ -0,0 +1,118 @@
+/*
+* Copyright (C) 2015 Samsung System LSI
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*      http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package android.bluetooth;
+
+import java.util.Arrays;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * Data representation of a Object Push Profile Server side SDP record.
+ */
+/** @hide */
+public class SdpOppOpsRecord implements Parcelable {
+
+    private final String mServiceName;
+    private final int mRfcommChannel;
+    private final int mL2capPsm;
+    private final int mProfileVersion;
+    private final byte[] mFormatsList;
+
+    public SdpOppOpsRecord(String serviceName, int rfcommChannel,
+            int l2capPsm, int version, byte[] formatsList) {
+        super();
+        this.mServiceName = serviceName;
+        this.mRfcommChannel = rfcommChannel;
+        this.mL2capPsm = l2capPsm;
+        this.mProfileVersion = version;
+        this.mFormatsList = formatsList;
+    }
+
+    public String getServiceName() {
+        return mServiceName;
+    }
+
+    public int getRfcommChannel() {
+        return mRfcommChannel;
+    }
+
+    public int getL2capPsm() {
+        return mL2capPsm;
+    }
+
+    public int getProfileVersion() {
+        return mProfileVersion;
+    }
+
+    public byte[] getFormatsList() {
+        return mFormatsList;
+    }
+
+    @Override
+    public int describeContents() {
+        /* No special objects */
+        return 0;
+    }
+
+    public SdpOppOpsRecord(Parcel in){
+        this.mRfcommChannel = in.readInt();
+        this.mL2capPsm = in.readInt();
+        this.mProfileVersion = in.readInt();
+        this.mServiceName = in.readString();
+        int arrayLength = in.readInt();
+        if(arrayLength > 0) {
+            byte[] bytes = new byte[arrayLength];
+            in.readByteArray(bytes);
+            this.mFormatsList = bytes;
+        } else {
+            this.mFormatsList = null;
+        }
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeInt(mRfcommChannel);
+        dest.writeInt(mL2capPsm);
+        dest.writeInt(mProfileVersion);
+        dest.writeString(mServiceName);
+        if(mFormatsList!= null && mFormatsList.length > 0) {
+            dest.writeInt(mFormatsList.length);
+            dest.writeByteArray(mFormatsList);
+        } else {
+            dest.writeInt(0);
+        }
+    }
+
+    public String toString(){
+        StringBuilder sb = new StringBuilder("Bluetooth OPP Server SDP Record:\n");
+        sb.append("  RFCOMM Chan Number: ").append(mRfcommChannel);
+        sb.append("\n  L2CAP PSM: ").append(mL2capPsm);
+        sb.append("\n  Profile version: ").append(mProfileVersion);
+        sb.append("\n  Service Name: ").append(mServiceName);
+        sb.append("\n  Formats List: ").append(Arrays.toString(mFormatsList));
+        return sb.toString();
+    }
+
+    public static final Parcelable.Creator CREATOR = new Parcelable.Creator() {
+        public SdpOppOpsRecord createFromParcel(Parcel in) {
+            return new SdpOppOpsRecord(in);
+        }
+        public SdpOppOpsRecord[] newArray(int size) {
+            return new SdpOppOpsRecord[size];
+        }
+    };
+
+}
diff --git a/core/java/android/bluetooth/SdpPseRecord.java b/core/java/android/bluetooth/SdpPseRecord.java
new file mode 100644
index 0000000..2c159cc
--- /dev/null
+++ b/core/java/android/bluetooth/SdpPseRecord.java
@@ -0,0 +1,125 @@
+/*
+* Copyright (C) 2015 Samsung System LSI
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*      http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+package android.bluetooth;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/** @hide */
+public class SdpPseRecord implements Parcelable {
+    private final int mL2capPsm;
+    private final int mRfcommChannelNumber;
+    private final int mProfileVersion;
+    private final int mSupportedFeatures;
+    private final int mSupportedRepositories;
+    private final String mServiceName;
+
+    public SdpPseRecord(int l2cap_psm,
+            int rfcomm_channel_number,
+            int profile_version,
+            int supported_features,
+            int supported_repositories,
+            String service_name){
+        this.mL2capPsm = l2cap_psm;
+        this.mRfcommChannelNumber = rfcomm_channel_number;
+        this.mProfileVersion = profile_version;
+        this.mSupportedFeatures = supported_features;
+        this.mSupportedRepositories = supported_repositories;
+        this.mServiceName = service_name;
+    }
+
+    public SdpPseRecord(Parcel in){
+           this.mRfcommChannelNumber = in.readInt();
+           this.mL2capPsm = in.readInt();
+           this.mProfileVersion = in.readInt();
+           this.mSupportedFeatures = in.readInt();
+           this.mSupportedRepositories = in.readInt();
+           this.mServiceName = in.readString();
+    }
+    @Override
+    public int describeContents() {
+        // TODO Auto-generated method stub
+        return 0;
+    }
+
+    public int getL2capPsm() {
+        return mL2capPsm;
+    }
+
+    public int getRfcommChannelNumber() {
+        return mRfcommChannelNumber;
+    }
+
+    public int getSupportedFeatures() {
+        return mSupportedFeatures;
+    }
+
+    public String getServiceName() {
+        return mServiceName;
+    }
+
+    public int getProfileVersion() {
+        return mProfileVersion;
+    }
+
+    public int getSupportedRepositories() {
+        return mSupportedRepositories;
+    }
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeInt(mRfcommChannelNumber);
+        dest.writeInt(mL2capPsm);
+        dest.writeInt(mProfileVersion);
+        dest.writeInt(mSupportedFeatures);
+        dest.writeInt(mSupportedRepositories);
+        dest.writeString(mServiceName);
+
+    }
+
+    public String toString(){
+        String ret = "Bluetooth MNS SDP Record:\n";
+
+        if(mRfcommChannelNumber != -1){
+            ret += "RFCOMM Chan Number: " + mRfcommChannelNumber + "\n";
+        }
+        if(mL2capPsm != -1){
+            ret += "L2CAP PSM: " + mL2capPsm + "\n";
+        }
+        if(mProfileVersion != -1){
+            ret += "profile version: " + mProfileVersion + "\n";
+        }
+        if(mServiceName != null){
+            ret += "Service Name: " + mServiceName + "\n";
+        }
+        if(mSupportedFeatures != -1){
+            ret += "Supported features: " + mSupportedFeatures + "\n";
+        }
+        if(mSupportedRepositories != -1){
+            ret += "Supported repositories: " + mSupportedRepositories + "\n";
+        }
+
+        return ret;
+    }
+
+    public static final Parcelable.Creator CREATOR = new Parcelable.Creator() {
+        public SdpPseRecord createFromParcel(Parcel in) {
+            return new SdpPseRecord(in);
+        }
+        public SdpPseRecord[] newArray(int size) {
+            return new SdpPseRecord[size];
+        }
+    };
+}
diff --git a/core/java/android/bluetooth/SdpRecord.java b/core/java/android/bluetooth/SdpRecord.java
new file mode 100644
index 0000000..6f1065e
--- /dev/null
+++ b/core/java/android/bluetooth/SdpRecord.java
@@ -0,0 +1,76 @@
+/*
+* Copyright (C) 2015 Samsung System LSI
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*      http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+package android.bluetooth;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.Arrays;
+
+/** @hide */
+public class SdpRecord implements Parcelable{
+
+    private final byte[] mRawData;
+    private final int mRawSize;
+
+    @Override
+    public String toString() {
+        return "BluetoothSdpRecord [rawData=" + Arrays.toString(mRawData)
+                + ", rawSize=" + mRawSize + "]";
+    }
+
+    public SdpRecord(int size_record, byte[] record){
+        this.mRawData = record;
+        this.mRawSize = size_record;
+    }
+
+    public SdpRecord(Parcel in){
+        this.mRawSize = in.readInt();
+        this.mRawData = new byte[mRawSize];
+        in.readByteArray(this.mRawData);
+
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeInt(this.mRawSize);
+        dest.writeByteArray(this.mRawData);
+
+
+    }
+    public static final Parcelable.Creator CREATOR = new Parcelable.Creator() {
+        public SdpRecord createFromParcel(Parcel in) {
+            return new SdpRecord(in);
+        }
+
+        public SdpRecord[] newArray(int size) {
+            return new SdpRecord[size];
+        }
+    };
+
+    public byte[] getRawData() {
+        return mRawData;
+    }
+
+    public int getRawSize() {
+        return mRawSize;
+    }
+}
diff --git a/core/java/android/bluetooth/le/BluetoothLeScanner.java b/core/java/android/bluetooth/le/BluetoothLeScanner.java
index b6bcfb8..3078951 100644
--- a/core/java/android/bluetooth/le/BluetoothLeScanner.java
+++ b/core/java/android/bluetooth/le/BluetoothLeScanner.java
@@ -409,8 +409,8 @@
                         List <ScanFilter> filterList) {
         final int callbackType = settings.getCallbackType();
         // If onlost/onfound is requested, a non-empty filter is expected
-        if ((callbackType & ScanSettings.CALLBACK_TYPE_FIRST_MATCH
-                        | ScanSettings.CALLBACK_TYPE_MATCH_LOST) != 0) {
+        if ((callbackType & (ScanSettings.CALLBACK_TYPE_FIRST_MATCH
+                        | ScanSettings.CALLBACK_TYPE_MATCH_LOST)) != 0) {
             if (filterList == null) {
                 return false;
             }
diff --git a/core/java/android/bluetooth/le/BluetoothLeUtils.java b/core/java/android/bluetooth/le/BluetoothLeUtils.java
index 4916bd9..c40256b 100644
--- a/core/java/android/bluetooth/le/BluetoothLeUtils.java
+++ b/core/java/android/bluetooth/le/BluetoothLeUtils.java
@@ -132,7 +132,7 @@
      *             {@link BluetoothAdapter#STATE_ON}.
      */
     static void checkAdapterStateOn(BluetoothAdapter adapter) {
-        if (adapter == null || adapter.getState() != BluetoothAdapter.STATE_ON) {
+        if (adapter == null || !adapter.isLeEnabled()) {//adapter.getState() != BluetoothAdapter.STATE_ON) {
             throw new IllegalStateException("BT Adapter is not turned ON");
         }
     }
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index e5e55d6..370f61c 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -2193,6 +2193,7 @@
             MEDIA_ROUTER_SERVICE,
             TELEPHONY_SERVICE,
             TELEPHONY_SUBSCRIPTION_SERVICE,
+            CARRIER_CONFIG_SERVICE,
             TELECOM_SERVICE,
             CLIPBOARD_SERVICE,
             INPUT_METHOD_SERVICE,
@@ -2338,6 +2339,8 @@
      * @see android.telephony.TelephonyManager
      * @see #TELEPHONY_SUBSCRIPTION_SERVICE
      * @see android.telephony.SubscriptionManager
+     * @see #CARRIER_CONFIG_SERVICE
+     * @see android.telephony.CarrierConfigManager
      * @see #INPUT_METHOD_SERVICE
      * @see android.view.inputmethod.InputMethodManager
      * @see #UI_MODE_SERVICE
@@ -2755,6 +2758,16 @@
 
     /**
      * Use with {@link #getSystemService} to retrieve a
+     * {@link android.telephony.CarrierConfigManager} for reading carrier configuration values.
+     *
+     * @see #getSystemService
+     * @see android.telephony.CarrierConfigManager
+     */
+    public static final String CARRIER_CONFIG_SERVICE = "carrier_config";
+
+    /**
+     * Use with {@link #getSystemService} to retrieve a
+     * {@link android.text.ClipboardManager} for accessing and modifying
      * {@link android.content.ClipboardManager} for accessing and modifying
      * the contents of the global clipboard.
      *
@@ -3077,6 +3090,15 @@
      */
     public static final String RADIO_SERVICE = "radio";
 
+    /**
+     * Use with {@link #getSystemService} to retrieve a
+     * {@link android.media.AudioDevicesManager} for handling device enumeration & notification.
+     *
+     * @see #getSystemService
+     * @see android.media.AudioDevicesManager
+     */
+    public static final String AUDIO_DEVICES_SERVICE = "audio_devices_manager";
+
 
     /**
      * Determine whether the given permission is allowed for a particular
diff --git a/core/java/android/content/pm/ActivityInfo.java b/core/java/android/content/pm/ActivityInfo.java
index 4723c0d..16f6b1e 100644
--- a/core/java/android/content/pm/ActivityInfo.java
+++ b/core/java/android/content/pm/ActivityInfo.java
@@ -223,11 +223,12 @@
      */
     public static final int FLAG_HARDWARE_ACCELERATED = 0x0200;
     /**
-     * Value for {@link #flags}: true when the application can be displayed over the lockscreen
-     * and consequently over all users' windows.
+     * Value for {@link #flags}: true when the application can be displayed for all users
+     * regardless of if the user of the application is the current user. Set from the
+     * {@link android.R.attr#showForAllUsers} attribute.
      * @hide
      */
-    public static final int FLAG_SHOW_ON_LOCK_SCREEN = 0x0400;
+    public static final int FLAG_SHOW_FOR_ALL_USERS = 0x0400;
     /**
      * Bit in {@link #flags} corresponding to an immersive activity
      * that wishes not to be interrupted by notifications.
@@ -609,7 +610,7 @@
      * attribute.
      */
     public int configChanges;
-    
+
     /**
      * The desired soft input mode for this activity's main window.
      * Set from the {@link android.R.attr#windowSoftInputMode} attribute
@@ -648,6 +649,37 @@
      */
     public boolean resizeable;
 
+    /** @hide */
+    public static final int LOCK_TASK_LAUNCH_MODE_DEFAULT = 0;
+    /** @hide */
+    public static final int LOCK_TASK_LAUNCH_MODE_NEVER = 1;
+    /** @hide */
+    public static final int LOCK_TASK_LAUNCH_MODE_ALWAYS = 2;
+    /** @hide */
+    public static final int LOCK_TASK_LAUNCH_MODE_IF_WHITELISTED = 3;
+
+    /** @hide */
+    public static final String lockTaskLaunchModeToString(int lockTaskLaunchMode) {
+        switch (lockTaskLaunchMode) {
+            case LOCK_TASK_LAUNCH_MODE_DEFAULT:
+                return "LOCK_TASK_LAUNCH_MODE_DEFAULT";
+            case LOCK_TASK_LAUNCH_MODE_NEVER:
+                return "LOCK_TASK_LAUNCH_MODE_NEVER";
+            case LOCK_TASK_LAUNCH_MODE_ALWAYS:
+                return "LOCK_TASK_LAUNCH_MODE_ALWAYS";
+            case LOCK_TASK_LAUNCH_MODE_IF_WHITELISTED:
+                return "LOCK_TASK_LAUNCH_MODE_IF_WHITELISTED";
+            default:
+                return "unknown=" + lockTaskLaunchMode;
+        }
+    }
+    /**
+     * Value indicating if the activity is to be locked at startup. Takes on the values from
+     * {@link android.R.attr#lockTaskMode}.
+     * @hide
+     */
+    public int lockTaskLaunchMode;
+
     public ActivityInfo() {
     }
 
@@ -665,13 +697,15 @@
         uiOptions = orig.uiOptions;
         parentActivityName = orig.parentActivityName;
         maxRecents = orig.maxRecents;
+        resizeable = orig.resizeable;
+        lockTaskLaunchMode = orig.lockTaskLaunchMode;
     }
-    
+
     /**
      * Return the theme resource identifier to use for this activity.  If
      * the activity defines a theme, that is used; else, the application
      * theme is used.
-     * 
+     *
      * @return The theme associated with this activity.
      */
     public final int getThemeResource() {
@@ -709,7 +743,8 @@
         if (uiOptions != 0) {
             pw.println(prefix + " uiOptions=0x" + Integer.toHexString(uiOptions));
         }
-        pw.println(prefix + "resizeable=" + resizeable);
+        pw.println(prefix + "resizeable=" + resizeable + " lockTaskLaunchMode="
+                + lockTaskLaunchModeToString(lockTaskLaunchMode));
         super.dumpBack(pw, prefix);
     }
     
@@ -739,6 +774,7 @@
         dest.writeInt(persistableMode);
         dest.writeInt(maxRecents);
         dest.writeInt(resizeable ? 1 : 0);
+        dest.writeInt(lockTaskLaunchMode);
     }
 
     public static final Parcelable.Creator<ActivityInfo> CREATOR
@@ -767,5 +803,6 @@
         persistableMode = source.readInt();
         maxRecents = source.readInt();
         resizeable = (source.readInt() == 1);
+        lockTaskLaunchMode = source.readInt();
     }
 }
diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java
index 5bdb7bb..e2701ee 100644
--- a/core/java/android/content/pm/ApplicationInfo.java
+++ b/core/java/android/content/pm/ApplicationInfo.java
@@ -21,6 +21,7 @@
 import android.graphics.drawable.Drawable;
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.text.TextUtils;
 import android.util.Printer;
 
 import com.android.internal.util.ArrayUtils;
@@ -338,8 +339,8 @@
      * (e.g., HTTP rather than HTTPS; WebSockets rather than WebSockets Secure; XMPP, IMAP, STMP
      * without STARTTLS or TLS). If {@code false}, the app declares that it does not intend to use
      * cleartext network traffic, in which case platform components (e.g., HTTP stacks,
-     * {@code WebView}, {@code MediaPlayer}) will refuse app's requests to use cleartext traffic.
-     * Third-party libraries are encouraged to honor this flag as well.
+     * {@code WebView}, {@code DownloadManager}, {@code MediaPlayer}) will refuse app's requests to
+     * use cleartext traffic. Third-party libraries are encouraged to honor this flag as well.
      */
     public static final int FLAG_USES_CLEARTEXT_TRAFFIC = 1<<27;
 
@@ -937,6 +938,17 @@
         return (flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0;
     }
 
+    /** @hide */
+    public boolean isInternal() {
+        return (flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) == 0;
+    }
+
+    /** @hide */
+    public boolean isExternalAsec() {
+        return TextUtils.isEmpty(volumeUuid)
+                && (flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0;
+    }
+
     /**
      * @hide
      */
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 8844ea8..a1288729 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -20,6 +20,7 @@
 import android.annotation.DrawableRes;
 import android.annotation.IntDef;
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.annotation.SdkConstant;
 import android.annotation.SdkConstant.SdkConstantType;
 import android.annotation.StringRes;
@@ -4162,6 +4163,12 @@
     public abstract void movePackageAndData(String packageName, String volumeUuid,
             IPackageMoveObserver observer);
 
+    /** {@hide} */
+    public abstract @Nullable VolumeInfo getApplicationCurrentVolume(ApplicationInfo app);
+
+    /** {@hide} */
+    public abstract @NonNull List<VolumeInfo> getApplicationCandidateVolumes(ApplicationInfo app);
+
     /**
      * Returns the device identity that verifiers can use to associate their scheme to a particular
      * device. This should not be used by anything other than a package verifier.
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index 7464cab..fed9261 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -3099,8 +3099,9 @@
             a.info.flags |= ActivityInfo.FLAG_FINISH_ON_CLOSE_SYSTEM_DIALOGS;
         }
 
-        if (sa.getBoolean(R.styleable.AndroidManifestActivity_showOnLockScreen, false)) {
-            a.info.flags |= ActivityInfo.FLAG_SHOW_ON_LOCK_SCREEN;
+        if (sa.getBoolean(R.styleable.AndroidManifestActivity_showOnLockScreen, false)
+                || sa.getBoolean(R.styleable.AndroidManifestActivity_showForAllUsers, false)) {
+            a.info.flags |= ActivityInfo.FLAG_SHOW_FOR_ALL_USERS;
         }
 
         if (sa.getBoolean(R.styleable.AndroidManifestActivity_immersive, false)) {
@@ -3159,6 +3160,9 @@
                         R.styleable.AndroidManifestActivity_screenOrientation,
                         ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED);
             }
+
+            a.info.lockTaskLaunchMode =
+                    sa.getInt(R.styleable.AndroidManifestActivity_lockTaskMode, 0);
         } else {
             a.info.launchMode = ActivityInfo.LAUNCH_MULTIPLE;
             a.info.configChanges = 0;
diff --git a/core/java/android/hardware/Camera.java b/core/java/android/hardware/Camera.java
index 49f6513..d88594d 100644
--- a/core/java/android/hardware/Camera.java
+++ b/core/java/android/hardware/Camera.java
@@ -1803,8 +1803,6 @@
         public Point mouth = null;
     }
 
-    // Error codes match the enum in include/ui/Camera.h
-
     /**
      * Unspecified camera error.
      * @see Camera.ErrorCallback
@@ -1812,6 +1810,12 @@
     public static final int CAMERA_ERROR_UNKNOWN = 1;
 
     /**
+     * Camera was disconnected due to use by higher priority user.
+     * @see Camera.ErrorCallback
+     */
+    public static final int CAMERA_ERROR_EVICTED = 2;
+
+    /**
      * Media server died. In this case, the application must release the
      * Camera object and instantiate a new one.
      * @see Camera.ErrorCallback
diff --git a/core/java/android/hardware/camera2/CameraCaptureSession.java b/core/java/android/hardware/camera2/CameraCaptureSession.java
index 6b6f026..31e6e25 100644
--- a/core/java/android/hardware/camera2/CameraCaptureSession.java
+++ b/core/java/android/hardware/camera2/CameraCaptureSession.java
@@ -18,6 +18,7 @@
 
 import android.os.Handler;
 import android.view.Surface;
+
 import java.util.List;
 
 
@@ -69,6 +70,61 @@
     public abstract CameraDevice getDevice();
 
     /**
+     * <p>Pre-allocate all buffers for an output Surface.</p>
+     *
+     * <p>Normally, the image buffers for a given output Surface are allocated on-demand,
+     * to minimize startup latency and memory overhead.</p>
+     *
+     * <p>However, in some cases, it may be desirable for the buffers to be allocated before
+     * any requests targeting the Surface are actually submitted to the device. Large buffers
+     * may take some time to allocate, which can result in delays in submitting requests until
+     * sufficient buffers are allocated to reach steady-state behavior. Such delays can cause
+     * bursts to take longer than desired, or cause skips or stutters in preview output.</p>
+     *
+     * <p>The prepare() method can be used to perform this preallocation. It may only be called for
+     * a given output Surface before that Surface is used as a target for a request. The number of
+     * buffers allocated is the sum of the count needed by the consumer providing the output
+     * Surface, and the maximum number needed by the camera device to fill its pipeline. Since this
+     * may be a larger number than what is actually required for steady-state operation, using
+     * prepare may result in higher memory consumption than the normal on-demand behavior results
+     * in. Prepare() will also delay the time to first output to a given Surface, in exchange for
+     * smoother frame rate once the allocation is complete.</p>
+     *
+     * <p>For example, an application that creates an
+     * {@link android.media.ImageReader#newInstance ImageReader} with a maxImages argument of 10,
+     * but only uses 3 simultaneous Images at once would normally only cause those 3 images to be
+     * allocated (plus what is needed by the camera device for smooth operation).  But using
+     * prepare() on the ImageReader Surface will result in all 10 Images being allocated. So
+     * applications using this method should take care to request only the number of buffers
+     * actually necessary for their application.</p>
+     *
+     * <p>If the same output Surface is used in consecutive sessions (without closing the first
+     * session explicitly), then its already-allocated buffers are carried over, and if it was
+     * used as a target of a capture request in the first session, prepare cannot be called on it
+     * in the second session.</p>
+     *
+     * <p>Once allocation is complete, {@link StateCallback#onSurfacePrepared} will be invoked with
+     * the Surface provided to this method. Between the prepare call and the onSurfacePrepared call,
+     * the Surface provided to prepare must not be used as a target of a CaptureRequest submitted
+     * to this session.</p>
+     *
+     * @param surface the output Surface for which buffers should be pre-allocated. Must be one of
+     * the output Surfaces used to create this session.
+     *
+     * @throws CameraAccessException if the camera device is no longer connected or has
+     *                               encountered a fatal error
+     * @throws IllegalStateException if this session is no longer active, either because the session
+     *                               was explicitly closed, a new session has been created
+     *                               or the camera device has been closed.
+     * @throws IllegalArgumentException if the Surface is invalid, not part of this Session, or has
+     *                                  already been used as a target of a CaptureRequest in this
+     *                                  session or immediately prior sessions.
+     *
+     * @see StateCallback#onSurfacePrepared
+     */
+    public abstract void prepare(Surface surface) throws CameraAccessException;
+
+    /**
      * <p>Submit a request for an image to be captured by the camera device.</p>
      *
      * <p>The request defines all the parameters for capturing the single image,
@@ -110,9 +166,10 @@
      *                               was explicitly closed, a new session has been created
      *                               or the camera device has been closed.
      * @throws IllegalArgumentException if the request targets no Surfaces or Surfaces that are not
-     *                                  configured as outputs for this session. Or if a reprocess
+     *                                  configured as outputs for this session; or a reprocess
      *                                  capture request is submitted in a non-reprocessible capture
-     *                                  session. Or if the handler is
+     *                                  session; or the capture targets a Surface in the middle
+     *                                  of being {@link #prepare prepared}; or the handler is
      *                                  null, the listener is not null, and the calling thread has
      *                                  no looper.
      *
@@ -164,13 +221,15 @@
      * @throws IllegalStateException if this session is no longer active, either because the session
      *                               was explicitly closed, a new session has been created
      *                               or the camera device has been closed.
-     * @throws IllegalArgumentException If the requests target no Surfaces, or target Surfaces not
-     *                                  currently configured as outputs. Or if a reprocess
+     * @throws IllegalArgumentException If the requests target no Surfaces, or the requests target
+     *                                  Surfaces not currently configured as outputs; or a reprocess
      *                                  capture request is submitted in a non-reprocessible capture
-     *                                  session. Or if the list of requests contains both requests
-     *                                  to capture images from the camera and reprocess capture
-     *                                  requests. Or if the handler is null, the listener is not
-     *                                  null, and the calling thread has no looper.
+     *                                  session; or the list of requests contains both requests to
+     *                                  capture images from the camera and reprocess capture
+     *                                  requests; or one of the captures targets a Surface in the
+     *                                  middle of being {@link #prepare prepared}; or if the handler
+     *                                  is null, the listener is not null, and the calling thread
+     *                                  has no looper.
      *
      * @see #capture
      * @see #setRepeatingRequest
@@ -230,11 +289,12 @@
      * @throws IllegalStateException if this session is no longer active, either because the session
      *                               was explicitly closed, a new session has been created
      *                               or the camera device has been closed.
-     * @throws IllegalArgumentException If the requests reference no Surfaces or Surfaces that are
-     *                                  not currently configured as outputs. Or if the request is
-     *                                  a reprocess capture request. Or if the handler is null, the
-     *                                  listener is not null, and the calling thread has no looper.
-     *                                  Or if no requests were passed in.
+     * @throws IllegalArgumentException If the request references no Surfaces or references Surfaces
+     *                                  that are not currently configured as outputs; or the request
+     *                                  is a reprocess capture request; or the capture targets a
+     *                                  Surface in the middle of being {@link #prepare prepared}; or
+     *                                  the handler is null, the listener is not null, and the
+     *                                  calling thread has no looper; or no requests were passed in.
      *
      * @see #capture
      * @see #captureBurst
@@ -299,11 +359,13 @@
      * @throws IllegalStateException if this session is no longer active, either because the session
      *                               was explicitly closed, a new session has been created
      *                               or the camera device has been closed.
-     * @throws IllegalArgumentException If the requests reference no Surfaces or Surfaces not
-     *                                  currently configured as outputs. Or if one of the requests
-     *                                  is a reprocess capture request. Or if the handler is null,
-     *                                  the listener is not null, and the calling thread has no
-     *                                  looper. Or if no requests were passed in.
+     * @throws IllegalArgumentException If the requests reference no Surfaces or reference Surfaces
+     *                                  not currently configured as outputs; or one of the requests
+     *                                  is a reprocess capture request; or one of the captures
+     *                                  targets a Surface in the middle of being
+     *                                  {@link #prepare prepared}; or the handler is null, the
+     *                                  listener is not null, and the calling thread has no looper;
+     *                                  or no requests were passed in.
      *
      * @see #capture
      * @see #captureBurst
@@ -514,6 +576,25 @@
         public void onClosed(CameraCaptureSession session) {
             // default empty implementation
         }
+
+        /**
+         * This method is called when the buffer pre-allocation for an output Surface is complete.
+         *
+         * <p>Buffer pre-allocation for an output Surface is started by the {@link #prepare} call.
+         * While allocation is underway, the Surface must not be used as a capture target.
+         * Once this callback fires, the output Surface provided can again be used as a target for
+         * a capture request.</p>
+         *
+         * <p>In case of a error during pre-allocation (such as running out of suitable memory),
+         * this callback is still invoked after the error is encountered, though some buffers may
+         * not have been successfully pre-allocated.</p>
+         *
+         * @param session the session returned by {@link CameraDevice#createCaptureSession}
+         * @param surface the Surface that was used with the {@link #prepare} call.
+         */
+        public void onSurfacePrepared(CameraCaptureSession session, Surface surface) {
+            // default empty implementation
+        }
     }
 
     /**
diff --git a/core/java/android/hardware/camera2/CameraCharacteristics.java b/core/java/android/hardware/camera2/CameraCharacteristics.java
index 8f7aed4..87a1ca9 100644
--- a/core/java/android/hardware/camera2/CameraCharacteristics.java
+++ b/core/java/android/hardware/camera2/CameraCharacteristics.java
@@ -1030,12 +1030,13 @@
      * point, <code>z_s = 1</code>, and <code>w_s</code> is a measurement of disparity
      * (depth) in pixel coordinates.</p>
      * <p><b>Units</b>:
-     * Pixels in the android.sensor.activeArraySize coordinate
+     * Pixels in the {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize} coordinate
      * system.</p>
      * <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
      *
      * @see CameraCharacteristics#LENS_POSE_ROTATION
      * @see CameraCharacteristics#LENS_POSE_TRANSLATION
+     * @see CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE
      */
     @PublicKey
     public static final Key<float[]> LENS_INTRINSIC_CALIBRATION =
@@ -1330,6 +1331,7 @@
      *   <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>
+     *   <li>{@link #REQUEST_AVAILABLE_CAPABILITIES_DEPTH_OUTPUT DEPTH_OUTPUT}</li>
      * </ul></p>
      * <p>This key is available on all devices.</p>
      *
@@ -1342,6 +1344,7 @@
      * @see #REQUEST_AVAILABLE_CAPABILITIES_READ_SENSOR_SETTINGS
      * @see #REQUEST_AVAILABLE_CAPABILITIES_BURST_CAPTURE
      * @see #REQUEST_AVAILABLE_CAPABILITIES_YUV_REPROCESSING
+     * @see #REQUEST_AVAILABLE_CAPABILITIES_DEPTH_OUTPUT
      */
     @PublicKey
     public static final Key<int[]> REQUEST_AVAILABLE_CAPABILITIES =
@@ -2542,11 +2545,23 @@
      * post-processing, arbitrary cropping regions, and has relaxed performance constraints.</p>
      * <p>Each higher level supports everything the lower level supports
      * in this order: FULL <code>&gt;</code> LIMITED <code>&gt;</code> LEGACY.</p>
+     * <p>A HIGH_RESOLUTION device is equivalent to a FULL device, except that:</p>
+     * <ul>
+     * <li>At least one output resolution of 8 megapixels or higher in uncompressed YUV is
+     *   supported at <code>&gt;=</code> 20 fps.</li>
+     * <li>Maximum-size (sensor resolution) uncompressed YUV is supported  at <code>&gt;=</code> 10
+     *   fps.</li>
+     * <li>For devices that list the RAW capability and support either RAW10 or RAW12 output,
+     *   maximum-resolution RAW10 or RAW12 capture will operate at least at the rate of
+     *   maximum-resolution YUV capture, and at least one supported output resolution of
+     *   8 megapixels or higher in RAW10 or RAW12 is supported <code>&gt;=</code> 20 fps.</li>
+     * </ul>
      * <p><b>Possible values:</b>
      * <ul>
      *   <li>{@link #INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED LIMITED}</li>
      *   <li>{@link #INFO_SUPPORTED_HARDWARE_LEVEL_FULL FULL}</li>
      *   <li>{@link #INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY LEGACY}</li>
+     *   <li>{@link #INFO_SUPPORTED_HARDWARE_LEVEL_HIGH_RESOLUTION HIGH_RESOLUTION}</li>
      * </ul></p>
      * <p>This key is available on all devices.</p>
      *
@@ -2564,6 +2579,7 @@
      * @see #INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED
      * @see #INFO_SUPPORTED_HARDWARE_LEVEL_FULL
      * @see #INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY
+     * @see #INFO_SUPPORTED_HARDWARE_LEVEL_HIGH_RESOLUTION
      */
     @PublicKey
     public static final Key<Integer> INFO_SUPPORTED_HARDWARE_LEVEL =
@@ -2694,6 +2710,29 @@
     public static final Key<android.hardware.camera2.params.StreamConfigurationDuration[]> DEPTH_AVAILABLE_DEPTH_STALL_DURATIONS =
             new Key<android.hardware.camera2.params.StreamConfigurationDuration[]>("android.depth.availableDepthStallDurations", android.hardware.camera2.params.StreamConfigurationDuration[].class);
 
+    /**
+     * <p>Indicates whether a capture request may target both a
+     * DEPTH16 / DEPTH_POINT_CLOUD output, and normal color outputs (such as
+     * YUV_420_888, JPEG, or RAW) simultaneously.</p>
+     * <p>If TRUE, including both depth and color outputs in a single
+     * capture request is not supported. An application must interleave color
+     * and depth requests.  If FALSE, a single request can target both types
+     * of output.</p>
+     * <p>Typically, this restriction exists on camera devices that
+     * need to emit a specific pattern or wavelength of light to
+     * measure depth values, which causes the color image to be
+     * corrupted during depth measurement.</p>
+     * <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
+     * <p><b>Limited capability</b> -
+     * 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 CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL
+     */
+    @PublicKey
+    public static final Key<Boolean> DEPTH_DEPTH_IS_EXCLUSIVE =
+            new Key<Boolean>("android.depth.depthIsExclusive", boolean.class);
+
     /*~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~
      * End generated code
      *~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~O@*/
diff --git a/core/java/android/hardware/camera2/CameraMetadata.java b/core/java/android/hardware/camera2/CameraMetadata.java
index 4a5bd08..e3f1d73 100644
--- a/core/java/android/hardware/camera2/CameraMetadata.java
+++ b/core/java/android/hardware/camera2/CameraMetadata.java
@@ -303,10 +303,13 @@
      * <p>The minimal set of capabilities that every camera
      * device (regardless of {@link CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL android.info.supportedHardwareLevel})
      * supports.</p>
-     * <p>This capability is listed by all devices, and
+     * <p>This capability is listed by all normal devices, and
      * indicates that the camera device has a feature set
      * that's comparable to the baseline requirements for the
      * older android.hardware.Camera API.</p>
+     * <p>Devices with the DEPTH_OUTPUT capability might not list this
+     * capability, indicating that they support only depth measurement,
+     * not standard color output.</p>
      *
      * @see CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL
      * @see CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES
@@ -545,6 +548,11 @@
      * {@link CameraCharacteristics#CONTROL_AWB_LOCK_AVAILABLE android.control.awbLockAvailable} are also guaranteed
      * to be <code>true</code> so burst capture with these two locks ON
      * yields consistent image output.</p>
+     * <p>On a camera device that reports the HIGH_RESOLUTION hardware
+     * level, meaning the device supports very large capture sizes,
+     * BURST_CAPTURE means that at least 8-megapixel images can be
+     * captured at <code>&gt;=</code> 20 fps, and maximum-resolution images can be
+     * captured at <code>&gt;=</code> 10 fps.</p>
      *
      * @see CameraCharacteristics#CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES
      * @see CameraCharacteristics#CONTROL_AE_LOCK_AVAILABLE
@@ -596,6 +604,42 @@
      */
     public static final int REQUEST_AVAILABLE_CAPABILITIES_YUV_REPROCESSING = 7;
 
+    /**
+     * <p>The camera device can produce depth measurements from its field of view.</p>
+     * <p>This capability requires the camera device to support the following:</p>
+     * <ul>
+     * <li>DEPTH16 is supported as an output format.</li>
+     * <li>DEPTH_POINT_CLOUD is optionally supported as an output format.</li>
+     * <li>This camera device, and all camera devices with the same android.lens.info.facing,
+     *   will list the following calibration entries in both CameraCharacteristics and
+     *   CaptureResults:<ul>
+     * <li>{@link CameraCharacteristics#LENS_POSE_TRANSLATION android.lens.poseTranslation}</li>
+     * <li>{@link CameraCharacteristics#LENS_POSE_ROTATION android.lens.poseRotation}</li>
+     * <li>android.lens.intrinsicCalibration</li>
+     * <li>android.lens.radialDistortion</li>
+     * </ul>
+     * </li>
+     * <li>The {@link CameraCharacteristics#DEPTH_DEPTH_IS_EXCLUSIVE android.depth.depthIsExclusive} entry is listed by this device.</li>
+     * <li>A LIMITED camera with only the DEPTH_OUTPUT capability does not have to support
+     *   normal YUV_420_888, JPEG, and PRIV-format outputs. It only has to support the DEPTH16
+     *   format.</li>
+     * </ul>
+     * <p>Generally, depth output operates at a slower frame rate than standard color capture,
+     * so the DEPTH16 and DEPTH_POINT_CLOUD formats will commonly have a stall duration that
+     * should be accounted for (see
+     * android.hardware.camera2.StreamConfigurationMap#getOutputStallDuration).  On a device
+     * that supports both depth and color-based output, to enable smooth preview, using a
+     * repeating burst is recommended, where a depth-output target is only included once
+     * every N frames, where N is the ratio between preview output rate and depth output
+     * rate, including depth stall time.</p>
+     *
+     * @see CameraCharacteristics#DEPTH_DEPTH_IS_EXCLUSIVE
+     * @see CameraCharacteristics#LENS_POSE_ROTATION
+     * @see CameraCharacteristics#LENS_POSE_TRANSLATION
+     * @see CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES
+     */
+    public static final int REQUEST_AVAILABLE_CAPABILITIES_DEPTH_OUTPUT = 8;
+
     //
     // Enumeration values for CameraCharacteristics#SCALER_CROPPING_TYPE
     //
@@ -808,6 +852,13 @@
      */
     public static final int INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY = 2;
 
+    /**
+     * <p>This camera device is capable of supporting advanced imaging applications at full rate,
+     * and additional high-resolution outputs at lower rates.</p>
+     * @see CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL
+     */
+    public static final int INFO_SUPPORTED_HARDWARE_LEVEL_HIGH_RESOLUTION = 3;
+
     //
     // Enumeration values for CameraCharacteristics#SYNC_MAX_LATENCY
     //
diff --git a/core/java/android/hardware/camera2/CaptureResult.java b/core/java/android/hardware/camera2/CaptureResult.java
index 4134d28..ef5d75c 100644
--- a/core/java/android/hardware/camera2/CaptureResult.java
+++ b/core/java/android/hardware/camera2/CaptureResult.java
@@ -2628,12 +2628,13 @@
      * point, <code>z_s = 1</code>, and <code>w_s</code> is a measurement of disparity
      * (depth) in pixel coordinates.</p>
      * <p><b>Units</b>:
-     * Pixels in the android.sensor.activeArraySize coordinate
+     * Pixels in the {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize} coordinate
      * system.</p>
      * <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
      *
      * @see CameraCharacteristics#LENS_POSE_ROTATION
      * @see CameraCharacteristics#LENS_POSE_TRANSLATION
+     * @see CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE
      */
     @PublicKey
     public static final Key<float[]> LENS_INTRINSIC_CALIBRATION =
diff --git a/core/java/android/hardware/camera2/ICameraDeviceCallbacks.aidl b/core/java/android/hardware/camera2/ICameraDeviceCallbacks.aidl
index ca0935c..151c918 100644
--- a/core/java/android/hardware/camera2/ICameraDeviceCallbacks.aidl
+++ b/core/java/android/hardware/camera2/ICameraDeviceCallbacks.aidl
@@ -31,4 +31,5 @@
     oneway void onCaptureStarted(in CaptureResultExtras resultExtras, long timestamp);
     oneway void onResultReceived(in CameraMetadataNative result,
                                  in CaptureResultExtras resultExtras);
+    oneway void onPrepared(int streamId);
 }
diff --git a/core/java/android/hardware/camera2/ICameraDeviceUser.aidl b/core/java/android/hardware/camera2/ICameraDeviceUser.aidl
index 23bfa66..375b310 100644
--- a/core/java/android/hardware/camera2/ICameraDeviceUser.aidl
+++ b/core/java/android/hardware/camera2/ICameraDeviceUser.aidl
@@ -98,4 +98,6 @@
     int waitUntilIdle();
 
     int flush(out LongParcelable lastFrameNumber);
+
+    int prepare(int streamId);
 }
diff --git a/core/java/android/hardware/camera2/impl/CallbackProxies.java b/core/java/android/hardware/camera2/impl/CallbackProxies.java
index f0217ac..dac2ef8 100644
--- a/core/java/android/hardware/camera2/impl/CallbackProxies.java
+++ b/core/java/android/hardware/camera2/impl/CallbackProxies.java
@@ -23,6 +23,7 @@
 import android.hardware.camera2.TotalCaptureResult;
 import android.hardware.camera2.dispatch.Dispatchable;
 import android.hardware.camera2.dispatch.MethodNameInvoker;
+import android.view.Surface;
 
 import static com.android.internal.util.Preconditions.*;
 
@@ -175,6 +176,12 @@
         public void onClosed(CameraCaptureSession session) {
             mProxy.invoke("onClosed", session);
         }
+
+        @Override
+        public void onSurfacePrepared(CameraCaptureSession session, Surface surface) {
+            mProxy.invoke("onSurfacePrepared", session, surface);
+        }
+
     }
 
     private CallbackProxies() {
diff --git a/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java b/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java
index fb5b13c..c74204d 100644
--- a/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java
+++ b/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java
@@ -144,6 +144,11 @@
     }
 
     @Override
+    public void prepare(Surface surface) throws CameraAccessException {
+        mDeviceImpl.prepare(surface);
+    }
+
+    @Override
     public synchronized int capture(CaptureRequest request, CaptureCallback callback,
             Handler handler) throws CameraAccessException {
         if (request == null) {
@@ -589,6 +594,13 @@
                     }
                 }
             }
+
+            @Override
+            public void onSurfacePrepared(Surface surface) {
+                if (VERBOSE) Log.v(TAG, mIdString + "onPrepared");
+                mStateCallback.onSurfacePrepared(session, surface);
+            }
+
         };
 
     }
diff --git a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
index 91388c3..1e680dfd 100644
--- a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
+++ b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
@@ -605,6 +605,29 @@
         }
     }
 
+    public void prepare(Surface surface) throws CameraAccessException {
+        synchronized(mInterfaceLock) {
+            int streamId = -1;
+            for (int i = 0; i < mConfiguredOutputs.size(); i++) {
+                if (surface == mConfiguredOutputs.valueAt(i).getSurface()) {
+                    streamId = mConfiguredOutputs.keyAt(i);
+                    break;
+                }
+            }
+            if (streamId == -1) {
+                throw new IllegalArgumentException("Surface is not part of this session");
+            }
+            try {
+                mRemoteDevice.prepare(streamId);
+            } catch (CameraRuntimeException e) {
+                throw e.asChecked();
+            } catch (RemoteException e) {
+                // impossible
+                return;
+            }
+        }
+    }
+
     public int capture(CaptureRequest request, CaptureCallback callback, Handler handler)
             throws CameraAccessException {
         if (DEBUG) {
@@ -1056,6 +1079,14 @@
         public void onIdle(CameraDevice camera) {
             // Default empty implementation
         }
+
+        /**
+         * The method called when the camera device has finished preparing
+         * an output Surface
+         */
+        public void onSurfacePrepared(Surface surface) {
+            // Default empty implementation
+        }
     }
 
     static class CaptureCallbackHolder {
@@ -1643,6 +1674,31 @@
             }
         }
 
+        @Override
+        public void onPrepared(int streamId) {
+            final OutputConfiguration output;
+            final StateCallbackKK sessionCallback;
+
+            if (DEBUG) {
+                Log.v(TAG, "Stream " + streamId + " is prepared");
+            }
+
+            synchronized(mInterfaceLock) {
+                output = mConfiguredOutputs.get(streamId);
+                sessionCallback = mSessionStateCallback;
+            }
+
+            if (sessionCallback == null) return;
+
+            if (output == null) {
+                Log.w(TAG, "onPrepared invoked for unknown output Surface");
+                return;
+            }
+            final Surface surface = output.getSurface();
+
+            sessionCallback.onSurfacePrepared(surface);
+        }
+
         /**
          * Called by onDeviceError for handling single-capture failures.
          */
diff --git a/core/java/android/hardware/camera2/legacy/CameraDeviceUserShim.java b/core/java/android/hardware/camera2/legacy/CameraDeviceUserShim.java
index 4cd9414..abe26ea 100644
--- a/core/java/android/hardware/camera2/legacy/CameraDeviceUserShim.java
+++ b/core/java/android/hardware/camera2/legacy/CameraDeviceUserShim.java
@@ -252,6 +252,11 @@
         }
 
         @Override
+        public void onPrepared(int streamId) {
+            // TODO
+        }
+
+        @Override
         public IBinder asBinder() {
             // This is solely intended to be used for in-process binding.
             return null;
@@ -617,6 +622,19 @@
         return CameraBinderDecorator.NO_ERROR;
     }
 
+    public int prepare(int streamId) {
+        if (DEBUG) {
+            Log.d(TAG, "prepare called.");
+        }
+        if (mLegacyDevice.isClosed()) {
+            Log.e(TAG, "Cannot prepare stream, device has been closed.");
+            return CameraBinderDecorator.ENODEV;
+        }
+
+        // TODO: Implement and fire callback
+        return CameraBinderDecorator.NO_ERROR;
+    }
+
     @Override
     public IBinder asBinder() {
         // This is solely intended to be used for in-process binding.
diff --git a/core/java/android/hardware/fingerprint/FingerprintManager.java b/core/java/android/hardware/fingerprint/FingerprintManager.java
index b757a9a..0d7b261 100644
--- a/core/java/android/hardware/fingerprint/FingerprintManager.java
+++ b/core/java/android/hardware/fingerprint/FingerprintManager.java
@@ -199,12 +199,12 @@
      */
     public static class CryptoObject {
 
-        CryptoObject(Signature signature) {
+        public CryptoObject(Signature signature) {
             mSignature = signature;
             mCipher = null;
         }
 
-        CryptoObject(Cipher cipher) {
+        public CryptoObject(Cipher cipher) {
             mCipher = cipher;
             mSignature = null;
         }
@@ -535,9 +535,9 @@
      *
      * @hide
      */
-    public List<Fingerprint> getEnrolledFingerprints() {
+    public List<Fingerprint> getEnrolledFingerprints(int userId) {
         if (mService != null) try {
-            return mService.getEnrolledFingerprints(getCurrentUserId());
+            return mService.getEnrolledFingerprints(userId);
         } catch (RemoteException e) {
             Log.v(TAG, "Remote exception in getEnrolledFingerprints: ", e);
         }
@@ -545,11 +545,34 @@
     }
 
     /**
-     * Determine if fingerprint hardware is present and functional.
-     * @return true if hardware is present and functional, false otherwise.
+     * Obtain the list of enrolled fingerprints templates.
+     * @return list of current fingerprint items
      *
      * @hide
      */
+    public List<Fingerprint> getEnrolledFingerprints() {
+        return getEnrolledFingerprints(UserHandle.myUserId());
+    }
+
+    /**
+     * Determine if there is at least one fingerprint enrolled.
+     *
+     * @return true if at least one fingerprint is enrolled, false otherwise
+     */
+    public boolean hasEnrolledFingerprints() {
+        if (mService != null) try {
+            return mService.hasEnrolledFingerprints(UserHandle.myUserId());
+        } catch (RemoteException e) {
+            Log.v(TAG, "Remote exception in getEnrolledFingerprints: ", e);
+        }
+        return false;
+    }
+
+    /**
+     * Determine if fingerprint hardware is present and functional.
+     *
+     * @return true if hardware is present and functional, false otherwise.
+     */
     public boolean isHardwareDetected() {
         if (mService != null) {
             try {
diff --git a/core/java/android/hardware/fingerprint/IFingerprintService.aidl b/core/java/android/hardware/fingerprint/IFingerprintService.aidl
index 6fe72d5..51a0e4c 100644
--- a/core/java/android/hardware/fingerprint/IFingerprintService.aidl
+++ b/core/java/android/hardware/fingerprint/IFingerprintService.aidl
@@ -54,6 +54,9 @@
     // Get a pre-enrollment authentication token
     long preEnroll(IBinder token);
 
+    // Determine if a user has at least one enrolled fingerprint
+    boolean hasEnrolledFingerprints(int groupId);
+
     // Gets the number of hardware devices
     // int getHardwareDeviceCount();
 
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index 55e39b1..b341600 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -611,6 +611,27 @@
     }
 
     /**
+     * Returns a {@link Network} object corresponding to the currently active
+     * default data network.  In the event that the current active default data
+     * network disconnects, the returned {@code Network} object will no longer
+     * be usable.  This will return {@code null} when there is no default
+     * network.
+     *
+     * @return a {@link Network} object for the current default network or
+     *        {@code null} if no default network is currently active
+     *
+     * <p>This method requires the caller to hold the permission
+     * {@link android.Manifest.permission#ACCESS_NETWORK_STATE}.
+     */
+    public Network getActiveNetwork() {
+        try {
+            return mService.getActiveNetwork();
+        } catch (RemoteException e) {
+            return null;
+        }
+    }
+
+    /**
      * Returns details about the currently active default data network
      * for a given uid.  This is for internal use only to avoid spying
      * other apps.
@@ -831,48 +852,6 @@
     }
 
     /**
-     * Tells each network type to set its radio power state as directed.
-     *
-     * @param turnOn a boolean, {@code true} to turn the radios on,
-     *        {@code false} to turn them off.
-     * @return a boolean, {@code true} indicating success.  All network types
-     *        will be tried, even if some fail.
-     *
-     * <p>This method requires the caller to hold the permission
-     * {@link android.Manifest.permission#CHANGE_NETWORK_STATE}.
-     * {@hide}
-     */
-// TODO - check for any callers and remove
-//    public boolean setRadios(boolean turnOn) {
-//        try {
-//            return mService.setRadios(turnOn);
-//        } catch (RemoteException e) {
-//            return false;
-//        }
-//    }
-
-    /**
-     * Tells a given networkType to set its radio power state as directed.
-     *
-     * @param networkType the int networkType of interest.
-     * @param turnOn a boolean, {@code true} to turn the radio on,
-     *        {@code} false to turn it off.
-     * @return a boolean, {@code true} indicating success.
-     *
-     * <p>This method requires the caller to hold the permission
-     * {@link android.Manifest.permission#CHANGE_NETWORK_STATE}.
-     * {@hide}
-     */
-// TODO - check for any callers and remove
-//    public boolean setRadio(int networkType, boolean turnOn) {
-//        try {
-//            return mService.setRadio(networkType, turnOn);
-//        } catch (RemoteException e) {
-//            return false;
-//        }
-//    }
-
-    /**
      * Tells the underlying networking system that the caller wants to
      * begin using the named feature. The interpretation of {@code feature}
      * is completely up to each networking implementation.
@@ -1738,10 +1717,33 @@
      *
      * @param network The {@link Network} the application was attempting to use
      *                or {@code null} to indicate the current default network.
+     * @deprecated Use {@link #reportNetworkConnectivity} which allows reporting both
+     *             working and non-working connectivity.
      */
     public void reportBadNetwork(Network network) {
         try {
-            mService.reportBadNetwork(network);
+            // One of these will be ignored because it matches system's current state.
+            // The other will trigger the necessary reevaluation.
+            mService.reportNetworkConnectivity(network, true);
+            mService.reportNetworkConnectivity(network, false);
+        } catch (RemoteException e) {
+        }
+    }
+
+    /**
+     * Report to the framework whether a network has working connectivity.
+     * This provides a hint to the system that a particular network is providing
+     * working connectivity or not.  In response the framework may re-evaluate
+     * the network's connectivity and might take further action thereafter.
+     *
+     * @param network The {@link Network} the application was attempting to use
+     *                or {@code null} to indicate the current default network.
+     * @param hasConnectivity {@code true} if the application was able to successfully access the
+     *                        Internet using {@code network} or {@code false} if not.
+     */
+    public void reportNetworkConnectivity(Network network, boolean hasConnectivity) {
+        try {
+            mService.reportNetworkConnectivity(network, hasConnectivity);
         } catch (RemoteException e) {
         }
     }
@@ -1978,12 +1980,18 @@
         } catch (RemoteException e) { }
     }
 
-    /** {@hide} */
-    public void registerNetworkAgent(Messenger messenger, NetworkInfo ni, LinkProperties lp,
+    /**
+     * @hide
+     * Register a NetworkAgent with ConnectivityService.
+     * @return NetID corresponding to NetworkAgent.
+     */
+    public int registerNetworkAgent(Messenger messenger, NetworkInfo ni, LinkProperties lp,
             NetworkCapabilities nc, int score, NetworkMisc misc) {
         try {
-            mService.registerNetworkAgent(messenger, ni, lp, nc, score, misc);
-        } catch (RemoteException e) { }
+            return mService.registerNetworkAgent(messenger, ni, lp, nc, score, misc);
+        } catch (RemoteException e) {
+            return NETID_UNSET;
+        }
     }
 
     /**
@@ -2444,6 +2452,23 @@
     }
 
     /**
+     * Request connectivityservice to refresh network capabilities for the given
+     * {@link network}. This method returns true if the network is still active, false
+     * otherwise. Notice the method call assumes the caller has registered for
+     * listening NetworkCapabilities updates.
+     *
+     * @param network{@link Network} specifying which network you're interested.
+     * @hide
+     */
+    public boolean requestBwUpdate(Network network) {
+        try {
+            return mService.requestBwUpdate(network);
+        } catch (RemoteException e) {
+            return false;
+        }
+    }
+
+    /**
      * Unregisters callbacks about and possibly releases networks originating from
      * {@link #requestNetwork} and {@link #registerNetworkCallback} calls.  If the
      * given {@code NetworkCallback} had previously been used with {@code #requestNetwork},
diff --git a/core/java/android/net/IConnectivityManager.aidl b/core/java/android/net/IConnectivityManager.aidl
index 1aa9c45..9d9b1bf 100644
--- a/core/java/android/net/IConnectivityManager.aidl
+++ b/core/java/android/net/IConnectivityManager.aidl
@@ -43,6 +43,7 @@
 /** {@hide} */
 interface IConnectivityManager
 {
+    Network getActiveNetwork();
     NetworkInfo getActiveNetworkInfo();
     NetworkInfo getActiveNetworkInfoForUid(int uid);
     NetworkInfo getNetworkInfo(int networkType);
@@ -95,7 +96,7 @@
 
     void reportInetCondition(int networkType, int percentage);
 
-    void reportBadNetwork(in Network network);
+    void reportNetworkConnectivity(in Network network, boolean hasConnectivity);
 
     ProxyInfo getGlobalProxy();
 
@@ -121,8 +122,6 @@
 
     void captivePortalCheckCompleted(in NetworkInfo info, boolean isCaptivePortal);
 
-    int findConnectionTypeForIface(in String iface);
-
     int checkMobileProvisioning(int suggestedTimeOutMs);
 
     String getMobileProvisioningUrl();
@@ -135,9 +134,11 @@
 
     void registerNetworkFactory(in Messenger messenger, in String name);
 
+    boolean requestBwUpdate(in Network network);
+
     void unregisterNetworkFactory(in Messenger messenger);
 
-    void registerNetworkAgent(in Messenger messenger, in NetworkInfo ni, in LinkProperties lp,
+    int registerNetworkAgent(in Messenger messenger, in NetworkInfo ni, in LinkProperties lp,
             in NetworkCapabilities nc, int score, in NetworkMisc misc);
 
     NetworkRequest requestNetwork(in NetworkCapabilities networkCapabilities,
diff --git a/core/java/android/net/NetworkAgent.java b/core/java/android/net/NetworkAgent.java
index 95ceb2a..9c3a623 100644
--- a/core/java/android/net/NetworkAgent.java
+++ b/core/java/android/net/NetworkAgent.java
@@ -39,12 +39,18 @@
  * @hide
  */
 public abstract class NetworkAgent extends Handler {
+    // Guaranteed to be valid (not NETID_UNSET), otherwise registerNetworkAgent() would have thrown
+    // an exception.
+    public final int netId;
+
     private volatile AsyncChannel mAsyncChannel;
     private final String LOG_TAG;
     private static final boolean DBG = true;
     private static final boolean VDBG = false;
     private final Context mContext;
     private final ArrayList<Message>mPreConnectedQueue = new ArrayList<Message>();
+    private volatile long mLastBwRefreshTime = 0;
+    private static final long BW_REFRESH_MIN_WIN_MS = 500;
 
     private static final int BASE = Protocol.BASE_NETWORK_AGENT;
 
@@ -134,6 +140,11 @@
      */
     public static final int CMD_SAVE_ACCEPT_UNVALIDATED = BASE + 9;
 
+    /** Sent by ConnectivityService to the NetworkAgent to inform the agent to pull
+     * the underlying network connection for updated bandwidth information.
+     */
+    public static final int CMD_REQUEST_BANDWIDTH_UPDATE = BASE + 10;
+
     public NetworkAgent(Looper looper, Context context, String logTag, NetworkInfo ni,
             NetworkCapabilities nc, LinkProperties lp, int score) {
         this(looper, context, logTag, ni, nc, lp, score, null);
@@ -151,7 +162,7 @@
         if (VDBG) log("Registering NetworkAgent");
         ConnectivityManager cm = (ConnectivityManager)mContext.getSystemService(
                 Context.CONNECTIVITY_SERVICE);
-        cm.registerNetworkAgent(new Messenger(this), new NetworkInfo(ni),
+        netId = cm.registerNetworkAgent(new Messenger(this), new NetworkInfo(ni),
                 new LinkProperties(lp), new NetworkCapabilities(nc), score, misc);
     }
 
@@ -195,6 +206,15 @@
                 log("Unhandled Message " + msg);
                 break;
             }
+            case CMD_REQUEST_BANDWIDTH_UPDATE: {
+                if (VDBG) {
+                    log("CMD_REQUEST_BANDWIDTH_UPDATE request received.");
+                }
+                if (System.currentTimeMillis() > (mLastBwRefreshTime + BW_REFRESH_MIN_WIN_MS)) {
+                    pollLceData();
+                }
+                break;
+            }
             case CMD_REPORT_NETWORK_STATUS: {
                 if (VDBG) {
                     log("CMD_REPORT_NETWORK_STATUS(" +
@@ -240,6 +260,7 @@
      * Called by the bearer code when it has new NetworkCapabilities data.
      */
     public void sendNetworkCapabilities(NetworkCapabilities networkCapabilities) {
+        mLastBwRefreshTime = System.currentTimeMillis();
         queueOrSendMessage(EVENT_NETWORK_CAPABILITIES_CHANGED,
                 new NetworkCapabilities(networkCapabilities));
     }
@@ -294,6 +315,13 @@
     abstract protected void unwanted();
 
     /**
+     * Called when ConnectivityService request a bandwidth update. The parent factory
+     * shall try to overwrite this method and produce a bandwidth update if capable.
+     */
+    protected void pollLceData() {
+    }
+
+    /**
      * Called when the system determines the usefulness of this network.
      *
      * Networks claiming internet connectivity will have their internet
diff --git a/core/java/android/nfc/IAppCallback.aidl b/core/java/android/nfc/IAppCallback.aidl
index 9599308..c027d54 100644
--- a/core/java/android/nfc/IAppCallback.aidl
+++ b/core/java/android/nfc/IAppCallback.aidl
@@ -24,7 +24,7 @@
  */
 interface IAppCallback
 {
-    BeamShareData createBeamShareData();
-    void onNdefPushComplete();
+    BeamShareData createBeamShareData(byte peerLlcpVersion);
+    void onNdefPushComplete(byte peerLlcpVersion);
     void onTagDiscovered(in Tag tag);
 }
diff --git a/core/java/android/nfc/NfcActivityManager.java b/core/java/android/nfc/NfcActivityManager.java
index d009295..76bd0ec 100644
--- a/core/java/android/nfc/NfcActivityManager.java
+++ b/core/java/android/nfc/NfcActivityManager.java
@@ -46,7 +46,6 @@
     static final Boolean DBG = false;
 
     final NfcAdapter mAdapter;
-    final NfcEvent mDefaultEvent;  // cached NfcEvent (its currently always the same)
 
     // All objects in the lists are protected by this
     final List<NfcApplicationState> mApps;  // Application(s) that have NFC state. Usually one
@@ -200,7 +199,6 @@
         mAdapter = adapter;
         mActivities = new LinkedList<NfcActivityState>();
         mApps = new ArrayList<NfcApplicationState>(1);  // Android VM usually has 1 app
-        mDefaultEvent = new NfcEvent(mAdapter);
     }
 
     public void enableReaderMode(Activity activity, ReaderCallback callback, int flags,
@@ -354,13 +352,14 @@
 
     /** Callback from NFC service, usually on binder thread */
     @Override
-    public BeamShareData createBeamShareData() {
+    public BeamShareData createBeamShareData(byte peerLlcpVersion) {
         NfcAdapter.CreateNdefMessageCallback ndefCallback;
         NfcAdapter.CreateBeamUrisCallback urisCallback;
         NdefMessage message;
         Activity activity;
         Uri[] uris;
         int flags;
+        NfcEvent event = new NfcEvent(mAdapter, peerLlcpVersion);
         synchronized (NfcActivityManager.this) {
             NfcActivityState state = findResumedActivityState();
             if (state == null) return null;
@@ -375,10 +374,10 @@
 
         // Make callbacks without lock
         if (ndefCallback != null) {
-            message  = ndefCallback.createNdefMessage(mDefaultEvent);
+            message  = ndefCallback.createNdefMessage(event);
         }
         if (urisCallback != null) {
-            uris = urisCallback.createBeamUris(mDefaultEvent);
+            uris = urisCallback.createBeamUris(event);
             if (uris != null) {
                 ArrayList<Uri> validUris = new ArrayList<Uri>();
                 for (Uri uri : uris) {
@@ -412,7 +411,7 @@
 
     /** Callback from NFC service, usually on binder thread */
     @Override
-    public void onNdefPushComplete() {
+    public void onNdefPushComplete(byte peerLlcpVersion) {
         NfcAdapter.OnNdefPushCompleteCallback callback;
         synchronized (NfcActivityManager.this) {
             NfcActivityState state = findResumedActivityState();
@@ -420,10 +419,10 @@
 
             callback = state.onNdefPushCompleteCallback;
         }
-
+        NfcEvent event = new NfcEvent(mAdapter, peerLlcpVersion);
         // Make callback without lock
         if (callback != null) {
-            callback.onNdefPushComplete(mDefaultEvent);
+            callback.onNdefPushComplete(event);
         }
     }
 
diff --git a/core/java/android/nfc/NfcEvent.java b/core/java/android/nfc/NfcEvent.java
index 860700a..cf1d71a 100644
--- a/core/java/android/nfc/NfcEvent.java
+++ b/core/java/android/nfc/NfcEvent.java
@@ -38,7 +38,14 @@
      */
     public final NfcAdapter nfcAdapter;
 
-    NfcEvent(NfcAdapter nfcAdapter) {
+    /**
+     * The LLCP version of the peer associated with the NFC event.
+     * The major version is in the top nibble, the minor version is in the bottom nibble.
+     */
+    public final byte peerLlcpVersion;
+
+    NfcEvent(NfcAdapter nfcAdapter, byte peerLlcpVersion) {
         this.nfcAdapter = nfcAdapter;
+        this.peerLlcpVersion = peerLlcpVersion;
     }
 }
diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java
index 7c5ddee..4dfe0de 100644
--- a/core/java/android/os/BatteryStats.java
+++ b/core/java/android/os/BatteryStats.java
@@ -152,10 +152,15 @@
     private static final String[] STAT_NAMES = { "l", "c", "u" };
 
     /**
-     * Bump the version on this if the checkin format changes.
+     * Current version of checkin data format.
+     */
+    static final String CHECKIN_VERSION = "14";
+
+    /**
+     * Old version, we hit 9 and ran out of room, need to remove.
      */
     private static final int BATTERY_STATS_CHECKIN_VERSION = 9;
-    
+
     private static final long BYTES_PER_KB = 1024;
     private static final long BYTES_PER_MB = 1048576; // 1024^2
     private static final long BYTES_PER_GB = 1073741824; //1024^3
@@ -178,7 +183,9 @@
     private static final String BATTERY_DATA = "bt";
     private static final String BATTERY_DISCHARGE_DATA = "dc";
     private static final String BATTERY_LEVEL_DATA = "lv";
+    private static final String GLOBAL_WIFI_DATA = "gwfl";
     private static final String WIFI_DATA = "wfl";
+    private static final String GLOBAL_BLUETOOTH_DATA = "gble";
     private static final String MISC_DATA = "m";
     private static final String GLOBAL_NETWORK_DATA = "gn";
     private static final String HISTORY_STRING_POOL = "hsp";
@@ -195,8 +202,6 @@
     private static final String WIFI_SUPPL_STATE_COUNT_DATA = "wssc";
     private static final String WIFI_SIGNAL_STRENGTH_TIME_DATA = "wsgt";
     private static final String WIFI_SIGNAL_STRENGTH_COUNT_DATA = "wsgc";
-    private static final String BLUETOOTH_STATE_TIME_DATA = "bst";
-    private static final String BLUETOOTH_STATE_COUNT_DATA = "bsc";
     private static final String POWER_USE_SUMMARY_DATA = "pws";
     private static final String POWER_USE_ITEM_DATA = "pwi";
     private static final String DISCHARGE_STEP_DATA = "dsd";
@@ -1055,22 +1060,23 @@
         public static final int STATE_GPS_ON_FLAG = 1<<29;
         public static final int STATE_WIFI_FULL_LOCK_FLAG = 1<<28;
         public static final int STATE_WIFI_SCAN_FLAG = 1<<27;
-        public static final int STATE_WIFI_MULTICAST_ON_FLAG = 1<<26;
+        public static final int STATE_WIFI_RADIO_ACTIVE_FLAG = 1<<26;
         public static final int STATE_MOBILE_RADIO_ACTIVE_FLAG = 1<<25;
         // These are on the lower bits used for the command; if they change
         // we need to write another int of data.
         public static final int STATE_SENSOR_ON_FLAG = 1<<23;
         public static final int STATE_AUDIO_ON_FLAG = 1<<22;
         public static final int STATE_PHONE_SCANNING_FLAG = 1<<21;
-        public static final int STATE_SCREEN_ON_FLAG = 1<<20;
-        public static final int STATE_BATTERY_PLUGGED_FLAG = 1<<19;
-        public static final int STATE_PHONE_IN_CALL_FLAG = 1<<18;
-        public static final int STATE_CHARGING_FLAG = 1<<17;
-        public static final int STATE_BLUETOOTH_ON_FLAG = 1<<16;
+        public static final int STATE_SCREEN_ON_FLAG = 1<<20;       // consider moving to states2
+        public static final int STATE_BATTERY_PLUGGED_FLAG = 1<<19; // consider moving to states2
+        // empty slot
+        // empty slot
+        public static final int STATE_WIFI_MULTICAST_ON_FLAG = 1<<16;
 
         public static final int MOST_INTERESTING_STATES =
-            STATE_BATTERY_PLUGGED_FLAG | STATE_SCREEN_ON_FLAG
-            | STATE_PHONE_IN_CALL_FLAG | STATE_BLUETOOTH_ON_FLAG;
+            STATE_BATTERY_PLUGGED_FLAG | STATE_SCREEN_ON_FLAG;
+
+        public static final int SETTLE_TO_ZERO_STATES = 0xffff0000 & ~MOST_INTERESTING_STATES;
 
         public int states;
 
@@ -1088,9 +1094,15 @@
         public static final int STATE2_WIFI_ON_FLAG = 1<<28;
         public static final int STATE2_FLASHLIGHT_FLAG = 1<<27;
         public static final int STATE2_DEVICE_IDLE_FLAG = 1<<26;
+        public static final int STATE2_CHARGING_FLAG = 1<<25;
+        public static final int STATE2_PHONE_IN_CALL_FLAG = 1<<24;
+        public static final int STATE2_BLUETOOTH_ON_FLAG = 1<<23;
 
         public static final int MOST_INTERESTING_STATES2 =
-            STATE2_POWER_SAVE_FLAG | STATE2_WIFI_ON_FLAG | STATE2_DEVICE_IDLE_FLAG;
+            STATE2_POWER_SAVE_FLAG | STATE2_WIFI_ON_FLAG | STATE2_DEVICE_IDLE_FLAG
+            | STATE2_CHARGING_FLAG | STATE2_PHONE_IN_CALL_FLAG | STATE2_BLUETOOTH_ON_FLAG;
+
+        public static final int SETTLE_TO_ZERO_STATES2 = 0xffff0000 & ~MOST_INTERESTING_STATES2;
 
         public int states2;
 
@@ -1137,8 +1149,10 @@
         public static final int EVENT_PACKAGE_UNINSTALLED = 0x000d;
         // Event for a package being uninstalled.
         public static final int EVENT_ALARM = 0x000e;
+        // Record that we have decided we need to collect new stats data.
+        public static final int EVENT_COLLECT_EXTERNAL_STATS = 0x000f;
         // Number of event types.
-        public static final int EVENT_COUNT = 0x000f;
+        public static final int EVENT_COUNT = 0x0010;
         // Mask to extract out only the type part of the event.
         public static final int EVENT_TYPE_MASK = ~(EVENT_FLAG_START|EVENT_FLAG_FINISH);
 
@@ -1750,14 +1764,12 @@
         new BitDescription(HistoryItem.STATE_WIFI_FULL_LOCK_FLAG, "wifi_full_lock", "Wl"),
         new BitDescription(HistoryItem.STATE_WIFI_SCAN_FLAG, "wifi_scan", "Ws"),
         new BitDescription(HistoryItem.STATE_WIFI_MULTICAST_ON_FLAG, "wifi_multicast", "Wm"),
+        new BitDescription(HistoryItem.STATE_WIFI_RADIO_ACTIVE_FLAG, "wifi_radio", "Wr"),
         new BitDescription(HistoryItem.STATE_MOBILE_RADIO_ACTIVE_FLAG, "mobile_radio", "Pr"),
         new BitDescription(HistoryItem.STATE_PHONE_SCANNING_FLAG, "phone_scanning", "Psc"),
         new BitDescription(HistoryItem.STATE_AUDIO_ON_FLAG, "audio", "a"),
         new BitDescription(HistoryItem.STATE_SCREEN_ON_FLAG, "screen", "S"),
         new BitDescription(HistoryItem.STATE_BATTERY_PLUGGED_FLAG, "plugged", "BP"),
-        new BitDescription(HistoryItem.STATE_PHONE_IN_CALL_FLAG, "phone_in_call", "Pcl"),
-        new BitDescription(HistoryItem.STATE_CHARGING_FLAG, "charging", "ch"),
-        new BitDescription(HistoryItem.STATE_BLUETOOTH_ON_FLAG, "bluetooth", "b"),
         new BitDescription(HistoryItem.STATE_DATA_CONNECTION_MASK,
                 HistoryItem.STATE_DATA_CONNECTION_SHIFT, "data_conn", "Pcn",
                 DATA_CONNECTION_NAMES, DATA_CONNECTION_NAMES),
@@ -1778,10 +1790,13 @@
             = new BitDescription[] {
         new BitDescription(HistoryItem.STATE2_POWER_SAVE_FLAG, "power_save", "ps"),
         new BitDescription(HistoryItem.STATE2_VIDEO_ON_FLAG, "video", "v"),
-        new BitDescription(HistoryItem.STATE2_WIFI_RUNNING_FLAG, "wifi_running", "Wr"),
+        new BitDescription(HistoryItem.STATE2_WIFI_RUNNING_FLAG, "wifi_running", "Ww"),
         new BitDescription(HistoryItem.STATE2_WIFI_ON_FLAG, "wifi", "W"),
         new BitDescription(HistoryItem.STATE2_FLASHLIGHT_FLAG, "flashlight", "fl"),
         new BitDescription(HistoryItem.STATE2_DEVICE_IDLE_FLAG, "device_idle", "di"),
+        new BitDescription(HistoryItem.STATE2_CHARGING_FLAG, "charging", "ch"),
+        new BitDescription(HistoryItem.STATE2_PHONE_IN_CALL_FLAG, "phone_in_call", "Pcl"),
+        new BitDescription(HistoryItem.STATE2_BLUETOOTH_ON_FLAG, "bluetooth", "b"),
         new BitDescription(HistoryItem.STATE2_WIFI_SIGNAL_STRENGTH_MASK,
                 HistoryItem.STATE2_WIFI_SIGNAL_STRENGTH_SHIFT, "wifi_signal_strength", "Wss",
                 new String[] { "0", "1", "2", "3", "4" },
@@ -1793,12 +1808,12 @@
 
     public static final String[] HISTORY_EVENT_NAMES = new String[] {
             "null", "proc", "fg", "top", "sync", "wake_lock_in", "job", "user", "userfg", "conn",
-            "motion", "active", "pkginst", "pkgunin", "alarm"
+            "motion", "active", "pkginst", "pkgunin", "alarm", "stats"
     };
 
     public static final String[] HISTORY_EVENT_CHECKIN_NAMES = new String[] {
             "Enl", "Epr", "Efg", "Etp", "Esy", "Ewl", "Ejb", "Eur", "Euf", "Ecn",
-            "Esm", "Eac", "Epi", "Epu", "Eal"
+            "Esm", "Eac", "Epi", "Epu", "Eal", "Est"
     };
 
     /**
@@ -1883,43 +1898,6 @@
     public abstract int getWifiSignalStrengthCount(int strengthBin, int which);
 
     /**
-     * Returns the time in microseconds that bluetooth has been on while the device was
-     * running on battery.
-     * 
-     * {@hide}
-     */
-    public abstract long getBluetoothOnTime(long elapsedRealtimeUs, int which);
-    
-    public abstract int getBluetoothPingCount();
-
-    public static final int BLUETOOTH_STATE_INACTIVE = 0;
-    public static final int BLUETOOTH_STATE_LOW = 1;
-    public static final int BLUETOOTH_STATE_MEDIUM = 2;
-    public static final int BLUETOOTH_STATE_HIGH = 3;
-
-    static final String[] BLUETOOTH_STATE_NAMES = {
-        "inactive", "low", "med", "high"
-    };
-
-    public static final int NUM_BLUETOOTH_STATES = BLUETOOTH_STATE_HIGH +1;
-
-    /**
-     * Returns the time in microseconds that Bluetooth has been running in the
-     * given active state.
-     *
-     * {@hide}
-     */
-    public abstract long getBluetoothStateTime(int bluetoothState,
-            long elapsedRealtimeUs, int which);
-
-    /**
-     * Returns the number of times that Bluetooth has entered the given active state.
-     *
-     * {@hide}
-     */
-    public abstract int getBluetoothStateCount(int bluetoothState, int which);
-
-    /**
      * Returns the time in microseconds that the flashlight has been on while the device was
      * running on battery.
      *
@@ -2431,9 +2409,6 @@
         final long deviceIdlingTime = getDeviceIdlingTime(rawRealtime, which);
         final int connChanges = getNumConnectivityChange(which);
         final long phoneOnTime = getPhoneOnTime(rawRealtime, which);
-        final long wifiOnTime = getWifiOnTime(rawRealtime, which);
-        final long wifiRunningTime = getGlobalWifiRunningTime(rawRealtime, which);
-        final long bluetoothOnTime = getBluetoothOnTime(rawRealtime, which);
 
         final StringBuilder sb = new StringBuilder(128);
         
@@ -2475,7 +2450,8 @@
                 }
             }
         }
-        
+
+        // Dump network stats
         final long mobileRxTotalBytes = getNetworkActivityBytes(NETWORK_MOBILE_RX_DATA, which);
         final long mobileTxTotalBytes = getNetworkActivityBytes(NETWORK_MOBILE_TX_DATA, which);
         final long wifiRxTotalBytes = getNetworkActivityBytes(NETWORK_WIFI_RX_DATA, which);
@@ -2484,19 +2460,34 @@
         final long mobileTxTotalPackets = getNetworkActivityPackets(NETWORK_MOBILE_TX_DATA, which);
         final long wifiRxTotalPackets = getNetworkActivityPackets(NETWORK_WIFI_RX_DATA, which);
         final long wifiTxTotalPackets = getNetworkActivityPackets(NETWORK_WIFI_TX_DATA, which);
-
-        // Dump network stats
         dumpLine(pw, 0 /* uid */, category, GLOBAL_NETWORK_DATA,
                 mobileRxTotalBytes, mobileTxTotalBytes, wifiRxTotalBytes, wifiTxTotalBytes,
                 mobileRxTotalPackets, mobileTxTotalPackets, wifiRxTotalPackets, wifiTxTotalPackets);
 
+        // Dump Wifi controller stats
+        final long wifiOnTime = getWifiOnTime(rawRealtime, which);
+        final long wifiRunningTime = getGlobalWifiRunningTime(rawRealtime, which);
+        final long wifiIdleTimeMs = getWifiControllerActivity(CONTROLLER_IDLE_TIME, which);
+        final long wifiRxTimeMs = getWifiControllerActivity(CONTROLLER_RX_TIME, which);
+        final long wifiTxTimeMs = getWifiControllerActivity(CONTROLLER_TX_TIME, which);
+        final long wifiPowerMaMs = getWifiControllerActivity(CONTROLLER_POWER_DRAIN, which);
+        dumpLine(pw, 0 /* uid */, category, GLOBAL_WIFI_DATA,
+                wifiOnTime / 1000, wifiRunningTime / 1000,
+                wifiIdleTimeMs, wifiRxTimeMs, wifiTxTimeMs, wifiPowerMaMs / (1000*60*60));
+
+        // Dump Bluetooth controller stats
+        final long btIdleTimeMs = getBluetoothControllerActivity(CONTROLLER_IDLE_TIME, which);
+        final long btRxTimeMs = getBluetoothControllerActivity(CONTROLLER_RX_TIME, which);
+        final long btTxTimeMs = getBluetoothControllerActivity(CONTROLLER_TX_TIME, which);
+        final long btPowerMaMs = getBluetoothControllerActivity(CONTROLLER_POWER_DRAIN, which);
+        dumpLine(pw, 0 /* uid */, category, GLOBAL_BLUETOOTH_DATA,
+                btIdleTimeMs, btRxTimeMs, btTxTimeMs, btPowerMaMs / (1000*60*60));
+
         // Dump misc stats
         dumpLine(pw, 0 /* uid */, category, MISC_DATA,
-                screenOnTime / 1000, phoneOnTime / 1000, wifiOnTime / 1000,
-                wifiRunningTime / 1000, bluetoothOnTime / 1000,
-                mobileRxTotalBytes, mobileTxTotalBytes, wifiRxTotalBytes, wifiTxTotalBytes,
+                screenOnTime / 1000, phoneOnTime / 1000,
                 fullWakeLockTimeTotal / 1000, partialWakeLockTimeTotal / 1000,
-                0 /*legacy input event count*/, getMobileRadioActiveTime(rawRealtime, which) / 1000,
+                getMobileRadioActiveTime(rawRealtime, which) / 1000,
                 getMobileRadioActiveAdjustedTime(which) / 1000, interactiveTime / 1000,
                 powerSaveModeEnabledTime / 1000, connChanges, deviceIdleModeEnabledTime / 1000,
                 getDeviceIdleModeEnabledCount(which), deviceIdlingTime / 1000,
@@ -2566,17 +2557,6 @@
         }
         dumpLine(pw, 0 /* uid */, category, WIFI_SIGNAL_STRENGTH_COUNT_DATA, args);
 
-        // Dump bluetooth state stats
-        args = new Object[NUM_BLUETOOTH_STATES];
-        for (int i=0; i<NUM_BLUETOOTH_STATES; i++) {
-            args[i] = getBluetoothStateTime(i, rawRealtime, which) / 1000;
-        }
-        dumpLine(pw, 0 /* uid */, category, BLUETOOTH_STATE_TIME_DATA, args);
-        for (int i=0; i<NUM_BLUETOOTH_STATES; i++) {
-            args[i] = getBluetoothStateCount(i, which);
-        }
-        dumpLine(pw, 0 /* uid */, category, BLUETOOTH_STATE_COUNT_DATA, args);
-
         if (which == STATS_SINCE_UNPLUGGED) {
             dumpLine(pw, 0 /* uid */, category, BATTERY_LEVEL_DATA, getDischargeStartLevel(),
                     getDischargeCurrentLevel());
@@ -2681,6 +2661,7 @@
                 continue;
             }
             final Uid u = uidStats.valueAt(iu);
+
             // Dump Network stats per uid, if any
             final long mobileBytesRx = u.getNetworkActivityBytes(NETWORK_MOBILE_RX_DATA, which);
             final long mobileBytesTx = u.getNetworkActivityBytes(NETWORK_MOBILE_TX_DATA, which);
@@ -2692,11 +2673,6 @@
             final int mobileActiveCount = u.getMobileRadioActiveCount(which);
             final long wifiPacketsRx = u.getNetworkActivityPackets(NETWORK_WIFI_RX_DATA, which);
             final long wifiPacketsTx = u.getNetworkActivityPackets(NETWORK_WIFI_TX_DATA, which);
-            final long fullWifiLockOnTime = u.getFullWifiLockTime(rawRealtime, which);
-            final long wifiScanTime = u.getWifiScanTime(rawRealtime, which);
-            final int wifiScanCount = u.getWifiScanCount(which);
-            final long uidWifiRunningTime = u.getWifiRunningTime(rawRealtime, which);
-
             if (mobileBytesRx > 0 || mobileBytesTx > 0 || wifiBytesRx > 0 || wifiBytesTx > 0
                     || mobilePacketsRx > 0 || mobilePacketsTx > 0 || wifiPacketsRx > 0
                     || wifiPacketsTx > 0 || mobileActiveTime > 0 || mobileActiveCount > 0) {
@@ -2707,10 +2683,19 @@
                         mobileActiveTime, mobileActiveCount);
             }
 
+            final long fullWifiLockOnTime = u.getFullWifiLockTime(rawRealtime, which);
+            final long wifiScanTime = u.getWifiScanTime(rawRealtime, which);
+            final int wifiScanCount = u.getWifiScanCount(which);
+            final long uidWifiRunningTime = u.getWifiRunningTime(rawRealtime, which);
+            final long uidWifiIdleTimeMs = u.getWifiControllerActivity(CONTROLLER_IDLE_TIME, which);
+            final long uidWifiRxTimeMs = u.getWifiControllerActivity(CONTROLLER_RX_TIME, which);
+            final long uidWifiTxTimeMs = u.getWifiControllerActivity(CONTROLLER_TX_TIME, which);
             if (fullWifiLockOnTime != 0 || wifiScanTime != 0 || wifiScanCount != 0
-                    || uidWifiRunningTime != 0) {
+                    || uidWifiRunningTime != 0 || uidWifiIdleTimeMs != 0 || uidWifiRxTimeMs != 0
+                    || uidWifiTxTimeMs != 0) {
                 dumpLine(pw, uid, category, WIFI_DATA,
-                        fullWifiLockOnTime, wifiScanTime, uidWifiRunningTime, wifiScanCount);
+                        fullWifiLockOnTime, wifiScanTime, uidWifiRunningTime, wifiScanCount,
+                        uidWifiIdleTimeMs, uidWifiRxTimeMs, uidWifiTxTimeMs);
             }
 
             if (u.hasUserActivity()) {
@@ -2968,7 +2953,6 @@
         final long phoneOnTime = getPhoneOnTime(rawRealtime, which);
         final long wifiRunningTime = getGlobalWifiRunningTime(rawRealtime, which);
         final long wifiOnTime = getWifiOnTime(rawRealtime, which);
-        final long bluetoothOnTime = getBluetoothOnTime(rawRealtime, which);
         sb.setLength(0);
         sb.append(prefix);
                 sb.append("  Screen on: "); formatTimeMs(sb, screenOnTime / 1000);
@@ -3317,42 +3301,11 @@
 
         sb.setLength(0);
         sb.append(prefix);
-        sb.append("  WiFi Energy use: ").append(BatteryStatsHelper.makemAh(
+        sb.append("  WiFi Power drain: ").append(BatteryStatsHelper.makemAh(
                 getWifiControllerActivity(CONTROLLER_POWER_DRAIN, which) / (double)(1000*60*60)));
         sb.append(" mAh");
         pw.println(sb.toString());
 
-        sb.setLength(0);
-        sb.append(prefix);
-                sb.append("  Bluetooth on: "); formatTimeMs(sb, bluetoothOnTime / 1000);
-                sb.append("("); sb.append(formatRatioLocked(bluetoothOnTime, whichBatteryRealtime));
-                sb.append(")");
-        pw.println(sb.toString());
-
-        sb.setLength(0);
-        sb.append(prefix);
-        sb.append("  Bluetooth states:");
-        didOne = false;
-        for (int i=0; i<NUM_BLUETOOTH_STATES; i++) {
-            final long time = getBluetoothStateTime(i, rawRealtime, which);
-            if (time == 0) {
-                continue;
-            }
-            sb.append("\n    ");
-            didOne = true;
-            sb.append(BLUETOOTH_STATE_NAMES[i]);
-            sb.append(" ");
-            formatTimeMs(sb, time/1000);
-            sb.append("(");
-            sb.append(formatRatioLocked(time, whichBatteryRealtime));
-            sb.append(") ");
-            sb.append(getPhoneDataConnectionCount(i, which));
-            sb.append("x");
-        }
-
-        if (!didOne) sb.append(" (no activity)");
-        pw.println(sb.toString());
-
         final long bluetoothIdleTimeMs =
                 getBluetoothControllerActivity(CONTROLLER_IDLE_TIME, which);
         final long bluetoothRxTimeMs = getBluetoothControllerActivity(CONTROLLER_RX_TIME, which);
@@ -3384,6 +3337,14 @@
         sb.append(")");
         pw.println(sb.toString());
 
+        sb.setLength(0);
+        sb.append(prefix);
+        sb.append("  Bluetooth Power drain: ").append(BatteryStatsHelper.makemAh(
+                getBluetoothControllerActivity(CONTROLLER_POWER_DRAIN, which) /
+                        (double)(1000*60*60)));
+        sb.append(" mAh");
+        pw.println(sb.toString());
+
         pw.println();
 
         if (which == STATS_SINCE_UNPLUGGED) {
@@ -4883,7 +4844,8 @@
         prepareForDumpLocked();
 
         dumpLine(pw, 0 /* uid */, "i" /* category */, VERSION_DATA,
-                "13", getParcelVersion(), getStartPlatformVersion(), getEndPlatformVersion());
+                CHECKIN_VERSION, getParcelVersion(), getStartPlatformVersion(),
+                getEndPlatformVersion());
 
         long now = getHistoryBaseTime() + SystemClock.elapsedRealtime();
 
diff --git a/core/java/android/os/Binder.java b/core/java/android/os/Binder.java
index b77b82cd..64562a4 100644
--- a/core/java/android/os/Binder.java
+++ b/core/java/android/os/Binder.java
@@ -189,6 +189,7 @@
     /**
      * Call blocks until the number of executing binder threads is less
      * than the maximum number of binder threads allowed for this process.
+     * @hide
      */
     public static final native void blockUntilThreadAvailable();
 
diff --git a/core/java/android/os/Debug.java b/core/java/android/os/Debug.java
index 75b1101..4aff7a1 100644
--- a/core/java/android/os/Debug.java
+++ b/core/java/android/os/Debug.java
@@ -1173,7 +1173,6 @@
      *            the name of the runtime statistic to look up.
      * @return the value of the specified runtime statistic or {@code null} if the
      *         runtime statistic doesn't exist.
-     * @hide
      */
     public static String getRuntimeStat(String statName) {
         return VMDebug.getRuntimeStat(statName);
@@ -1184,7 +1183,6 @@
      * that {@link #getRuntimeStat(String)} supports.
      *
      * @return a map of the names/values of the supported runtime statistics.
-     * @hide
      */
     public static Map<String, String> getRuntimeStats() {
         return VMDebug.getRuntimeStats();
diff --git a/core/java/android/os/storage/DiskInfo.java b/core/java/android/os/storage/DiskInfo.java
index dc96640..2c60d41 100644
--- a/core/java/android/os/storage/DiskInfo.java
+++ b/core/java/android/os/storage/DiskInfo.java
@@ -26,6 +26,7 @@
 import com.android.internal.util.Preconditions;
 
 import java.io.CharArrayWriter;
+import java.util.Objects;
 
 /**
  * Information about a physical disk which may contain one or more
@@ -118,6 +119,20 @@
         }
     }
 
+    @Override
+    public boolean equals(Object o) {
+        if (o instanceof DiskInfo) {
+            return Objects.equals(id, ((DiskInfo) o).id);
+        } else {
+            return false;
+        }
+    }
+
+    @Override
+    public int hashCode() {
+        return id.hashCode();
+    }
+
     public static final Creator<DiskInfo> CREATOR = new Creator<DiskInfo>() {
         @Override
         public DiskInfo createFromParcel(Parcel in) {
diff --git a/core/java/android/os/storage/VolumeInfo.java b/core/java/android/os/storage/VolumeInfo.java
index f06fc8c..a241728 100644
--- a/core/java/android/os/storage/VolumeInfo.java
+++ b/core/java/android/os/storage/VolumeInfo.java
@@ -38,6 +38,8 @@
 
 import java.io.CharArrayWriter;
 import java.io.File;
+import java.util.Comparator;
+import java.util.Objects;
 
 /**
  * Information about a storage volume that may be mounted. A volume may be a
@@ -77,6 +79,22 @@
     private static SparseArray<String> sStateToEnvironment = new SparseArray<>();
     private static ArrayMap<String, String> sEnvironmentToBroadcast = new ArrayMap<>();
 
+    private static final Comparator<VolumeInfo>
+            sDescriptionComparator = new Comparator<VolumeInfo>() {
+        @Override
+        public int compare(VolumeInfo lhs, VolumeInfo rhs) {
+            if (VolumeInfo.ID_PRIVATE_INTERNAL.equals(lhs.getId())) {
+                return -1;
+            } else if (lhs.getDescription() == null) {
+                return 1;
+            } else if (rhs.getDescription() == null) {
+                return -1;
+            } else {
+                return lhs.getDescription().compareTo(rhs.getDescription());
+            }
+        }
+    };
+
     static {
         sStateToEnvironment.put(VolumeInfo.STATE_UNMOUNTED, Environment.MEDIA_UNMOUNTED);
         sStateToEnvironment.put(VolumeInfo.STATE_MOUNTING, Environment.MEDIA_CHECKING);
@@ -150,6 +168,10 @@
         return getBroadcastForEnvironment(getEnvironmentForState(state));
     }
 
+    public static @NonNull Comparator<VolumeInfo> getDescriptionComparator() {
+        return sDescriptionComparator;
+    }
+
     public @NonNull String getId() {
         return id;
     }
@@ -344,6 +366,20 @@
         }
     }
 
+    @Override
+    public boolean equals(Object o) {
+        if (o instanceof VolumeInfo) {
+            return Objects.equals(id, ((VolumeInfo) o).id);
+        } else {
+            return false;
+        }
+    }
+
+    @Override
+    public int hashCode() {
+        return id.hashCode();
+    }
+
     public static final Creator<VolumeInfo> CREATOR = new Creator<VolumeInfo>() {
         @Override
         public VolumeInfo createFromParcel(Parcel in) {
diff --git a/core/java/android/preference/SeekBarVolumizer.java b/core/java/android/preference/SeekBarVolumizer.java
index 30da0e76..4bd085f 100644
--- a/core/java/android/preference/SeekBarVolumizer.java
+++ b/core/java/android/preference/SeekBarVolumizer.java
@@ -121,13 +121,10 @@
 
     protected void updateSeekBar() {
         if (mNotificationOrRing && mRingerMode == AudioManager.RINGER_MODE_VIBRATE) {
-            mSeekBar.setEnabled(true);
             mSeekBar.setProgress(0);
         } else if (mMuted) {
-            mSeekBar.setEnabled(false);
             mSeekBar.setProgress(0);
         } else {
-            mSeekBar.setEnabled(true);
             mSeekBar.setProgress(mLastProgress > -1 ? mLastProgress : mOriginalStreamVolume);
         }
     }
@@ -136,6 +133,11 @@
     public boolean handleMessage(Message msg) {
         switch (msg.what) {
             case MSG_SET_STREAM_VOLUME:
+                if (mMuted && mLastProgress > 0) {
+                    mAudioManager.adjustStreamVolume(mStreamType, AudioManager.ADJUST_UNMUTE, 0);
+                } else if (!mMuted && mLastProgress == 0) {
+                    mAudioManager.adjustStreamVolume(mStreamType, AudioManager.ADJUST_MUTE, 0);
+                }
                 mAudioManager.setStreamVolume(mStreamType, mLastProgress,
                         AudioManager.FLAG_SHOW_UI_WARNINGS);
                 break;
@@ -375,7 +377,8 @@
                 final boolean streamMatch = mNotificationOrRing ? isNotificationOrRing(streamType)
                         : (streamType == mStreamType);
                 if (mSeekBar != null && streamMatch && streamValue != -1) {
-                    final boolean muted = mAudioManager.isStreamMute(mStreamType);
+                    final boolean muted = mAudioManager.isStreamMute(mStreamType)
+                            || streamValue == 0;
                     mUiHandler.postUpdateSlider(streamValue, muted);
                 }
             } else if (AudioManager.INTERNAL_RINGER_MODE_CHANGED_ACTION.equals(action)) {
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 109c23b..a622a21 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -2766,8 +2766,6 @@
          * It was about AudioManager's setting and thus affected all the applications which
          * relied on the setting, while this is purely about the vibration setting for incoming
          * calls.
-         *
-         * @hide
          */
         public static final String VIBRATE_WHEN_RINGING = "vibrate_when_ringing";
 
@@ -2788,7 +2786,6 @@
          * DTMF tone type played by the dialer when dialing.
          *                 0 = Normal
          *                 1 = Long
-         * @hide
          */
         public static final String DTMF_TONE_TYPE_WHEN_DIALING = "dtmf_tone_type";
 
@@ -6036,6 +6033,13 @@
        public static final String DISPLAY_SIZE_FORCED = "display_size_forced";
 
        /**
+        * The saved value for WindowManagerService.setForcedDisplayScalingMode().
+        * 0 or unset if scaling is automatic, 1 if scaling is disabled.
+        * @hide
+        */
+       public static final String DISPLAY_SCALING_FORCE = "display_scaling_force";
+
+       /**
         * The maximum size, in bytes, of a download that the download manager will transfer over
         * a non-wifi connection.
         * @hide
@@ -6963,6 +6967,9 @@
         /** {@hide} */
         public static final String
                 BLUETOOTH_MAP_PRIORITY_PREFIX = "bluetooth_map_priority_";
+        /** {@hide} */
+        public static final String
+                BLUETOOTH_SAP_PRIORITY_PREFIX = "bluetooth_sap_priority_";
 
         /**
          * Get the key that retrieves a bluetooth headset's priority.
@@ -6995,6 +7002,15 @@
         public static final String getBluetoothMapPriorityKey(String address) {
             return BLUETOOTH_MAP_PRIORITY_PREFIX + address.toUpperCase(Locale.ROOT);
         }
+
+        /**
+         * Get the key that retrieves a bluetooth map priority.
+         * @hide
+         */
+        public static final String getBluetoothSapPriorityKey(String address) {
+            return BLUETOOTH_SAP_PRIORITY_PREFIX + address.toUpperCase(Locale.ROOT);
+        }
+
         /**
          * Scaling factor for normal window animations. Setting to 0 will
          * disable window animations.
@@ -7057,7 +7073,6 @@
         /**
          * Setting to 1 will hide carrier network settings.
          * Default is 0.
-         * @hide
          */
         public static final String HIDE_CARRIER_NETWORK_SETTINGS =
                 "hide_carrier_network_settings";
@@ -7724,6 +7739,13 @@
          * @hide
          */
         public static final String NEW_CONTACT_AGGREGATOR = "new_contact_aggregator";
+
+        /**
+         * Whether to enable contacts metadata syncing or not
+         * The value 1 - enable, 0 - disable
+         * @hide
+         */
+        public static final String CONTACT_METADATA_SYNC = "contact_metadata_sync";
     }
 
     /**
diff --git a/core/java/android/security/NetworkSecurityPolicy.java b/core/java/android/security/NetworkSecurityPolicy.java
index 0b3bf453..70cd388 100644
--- a/core/java/android/security/NetworkSecurityPolicy.java
+++ b/core/java/android/security/NetworkSecurityPolicy.java
@@ -46,9 +46,9 @@
      * without TLS or STARTTLS) is permitted for this process.
      *
      * <p>When cleartext network traffic is not permitted, the platform's components (e.g. HTTP and
-     * FTP stacks, {@link android.webkit.WebView}, {@link android.media.MediaPlayer}) will refuse
-     * this process's requests to use cleartext traffic. Third-party libraries are strongly
-     * encouraged to honor this setting as well.
+     * FTP stacks, {@link android.webkit.WebView}, {@link android.app.DownloadManager},
+     * {@link android.media.MediaPlayer}) will refuse this process's requests to use cleartext
+     * traffic. Third-party libraries are strongly encouraged to honor this setting as well.
      *
      * <p>This flag is honored on a best effort basis because it's impossible to prevent all
      * cleartext traffic from Android applications given the level of access provided to them. For
diff --git a/core/java/android/security/keymaster/KeymasterArgument.java b/core/java/android/security/keymaster/KeymasterArgument.java
index 9a1c894..9adde35 100644
--- a/core/java/android/security/keymaster/KeymasterArgument.java
+++ b/core/java/android/security/keymaster/KeymasterArgument.java
@@ -42,6 +42,7 @@
                         case KeymasterDefs.KM_INT_REP:
                             return new KeymasterIntArgument(tag, in);
                         case KeymasterDefs.KM_LONG:
+                        case KeymasterDefs.KM_LONG_REP:
                             return new KeymasterLongArgument(tag, in);
                         case KeymasterDefs.KM_DATE:
                             return new KeymasterDateArgument(tag, in);
diff --git a/core/java/android/security/keymaster/KeymasterArguments.java b/core/java/android/security/keymaster/KeymasterArguments.java
index 8ed288c..82f65c7 100644
--- a/core/java/android/security/keymaster/KeymasterArguments.java
+++ b/core/java/android/security/keymaster/KeymasterArguments.java
@@ -63,6 +63,12 @@
         }
     }
 
+    public void addLongs(int tag, long... values) {
+        for (long value : values) {
+            addLong(tag, value);
+        }
+    }
+
     public void addBoolean(int tag) {
         mArguments.add(new KeymasterBooleanArgument(tag));
     }
@@ -111,8 +117,13 @@
     }
 
     public long getLong(int tag, long defaultValue) {
-        if (KeymasterDefs.getTagType(tag) != KeymasterDefs.KM_LONG) {
-            throw new IllegalArgumentException("Tag is not a long type: " + tag);
+        switch (KeymasterDefs.getTagType(tag)) {
+            case KeymasterDefs.KM_LONG:
+                break; // Accepted type
+            case KeymasterDefs.KM_LONG_REP:
+                throw new IllegalArgumentException("Repeatable tags must use getLongs: " + tag);
+            default:
+                throw new IllegalArgumentException("Tag is not a long type: " + tag);
         }
         KeymasterArgument arg = getArgumentByTag(tag);
         if (arg == null) {
@@ -175,6 +186,19 @@
         return values;
     }
 
+    public List<Long> getLongs(int tag) {
+        if (KeymasterDefs.getTagType(tag) != KeymasterDefs.KM_LONG_REP) {
+            throw new IllegalArgumentException("Tag is not a repeating long: " + tag);
+        }
+        List<Long> values = new ArrayList<Long>();
+        for (KeymasterArgument arg : mArguments) {
+            if (arg.tag == tag) {
+                values.add(((KeymasterLongArgument) arg).value);
+            }
+        }
+        return values;
+    }
+
     public int size() {
         return mArguments.size();
     }
diff --git a/core/java/android/security/keymaster/KeymasterDefs.java b/core/java/android/security/keymaster/KeymasterDefs.java
index ea53c0d..40baf9c 100644
--- a/core/java/android/security/keymaster/KeymasterDefs.java
+++ b/core/java/android/security/keymaster/KeymasterDefs.java
@@ -118,9 +118,9 @@
     public static final int KM_DIGEST_SHA_2_512 = 6;
 
     // Key origins.
-    public static final int KM_ORIGIN_HARDWARE = 0;
-    public static final int KM_ORIGIN_SOFTWARE = 1;
+    public static final int KM_ORIGIN_GENERATED = 0;
     public static final int KM_ORIGIN_IMPORTED = 2;
+    public static final int KM_ORIGIN_UNKNOWN = 3;
 
     // Key usability requirements.
     public static final int KM_BLOB_STANDALONE = 0;
@@ -192,6 +192,8 @@
     public static final int KM_ERROR_SECURE_HW_BUSY = -48;
     public static final int KM_ERROR_SECURE_HW_COMMUNICATION_FAILED = -49;
     public static final int KM_ERROR_UNSUPPORTED_EC_FIELD = -50;
+    public static final int KM_ERROR_MISSING_NONCE = -51;
+    public static final int KM_ERROR_INVALID_NONCE = -52;
     public static final int KM_ERROR_UNIMPLEMENTED = -100;
     public static final int KM_ERROR_VERSION_MISMATCH = -101;
     public static final int KM_ERROR_UNKNOWN_ERROR = -1000;
@@ -231,6 +233,8 @@
         sErrorCodeToString.put(KM_ERROR_INVALID_TAG, "Invalid tag");
         sErrorCodeToString.put(KM_ERROR_MEMORY_ALLOCATION_FAILED, "Memory allocation failed");
         sErrorCodeToString.put(KM_ERROR_UNSUPPORTED_EC_FIELD, "Unsupported EC field");
+        sErrorCodeToString.put(KM_ERROR_MISSING_NONCE, "Required IV missing");
+        sErrorCodeToString.put(KM_ERROR_INVALID_NONCE, "Invalid IV");
         sErrorCodeToString.put(KM_ERROR_UNIMPLEMENTED, "Not implemented");
         sErrorCodeToString.put(KM_ERROR_UNKNOWN_ERROR, "Unknown error");
     }
diff --git a/core/java/android/security/keymaster/KeymasterLongArgument.java b/core/java/android/security/keymaster/KeymasterLongArgument.java
index 9d2be09..eb17b7e 100644
--- a/core/java/android/security/keymaster/KeymasterLongArgument.java
+++ b/core/java/android/security/keymaster/KeymasterLongArgument.java
@@ -28,6 +28,7 @@
         super(tag);
         switch (KeymasterDefs.getTagType(tag)) {
             case KeymasterDefs.KM_LONG:
+            case KeymasterDefs.KM_LONG_REP:
                 break; // OK.
             default:
                 throw new IllegalArgumentException("Bad long tag " + tag);
diff --git a/core/java/android/service/carrier/CarrierConfigService.java b/core/java/android/service/carrier/CarrierConfigService.java
new file mode 100644
index 0000000..1880d16
--- /dev/null
+++ b/core/java/android/service/carrier/CarrierConfigService.java
@@ -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.
+ */
+
+package android.service.carrier;
+
+import android.app.Service;
+import android.content.Intent;
+import android.os.Bundle;
+import android.os.IBinder;
+
+/**
+ * A service that sets carrier configuration for telephony services.
+ * <p>
+ * To extend this class, you must declare the service in your manifest file to require the
+ * {@link android.Manifest.permission#BIND_CARRIER_CONFIG_SERVICE} permission and include an intent
+ * filter with the {@link #SERVICE_INTERFACE} action. For example:
+ * </p>
+ *
+ * <pre>{@code
+ * <service android:name=".MyCarrierConfigService"
+ *       android:label="@string/service_name"
+ *       android:permission="android.permission.BIND_CARRIER_CONFIG_SERVICE">
+ *  <intent-filter>
+ *      <action android:name="android.service.carrier.CarrierConfigService" />
+ *  </intent-filter>
+ * </service>
+ * }</pre>
+ */
+public abstract class CarrierConfigService extends Service {
+
+    public static final String SERVICE_INTERFACE = "android.service.carrier.CarrierConfigService";
+
+    private final ICarrierConfigService.Stub mStubWrapper;
+
+    public CarrierConfigService() {
+        mStubWrapper = new ICarrierConfigServiceWrapper();
+    }
+
+    /**
+     * Override this method to set carrier configuration.
+     * <p>
+     * This method will be called by telephony services to get carrier-specific configuration
+     * values. The returned config will be saved by the system until,
+     * <ol>
+     * <li>The carrier app package is updated, or</li>
+     * <li>The carrier app requests a reload with
+     * {@link android.telephony.CarrierConfigManager#reloadCarrierConfigForSubId
+     * reloadCarrierConfigForSubId}.</li>
+     * </ol>
+     * This method can be called after a SIM card loads, which may be before or after boot.
+     * </p>
+     * <p>
+     * This method should not block for a long time. If expensive operations (e.g. network access)
+     * are required, this method can schedule the work and return null. Then, use
+     * {@link android.telephony.CarrierConfigManager#reloadCarrierConfigForSubId
+     * reloadCarrierConfigForSubId} to trigger a reload when the config is ready.
+     * </p>
+     * <p>
+     * Implementations should use the keys defined in {@link android.telephony.CarrierConfigManager
+     * CarrierConfigManager}. Any configuration values not set in the returned {@link Bundle} may be
+     * overridden by the system's default configuration service.
+     * </p>
+     *
+     * @param id contains details about the current carrier that can be used do decide what
+     *            configuration values to return.
+     * @return a {@link Bundle} object containing the configuration or null if default values should
+     *         be used.
+     */
+    public abstract Bundle onLoadConfig(CarrierIdentifier id);
+
+    /** @hide */
+    @Override
+    public final IBinder onBind(Intent intent) {
+        if (!SERVICE_INTERFACE.equals(intent.getAction())) {
+            return null;
+        }
+        return mStubWrapper;
+    }
+
+    /**
+     * A wrapper around ICarrierConfigService that forwards calls to implementations of
+     * {@link CarrierConfigService}.
+     *
+     * @hide
+     */
+    private class ICarrierConfigServiceWrapper extends ICarrierConfigService.Stub {
+
+        @Override
+        public Bundle getCarrierConfig(CarrierIdentifier id) {
+            return CarrierConfigService.this.onLoadConfig(id);
+        }
+    }
+}
diff --git a/packages/SystemUI/res/drawable/ic_audio_phone.xml b/core/java/android/service/carrier/CarrierIdentifier.aidl
similarity index 65%
rename from packages/SystemUI/res/drawable/ic_audio_phone.xml
rename to core/java/android/service/carrier/CarrierIdentifier.aidl
index 64147f2b..48b1398 100644
--- a/packages/SystemUI/res/drawable/ic_audio_phone.xml
+++ b/core/java/android/service/carrier/CarrierIdentifier.aidl
@@ -1,7 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
- * Copyright 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.
@@ -15,9 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
--->
 
-<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
-    android:src="@*android:drawable/ic_audio_phone_am_alpha"
-    android:autoMirrored="true"
-    android:tint="#ffffffff" />
\ No newline at end of file
+package android.service.carrier;
+
+parcelable CarrierIdentifier;
diff --git a/core/java/android/service/carrier/CarrierIdentifier.java b/core/java/android/service/carrier/CarrierIdentifier.java
new file mode 100644
index 0000000..495fea6
--- /dev/null
+++ b/core/java/android/service/carrier/CarrierIdentifier.java
@@ -0,0 +1,117 @@
+/**
+ * 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.service.carrier;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * Used to pass info to CarrierConfigService implementations so they can decide what values to
+ * return.
+ */
+public class CarrierIdentifier implements Parcelable {
+
+    /** Used to create a {@link CarrierIdentifier} from a {@link Parcel}. */
+    public static final Creator<CarrierIdentifier> CREATOR = new Creator<CarrierIdentifier>() {
+            @Override
+        public CarrierIdentifier createFromParcel(Parcel parcel) {
+            return new CarrierIdentifier(parcel);
+        }
+
+            @Override
+        public CarrierIdentifier[] newArray(int i) {
+            return new CarrierIdentifier[i];
+        }
+    };
+
+    private String mMcc;
+    private String mMnc;
+    private String mSpn;
+    private String mImsi;
+    private String mGid1;
+    private String mGid2;
+
+    public CarrierIdentifier(String mcc, String mnc, String spn, String imsi, String gid1,
+            String gid2) {
+        mMcc = mcc;
+        mMnc = mnc;
+        mSpn = spn;
+        mImsi = imsi;
+        mGid1 = gid1;
+        mGid2 = gid2;
+    }
+
+    /** @hide */
+    public CarrierIdentifier(Parcel parcel) {
+        readFromParcel(parcel);
+    }
+
+    /** Get the mobile country code. */
+    public String getMcc() {
+        return mMcc;
+    }
+
+    /** Get the mobile network code. */
+    public String getMnc() {
+        return mMnc;
+    }
+
+    /** Get the service provider name. */
+    public String getSpn() {
+        return mSpn;
+    }
+
+    /** Get the international mobile subscriber identity. */
+    public String getImsi() {
+        return mImsi;
+    }
+
+    /** Get the group identifier level 1. */
+    public String getGid1() {
+        return mGid1;
+    }
+
+    /** Get the group identifier level 2. */
+    public String getGid2() {
+        return mGid2;
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel out, int flags) {
+        out.writeString(mMcc);
+        out.writeString(mMnc);
+        out.writeString(mSpn);
+        out.writeString(mImsi);
+        out.writeString(mGid1);
+        out.writeString(mGid2);
+    }
+
+    /** @hide */
+    public void readFromParcel(Parcel in) {
+        mMcc = in.readString();
+        mMnc = in.readString();
+        mSpn = in.readString();
+        mImsi = in.readString();
+        mGid1 = in.readString();
+        mGid2 = in.readString();
+    }
+}
diff --git a/core/java/android/service/carrier/ICarrierConfigService.aidl b/core/java/android/service/carrier/ICarrierConfigService.aidl
new file mode 100644
index 0000000..d8390b6
--- /dev/null
+++ b/core/java/android/service/carrier/ICarrierConfigService.aidl
@@ -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.service.carrier;
+
+import android.os.Bundle;
+import android.service.carrier.CarrierIdentifier;
+
+/**
+ * Service used to get carrier config from carrier apps.
+ *
+ * @see android.service.carrier.CarrierConfigService
+ * @hide
+ */
+interface ICarrierConfigService {
+
+    /** @see android.service.carrier.CarrierConfigService#onLoadConfig */
+    Bundle getCarrierConfig(in CarrierIdentifier id);
+}
\ No newline at end of file
diff --git a/core/java/android/service/gatekeeper/IGateKeeperService.aidl b/core/java/android/service/gatekeeper/IGateKeeperService.aidl
index 2f3e296..4f46701 100644
--- a/core/java/android/service/gatekeeper/IGateKeeperService.aidl
+++ b/core/java/android/service/gatekeeper/IGateKeeperService.aidl
@@ -53,13 +53,27 @@
      * Verifies an enrolled handle against a provided, plaintext blob.
      * @param uid The Android user ID associated to this enrollment
      * @param challenge a challenge to authenticate agaisnt the device credential. If successful
-     *                  authentication occurs, this value will be written to the returned 
+     *                  authentication occurs, this value will be written to the returned
      *                  authentication attestation.
      * @param enrolledPasswordHandle The handle against which the provided password will be
      *                               verified.
      * @param The plaintext blob to verify against enrolledPassword.
      * @return an opaque attestation of authentication on success, or null.
      */
-    byte[] verifyChallenge(int uid, long challenge, in byte[] enrolledPasswordHandle, 
+    byte[] verifyChallenge(int uid, long challenge, in byte[] enrolledPasswordHandle,
             in byte[] providedPassword);
+
+    /**
+     * Retrieves the secure identifier for the user with the provided Android ID,
+     * or 0 if none is found.
+     * @param uid the Android user id
+     */
+    long getSecureUserId(int uid);
+
+    /**
+     * Clears secure user id associated with the provided Android ID.
+     * Must be called when password is set to NONE.
+     * @param uid the Android user id.
+     */
+    void clearSecureUserId(int uid);
 }
diff --git a/core/java/android/service/notification/NotificationListenerService.java b/core/java/android/service/notification/NotificationListenerService.java
index fa782e4..cc7f880 100644
--- a/core/java/android/service/notification/NotificationListenerService.java
+++ b/core/java/android/service/notification/NotificationListenerService.java
@@ -21,6 +21,7 @@
 import android.app.INotificationManager;
 import android.app.Notification;
 import android.app.Notification.Builder;
+import android.app.NotificationManager.Policy;
 import android.app.Service;
 import android.content.ComponentName;
 import android.content.Context;
@@ -501,6 +502,22 @@
     }
 
     /**
+     * Gets the notification policy token associated with this listener.
+     *
+     * <p>
+     * Returns null if this listener is not currently active.
+     */
+    public final Policy.Token getNotificationPolicyToken() {
+        if (!isBound()) return null;
+        try {
+            return getNotificationInterface().getPolicyTokenFromListener(mWrapper);
+        } catch (android.os.RemoteException ex) {
+            Log.v(TAG, "Unable to contact notification manager", ex);
+            return null;
+        }
+    }
+
+    /**
      * Sets the desired {@link #getCurrentListenerHints() listener hints}.
      *
      * <p>
diff --git a/core/java/android/service/notification/ZenModeConfig.java b/core/java/android/service/notification/ZenModeConfig.java
index 1ed4779..14e947c 100644
--- a/core/java/android/service/notification/ZenModeConfig.java
+++ b/core/java/android/service/notification/ZenModeConfig.java
@@ -16,6 +16,7 @@
 
 package android.service.notification;
 
+import android.app.NotificationManager.Policy;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.res.Resources;
@@ -470,6 +471,59 @@
         }
     };
 
+    public Policy toNotificationPolicy() {
+        int priorityCategories = 0;
+        int prioritySenders = Policy.PRIORITY_SENDERS_ANY;
+        if (allowCalls) {
+            priorityCategories |= Policy.PRIORITY_CATEGORY_CALLS;
+        }
+        if (allowMessages) {
+            priorityCategories |= Policy.PRIORITY_CATEGORY_MESSAGES;
+        }
+        if (allowEvents) {
+            priorityCategories |= Policy.PRIORITY_CATEGORY_EVENTS;
+        }
+        if (allowReminders) {
+            priorityCategories |= Policy.PRIORITY_CATEGORY_REMINDERS;
+        }
+        if (allowRepeatCallers) {
+            priorityCategories |= Policy.PRIORITY_CATEGORY_REPEAT_CALLERS;
+        }
+        switch (allowFrom) {
+            case SOURCE_ANYONE:
+                prioritySenders = Policy.PRIORITY_SENDERS_ANY;
+                break;
+            case SOURCE_CONTACT:
+                prioritySenders = Policy.PRIORITY_SENDERS_CONTACTS;
+                break;
+            case SOURCE_STAR:
+                prioritySenders = Policy.PRIORITY_SENDERS_STARRED;
+                break;
+        }
+        return new Policy(priorityCategories, prioritySenders);
+    }
+
+    public void applyNotificationPolicy(Policy policy) {
+        if (policy == null) return;
+        allowCalls = (policy.priorityCategories & Policy.PRIORITY_CATEGORY_CALLS) != 0;
+        allowMessages = (policy.priorityCategories & Policy.PRIORITY_CATEGORY_MESSAGES) != 0;
+        allowEvents = (policy.priorityCategories & Policy.PRIORITY_CATEGORY_EVENTS) != 0;
+        allowReminders = (policy.priorityCategories & Policy.PRIORITY_CATEGORY_REMINDERS) != 0;
+        allowRepeatCallers = (policy.priorityCategories & Policy.PRIORITY_CATEGORY_REPEAT_CALLERS)
+                != 0;
+        switch (policy.prioritySenders) {
+            case Policy.PRIORITY_SENDERS_CONTACTS:
+                allowFrom = SOURCE_CONTACT;
+                break;
+            case Policy.PRIORITY_SENDERS_STARRED:
+                allowFrom = SOURCE_STAR;
+                break;
+            default:
+                allowFrom = SOURCE_ANYONE;
+                break;
+        }
+    }
+
     public static Condition toTimeCondition(Context context, int minutesFromNow, int userHandle) {
         final long now = System.currentTimeMillis();
         final long millis = minutesFromNow == 0 ? ZERO_VALUE_MS : minutesFromNow * MINUTES_MS;
@@ -881,4 +935,5 @@
     public interface Migration {
         ZenModeConfig migrate(XmlV1 v1);
     }
+
 }
diff --git a/core/java/android/service/trust/ITrustAgentServiceCallback.aidl b/core/java/android/service/trust/ITrustAgentServiceCallback.aidl
index 76b2be0..ec66cc8 100644
--- a/core/java/android/service/trust/ITrustAgentServiceCallback.aidl
+++ b/core/java/android/service/trust/ITrustAgentServiceCallback.aidl
@@ -24,7 +24,7 @@
  * @hide
  */
 oneway interface ITrustAgentServiceCallback {
-    void grantTrust(CharSequence message, long durationMs, boolean initiatedByUser);
+    void grantTrust(CharSequence message, long durationMs, int flags);
     void revokeTrust();
     void setManagingTrust(boolean managingTrust);
     void onConfigureCompleted(boolean result, IBinder token);
diff --git a/core/java/android/service/trust/TrustAgentService.java b/core/java/android/service/trust/TrustAgentService.java
index a3178e2..9d7ffad 100644
--- a/core/java/android/service/trust/TrustAgentService.java
+++ b/core/java/android/service/trust/TrustAgentService.java
@@ -17,6 +17,7 @@
 package android.service.trust;
 
 import android.Manifest;
+import android.annotation.IntDef;
 import android.annotation.SdkConstant;
 import android.annotation.SystemApi;
 import android.app.Service;
@@ -32,6 +33,8 @@
 import android.util.Log;
 import android.util.Slog;
 
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 import java.util.List;
 
 /**
@@ -69,6 +72,7 @@
  */
 @SystemApi
 public class TrustAgentService extends Service {
+
     private final String TAG = TrustAgentService.class.getSimpleName() +
             "[" + getClass().getSimpleName() + "]";
     private static final boolean DEBUG = false;
@@ -86,6 +90,34 @@
      */
     public static final String TRUST_AGENT_META_DATA = "android.service.trust.trustagent";
 
+
+    /**
+     * Flag for {@link #grantTrust(CharSequence, long, int)} indicating that trust is being granted
+     * as the direct result of user action - such as solving a security challenge. The hint is used
+     * by the system to optimize the experience. Behavior may vary by device and release, so
+     * one should only set this parameter if it meets the above criteria rather than relying on
+     * the behavior of any particular device or release.
+     */
+    public static final int FLAG_GRANT_TRUST_INITIATED_BY_USER = 1 << 0;
+
+    /**
+     * Flag for {@link #grantTrust(CharSequence, long, int)} indicating that the agent would like
+     * to dismiss the keyguard. When using this flag, the {@code TrustAgentService} must ensure
+     * it is only set in response to a direct user action with the expectation of dismissing the
+     * keyguard.
+     */
+    public static final int FLAG_GRANT_TRUST_DISMISS_KEYGUARD = 1 << 1;
+
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(flag = true,
+            value = {
+                    FLAG_GRANT_TRUST_INITIATED_BY_USER,
+                    FLAG_GRANT_TRUST_DISMISS_KEYGUARD,
+            })
+    public @interface GrantTrustFlags {}
+
+
     private static final int MSG_UNLOCK_ATTEMPT = 1;
     private static final int MSG_CONFIGURE = 2;
     private static final int MSG_TRUST_TIMEOUT = 3;
@@ -228,11 +260,35 @@
      *    direct result of user action - such as solving a security challenge. The hint is used
      *    by the system to optimize the experience. Behavior may vary by device and release, so
      *    one should only set this parameter if it meets the above criteria rather than relying on
-     *    the behavior of any particular device or release.
+     *    the behavior of any particular device or release. Corresponds to
+     *    {@link #FLAG_GRANT_TRUST_INITIATED_BY_USER}.
+     * @throws IllegalStateException if the agent is not currently managing trust.
+     *
+     * @deprecated use {@link #grantTrust(CharSequence, long, int)} instead.
+     */
+    @Deprecated
+    public final void grantTrust(
+            final CharSequence message, final long durationMs, final boolean initiatedByUser) {
+        grantTrust(message, durationMs, initiatedByUser ? FLAG_GRANT_TRUST_INITIATED_BY_USER : 0);
+    }
+
+    /**
+     * Call to grant trust on the device.
+     *
+     * @param message describes why the device is trusted, e.g. "Trusted by location".
+     * @param durationMs amount of time in milliseconds to keep the device in a trusted state.
+     *    Trust for this agent will automatically be revoked when the timeout expires unless
+     *    extended by a subsequent call to this function. The timeout is measured from the
+     *    invocation of this function as dictated by {@link SystemClock#elapsedRealtime())}.
+     *    For security reasons, the value should be no larger than necessary.
+     *    The value may be adjusted by the system as necessary to comply with a policy controlled
+     *    by the system or {@link DevicePolicyManager} restrictions. See {@link #onTrustTimeout()}
+     *    for determining when trust expires.
+     * @param flags TBDocumented
      * @throws IllegalStateException if the agent is not currently managing trust.
      */
     public final void grantTrust(
-            final CharSequence message, final long durationMs, final boolean initiatedByUser) {
+            final CharSequence message, final long durationMs, @GrantTrustFlags final int flags) {
         synchronized (mLock) {
             if (!mManagingTrust) {
                 throw new IllegalStateException("Cannot grant trust if agent is not managing trust."
@@ -240,7 +296,7 @@
             }
             if (mCallback != null) {
                 try {
-                    mCallback.grantTrust(message.toString(), durationMs, initiatedByUser);
+                    mCallback.grantTrust(message.toString(), durationMs, flags);
                 } catch (RemoteException e) {
                     onError("calling enableTrust()");
                 }
@@ -250,7 +306,7 @@
                 mPendingGrantTrustTask = new Runnable() {
                     @Override
                     public void run() {
-                        grantTrust(message, durationMs, initiatedByUser);
+                        grantTrust(message, durationMs, flags);
                     }
                 };
             }
diff --git a/core/java/android/service/wallpaper/WallpaperService.java b/core/java/android/service/wallpaper/WallpaperService.java
index 1674950..016541f 100644
--- a/core/java/android/service/wallpaper/WallpaperService.java
+++ b/core/java/android/service/wallpaper/WallpaperService.java
@@ -17,6 +17,7 @@
 package android.service.wallpaper;
 
 import android.content.res.TypedArray;
+import android.graphics.Canvas;
 import android.os.SystemProperties;
 import android.view.WindowInsets;
 
@@ -185,6 +186,7 @@
 
         DisplayManager mDisplayManager;
         Display mDisplay;
+        private int mDisplayState;
 
         final BaseSurfaceHolder mSurfaceHolder = new BaseSurfaceHolder() {
             {
@@ -228,7 +230,19 @@
                 throw new UnsupportedOperationException(
                         "Wallpapers do not support keep screen on");
             }
-            
+
+            @Override
+            public Canvas lockCanvas() {
+                if (mDisplayState == Display.STATE_DOZE
+                        || mDisplayState == Display.STATE_DOZE_SUSPEND) {
+                    try {
+                        mSession.pokeDrawLock(mWindow);
+                    } catch (RemoteException e) {
+                        // System server died, can be ignored.
+                    }
+                }
+                return super.lockCanvas();
+            }
         };
 
         final class WallpaperInputEventReceiver extends InputEventReceiver {
@@ -831,9 +845,12 @@
             
             mWindow.setSession(mSession);
 
+            mLayout.packageName = getPackageName();
+
             mDisplayManager = (DisplayManager)getSystemService(Context.DISPLAY_SERVICE);
             mDisplayManager.registerDisplayListener(mDisplayListener, mCaller.getHandler());
             mDisplay = mDisplayManager.getDisplay(Display.DEFAULT_DISPLAY);
+            mDisplayState = mDisplay.getState();
 
             if (DEBUG) Log.v(TAG, "onCreate(): " + this);
             onCreate(mSurfaceHolder);
@@ -873,8 +890,8 @@
 
         void reportVisibility() {
             if (!mDestroyed) {
-                boolean visible = mVisible
-                        & mDisplay != null && mDisplay.getState() != Display.STATE_OFF;
+                mDisplayState = mDisplay == null ? Display.STATE_UNKNOWN : mDisplay.getState();
+                boolean visible = mVisible && mDisplayState != Display.STATE_OFF;
                 if (mReportedVisible != visible) {
                     mReportedVisible = visible;
                     if (DEBUG) Log.v(TAG, "onVisibilityChanged(" + visible
diff --git a/core/java/android/text/StaticLayout.java b/core/java/android/text/StaticLayout.java
index 7828851..67794b1 100644
--- a/core/java/android/text/StaticLayout.java
+++ b/core/java/android/text/StaticLayout.java
@@ -165,6 +165,19 @@
             return this;
         }
 
+        public Builder setIndents(int[] leftIndents, int[] rightIndents) {
+            int leftLen = leftIndents == null ? 0 : leftIndents.length;
+            int rightLen = rightIndents == null ? 0 : rightIndents.length;
+            int[] indents = new int[Math.max(leftLen, rightLen)];
+            for (int i = 0; i < indents.length; i++) {
+                int leftMargin = i < leftLen ? leftIndents[i] : 0;
+                int rightMargin = i < rightLen ? rightIndents[i] : 0;
+                indents[i] = leftMargin + rightMargin;
+            }
+            nSetIndents(mNativePtr, indents);
+            return this;
+        }
+
         /**
          * Measurement and break iteration is done in native code. The protocol for using
          * the native code is as follows.
@@ -1009,6 +1022,8 @@
 
     private static native void nSetLocale(long nativePtr, String locale, long nativeHyphenator);
 
+    private static native void nSetIndents(long nativePtr, int[] indents);
+
     // Set up paragraph text and settings; done as one big method to minimize jni crossings
     private static native void nSetupParagraph(long nativePtr, char[] text, int length,
             float firstWidth, int firstWidthLineCount, float restWidth,
diff --git a/core/java/android/text/TextDirectionHeuristics.java b/core/java/android/text/TextDirectionHeuristics.java
index 866137c..354c15f 100644
--- a/core/java/android/text/TextDirectionHeuristics.java
+++ b/core/java/android/text/TextDirectionHeuristics.java
@@ -81,29 +81,47 @@
     private static final int STATE_FALSE = 1;
     private static final int STATE_UNKNOWN = 2;
 
-    private static int isRtlText(int directionality) {
-        switch (directionality) {
+    /* Returns STATE_TRUE for strong RTL characters, STATE_FALSE for strong LTR characters, and
+     * STATE_UNKNOWN for everything else.
+     */
+    private static int isRtlCodePoint(int codePoint) {
+        switch (Character.getDirectionality(codePoint)) {
             case Character.DIRECTIONALITY_LEFT_TO_RIGHT:
                 return STATE_FALSE;
             case Character.DIRECTIONALITY_RIGHT_TO_LEFT:
             case Character.DIRECTIONALITY_RIGHT_TO_LEFT_ARABIC:
                 return STATE_TRUE;
-            default:
-                return STATE_UNKNOWN;
-        }
-    }
+            case Character.DIRECTIONALITY_UNDEFINED:
+                // Unassigned characters still have bidi direction, defined at:
+                // http://www.unicode.org/Public/UCD/latest/ucd/extracted/DerivedBidiClass.txt
 
-    private static int isRtlTextOrFormat(int directionality) {
-        switch (directionality) {
-            case Character.DIRECTIONALITY_LEFT_TO_RIGHT:
-            case Character.DIRECTIONALITY_LEFT_TO_RIGHT_EMBEDDING:
-            case Character.DIRECTIONALITY_LEFT_TO_RIGHT_OVERRIDE:
-                return STATE_FALSE;
-            case Character.DIRECTIONALITY_RIGHT_TO_LEFT:
-            case Character.DIRECTIONALITY_RIGHT_TO_LEFT_ARABIC:
-            case Character.DIRECTIONALITY_RIGHT_TO_LEFT_EMBEDDING:
-            case Character.DIRECTIONALITY_RIGHT_TO_LEFT_OVERRIDE:
-                return STATE_TRUE;
+                if ((0x0590 <= codePoint && codePoint <= 0x08FF) ||
+                        (0xFB1D <= codePoint && codePoint <= 0xFDCF) ||
+                        (0xFDF0 <= codePoint && codePoint <= 0xFDFF) ||
+                        (0xFE70 <= codePoint && codePoint <= 0xFEFF) ||
+                        (0x10800 <= codePoint && codePoint <= 0x10FFF) ||
+                        (0x1E800 <= codePoint && codePoint <= 0x1EFFF)) {
+                    // Unassigned RTL character
+                    return STATE_TRUE;
+                } else if (
+                        // Potentially-unassigned Default_Ignorable. Ranges are from unassigned
+                        // characters that have Unicode property Other_Default_Ignorable_Code_Point
+                        // plus some enlargening to cover bidi isolates and simplify checks.
+                        (0x2065 <= codePoint && codePoint <= 0x2069) ||
+                        (0xFFF0 <= codePoint && codePoint <= 0xFFF8) ||
+                        (0xE0000 <= codePoint && codePoint <= 0xE0FFF) ||
+                        // Non-character
+                        (0xFDD0 <= codePoint && codePoint <= 0xFDEF) ||
+                        ((codePoint & 0xFFFE) == 0xFFFE) ||
+                        // Currency symbol
+                        (0x20A0 <= codePoint && codePoint <= 0x20CF) ||
+                        // Unpaired surrogate
+                        (0xD800 <= codePoint && codePoint <= 0xDFFF)) {
+                    return STATE_UNKNOWN;
+                } else {
+                    // Unassigned LTR character
+                    return STATE_FALSE;
+                }
             default:
                 return STATE_UNKNOWN;
         }
@@ -181,14 +199,26 @@
 
     /**
      * Algorithm that uses the first strong directional character to determine the paragraph
-     * direction. This is the standard Unicode Bidirectional algorithm.
+     * direction. This is the standard Unicode Bidirectional Algorithm (steps P2 and P3), with the
+     * exception that if no strong character is found, UNKNOWN is returned.
      */
     private static class FirstStrong implements TextDirectionAlgorithm {
         @Override
         public int checkRtl(CharSequence cs, int start, int count) {
             int result = STATE_UNKNOWN;
-            for (int i = start, e = start + count; i < e && result == STATE_UNKNOWN; ++i) {
-                result = isRtlTextOrFormat(Character.getDirectionality(cs.charAt(i)));
+            int openIsolateCount = 0;
+            for (int cp, i = start, end = start + count;
+                    i < end && result == STATE_UNKNOWN;
+                    i += Character.charCount(cp)) {
+                cp = Character.codePointAt(cs, i);
+                if (0x2066 <= cp && cp <= 0x2068) { // Opening isolates
+                    openIsolateCount += 1;
+                } else if (cp == 0x2069) { // POP DIRECTIONAL ISOLATE (PDI)
+                    if (openIsolateCount > 0) openIsolateCount -= 1;
+                } else if (openIsolateCount == 0) {
+                    // Only consider the characters outside isolate pairs
+                    result = isRtlCodePoint(cp);
+                }
             }
             return result;
         }
@@ -200,9 +230,10 @@
     }
 
     /**
-     * Algorithm that uses the presence of any strong directional non-format
-     * character (e.g. excludes LRE, LRO, RLE, RLO) to determine the
-     * direction of text.
+     * Algorithm that uses the presence of any strong directional character of the type indicated
+     * in the constructor parameter to determine the direction of text.
+     *
+     * Characters inside isolate pairs are skipped.
      */
     private static class AnyStrong implements TextDirectionAlgorithm {
         private final boolean mLookForRtl;
@@ -210,22 +241,31 @@
         @Override
         public int checkRtl(CharSequence cs, int start, int count) {
             boolean haveUnlookedFor = false;
-            for (int i = start, e = start + count; i < e; ++i) {
-                switch (isRtlText(Character.getDirectionality(cs.charAt(i)))) {
-                    case STATE_TRUE:
-                        if (mLookForRtl) {
-                            return STATE_TRUE;
-                        }
-                        haveUnlookedFor = true;
-                        break;
-                    case STATE_FALSE:
-                        if (!mLookForRtl) {
-                            return STATE_FALSE;
-                        }
-                        haveUnlookedFor = true;
-                        break;
-                    default:
-                        break;
+            int openIsolateCount = 0;
+            for (int cp, i = start, end = start + count; i < end; i += Character.charCount(cp)) {
+                cp = Character.codePointAt(cs, i);
+                if (0x2066 <= cp && cp <= 0x2068) { // Opening isolates
+                    openIsolateCount += 1;
+                } else if (cp == 0x2069) { // POP DIRECTIONAL ISOLATE (PDI)
+                    if (openIsolateCount > 0) openIsolateCount -= 1;
+                } else if (openIsolateCount == 0) {
+                    // Only consider the characters outside isolate pairs
+                    switch (isRtlCodePoint(cp)) {
+                        case STATE_TRUE:
+                            if (mLookForRtl) {
+                                return STATE_TRUE;
+                            }
+                            haveUnlookedFor = true;
+                            break;
+                        case STATE_FALSE:
+                            if (!mLookForRtl) {
+                                return STATE_FALSE;
+                            }
+                            haveUnlookedFor = true;
+                            break;
+                        default:
+                            break;
+                    }
                 }
             }
             if (haveUnlookedFor) {
diff --git a/core/java/android/text/method/BaseKeyListener.java b/core/java/android/text/method/BaseKeyListener.java
index 07c1ec3..fe7571f 100644
--- a/core/java/android/text/method/BaseKeyListener.java
+++ b/core/java/android/text/method/BaseKeyListener.java
@@ -97,7 +97,7 @@
         // Delete a character.
         final int start = Selection.getSelectionEnd(content);
         final int end;
-        if (isForwardDelete || event.isShiftPressed() || isShiftActive) {
+        if (isForwardDelete) {
             end = TextUtils.getOffsetAfter(content, start);
         } else {
             end = TextUtils.getOffsetBefore(content, start);
diff --git a/core/java/android/view/Display.java b/core/java/android/view/Display.java
index 71863b7..71e2251 100644
--- a/core/java/android/view/Display.java
+++ b/core/java/android/view/Display.java
@@ -170,6 +170,15 @@
     public static final int FLAG_PRESENTATION = 1 << 3;
 
     /**
+     * Display flag: Indicates that the contents of the display should not be scaled
+     * to fit the physical screen dimensions.  Used for development only to emulate
+     * devices with smaller physicals screens while preserving density.
+     *
+     * @hide
+     */
+    public static final int FLAG_SCALING_DISABLED = 1 << 30;
+
+    /**
      * Display type: Unknown display type.
      * @hide
      */
diff --git a/core/java/android/view/DisplayInfo.java b/core/java/android/view/DisplayInfo.java
index ecf45b4..243961c 100644
--- a/core/java/android/view/DisplayInfo.java
+++ b/core/java/android/view/DisplayInfo.java
@@ -538,6 +538,9 @@
         if ((flags & Display.FLAG_PRESENTATION) != 0) {
             result.append(", FLAG_PRESENTATION");
         }
+        if ((flags & Display.FLAG_SCALING_DISABLED) != 0) {
+            result.append(", FLAG_SCALING_DISABLED");
+        }
         return result.toString();
     }
 }
diff --git a/core/java/android/view/GestureDetector.java b/core/java/android/view/GestureDetector.java
index 2351548..b8544c6 100644
--- a/core/java/android/view/GestureDetector.java
+++ b/core/java/android/view/GestureDetector.java
@@ -149,12 +149,30 @@
     }
 
     /**
+     * The listener that is used to notify when a stylus button press occurs.
+     */
+    public interface OnStylusButtonPressListener {
+        /**
+         * Notified when a stylus button press occurs. This is when the stylus
+         * is touching the screen and the {@value MotionEvent#BUTTON_SECONDARY}
+         * is pressed.
+         *
+         * @param e The motion event that occurred during the stylus button
+         *            press.
+         * @return true if the event is consumed, else false
+         */
+        boolean onStylusButtonPress(MotionEvent e);
+    }
+
+    /**
      * A convenience class to extend when you only want to listen for a subset
      * of all the gestures. This implements all methods in the
-     * {@link OnGestureListener} and {@link OnDoubleTapListener} but does
-     * nothing and return {@code false} for all applicable methods.
+     * {@link OnGestureListener}, {@link OnDoubleTapListener}, and {@link OnStylusButtonPressListener}
+     * but does nothing and return {@code false} for all applicable methods.
      */
-    public static class SimpleOnGestureListener implements OnGestureListener, OnDoubleTapListener {
+    public static class SimpleOnGestureListener implements OnGestureListener, OnDoubleTapListener,
+            OnStylusButtonPressListener {
+
         public boolean onSingleTapUp(MotionEvent e) {
             return false;
         }
@@ -190,6 +208,10 @@
         public boolean onSingleTapConfirmed(MotionEvent e) {
             return false;
         }
+
+        public boolean onStylusButtonPress(MotionEvent e) {
+            return false;
+        }
     }
 
     private int mTouchSlopSquare;
@@ -211,10 +233,12 @@
     private final Handler mHandler;
     private final OnGestureListener mListener;
     private OnDoubleTapListener mDoubleTapListener;
+    private OnStylusButtonPressListener mStylusButtonListener;
 
     private boolean mStillDown;
     private boolean mDeferConfirmSingleTap;
     private boolean mInLongPress;
+    private boolean mInStylusButtonPress;
     private boolean mAlwaysInTapRegion;
     private boolean mAlwaysInBiggerTapRegion;
 
@@ -358,6 +382,9 @@
         if (listener instanceof OnDoubleTapListener) {
             setOnDoubleTapListener((OnDoubleTapListener) listener);
         }
+        if (listener instanceof OnStylusButtonPressListener) {
+            setOnStylusButtonPressListener((OnStylusButtonPressListener) listener);
+        }
         init(context);
     }
     
@@ -420,6 +447,19 @@
     }
 
     /**
+     * Sets the listener which will be called for stylus button related
+     * gestures.
+     *
+     * @param onStylusButtonPressListener the listener invoked for all the
+     *            callbacks, or null to stop listening for stylus button
+     *            gestures.
+     */
+    public void setOnStylusButtonPressListener(
+            OnStylusButtonPressListener onStylusButtonPressListener) {
+        mStylusButtonListener = onStylusButtonPressListener;
+    }
+
+    /**
      * Set whether longpress is enabled, if this is enabled when a user
      * presses and holds down you get a longpress event and nothing further.
      * If it's disabled the user can press and hold down and then later
@@ -512,7 +552,18 @@
             break;
 
         case MotionEvent.ACTION_DOWN:
-            if (mDoubleTapListener != null) {
+            if (mStylusButtonListener != null
+                    && ev.getToolType(0) == MotionEvent.TOOL_TYPE_STYLUS
+                    && (ev.getButtonState() & MotionEvent.BUTTON_SECONDARY) != 0) {
+                if (mStylusButtonListener.onStylusButtonPress(ev)) {
+                    mInStylusButtonPress = true;
+                    handled = true;
+                    mHandler.removeMessages(LONG_PRESS);
+                    mHandler.removeMessages(TAP);
+                }
+            }
+
+            if (mDoubleTapListener != null && !mInStylusButtonPress) {
                 boolean hadTapMessage = mHandler.hasMessages(TAP);
                 if (hadTapMessage) mHandler.removeMessages(TAP);
                 if ((mCurrentDownEvent != null) && (mPreviousUpEvent != null) && hadTapMessage &&
@@ -540,8 +591,8 @@
             mStillDown = true;
             mInLongPress = false;
             mDeferConfirmSingleTap = false;
-            
-            if (mIsLongpressEnabled) {
+
+            if (mIsLongpressEnabled && !mInStylusButtonPress) {
                 mHandler.removeMessages(LONG_PRESS);
                 mHandler.sendEmptyMessageAtTime(LONG_PRESS, mCurrentDownEvent.getDownTime()
                         + TAP_TIMEOUT + LONGPRESS_TIMEOUT);
@@ -551,7 +602,17 @@
             break;
 
         case MotionEvent.ACTION_MOVE:
-            if (mInLongPress) {
+            if (mStylusButtonListener != null && !mInStylusButtonPress && !mInLongPress
+                    && ev.getToolType(0) == MotionEvent.TOOL_TYPE_STYLUS
+                    && (ev.getButtonState() & MotionEvent.BUTTON_SECONDARY) != 0) {
+                if (mStylusButtonListener.onStylusButtonPress(ev)) {
+                    mInStylusButtonPress = true;
+                    handled = true;
+                    mHandler.removeMessages(LONG_PRESS);
+                    mHandler.removeMessages(TAP);
+                }
+            }
+            if (mInLongPress || mInStylusButtonPress) {
                 break;
             }
             final float scrollX = mLastFocusX - focusX;
@@ -591,6 +652,9 @@
             } else if (mInLongPress) {
                 mHandler.removeMessages(TAP);
                 mInLongPress = false;
+            } else if (mInStylusButtonPress) {
+                mHandler.removeMessages(TAP);
+                mInStylusButtonPress = false;
             } else if (mAlwaysInTapRegion) {
                 handled = mListener.onSingleTapUp(ev);
                 if (mDeferConfirmSingleTap && mDoubleTapListener != null) {
@@ -649,9 +713,8 @@
         mAlwaysInTapRegion = false;
         mAlwaysInBiggerTapRegion = false;
         mDeferConfirmSingleTap = false;
-        if (mInLongPress) {
-            mInLongPress = false;
-        }
+        mInLongPress = false;
+        mInStylusButtonPress = false;
     }
 
     private void cancelTaps() {
@@ -662,9 +725,8 @@
         mAlwaysInTapRegion = false;
         mAlwaysInBiggerTapRegion = false;
         mDeferConfirmSingleTap = false;
-        if (mInLongPress) {
-            mInLongPress = false;
-        }
+        mInLongPress = false;
+        mInStylusButtonPress = false;
     }
 
     private boolean isConsideredDoubleTap(MotionEvent firstDown, MotionEvent firstUp,
diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl
index d6625c837..5994d4f 100644
--- a/core/java/android/view/IWindowManager.aidl
+++ b/core/java/android/view/IWindowManager.aidl
@@ -70,6 +70,7 @@
     int getBaseDisplayDensity(int displayId);
     void setForcedDisplayDensity(int displayId, int density);
     void clearForcedDisplayDensity(int displayId);
+    void setForcedDisplayScalingMode(int displayId, int mode); // 0 = auto, 1 = disable
 
     void setOverscan(int displayId, int left, int top, int right, int bottom);
 
diff --git a/core/java/android/view/PhoneWindow.java b/core/java/android/view/PhoneWindow.java
index 5af2832..794c8e7 100644
--- a/core/java/android/view/PhoneWindow.java
+++ b/core/java/android/view/PhoneWindow.java
@@ -4257,7 +4257,13 @@
             if (deviceId != 0) {
                 searchEvent = new SearchEvent(InputDevice.getDevice(deviceId));
             }
-            result = cb.onSearchRequested(searchEvent);
+            try {
+                result = cb.onSearchRequested(searchEvent);
+            } catch (AbstractMethodError e) {
+                Log.e(TAG, "WindowCallback " + cb.getClass().getName() + " does not implement"
+                        + " method onSearchRequested(SearchEvent); fa", e);
+                result = cb.onSearchRequested();
+            }
         }
         if (!result && (getContext().getResources().getConfiguration().uiMode
                 & Configuration.UI_MODE_TYPE_MASK) == Configuration.UI_MODE_TYPE_TELEVISION) {
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 6b28746..6176b9a 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -15205,27 +15205,33 @@
 
     /**
      * This method is called by ViewGroup.drawChild() to have each child view draw itself.
-     * This draw() method is an implementation detail and is not intended to be overridden or
-     * to be called from anywhere else other than ViewGroup.drawChild().
+     *
+     * This is where the View specializes rendering behavior based on layer type,
+     * and hardware acceleration.
      */
     boolean draw(Canvas canvas, ViewGroup parent, long drawingTime) {
-        boolean usingRenderNodeProperties = mAttachInfo != null && mAttachInfo.mHardwareAccelerated;
+        final boolean hardwareAcceleratedCanvas = canvas.isHardwareAccelerated();
+        /* If an attached view draws to a HW canvas, it may use its RenderNode + DisplayList.
+         *
+         * If a view is dettached, its DisplayList shouldn't exist. If the canvas isn't
+         * HW accelerated, it can't handle drawing RenderNodes.
+         */
+        boolean drawingWithRenderNode = mAttachInfo != null
+                && mAttachInfo.mHardwareAccelerated
+                && hardwareAcceleratedCanvas;
+
         boolean more = false;
         final boolean childHasIdentityMatrix = hasIdentityMatrix();
-        final int flags = parent.mGroupFlags;
+        final int parentFlags = parent.mGroupFlags;
 
-        if ((flags & ViewGroup.FLAG_CLEAR_TRANSFORMATION) == ViewGroup.FLAG_CLEAR_TRANSFORMATION) {
+        if ((parentFlags & ViewGroup.FLAG_CLEAR_TRANSFORMATION) != 0) {
             parent.getChildTransformation().clear();
             parent.mGroupFlags &= ~ViewGroup.FLAG_CLEAR_TRANSFORMATION;
         }
 
         Transformation transformToApply = null;
         boolean concatMatrix = false;
-
-        boolean scalingRequired = mAttachInfo != null && mAttachInfo.mScalingRequired;
-        int layerType = getLayerType();
-        final boolean hardwareAccelerated = canvas.isHardwareAccelerated();
-
+        final boolean scalingRequired = mAttachInfo != null && mAttachInfo.mScalingRequired;
         final Animation a = getAnimation();
         if (a != null) {
             more = applyLegacyAnimation(parent, drawingTime, a, scalingRequired);
@@ -15240,8 +15246,8 @@
                 mRenderNode.setAnimationMatrix(null);
                 mPrivateFlags3 &= ~PFLAG3_VIEW_IS_ANIMATING_TRANSFORM;
             }
-            if (!usingRenderNodeProperties &&
-                    (flags & ViewGroup.FLAG_SUPPORT_STATIC_TRANSFORMATIONS) != 0) {
+            if (!drawingWithRenderNode
+                    && (parentFlags & ViewGroup.FLAG_SUPPORT_STATIC_TRANSFORMATIONS) != 0) {
                 final Transformation t = parent.getChildTransformation();
                 final boolean hasTransform = parent.getChildStaticTransformation(this, t);
                 if (hasTransform) {
@@ -15259,7 +15265,7 @@
         mPrivateFlags |= PFLAG_DRAWN;
 
         if (!concatMatrix &&
-                (flags & (ViewGroup.FLAG_SUPPORT_STATIC_TRANSFORMATIONS |
+                (parentFlags & (ViewGroup.FLAG_SUPPORT_STATIC_TRANSFORMATIONS |
                         ViewGroup.FLAG_CLIP_CHILDREN)) == ViewGroup.FLAG_CLIP_CHILDREN &&
                 canvas.quickReject(mLeft, mTop, mRight, mBottom, Canvas.EdgeType.BW) &&
                 (mPrivateFlags & PFLAG_DRAW_ANIMATION) == 0) {
@@ -15268,81 +15274,61 @@
         }
         mPrivateFlags2 &= ~PFLAG2_VIEW_QUICK_REJECTED;
 
-        if (hardwareAccelerated) {
+        if (hardwareAcceleratedCanvas) {
             // Clear INVALIDATED flag to allow invalidation to occur during rendering, but
             // retain the flag's value temporarily in the mRecreateDisplayList flag
-            mRecreateDisplayList = (mPrivateFlags & PFLAG_INVALIDATED) == PFLAG_INVALIDATED;
+            mRecreateDisplayList = (mPrivateFlags & PFLAG_INVALIDATED) != 0;
             mPrivateFlags &= ~PFLAG_INVALIDATED;
         }
 
         RenderNode renderNode = null;
         Bitmap cache = null;
-        boolean hasDisplayList = false;
-        if (!hardwareAccelerated) {
-            if (layerType != LAYER_TYPE_NONE) {
-                layerType = LAYER_TYPE_SOFTWARE;
-                buildDrawingCache(true);
-            }
+        int layerType = getLayerType();
+        if (layerType == LAYER_TYPE_SOFTWARE
+                || (!drawingWithRenderNode && layerType != LAYER_TYPE_NONE)) {
+            layerType = LAYER_TYPE_SOFTWARE;
+            buildDrawingCache(true);
             cache = getDrawingCache(true);
-        } else {
-            switch (layerType) {
-                case LAYER_TYPE_SOFTWARE:
-                    if (usingRenderNodeProperties) {
-                        hasDisplayList = canHaveDisplayList();
-                    } else {
-                        buildDrawingCache(true);
-                        cache = getDrawingCache(true);
-                    }
-                    break;
-                case LAYER_TYPE_HARDWARE:
-                    if (usingRenderNodeProperties) {
-                        hasDisplayList = canHaveDisplayList();
-                    }
-                    break;
-                case LAYER_TYPE_NONE:
-                    // Delay getting the display list until animation-driven alpha values are
-                    // set up and possibly passed on to the view
-                    hasDisplayList = canHaveDisplayList();
-                    break;
-            }
         }
-        usingRenderNodeProperties &= hasDisplayList;
-        if (usingRenderNodeProperties) {
+
+        if (drawingWithRenderNode) {
+            // Delay getting the display list until animation-driven alpha values are
+            // set up and possibly passed on to the view
             renderNode = getDisplayList();
             if (!renderNode.isValid()) {
                 // Uncommon, but possible. If a view is removed from the hierarchy during the call
                 // to getDisplayList(), the display list will be marked invalid and we should not
                 // try to use it again.
                 renderNode = null;
-                hasDisplayList = false;
-                usingRenderNodeProperties = false;
+                drawingWithRenderNode = false;
             }
         }
 
         int sx = 0;
         int sy = 0;
-        if (!hasDisplayList) {
+        if (!drawingWithRenderNode) {
             computeScroll();
             sx = mScrollX;
             sy = mScrollY;
         }
 
-        final boolean hasNoCache = cache == null || hasDisplayList;
-        final boolean offsetForScroll = cache == null && !hasDisplayList &&
-                layerType != LAYER_TYPE_HARDWARE;
+        final boolean hasNoCache = cache == null || drawingWithRenderNode;
+        final boolean offsetForScroll = cache == null
+                && !drawingWithRenderNode
+                && layerType != LAYER_TYPE_HARDWARE;
 
         int restoreTo = -1;
-        if (!usingRenderNodeProperties || transformToApply != null) {
+        if (!drawingWithRenderNode || transformToApply != null) {
             restoreTo = canvas.save();
         }
         if (offsetForScroll) {
             canvas.translate(mLeft - sx, mTop - sy);
         } else {
-            if (!usingRenderNodeProperties) {
+            if (!drawingWithRenderNode) {
                 canvas.translate(mLeft, mTop);
             }
             if (scalingRequired) {
-                if (usingRenderNodeProperties) {
+                if (drawingWithRenderNode) {
                     // TODO: Might not need this if we put everything inside the DL
                     restoreTo = canvas.save();
                 }
@@ -15352,9 +15338,11 @@
             }
         }
 
-        float alpha = usingRenderNodeProperties ? 1 : (getAlpha() * getTransitionAlpha());
-        if (transformToApply != null || alpha < 1 ||  !hasIdentityMatrix() ||
-                (mPrivateFlags3 & PFLAG3_VIEW_IS_ANIMATING_ALPHA) == PFLAG3_VIEW_IS_ANIMATING_ALPHA) {
+        float alpha = drawingWithRenderNode ? 1 : (getAlpha() * getTransitionAlpha());
+        if (transformToApply != null
+                || alpha < 1
+                || !hasIdentityMatrix()
+                || (mPrivateFlags3 & PFLAG3_VIEW_IS_ANIMATING_ALPHA) != 0) {
             if (transformToApply != null || !childHasIdentityMatrix) {
                 int transX = 0;
                 int transY = 0;
@@ -15366,7 +15354,7 @@
 
                 if (transformToApply != null) {
                     if (concatMatrix) {
-                        if (usingRenderNodeProperties) {
+                        if (drawingWithRenderNode) {
                             renderNode.setAnimationMatrix(transformToApply.getMatrix());
                         } else {
                             // Undo the scroll translation, apply the transformation matrix,
@@ -15385,7 +15373,7 @@
                     }
                 }
 
-                if (!childHasIdentityMatrix && !usingRenderNodeProperties) {
+                if (!childHasIdentityMatrix && !drawingWithRenderNode) {
                     canvas.translate(-transX, -transY);
                     canvas.concat(getMatrix());
                     canvas.translate(transX, transY);
@@ -15393,8 +15381,7 @@
             }
 
             // Deal with alpha if it is or used to be <1
-            if (alpha < 1 ||
-                    (mPrivateFlags3 & PFLAG3_VIEW_IS_ANIMATING_ALPHA) == PFLAG3_VIEW_IS_ANIMATING_ALPHA) {
+            if (alpha < 1 || (mPrivateFlags3 & PFLAG3_VIEW_IS_ANIMATING_ALPHA) != 0) {
                 if (alpha < 1) {
                     mPrivateFlags3 |= PFLAG3_VIEW_IS_ANIMATING_ALPHA;
                 } else {
@@ -15405,17 +15392,14 @@
                     final int multipliedAlpha = (int) (255 * alpha);
                     if (!onSetAlpha(multipliedAlpha)) {
                         int layerFlags = Canvas.HAS_ALPHA_LAYER_SAVE_FLAG;
-                        if ((flags & ViewGroup.FLAG_CLIP_CHILDREN) != 0 ||
-                                layerType != LAYER_TYPE_NONE) {
+                        if ((parentFlags & ViewGroup.FLAG_CLIP_CHILDREN) != 0
+                                || layerType != LAYER_TYPE_NONE) {
                             layerFlags |= Canvas.CLIP_TO_LAYER_SAVE_FLAG;
                         }
-                        if (usingRenderNodeProperties) {
+                        if (drawingWithRenderNode) {
                             renderNode.setAlpha(alpha * getAlpha() * getTransitionAlpha());
-                        } else  if (layerType == LAYER_TYPE_NONE) {
-                            final int scrollX = hasDisplayList ? 0 : sx;
-                            final int scrollY = hasDisplayList ? 0 : sy;
-                            canvas.saveLayerAlpha(scrollX, scrollY,
-                                    scrollX + (mRight - mLeft), scrollY + (mBottom - mTop),
+                        } else if (layerType == LAYER_TYPE_NONE) {
+                            canvas.saveLayerAlpha(sx, sy, sx + getWidth(), sy + getHeight(),
                                     multipliedAlpha, layerFlags);
                         }
                     } else {
@@ -15429,15 +15413,14 @@
             mPrivateFlags &= ~PFLAG_ALPHA_SET;
         }
 
-        if (!usingRenderNodeProperties) {
+        if (!drawingWithRenderNode) {
             // apply clips directly, since RenderNode won't do it for this draw
-            if ((flags & ViewGroup.FLAG_CLIP_CHILDREN) == ViewGroup.FLAG_CLIP_CHILDREN
-                    && cache == null) {
+            if ((parentFlags & ViewGroup.FLAG_CLIP_CHILDREN) != 0 && cache == null) {
                 if (offsetForScroll) {
-                    canvas.clipRect(sx, sy, sx + (mRight - mLeft), sy + (mBottom - mTop));
+                    canvas.clipRect(sx, sy, sx + getWidth(), sy + getHeight());
                 } else {
                     if (!scalingRequired || cache == null) {
-                        canvas.clipRect(0, 0, mRight - mLeft, mBottom - mTop);
+                        canvas.clipRect(0, 0, getWidth(), getHeight());
                     } else {
                         canvas.clipRect(0, 0, cache.getWidth(), cache.getHeight());
                     }
@@ -15450,22 +15433,9 @@
             }
         }
 
-
-
-        if (!usingRenderNodeProperties && hasDisplayList) {
-            renderNode = getDisplayList();
-            if (!renderNode.isValid()) {
-                // Uncommon, but possible. If a view is removed from the hierarchy during the call
-                // to getDisplayList(), the display list will be marked invalid and we should not
-                // try to use it again.
-                renderNode = null;
-                hasDisplayList = false;
-            }
-        }
-
         if (hasNoCache) {
             boolean layerRendered = false;
-            if (layerType == LAYER_TYPE_HARDWARE && !usingRenderNodeProperties) {
+            if (layerType == LAYER_TYPE_HARDWARE && !drawingWithRenderNode) {
                 final HardwareLayer layer = getHardwareLayer();
                 if (layer != null && layer.isValid()) {
                     int restoreAlpha = mLayerPaint.getAlpha();
@@ -15474,16 +15444,12 @@
                     mLayerPaint.setAlpha(restoreAlpha);
                     layerRendered = true;
                 } else {
-                    final int scrollX = hasDisplayList ? 0 : sx;
-                    final int scrollY = hasDisplayList ? 0 : sy;
-                    canvas.saveLayer(scrollX, scrollY,
-                            scrollX + mRight - mLeft, scrollY + mBottom - mTop, mLayerPaint,
-                            Canvas.HAS_ALPHA_LAYER_SAVE_FLAG | Canvas.CLIP_TO_LAYER_SAVE_FLAG);
+                    canvas.saveLayer(sx, sy, sx + getWidth(), sy + getHeight(), mLayerPaint);
                 }
             }
 
             if (!layerRendered) {
-                if (!hasDisplayList) {
+                if (!drawingWithRenderNode) {
                     // Fast path for layouts with no backgrounds
                     if ((mPrivateFlags & PFLAG_SKIP_DRAW) == PFLAG_SKIP_DRAW) {
                         mPrivateFlags &= ~PFLAG_DIRTY_MASK;
@@ -15493,7 +15459,7 @@
                     }
                 } else {
                     mPrivateFlags &= ~PFLAG_DIRTY_MASK;
-                    ((DisplayListCanvas) canvas).drawRenderNode(renderNode, flags);
+                    ((DisplayListCanvas) canvas).drawRenderNode(renderNode, parentFlags);
                 }
             }
         } else if (cache != null) {
@@ -15522,13 +15488,13 @@
         }
 
         if (a != null && !more) {
-            if (!hardwareAccelerated && !a.getFillAfter()) {
+            if (!hardwareAcceleratedCanvas && !a.getFillAfter()) {
                 onSetAlpha(255);
             }
             parent.finishAnimatingView(this, a);
         }
 
-        if (more && hardwareAccelerated) {
+        if (more && hardwareAcceleratedCanvas) {
             if (a.hasAlpha() && (mPrivateFlags & PFLAG_ALPHA_SET) == PFLAG_ALPHA_SET) {
                 // alpha animations should cause the child to recreate its display list
                 invalidate(true);
@@ -18207,7 +18173,8 @@
             // flag not set, setMeasuredDimension() was not invoked, we raise
             // an exception to warn the developer
             if ((mPrivateFlags & PFLAG_MEASURED_DIMENSION_SET) != PFLAG_MEASURED_DIMENSION_SET) {
-                throw new IllegalStateException("onMeasure() did not set the"
+                throw new IllegalStateException("View with id " + getId() + ": "
+                        + getClass().getName() + "#onMeasure() did not set the"
                         + " measured dimension by calling"
                         + " setMeasuredDimension()");
             }
@@ -20465,11 +20432,12 @@
 
         static int adjust(int measureSpec, int delta) {
             final int mode = getMode(measureSpec);
+            int size = getSize(measureSpec);
             if (mode == UNSPECIFIED) {
                 // No need to adjust size for UNSPECIFIED mode.
-                return makeMeasureSpec(0, UNSPECIFIED);
+                return makeMeasureSpec(size, UNSPECIFIED);
             }
-            int size = getSize(measureSpec) + delta;
+            size += delta;
             if (size < 0) {
                 Log.e(VIEW_LOG_TAG, "MeasureSpec.adjust: new size would be negative! (" + size +
                         ") spec: " + toString(measureSpec) + " delta: " + delta);
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index 98b895d..8f2be99 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -494,6 +494,11 @@
      */
     private int mNestedScrollAxes;
 
+    // Used to manage the list of transient views, added by addTransientView()
+    private List<Integer> mTransientIndices = null;
+    private List<View> mTransientViews = null;
+
+
     /**
      * Empty ActionMode used as a sentinel in recursive entries to startActionModeForChild.
      *
@@ -2822,6 +2827,11 @@
             child.dispatchAttachedToWindow(info,
                     visibility | (child.mViewFlags & VISIBILITY_MASK));
         }
+        final int transientCount = mTransientIndices == null ? 0 : mTransientIndices.size();
+        for (int i = 0; i < transientCount; ++i) {
+            View view = mTransientViews.get(i);
+            view.dispatchAttachedToWindow(info, visibility | (view.mViewFlags & VISIBILITY_MASK));
+        }
     }
 
     @Override
@@ -2991,6 +3001,11 @@
             children[i].dispatchDetachedFromWindow();
         }
         clearDisappearingChildren();
+        final int transientCount = mTransientViews == null ? 0 : mTransientIndices.size();
+        for (int i = 0; i < transientCount; ++i) {
+            View view = mTransientViews.get(i);
+            view.dispatchDetachedFromWindow();
+        }
         super.dispatchDetachedFromWindow();
     }
 
@@ -3291,6 +3306,8 @@
         final long drawingTime = getDrawingTime();
 
         if (usingRenderNodeProperties) canvas.insertReorderBarrier();
+        final int transientCount = mTransientIndices == null ? 0 : mTransientIndices.size();
+        int transientIndex = transientCount != 0 ? 0 : -1;
         // Only use the preordered list if not HW accelerated, since the HW pipeline will do the
         // draw reordering internally
         final ArrayList<View> preorderedList = usingRenderNodeProperties
@@ -3298,6 +3315,17 @@
         final boolean customOrder = preorderedList == null
                 && isChildrenDrawingOrderEnabled();
         for (int i = 0; i < childrenCount; i++) {
+            while (transientIndex >= 0 && mTransientIndices.get(transientIndex) == i) {
+                final View transientChild = mTransientViews.get(transientIndex);
+                if ((transientChild.mViewFlags & VISIBILITY_MASK) == VISIBLE ||
+                        transientChild.getAnimation() != null) {
+                    more |= drawChild(canvas, transientChild, drawingTime);
+                }
+                transientIndex++;
+                if (transientIndex >= transientCount) {
+                    transientIndex = -1;
+                }
+            }
             int childIndex = customOrder ? getChildDrawingOrder(childrenCount, i) : i;
             final View child = (preorderedList == null)
                     ? children[childIndex] : preorderedList.get(childIndex);
@@ -3305,6 +3333,18 @@
                 more |= drawChild(canvas, child, drawingTime);
             }
         }
+        while (transientIndex >= 0) {
+            // there may be additional transient views after the normal views
+            final View transientChild = mTransientViews.get(transientIndex);
+            if ((transientChild.mViewFlags & VISIBILITY_MASK) == VISIBLE ||
+                    transientChild.getAnimation() != null) {
+                more |= drawChild(canvas, transientChild, drawingTime);
+            }
+            transientIndex++;
+            if (transientIndex >= transientCount) {
+                break;
+            }
+        }
         if (preorderedList != null) preorderedList.clear();
 
         // Draw any disappearing views that have animations
@@ -3785,6 +3825,145 @@
     }
 
     /**
+     * This method adds a view to this container at the specified index purely for the
+     * purposes of allowing that view to draw even though it is not a normal child of
+     * the container. That is, the view does not participate in layout, focus, accessibility,
+     * input, or other normal view operations; it is purely an item to be drawn during the normal
+     * rendering operation of this container. The index that it is added at is the order
+     * in which it will be drawn, with respect to the other views in the container.
+     * For example, a transient view added at index 0 will be drawn before all other views
+     * in the container because it will be drawn first (including before any real view
+     * at index 0). There can be more than one transient view at any particular index;
+     * these views will be drawn in the order in which they were added to the list of
+     * transient views. The index of transient views can also be greater than the number
+     * of normal views in the container; that just means that they will be drawn after all
+     * other views are drawn.
+     *
+     * <p>Note that since transient views do not participate in layout, they must be sized
+     * manually or, more typically, they should just use the size that they had before they
+     * were removed from their container.</p>
+     *
+     * <p>Transient views are useful for handling animations of views that have been removed
+     * from the container, but which should be animated out after the removal. Adding these
+     * views as transient views allows them to participate in drawing without side-effecting
+     * the layout of the container.</p>
+     *
+     * <p>Transient views must always be explicitly {@link #removeTransientView(View) removed}
+     * from the container when they are no longer needed. For example, a transient view
+     * which is added in order to fade it out in its old location should be removed
+     * once the animation is complete.</p>
+     *
+     * @param view The view to be added
+     * @param index The index at which this view should be drawn, must be >= 0.
+     * This value is relative to the {@link #getChildAt(int) index} values in the normal
+     * child list of this container, where any transient view at a particular index will
+     * be drawn before any normal child at that same index.
+     *
+     * @hide
+     */
+    public void addTransientView(View view, int index) {
+        if (index < 0) {
+            return;
+        }
+        if (mTransientIndices == null) {
+            mTransientIndices = new ArrayList<Integer>();
+            mTransientViews = new ArrayList<View>();
+        }
+        final int oldSize = mTransientIndices.size();
+        if (oldSize > 0) {
+            int insertionIndex;
+            for (insertionIndex = 0; insertionIndex < oldSize; ++insertionIndex) {
+                if (index < mTransientIndices.get(insertionIndex)) {
+                    break;
+                }
+            }
+            mTransientIndices.add(insertionIndex, index);
+            mTransientViews.add(insertionIndex, view);
+        } else {
+            mTransientIndices.add(index);
+            mTransientViews.add(view);
+        }
+        view.mParent = this;
+        view.dispatchAttachedToWindow(mAttachInfo, (mViewFlags&VISIBILITY_MASK));
+        invalidate(true);
+    }
+
+    /**
+     * Removes a view from the list of transient views in this container. If there is no
+     * such transient view, this method does nothing.
+     *
+     * @param view The transient view to be removed
+     *
+     * @hide
+     */
+    public void removeTransientView(View view) {
+        if (mTransientViews == null) {
+            return;
+        }
+        final int size = mTransientViews.size();
+        for (int i = 0; i < size; ++i) {
+            if (view == mTransientViews.get(i)) {
+                mTransientViews.remove(i);
+                mTransientIndices.remove(i);
+                view.mParent = null;
+                view.dispatchDetachedFromWindow();
+                invalidate(true);
+                return;
+            }
+        }
+    }
+
+    /**
+     * Returns the number of transient views in this container. Specific transient
+     * views and the index at which they were added can be retrieved via
+     * {@link #getTransientView(int)} and {@link #getTransientViewIndex(int)}.
+     *
+     * @see #addTransientView(View, int)
+     * @return The number of transient views in this container
+     *
+     * @hide
+     */
+    public int getTransientViewCount() {
+        return mTransientIndices == null ? 0 : mTransientIndices.size();
+    }
+
+    /**
+     * Given a valid position within the list of transient views, returns the index of
+     * the transient view at that position.
+     *
+     * @param position The position of the index being queried. Must be at least 0
+     * and less than the value returned by {@link #getTransientViewCount()}.
+     * @return The index of the transient view stored in the given position if the
+     * position is valid, otherwise -1
+     *
+     * @hide
+     */
+    public int getTransientViewIndex(int position) {
+        if (position < 0 || mTransientIndices == null || position >= mTransientIndices.size()) {
+            return -1;
+        }
+        return mTransientIndices.get(position);
+    }
+
+    /**
+     * Given a valid position within the list of transient views, returns the
+     * transient view at that position.
+     *
+     * @param position The position of the view being queried. Must be at least 0
+     * and less than the value returned by {@link #getTransientViewCount()}.
+     * @return The transient view stored in the given position if the
+     * position is valid, otherwise null
+     *
+     * @hide
+     */
+    public View getTransientView(int position) {
+        if (mTransientViews == null || position >= mTransientViews.size()) {
+            return null;
+        }
+        return mTransientViews.get(position);
+    }
+
+    /**
      * <p>Adds a child view. If no layout parameters are already set on the child, the
      * default parameters for this ViewGroup are set on the child.</p>
      * 
@@ -4096,6 +4275,16 @@
         if (child.getVisibility() != View.GONE) {
             notifySubtreeAccessibilityStateChangedIfNeeded();
         }
+
+        if (mTransientIndices != null) {
+            final int transientCount = mTransientIndices.size();
+            for (int i = 0; i < transientCount; ++i) {
+                final int oldIndex = mTransientIndices.get(i);
+                if (index <= oldIndex) {
+                    mTransientIndices.set(i, oldIndex + 1);
+                }
+            }
+        }
     }
 
     private void addInArray(View child, int index) {
@@ -4340,6 +4529,14 @@
         if (view.getVisibility() != View.GONE) {
             notifySubtreeAccessibilityStateChangedIfNeeded();
         }
+
+        int transientCount = mTransientIndices == null ? 0 : mTransientIndices.size();
+        for (int i = 0; i < transientCount; ++i) {
+            final int oldIndex = mTransientIndices.get(i);
+            if (index < oldIndex) {
+                mTransientIndices.set(i, oldIndex - 1);
+            }
+        }
     }
 
     /**
@@ -6353,6 +6550,7 @@
     public void onStopNestedScroll(View child) {
         // Stop any recursive nested scrolling.
         stopNestedScroll();
+        mNestedScrollAxes = 0;
     }
 
     /**
diff --git a/core/java/android/view/Window.java b/core/java/android/view/Window.java
index 9d0d5ff4..49a72ce 100644
--- a/core/java/android/view/Window.java
+++ b/core/java/android/view/Window.java
@@ -595,6 +595,8 @@
                     title="Panel";
                 } else if (wp.type == WindowManager.LayoutParams.TYPE_APPLICATION_SUB_PANEL) {
                     title="SubPanel";
+                } else if (wp.type == WindowManager.LayoutParams.TYPE_APPLICATION_ABOVE_SUB_PANEL) {
+                    title="AboveSubPanel";
                 } else if (wp.type == WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG) {
                     title="AtchDlg";
                 } else {
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index 54d78f3..e983910 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -164,13 +164,14 @@
          * be used by applications, and a special permission is required
          * to use them.
          * </ul>
-         * 
+         *
          * @see #TYPE_BASE_APPLICATION
          * @see #TYPE_APPLICATION
          * @see #TYPE_APPLICATION_STARTING
          * @see #TYPE_APPLICATION_PANEL
          * @see #TYPE_APPLICATION_MEDIA
          * @see #TYPE_APPLICATION_SUB_PANEL
+         * @see #TYPE_APPLICATION_ABOVE_SUB_PANEL
          * @see #TYPE_APPLICATION_ATTACHED_DIALOG
          * @see #TYPE_STATUS_BAR
          * @see #TYPE_SEARCH_BAR
@@ -193,6 +194,7 @@
             @ViewDebug.IntToString(from = TYPE_APPLICATION_PANEL, to = "TYPE_APPLICATION_PANEL"),
             @ViewDebug.IntToString(from = TYPE_APPLICATION_MEDIA, to = "TYPE_APPLICATION_MEDIA"),
             @ViewDebug.IntToString(from = TYPE_APPLICATION_SUB_PANEL, to = "TYPE_APPLICATION_SUB_PANEL"),
+            @ViewDebug.IntToString(from = TYPE_APPLICATION_ABOVE_SUB_PANEL, to = "TYPE_APPLICATION_ABOVE_SUB_PANEL"),
             @ViewDebug.IntToString(from = TYPE_APPLICATION_ATTACHED_DIALOG, to = "TYPE_APPLICATION_ATTACHED_DIALOG"),
             @ViewDebug.IntToString(from = TYPE_APPLICATION_MEDIA_OVERLAY, to = "TYPE_APPLICATION_MEDIA_OVERLAY"),
             @ViewDebug.IntToString(from = TYPE_STATUS_BAR, to = "TYPE_STATUS_BAR"),
@@ -260,40 +262,40 @@
          * End of types of application windows.
          */
         public static final int LAST_APPLICATION_WINDOW = 99;
-    
+
         /**
          * Start of types of sub-windows.  The {@link #token} of these windows
          * must be set to the window they are attached to.  These types of
          * windows are kept next to their attached window in Z-order, and their
          * coordinate space is relative to their attached window.
          */
-        public static final int FIRST_SUB_WINDOW        = 1000;
-    
+        public static final int FIRST_SUB_WINDOW = 1000;
+
         /**
          * Window type: a panel on top of an application window.  These windows
          * appear on top of their attached window.
          */
-        public static final int TYPE_APPLICATION_PANEL  = FIRST_SUB_WINDOW;
-    
+        public static final int TYPE_APPLICATION_PANEL = FIRST_SUB_WINDOW;
+
         /**
          * Window type: window for showing media (such as video).  These windows
          * are displayed behind their attached window.
          */
-        public static final int TYPE_APPLICATION_MEDIA  = FIRST_SUB_WINDOW+1;
-    
+        public static final int TYPE_APPLICATION_MEDIA = FIRST_SUB_WINDOW + 1;
+
         /**
          * Window type: a sub-panel on top of an application window.  These
          * windows are displayed on top their attached window and any
          * {@link #TYPE_APPLICATION_PANEL} panels.
          */
-        public static final int TYPE_APPLICATION_SUB_PANEL = FIRST_SUB_WINDOW+2;
+        public static final int TYPE_APPLICATION_SUB_PANEL = FIRST_SUB_WINDOW + 2;
 
         /** Window type: like {@link #TYPE_APPLICATION_PANEL}, but layout
          * of the window happens as that of a top-level window, <em>not</em>
          * as a child of its container.
          */
-        public static final int TYPE_APPLICATION_ATTACHED_DIALOG = FIRST_SUB_WINDOW+3;
-        
+        public static final int TYPE_APPLICATION_ATTACHED_DIALOG = FIRST_SUB_WINDOW + 3;
+
         /**
          * Window type: window for showing overlays on top of media windows.
          * These windows are displayed between TYPE_APPLICATION_MEDIA and the
@@ -301,19 +303,26 @@
          * is a big ugly hack so:
          * @hide
          */
-        public static final int TYPE_APPLICATION_MEDIA_OVERLAY  = FIRST_SUB_WINDOW+4;
-    
+        public static final int TYPE_APPLICATION_MEDIA_OVERLAY  = FIRST_SUB_WINDOW + 4;
+
+        /**
+         * Window type: a above sub-panel on top of an application window and it's
+         * sub-panel windows. These windows are displayed on top of their attached window
+         * and any {@link #TYPE_APPLICATION_SUB_PANEL} panels.
+         */
+        public static final int TYPE_APPLICATION_ABOVE_SUB_PANEL = FIRST_SUB_WINDOW + 5;
+
         /**
          * End of types of sub-windows.
          */
-        public static final int LAST_SUB_WINDOW         = 1999;
-        
+        public static final int LAST_SUB_WINDOW = 1999;
+
         /**
          * Start of system-specific window types.  These are not normally
          * created by applications.
          */
         public static final int FIRST_SYSTEM_WINDOW     = 2000;
-    
+
         /**
          * Window type: the status bar.  There can be only one status bar
          * window; it is placed at the top of the screen, and all other
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index e7c4328..7ab5aaa 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -2427,9 +2427,7 @@
 
     @Override
     public void onProvideVirtualAssistStructure(ViewAssistStructure structure) {
-        super.onProvideVirtualAssistStructure(structure);
-        // TODO: enable when chromium backend lands.
-        // mProvider.getViewDelegate().onProvideVirtualAssistStructure(structure);
+        mProvider.getViewDelegate().onProvideVirtualAssistStructure(structure);
     }
 
     /** @hide */
diff --git a/core/java/android/webkit/WebViewFactory.java b/core/java/android/webkit/WebViewFactory.java
index cafe053..4d8dce1 100644
--- a/core/java/android/webkit/WebViewFactory.java
+++ b/core/java/android/webkit/WebViewFactory.java
@@ -76,9 +76,54 @@
     private static boolean sAddressSpaceReserved = false;
     private static PackageInfo sPackageInfo;
 
+    /** @hide */
+    public static String[] getWebViewPackageNames() {
+        return AppGlobals.getInitialApplication().getResources().getStringArray(
+                com.android.internal.R.array.config_webViewPackageNames);
+    }
+
+    // TODO (gsennton) remove when committing webview xts test change
     public static String getWebViewPackageName() {
-        return AppGlobals.getInitialApplication().getString(
-                com.android.internal.R.string.config_webViewPackageName);
+        String[] webViewPackageNames = getWebViewPackageNames();
+        return webViewPackageNames[webViewPackageNames.length-1];
+    }
+
+    /**
+     * Return the package info of the first package in the webview priority list that contains
+     * webview.
+     *
+     * @hide
+     */
+    public static PackageInfo findPreferredWebViewPackage() {
+        PackageManager pm = AppGlobals.getInitialApplication().getPackageManager();
+
+        for (String packageName : getWebViewPackageNames()) {
+            try {
+                PackageInfo packageInfo = pm.getPackageInfo(packageName,
+                    PackageManager.GET_META_DATA);
+                ApplicationInfo applicationInfo = packageInfo.applicationInfo;
+
+                // If the correct flag is set the package contains webview.
+                if (getWebViewLibrary(applicationInfo) != null) {
+                    return packageInfo;
+                }
+            } catch (PackageManager.NameNotFoundException e) {
+            }
+        }
+        throw new AndroidRuntimeException("Could not find a loadable WebView package");
+    }
+
+    private static ApplicationInfo getWebViewApplicationInfo() {
+        if (sPackageInfo == null)
+            return findPreferredWebViewPackage().applicationInfo;
+        else
+            return sPackageInfo.applicationInfo;
+    }
+
+    private static String getWebViewLibrary(ApplicationInfo ai) {
+        if (ai.metaData != null)
+            return ai.metaData.getString("com.android.webview.WebViewLibrary");
+        return null;
     }
 
     public static PackageInfo getLoadedPackageInfo() {
@@ -99,6 +144,11 @@
 
             Trace.traceBegin(Trace.TRACE_TAG_WEBVIEW, "WebViewFactory.getProvider()");
             try {
+                // First fetch the package info so we can log the webview package version.
+                sPackageInfo = findPreferredWebViewPackage();
+                Log.i(LOGTAG, "Loading " + sPackageInfo.packageName + " version " +
+                    sPackageInfo.versionName + " (code " + sPackageInfo.versionCode + ")");
+
                 Trace.traceBegin(Trace.TRACE_TAG_WEBVIEW, "WebViewFactory.loadNativeLibrary()");
                 loadNativeLibrary();
                 Trace.traceEnd(Trace.TRACE_TAG_WEBVIEW);
@@ -137,15 +187,10 @@
     private static Class<WebViewFactoryProvider> getFactoryClass() throws ClassNotFoundException {
         Application initialApplication = AppGlobals.getInitialApplication();
         try {
-            // First fetch the package info so we can log the webview package version.
-            String packageName = getWebViewPackageName();
-            sPackageInfo = initialApplication.getPackageManager().getPackageInfo(packageName, 0);
-            Log.i(LOGTAG, "Loading " + packageName + " version " + sPackageInfo.versionName +
-                          " (code " + sPackageInfo.versionCode + ")");
-
             // Construct a package context to load the Java code into the current app.
-            Context webViewContext = initialApplication.createPackageContext(packageName,
-                    Context.CONTEXT_INCLUDE_CODE | Context.CONTEXT_IGNORE_SECURITY);
+            Context webViewContext = initialApplication.createPackageContext(
+                sPackageInfo.packageName,
+                Context.CONTEXT_INCLUDE_CODE | Context.CONTEXT_IGNORE_SECURITY);
             initialApplication.getAssets().addAssetPath(
                     webViewContext.getApplicationInfo().sourceDir);
             ClassLoader clazzLoader = webViewContext.getClassLoader();
@@ -272,10 +317,8 @@
 
     private static String[] getWebViewNativeLibraryPaths()
             throws PackageManager.NameNotFoundException {
-        final String NATIVE_LIB_FILE_NAME = "libwebviewchromium.so";
-
-        PackageManager pm = AppGlobals.getInitialApplication().getPackageManager();
-        ApplicationInfo ai = pm.getApplicationInfo(getWebViewPackageName(), 0);
+        ApplicationInfo ai = getWebViewApplicationInfo();
+        final String NATIVE_LIB_FILE_NAME = getWebViewLibrary(ai);
 
         String path32;
         String path64;
diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java
index e7b6238..c9d9a8c 100644
--- a/core/java/android/widget/AbsListView.java
+++ b/core/java/android/widget/AbsListView.java
@@ -3116,6 +3116,25 @@
         }
     }
 
+    private boolean performStylusButtonPressAction(MotionEvent ev) {
+        if (ev.getToolType(0) == MotionEvent.TOOL_TYPE_STYLUS
+                && ev.isButtonPressed(MotionEvent.BUTTON_SECONDARY)
+                && mChoiceMode == CHOICE_MODE_MULTIPLE_MODAL && mChoiceActionMode == null) {
+            final View child = getChildAt(mMotionPosition - mFirstPosition);
+            if (child != null) {
+                final int longPressPosition = mMotionPosition;
+                final long longPressId = mAdapter.getItemId(mMotionPosition);
+                if (performLongPress(child, longPressPosition, longPressId)) {
+                    mTouchMode = TOUCH_MODE_REST;
+                    setPressed(false);
+                    child.setPressed(false);
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
     boolean performLongPress(final View child,
             final int longPressPosition, final long longPressId) {
         // CHOICE_MODE_MULTIPLE_MODAL takes over long press.
@@ -3757,8 +3776,8 @@
         }
 
         if (mTouchMode == TOUCH_MODE_DOWN && mMotionPosition != INVALID_POSITION
-                && performButtonActionOnTouchDown(ev)) {
-            removeCallbacks(mPendingCheckForTap);
+                && (performButtonActionOnTouchDown(ev) || performStylusButtonPressAction(ev))) {
+                removeCallbacks(mPendingCheckForTap);
         }
     }
 
@@ -3800,6 +3819,11 @@
                     mTouchMode = TOUCH_MODE_DONE_WAITING;
                     updateSelectorState();
                 } else if (motionView != null) {
+                    if (performStylusButtonPressAction(ev)) {
+                        removeCallbacks(mPendingCheckForTap);
+                        removeCallbacks(mPendingCheckForLongPress);
+                    }
+
                     // Still within bounds, update the hotspot.
                     final float[] point = mTmpPoint;
                     point[0] = x;
diff --git a/core/java/android/widget/AbsSeekBar.java b/core/java/android/widget/AbsSeekBar.java
index d6f9f78..ff74c60 100644
--- a/core/java/android/widget/AbsSeekBar.java
+++ b/core/java/android/widget/AbsSeekBar.java
@@ -703,9 +703,9 @@
                     // fallthrough
                 case KeyEvent.KEYCODE_DPAD_RIGHT:
                     increment = isLayoutRtl() ? -increment : increment;
-                    int progress = getProgress() + increment;
-                    if (progress > 0 && progress < getMax()) {
-                        setProgress(progress, true);
+
+                    // Let progress bar handle clamping values.
+                    if (setProgress(getProgress() + increment, true)) {
                         onKeyChange();
                         return true;
                     }
@@ -729,10 +729,10 @@
         if (isEnabled()) {
             final int progress = getProgress();
             if (progress > 0) {
-                info.addAction(AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD);
+                info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_BACKWARD);
             }
             if (progress < getMax()) {
-                info.addAction(AccessibilityNodeInfo.ACTION_SCROLL_FORWARD);
+                info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_FORWARD);
             }
         }
     }
@@ -743,29 +743,26 @@
         if (super.performAccessibilityActionInternal(action, arguments)) {
             return true;
         }
+
         if (!isEnabled()) {
             return false;
         }
-        final int progress = getProgress();
-        final int increment = Math.max(1, Math.round((float) getMax() / 5));
-        switch (action) {
-            case AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD: {
-                if (progress <= 0) {
-                    return false;
-                }
-                setProgress(progress - increment, true);
+
+        if (action == AccessibilityNodeInfo.ACTION_SCROLL_FORWARD
+                || action == AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD) {
+            int increment = Math.max(1, Math.round((float) getMax() / 5));
+            if (action == AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD) {
+                increment = -increment;
+            }
+
+            // Let progress bar handle clamping values.
+            if (setProgress(getProgress() + increment, true)) {
                 onKeyChange();
                 return true;
             }
-            case AccessibilityNodeInfo.ACTION_SCROLL_FORWARD: {
-                if (progress >= getMax()) {
-                    return false;
-                }
-                setProgress(progress + increment, true);
-                onKeyChange();
-                return true;
-            }
+            return false;
         }
+
         return false;
     }
 
diff --git a/core/java/android/widget/CalendarViewMaterialDelegate.java b/core/java/android/widget/CalendarViewMaterialDelegate.java
index 7bce756..0ed75d5 100644
--- a/core/java/android/widget/CalendarViewMaterialDelegate.java
+++ b/core/java/android/widget/CalendarViewMaterialDelegate.java
@@ -19,6 +19,7 @@
 import android.annotation.StyleRes;
 import android.content.Context;
 import android.util.AttributeSet;
+import android.widget.DayPickerView.OnDaySelectedListener;
 
 import java.util.Calendar;
 
@@ -109,8 +110,7 @@
         mOnDateChangeListener = listener;
     }
 
-    private final DayPickerView.OnDaySelectedListener mOnDaySelectedListener =
-            new DayPickerView.OnDaySelectedListener() {
+    private final OnDaySelectedListener mOnDaySelectedListener = new OnDaySelectedListener() {
         @Override
         public void onDaySelected(DayPickerView view, Calendar day) {
             if (mOnDateChangeListener != null) {
diff --git a/core/java/android/widget/DatePickerCalendarDelegate.java b/core/java/android/widget/DatePickerCalendarDelegate.java
index 06a5bd2..d38a225 100755
--- a/core/java/android/widget/DatePickerCalendarDelegate.java
+++ b/core/java/android/widget/DatePickerCalendarDelegate.java
@@ -34,8 +34,6 @@
 import android.view.View.OnClickListener;
 import android.view.ViewGroup;
 import android.view.accessibility.AccessibilityEvent;
-import android.view.animation.AlphaAnimation;
-import android.view.animation.Animation;
 import android.widget.DayPickerView.OnDaySelectedListener;
 import android.widget.YearPickerView.OnYearSelectedListener;
 
@@ -549,7 +547,7 @@
         final int listPosition = ss.getListPosition();
         if (listPosition != -1) {
             if (currentView == VIEW_MONTH_DAY) {
-                mDayPickerView.setCurrentItem(listPosition);
+                mDayPickerView.setPosition(listPosition);
             } else if (currentView == VIEW_YEAR) {
                 final int listPositionOffset = ss.getListPositionOffset();
                 mYearPickerView.setSelectionFromTop(listPosition, listPositionOffset);
diff --git a/core/java/android/widget/DayPickerAdapter.java b/core/java/android/widget/DayPickerPagerAdapter.java
similarity index 92%
rename from core/java/android/widget/DayPickerAdapter.java
rename to core/java/android/widget/DayPickerPagerAdapter.java
index 9a4b6f5..478fa00 100644
--- a/core/java/android/widget/DayPickerAdapter.java
+++ b/core/java/android/widget/DayPickerPagerAdapter.java
@@ -36,7 +36,7 @@
 /**
  * An adapter for a list of {@link android.widget.SimpleMonthView} items.
  */
-class DayPickerAdapter extends PagerAdapter {
+class DayPickerPagerAdapter extends PagerAdapter {
     private static final int MONTHS_IN_YEAR = 12;
 
     private final Calendar mMinDate = Calendar.getInstance();
@@ -63,7 +63,7 @@
     private int mCount;
     private int mFirstDayOfWeek;
 
-    public DayPickerAdapter(@NonNull Context context, @LayoutRes int layoutResId,
+    public DayPickerPagerAdapter(@NonNull Context context, @LayoutRes int layoutResId,
             @IdRes int calendarViewId) {
         mInflater = LayoutInflater.from(context);
         mLayoutResId = layoutResId;
@@ -200,7 +200,8 @@
 
         final int yearOffset = (day.get(Calendar.YEAR) - mMinDate.get(Calendar.YEAR));
         final int monthOffset = (day.get(Calendar.MONTH) - mMinDate.get(Calendar.MONTH));
-        return yearOffset * MONTHS_IN_YEAR + monthOffset;
+        final int position = yearOffset * MONTHS_IN_YEAR + monthOffset;
+        return position;
     }
 
     @Override
@@ -253,8 +254,6 @@
 
         v.setMonthParams(selectedDay, month, year, mFirstDayOfWeek,
                 enabledDayRangeStart, enabledDayRangeEnd);
-        v.setPrevEnabled(position > 0);
-        v.setNextEnabled(position < mCount - 1);
 
         final ViewHolder holder = new ViewHolder(position, itemView, v);
         mItems.put(position, holder);
@@ -298,17 +297,10 @@
                 setSelectedDay(day);
 
                 if (mOnDaySelectedListener != null) {
-                    mOnDaySelectedListener.onDaySelected(DayPickerAdapter.this, day);
+                    mOnDaySelectedListener.onDaySelected(DayPickerPagerAdapter.this, day);
                 }
             }
         }
-
-        @Override
-        public void onNavigationClick(SimpleMonthView view, int direction, boolean animate) {
-            if (mOnDaySelectedListener != null) {
-                mOnDaySelectedListener.onNavigationClick(DayPickerAdapter.this, direction, animate);
-            }
-        }
     };
 
     private static class ViewHolder {
@@ -324,7 +316,6 @@
     }
 
     public interface OnDaySelectedListener {
-        public void onDaySelected(DayPickerAdapter view, Calendar day);
-        public void onNavigationClick(DayPickerAdapter view, int direction, boolean animate);
+        public void onDaySelected(DayPickerPagerAdapter view, Calendar day);
     }
 }
diff --git a/core/java/android/widget/DayPickerView.java b/core/java/android/widget/DayPickerView.java
index 0e0b2d3..c6b4d7e 100644
--- a/core/java/android/widget/DayPickerView.java
+++ b/core/java/android/widget/DayPickerView.java
@@ -1,5 +1,5 @@
 /*
- * 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.
@@ -16,37 +16,44 @@
 
 package android.widget;
 
-import com.android.internal.widget.ViewPager;
 import com.android.internal.R;
+import com.android.internal.widget.ViewPager;
+import com.android.internal.widget.ViewPager.OnPageChangeListener;
 
+import android.annotation.Nullable;
 import android.content.Context;
 import android.content.res.ColorStateList;
 import android.content.res.TypedArray;
-import android.graphics.drawable.Drawable;
 import android.util.AttributeSet;
 import android.util.MathUtils;
+import android.view.LayoutInflater;
 import android.view.View;
+import android.view.ViewGroup;
+import android.view.accessibility.AccessibilityManager;
 
-import java.util.ArrayList;
 import java.util.Calendar;
 import java.util.Locale;
 
 import libcore.icu.LocaleData;
 
-/**
- * This displays a list of months in a calendar format with selectable days.
- */
-class DayPickerView extends ViewPager {
+class DayPickerView extends ViewGroup {
+    private static final int DEFAULT_LAYOUT = R.layout.day_picker_content_material;
     private static final int DEFAULT_START_YEAR = 1900;
     private static final int DEFAULT_END_YEAR = 2100;
 
+    private static final int[] ATTRS_TEXT_COLOR = new int[] { R.attr.textColor };
+
     private final Calendar mSelectedDay = Calendar.getInstance();
     private final Calendar mMinDate = Calendar.getInstance();
     private final Calendar mMaxDate = Calendar.getInstance();
 
-    private final ArrayList<View> mMatchParentChildren = new ArrayList<>(1);
+    private final AccessibilityManager mAccessibilityManager;
 
-    private final DayPickerAdapter mAdapter;
+    private final ViewPager mViewPager;
+    private final ImageButton mPrevButton;
+    private final ImageButton mNextButton;
+
+    private final DayPickerPagerAdapter mAdapter;
 
     /** Temporary calendar used for date calculations. */
     private Calendar mTempCalendar;
@@ -57,17 +64,21 @@
         this(context, null);
     }
 
-    public DayPickerView(Context context, AttributeSet attrs) {
+    public DayPickerView(Context context, @Nullable AttributeSet attrs) {
         this(context, attrs, R.attr.calendarViewStyle);
     }
 
-    public DayPickerView(Context context, AttributeSet attrs, int defStyleAttr) {
+    public DayPickerView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
         this(context, attrs, defStyleAttr, 0);
     }
 
-    public DayPickerView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
+    public DayPickerView(Context context, @Nullable AttributeSet attrs, int defStyleAttr,
+            int defStyleRes) {
         super(context, attrs, defStyleAttr, defStyleRes);
 
+        mAccessibilityManager = (AccessibilityManager) context.getSystemService(
+                Context.ACCESSIBILITY_SERVICE);
+
         final TypedArray a = context.obtainStyledAttributes(attrs,
                 R.styleable.CalendarView, defStyleAttr, defStyleRes);
 
@@ -93,14 +104,44 @@
         a.recycle();
 
         // Set up adapter.
-        mAdapter = new DayPickerAdapter(context,
+        mAdapter = new DayPickerPagerAdapter(context,
                 R.layout.date_picker_month_item_material, R.id.month_view);
         mAdapter.setMonthTextAppearance(monthTextAppearanceResId);
         mAdapter.setDayOfWeekTextAppearance(dayOfWeekTextAppearanceResId);
         mAdapter.setDayTextAppearance(dayTextAppearanceResId);
         mAdapter.setDaySelectorColor(daySelectorColor);
 
-        setAdapter(mAdapter);
+        final LayoutInflater inflater = LayoutInflater.from(context);
+        final ViewGroup content = (ViewGroup) inflater.inflate(DEFAULT_LAYOUT, this, false);
+
+        // Transfer all children from content to here.
+        while (content.getChildCount() > 0) {
+            final View child = content.getChildAt(0);
+            content.removeViewAt(0);
+            addView(child);
+        }
+
+        mPrevButton = (ImageButton) findViewById(R.id.prev);
+        mPrevButton.setOnClickListener(mOnClickListener);
+
+        mNextButton = (ImageButton) findViewById(R.id.next);
+        mNextButton.setOnClickListener(mOnClickListener);
+
+        mViewPager = (ViewPager) findViewById(R.id.day_picker_view_pager);
+        mViewPager.setAdapter(mAdapter);
+        mViewPager.setOnPageChangeListener(mOnPageChangedListener);
+
+        // Proxy the month text color into the previous and next buttons.
+        if (monthTextAppearanceResId != 0) {
+            final TypedArray ta = mContext.obtainStyledAttributes(null,
+                    ATTRS_TEXT_COLOR, 0, monthTextAppearanceResId);
+            final ColorStateList monthColor = ta.getColorStateList(0);
+            if (monthColor != null) {
+                mPrevButton.setImageTintList(monthColor);
+                mNextButton.setImageTintList(monthColor);
+            }
+            ta.recycle();
+        }
 
         // Set up min and max dates.
         final Calendar tempDate = Calendar.getInstance();
@@ -127,109 +168,68 @@
         setDate(setDateMillis, false);
 
         // Proxy selection callbacks to our own listener.
-        mAdapter.setOnDaySelectedListener(new DayPickerAdapter.OnDaySelectedListener() {
+        mAdapter.setOnDaySelectedListener(new DayPickerPagerAdapter.OnDaySelectedListener() {
             @Override
-            public void onDaySelected(DayPickerAdapter adapter, Calendar day) {
+            public void onDaySelected(DayPickerPagerAdapter adapter, Calendar day) {
                 if (mOnDaySelectedListener != null) {
                     mOnDaySelectedListener.onDaySelected(DayPickerView.this, day);
                 }
             }
-
-            @Override
-            public void onNavigationClick(DayPickerAdapter view, int direction, boolean animate) {
-                // ViewPager clamps input values, so we don't need to worry
-                // about passing invalid indices.
-                final int nextItem = getCurrentItem() + direction;
-                setCurrentItem(nextItem, animate);
-            }
         });
     }
 
     @Override
     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
-        populate();
+        final ViewPager viewPager = mViewPager;
+        measureChild(viewPager, widthMeasureSpec, heightMeasureSpec);
 
-        // Everything below is mostly copied from FrameLayout.
-        int count = getChildCount();
+        final int measuredWidthAndState = viewPager.getMeasuredWidthAndState();
+        final int measuredHeightAndState = viewPager.getMeasuredHeightAndState();
+        setMeasuredDimension(measuredWidthAndState, measuredHeightAndState);
 
-        final boolean measureMatchParentChildren =
-                MeasureSpec.getMode(widthMeasureSpec) != MeasureSpec.EXACTLY ||
-                        MeasureSpec.getMode(heightMeasureSpec) != MeasureSpec.EXACTLY;
+        final int pagerWidth = viewPager.getMeasuredWidth();
+        final int pagerHeight = viewPager.getMeasuredHeight();
+        final int buttonWidthSpec = MeasureSpec.makeMeasureSpec(pagerWidth, MeasureSpec.AT_MOST);
+        final int buttonHeightSpec = MeasureSpec.makeMeasureSpec(pagerHeight, MeasureSpec.AT_MOST);
+        mPrevButton.measure(buttonWidthSpec, buttonHeightSpec);
+        mNextButton.measure(buttonWidthSpec, buttonHeightSpec);
+    }
 
-        int maxHeight = 0;
-        int maxWidth = 0;
-        int childState = 0;
+    @Override
+    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+        final ImageButton leftButton = mPrevButton;
+        final ImageButton rightButton = mNextButton;
 
-        for (int i = 0; i < count; i++) {
-            final View child = getChildAt(i);
-            if (child.getVisibility() != GONE) {
-                measureChild(child, widthMeasureSpec, heightMeasureSpec);
-                final LayoutParams lp = (LayoutParams) child.getLayoutParams();
-                maxWidth = Math.max(maxWidth, child.getMeasuredWidth());
-                maxHeight = Math.max(maxHeight, child.getMeasuredHeight());
-                childState = combineMeasuredStates(childState, child.getMeasuredState());
-                if (measureMatchParentChildren) {
-                    if (lp.width == LayoutParams.MATCH_PARENT ||
-                            lp.height == LayoutParams.MATCH_PARENT) {
-                        mMatchParentChildren.add(child);
-                    }
-                }
-            }
+        final int width = right - left;
+        final int height = bottom - top;
+        mViewPager.layout(0, 0, width, height);
+
+        if (mViewPager.getChildCount() < 1) {
+            leftButton.setVisibility(View.INVISIBLE);
+            rightButton.setVisibility(View.INVISIBLE);
+            return;
         }
 
-        // Account for padding too
-        maxWidth += getPaddingLeft() + getPaddingRight();
-        maxHeight += getPaddingTop() + getPaddingBottom();
+        final SimpleMonthView monthView = (SimpleMonthView) mViewPager.getChildAt(0);
+        final int monthHeight = monthView.getMonthHeight();
+        final int cellWidth = monthView.getCellWidth();
 
-        // Check against our minimum height and width
-        maxHeight = Math.max(maxHeight, getSuggestedMinimumHeight());
-        maxWidth = Math.max(maxWidth, getSuggestedMinimumWidth());
+        // Vertically center the previous/next buttons within the month
+        // header, horizontally center within the day cell.
+        final int leftDW = leftButton.getMeasuredWidth();
+        final int leftDH = leftButton.getMeasuredHeight();
+        final int leftIconTop = monthView.getPaddingTop() + (monthHeight - leftDH) / 2;
+        final int leftIconLeft = monthView.getPaddingLeft() + (cellWidth - leftDW) / 2;
+        leftButton.layout(leftIconLeft, leftIconTop, leftIconLeft + leftDW, leftIconTop + leftDH);
+        leftButton.setVisibility(View.VISIBLE);
 
-        // Check against our foreground's minimum height and width
-        final Drawable drawable = getForeground();
-        if (drawable != null) {
-            maxHeight = Math.max(maxHeight, drawable.getMinimumHeight());
-            maxWidth = Math.max(maxWidth, drawable.getMinimumWidth());
-        }
-
-        setMeasuredDimension(resolveSizeAndState(maxWidth, widthMeasureSpec, childState),
-                resolveSizeAndState(maxHeight, heightMeasureSpec,
-                        childState << MEASURED_HEIGHT_STATE_SHIFT));
-
-        count = mMatchParentChildren.size();
-        if (count > 1) {
-            for (int i = 0; i < count; i++) {
-                final View child = mMatchParentChildren.get(i);
-
-                final LayoutParams lp = (LayoutParams) child.getLayoutParams();
-                final int childWidthMeasureSpec;
-                final int childHeightMeasureSpec;
-
-                if (lp.width == LayoutParams.MATCH_PARENT) {
-                    childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(
-                            getMeasuredWidth() - getPaddingLeft() - getPaddingRight(),
-                            MeasureSpec.EXACTLY);
-                } else {
-                    childWidthMeasureSpec = getChildMeasureSpec(widthMeasureSpec,
-                            getPaddingLeft() + getPaddingRight(),
-                            lp.width);
-                }
-
-                if (lp.height == LayoutParams.MATCH_PARENT) {
-                    childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(
-                            getMeasuredHeight() - getPaddingTop() - getPaddingBottom(),
-                            MeasureSpec.EXACTLY);
-                } else {
-                    childHeightMeasureSpec = getChildMeasureSpec(heightMeasureSpec,
-                            getPaddingTop() + getPaddingBottom(),
-                            lp.height);
-                }
-
-                child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
-            }
-        }
-
-        mMatchParentChildren.clear();
+        final int rightDW = rightButton.getMeasuredWidth();
+        final int rightDH = rightButton.getMeasuredHeight();
+        final int rightIconTop = monthView.getPaddingTop() + (monthHeight - rightDH) / 2;
+        final int rightIconRight = width - monthView.getPaddingRight() - (cellWidth - rightDW) / 2;
+        rightButton.layout(rightIconRight - rightDW, rightIconTop,
+                rightIconRight, rightIconTop + rightDH);
+        rightButton.setVisibility(View.VISIBLE);
     }
 
     public void setDayOfWeekTextAppearance(int resId) {
@@ -284,8 +284,8 @@
         }
 
         final int position = getPositionFromDay(timeInMillis);
-        if (position != getCurrentItem()) {
-            setCurrentItem(position, animate);
+        if (position != mViewPager.getCurrentItem()) {
+            mViewPager.setCurrentItem(position, animate);
         }
 
         mTempCalendar.setTimeInMillis(timeInMillis);
@@ -365,10 +365,57 @@
      * Gets the position of the view that is most prominently displayed within the list view.
      */
     public int getMostVisiblePosition() {
-        return getCurrentItem();
+        return mViewPager.getCurrentItem();
     }
 
+    public void setPosition(int position) {
+        mViewPager.setCurrentItem(position, false);
+    }
+
+    private final OnPageChangeListener mOnPageChangedListener = new OnPageChangeListener() {
+        @Override
+        public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
+            final float alpha = Math.abs(0.5f - positionOffset) * 2.0f;
+            mPrevButton.setAlpha(alpha);
+            mNextButton.setAlpha(alpha);
+        }
+
+        @Override
+        public void onPageScrollStateChanged(int state) {}
+
+        @Override
+        public void onPageSelected(int position) {
+            mPrevButton.setVisibility(
+                    position > 0 ? View.VISIBLE : View.INVISIBLE);
+            mNextButton.setVisibility(
+                    position < (mAdapter.getCount() - 1) ? View.VISIBLE : View.INVISIBLE);
+        }
+    };
+
+    private final OnClickListener mOnClickListener = new OnClickListener() {
+        @Override
+        public void onClick(View v) {
+            final int direction;
+            if (v == mPrevButton) {
+                direction = -1;
+            } else if (v == mNextButton) {
+                direction = 1;
+            } else {
+                return;
+            }
+
+            // Animation is expensive for accessibility services since it sends
+            // lots of scroll and content change events.
+            final boolean animate = !mAccessibilityManager.isEnabled();
+
+            // ViewPager clamps input values, so we don't need to worry
+            // about passing invalid indices.
+            final int nextItem = mViewPager.getCurrentItem() + direction;
+            mViewPager.setCurrentItem(nextItem, animate);
+        }
+    };
+
     public interface OnDaySelectedListener {
-        public void onDaySelected(DayPickerView view, Calendar day);
+        void onDaySelected(DayPickerView view, Calendar day);
     }
 }
diff --git a/core/java/android/widget/DayPickerViewPager.java b/core/java/android/widget/DayPickerViewPager.java
new file mode 100644
index 0000000..bb6e3a4
--- /dev/null
+++ b/core/java/android/widget/DayPickerViewPager.java
@@ -0,0 +1,137 @@
+/*
+ * 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.widget;
+
+import com.android.internal.widget.ViewPager;
+
+import android.content.Context;
+import android.graphics.drawable.Drawable;
+import android.util.AttributeSet;
+import android.view.View;
+
+import java.util.ArrayList;
+
+/**
+ * This displays a list of months in a calendar format with selectable days.
+ */
+class DayPickerViewPager extends ViewPager {
+    private final ArrayList<View> mMatchParentChildren = new ArrayList<>(1);
+
+    public DayPickerViewPager(Context context) {
+        this(context, null);
+    }
+
+    public DayPickerViewPager(Context context, AttributeSet attrs) {
+        this(context, attrs, 0);
+    }
+
+    public DayPickerViewPager(Context context, AttributeSet attrs, int defStyleAttr) {
+        this(context, attrs, defStyleAttr, 0);
+    }
+
+    public DayPickerViewPager(Context context, AttributeSet attrs, int defStyleAttr,
+            int defStyleRes) {
+        super(context, attrs, defStyleAttr, defStyleRes);
+    }
+
+    @Override
+    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+        populate();
+
+        // Everything below is mostly copied from FrameLayout.
+        int count = getChildCount();
+
+        final boolean measureMatchParentChildren =
+                MeasureSpec.getMode(widthMeasureSpec) != MeasureSpec.EXACTLY ||
+                        MeasureSpec.getMode(heightMeasureSpec) != MeasureSpec.EXACTLY;
+
+        int maxHeight = 0;
+        int maxWidth = 0;
+        int childState = 0;
+
+        for (int i = 0; i < count; i++) {
+            final View child = getChildAt(i);
+            if (child.getVisibility() != GONE) {
+                measureChild(child, widthMeasureSpec, heightMeasureSpec);
+                final LayoutParams lp = (LayoutParams) child.getLayoutParams();
+                maxWidth = Math.max(maxWidth, child.getMeasuredWidth());
+                maxHeight = Math.max(maxHeight, child.getMeasuredHeight());
+                childState = combineMeasuredStates(childState, child.getMeasuredState());
+                if (measureMatchParentChildren) {
+                    if (lp.width == LayoutParams.MATCH_PARENT ||
+                            lp.height == LayoutParams.MATCH_PARENT) {
+                        mMatchParentChildren.add(child);
+                    }
+                }
+            }
+        }
+
+        // Account for padding too
+        maxWidth += getPaddingLeft() + getPaddingRight();
+        maxHeight += getPaddingTop() + getPaddingBottom();
+
+        // Check against our minimum height and width
+        maxHeight = Math.max(maxHeight, getSuggestedMinimumHeight());
+        maxWidth = Math.max(maxWidth, getSuggestedMinimumWidth());
+
+        // Check against our foreground's minimum height and width
+        final Drawable drawable = getForeground();
+        if (drawable != null) {
+            maxHeight = Math.max(maxHeight, drawable.getMinimumHeight());
+            maxWidth = Math.max(maxWidth, drawable.getMinimumWidth());
+        }
+
+        setMeasuredDimension(resolveSizeAndState(maxWidth, widthMeasureSpec, childState),
+                resolveSizeAndState(maxHeight, heightMeasureSpec,
+                        childState << MEASURED_HEIGHT_STATE_SHIFT));
+
+        count = mMatchParentChildren.size();
+        if (count > 1) {
+            for (int i = 0; i < count; i++) {
+                final View child = mMatchParentChildren.get(i);
+
+                final LayoutParams lp = (LayoutParams) child.getLayoutParams();
+                final int childWidthMeasureSpec;
+                final int childHeightMeasureSpec;
+
+                if (lp.width == LayoutParams.MATCH_PARENT) {
+                    childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(
+                            getMeasuredWidth() - getPaddingLeft() - getPaddingRight(),
+                            MeasureSpec.EXACTLY);
+                } else {
+                    childWidthMeasureSpec = getChildMeasureSpec(widthMeasureSpec,
+                            getPaddingLeft() + getPaddingRight(),
+                            lp.width);
+                }
+
+                if (lp.height == LayoutParams.MATCH_PARENT) {
+                    childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(
+                            getMeasuredHeight() - getPaddingTop() - getPaddingBottom(),
+                            MeasureSpec.EXACTLY);
+                } else {
+                    childHeightMeasureSpec = getChildMeasureSpec(heightMeasureSpec,
+                            getPaddingTop() + getPaddingBottom(),
+                            lp.height);
+                }
+
+                child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
+            }
+        }
+
+        mMatchParentChildren.clear();
+    }
+}
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java
index 1be05f3..955ad06 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -17,6 +17,7 @@
 package android.widget;
 
 import android.R;
+import android.annotation.Nullable;
 import android.app.PendingIntent;
 import android.app.PendingIntent.CanceledException;
 import android.content.ClipData;
@@ -1008,14 +1009,14 @@
                 stopSelectionActionMode();
             } else {
                 stopSelectionActionMode();
-                startSelectionActionModeWithSelection();
+                startSelectionActionModeWithSelectionAndStartDrag();
             }
             handled = true;
         }
 
         // Start a new selection
         if (!handled) {
-            handled = startSelectionActionModeWithSelection();
+            handled = startSelectionActionModeWithSelectionAndStartDrag();
         }
 
         return handled;
@@ -1281,74 +1282,79 @@
                 EXTRACT_UNKNOWN, outText);
     }
 
-    private boolean extractTextInternal(ExtractedTextRequest request,
+    private boolean extractTextInternal(@Nullable ExtractedTextRequest request,
             int partialStartOffset, int partialEndOffset, int delta,
-            ExtractedText outText) {
-        final CharSequence content = mTextView.getText();
-        if (content != null) {
-            if (partialStartOffset != EXTRACT_NOTHING) {
-                final int N = content.length();
-                if (partialStartOffset < 0) {
-                    outText.partialStartOffset = outText.partialEndOffset = -1;
-                    partialStartOffset = 0;
-                    partialEndOffset = N;
-                } else {
-                    // Now use the delta to determine the actual amount of text
-                    // we need.
-                    partialEndOffset += delta;
-                    // Adjust offsets to ensure we contain full spans.
-                    if (content instanceof Spanned) {
-                        Spanned spanned = (Spanned)content;
-                        Object[] spans = spanned.getSpans(partialStartOffset,
-                                partialEndOffset, ParcelableSpan.class);
-                        int i = spans.length;
-                        while (i > 0) {
-                            i--;
-                            int j = spanned.getSpanStart(spans[i]);
-                            if (j < partialStartOffset) partialStartOffset = j;
-                            j = spanned.getSpanEnd(spans[i]);
-                            if (j > partialEndOffset) partialEndOffset = j;
-                        }
-                    }
-                    outText.partialStartOffset = partialStartOffset;
-                    outText.partialEndOffset = partialEndOffset - delta;
-
-                    if (partialStartOffset > N) {
-                        partialStartOffset = N;
-                    } else if (partialStartOffset < 0) {
-                        partialStartOffset = 0;
-                    }
-                    if (partialEndOffset > N) {
-                        partialEndOffset = N;
-                    } else if (partialEndOffset < 0) {
-                        partialEndOffset = 0;
-                    }
-                }
-                if ((request.flags&InputConnection.GET_TEXT_WITH_STYLES) != 0) {
-                    outText.text = content.subSequence(partialStartOffset,
-                            partialEndOffset);
-                } else {
-                    outText.text = TextUtils.substring(content, partialStartOffset,
-                            partialEndOffset);
-                }
-            } else {
-                outText.partialStartOffset = 0;
-                outText.partialEndOffset = 0;
-                outText.text = "";
-            }
-            outText.flags = 0;
-            if (MetaKeyKeyListener.getMetaState(content, MetaKeyKeyListener.META_SELECTING) != 0) {
-                outText.flags |= ExtractedText.FLAG_SELECTING;
-            }
-            if (mTextView.isSingleLine()) {
-                outText.flags |= ExtractedText.FLAG_SINGLE_LINE;
-            }
-            outText.startOffset = 0;
-            outText.selectionStart = mTextView.getSelectionStart();
-            outText.selectionEnd = mTextView.getSelectionEnd();
-            return true;
+            @Nullable ExtractedText outText) {
+        if (request == null || outText == null) {
+            return false;
         }
-        return false;
+
+        final CharSequence content = mTextView.getText();
+        if (content == null) {
+            return false;
+        }
+
+        if (partialStartOffset != EXTRACT_NOTHING) {
+            final int N = content.length();
+            if (partialStartOffset < 0) {
+                outText.partialStartOffset = outText.partialEndOffset = -1;
+                partialStartOffset = 0;
+                partialEndOffset = N;
+            } else {
+                // Now use the delta to determine the actual amount of text
+                // we need.
+                partialEndOffset += delta;
+                // Adjust offsets to ensure we contain full spans.
+                if (content instanceof Spanned) {
+                    Spanned spanned = (Spanned)content;
+                    Object[] spans = spanned.getSpans(partialStartOffset,
+                            partialEndOffset, ParcelableSpan.class);
+                    int i = spans.length;
+                    while (i > 0) {
+                        i--;
+                        int j = spanned.getSpanStart(spans[i]);
+                        if (j < partialStartOffset) partialStartOffset = j;
+                        j = spanned.getSpanEnd(spans[i]);
+                        if (j > partialEndOffset) partialEndOffset = j;
+                    }
+                }
+                outText.partialStartOffset = partialStartOffset;
+                outText.partialEndOffset = partialEndOffset - delta;
+
+                if (partialStartOffset > N) {
+                    partialStartOffset = N;
+                } else if (partialStartOffset < 0) {
+                    partialStartOffset = 0;
+                }
+                if (partialEndOffset > N) {
+                    partialEndOffset = N;
+                } else if (partialEndOffset < 0) {
+                    partialEndOffset = 0;
+                }
+            }
+            if ((request.flags&InputConnection.GET_TEXT_WITH_STYLES) != 0) {
+                outText.text = content.subSequence(partialStartOffset,
+                        partialEndOffset);
+            } else {
+                outText.text = TextUtils.substring(content, partialStartOffset,
+                        partialEndOffset);
+            }
+        } else {
+            outText.partialStartOffset = 0;
+            outText.partialEndOffset = 0;
+            outText.text = "";
+        }
+        outText.flags = 0;
+        if (MetaKeyKeyListener.getMetaState(content, MetaKeyKeyListener.META_SELECTING) != 0) {
+            outText.flags |= ExtractedText.FLAG_SELECTING;
+        }
+        if (mTextView.isSingleLine()) {
+            outText.flags |= ExtractedText.FLAG_SINGLE_LINE;
+        }
+        outText.startOffset = 0;
+        outText.selectionStart = mTextView.getSelectionStart();
+        outText.selectionEnd = mTextView.getSelectionEnd();
+        return true;
     }
 
     boolean reportExtractedText() {
@@ -1680,9 +1686,34 @@
     }
 
     /**
+     * Starts a Selection Action Mode with the current selection and enters drag mode. This should
+     * be used whenever the mode is started from a touch event.
+     *
+     * @return true if the selection mode was actually started.
+     */
+    private boolean startSelectionActionModeWithSelectionAndStartDrag() {
+        boolean selectionStarted = startSelectionActionModeWithSelectionInternal();
+        if (selectionStarted) {
+            getSelectionController().enterDrag();
+        }
+        return selectionStarted;
+    }
+
+    /**
+     * Starts a Selection Action Mode with the current selection and ensures the selection handles
+     * are shown. This should be used when the mode is started from a non-touch event.
+     *
      * @return true if the selection mode was actually started.
      */
     boolean startSelectionActionModeWithSelection() {
+        boolean selectionStarted = startSelectionActionModeWithSelectionInternal();
+        if (selectionStarted) {
+            getSelectionController().show();
+        }
+        return selectionStarted;
+    }
+
+    private boolean startSelectionActionModeWithSelectionInternal() {
         if (mSelectionActionMode != null) {
             // Selection action mode is already started
             mSelectionActionMode.invalidate();
@@ -1721,10 +1752,6 @@
                 imm.showSoftInput(mTextView, 0, null);
             }
         }
-
-        if (selectionStarted) {
-            getSelectionController().enterDrag();
-        }
         return selectionStarted;
     }
 
@@ -3093,6 +3120,7 @@
             if (item.getIntent() != null
                     && item.getIntent().getAction().equals(Intent.ACTION_PROCESS_TEXT)) {
                 item.getIntent().putExtra(Intent.EXTRA_PROCESS_TEXT, mTextView.getSelectedText());
+                mPreserveDetachedSelection = true;
                 mTextView.startActivityForResult(
                         item.getIntent(), TextView.PROCESS_TEXT_REQUEST_CODE);
                 return true;
@@ -4231,7 +4259,7 @@
                             boolean stayedInArea = distanceSquared < doubleTapSlop * doubleTapSlop;
 
                             if (stayedInArea && isPositionOnText(x, y)) {
-                                startSelectionActionModeWithSelection();
+                                startSelectionActionModeWithSelectionAndStartDrag();
                                 mDiscardNextActionUp = true;
                             }
                         }
diff --git a/core/java/android/widget/FastScroller.java b/core/java/android/widget/FastScroller.java
index 4b5407a..552b274 100644
--- a/core/java/android/widget/FastScroller.java
+++ b/core/java/android/widget/FastScroller.java
@@ -662,7 +662,8 @@
 
         final int adjMaxWidth = maxWidth - marginLeft - marginRight;
         final int widthMeasureSpec = MeasureSpec.makeMeasureSpec(adjMaxWidth, MeasureSpec.AT_MOST);
-        final int heightMeasureSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
+        final int heightMeasureSpec = MeasureSpec.makeMeasureSpec(container.height(),
+                MeasureSpec.UNSPECIFIED);
         view.measure(widthMeasureSpec, heightMeasureSpec);
 
         // Align to the left or right.
@@ -701,7 +702,8 @@
         final int containerWidth = container.width();
         final int adjMaxWidth = containerWidth - marginLeft - marginRight;
         final int widthMeasureSpec = MeasureSpec.makeMeasureSpec(adjMaxWidth, MeasureSpec.AT_MOST);
-        final int heightMeasureSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
+        final int heightMeasureSpec = MeasureSpec.makeMeasureSpec(container.height(),
+                MeasureSpec.UNSPECIFIED);
         preview.measure(widthMeasureSpec, heightMeasureSpec);
 
         // Align at the vertical center, 10% from the top.
@@ -766,7 +768,8 @@
         final Rect container = mContainerRect;
         final int maxWidth = container.width();
         final int widthMeasureSpec = MeasureSpec.makeMeasureSpec(maxWidth, MeasureSpec.AT_MOST);
-        final int heightMeasureSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
+        final int heightMeasureSpec = MeasureSpec.makeMeasureSpec(container.height(),
+                MeasureSpec.UNSPECIFIED);
         track.measure(widthMeasureSpec, heightMeasureSpec);
 
         final int top;
diff --git a/core/java/android/widget/GridView.java b/core/java/android/widget/GridView.java
index be0ca719..9ecdc9c 100644
--- a/core/java/android/widget/GridView.java
+++ b/core/java/android/widget/GridView.java
@@ -1071,7 +1071,8 @@
             p.forceAdd = true;
 
             int childHeightSpec = getChildMeasureSpec(
-                    MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED), 0, p.height);
+                    MeasureSpec.makeMeasureSpec(MeasureSpec.getSize(heightMeasureSpec),
+                            MeasureSpec.UNSPECIFIED), 0, p.height);
             int childWidthSpec = getChildMeasureSpec(
                     MeasureSpec.makeMeasureSpec(mColumnWidth, MeasureSpec.EXACTLY), 0, p.width);
             child.measure(childWidthSpec, childHeightSpec);
diff --git a/core/java/android/widget/LinearLayout.java b/core/java/android/widget/LinearLayout.java
index da15302..72f51c9 100644
--- a/core/java/android/widget/LinearLayout.java
+++ b/core/java/android/widget/LinearLayout.java
@@ -1058,8 +1058,11 @@
                 // use as much space as it wants because we can shrink things
                 // later (and re-measure).
                 if (baselineAligned) {
-                    final int freeSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
-                    child.measure(freeSpec, freeSpec);
+                    final int freeWidthSpec = MeasureSpec.makeMeasureSpec(
+                            MeasureSpec.getSize(widthMeasureSpec), MeasureSpec.UNSPECIFIED);
+                    final int freeHeightSpec = MeasureSpec.makeMeasureSpec(
+                            MeasureSpec.getSize(heightMeasureSpec), MeasureSpec.UNSPECIFIED);
+                    child.measure(freeWidthSpec, freeHeightSpec);
                 } else {
                     skippedMeasure = true;
                 }
diff --git a/core/java/android/widget/ListView.java b/core/java/android/widget/ListView.java
index def5929..a79c8e8 100644
--- a/core/java/android/widget/ListView.java
+++ b/core/java/android/widget/ListView.java
@@ -1155,7 +1155,7 @@
                 heightMode == MeasureSpec.UNSPECIFIED)) {
             final View child = obtainView(0, mIsScrap);
 
-            measureScrapChild(child, 0, widthMeasureSpec);
+            measureScrapChild(child, 0, widthMeasureSpec, heightSize);
 
             childWidth = child.getMeasuredWidth();
             childHeight = child.getMeasuredHeight();
@@ -1188,7 +1188,7 @@
         mWidthMeasureSpec = widthMeasureSpec;        
     }
 
-    private void measureScrapChild(View child, int position, int widthMeasureSpec) {
+    private void measureScrapChild(View child, int position, int widthMeasureSpec, int heightHint) {
         LayoutParams p = (LayoutParams) child.getLayoutParams();
         if (p == null) {
             p = (AbsListView.LayoutParams) generateDefaultLayoutParams();
@@ -1204,7 +1204,7 @@
         if (lpHeight > 0) {
             childHeightSpec = MeasureSpec.makeMeasureSpec(lpHeight, MeasureSpec.EXACTLY);
         } else {
-            childHeightSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
+            childHeightSpec = MeasureSpec.makeMeasureSpec(heightHint, MeasureSpec.UNSPECIFIED);
         }
         child.measure(childWidthSpec, childHeightSpec);
     }
@@ -1271,7 +1271,7 @@
         for (i = startPosition; i <= endPosition; ++i) {
             child = obtainView(i, isScrap);
 
-            measureScrapChild(child, i, widthMeasureSpec);
+            measureScrapChild(child, i, widthMeasureSpec, maxHeight);
 
             if (i > 0) {
                 // Count the divider for all but one child
@@ -1941,7 +1941,8 @@
             if (lpHeight > 0) {
                 childHeightSpec = MeasureSpec.makeMeasureSpec(lpHeight, MeasureSpec.EXACTLY);
             } else {
-                childHeightSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
+                childHeightSpec = MeasureSpec.makeMeasureSpec(getMeasuredHeight(),
+                        MeasureSpec.UNSPECIFIED);
             }
             child.measure(childWidthSpec, childHeightSpec);
         } else {
@@ -2695,7 +2696,8 @@
         if (lpHeight > 0) {
             childHeightSpec = MeasureSpec.makeMeasureSpec(lpHeight, MeasureSpec.EXACTLY);
         } else {
-            childHeightSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
+            childHeightSpec = MeasureSpec.makeMeasureSpec(getMeasuredHeight(),
+                    MeasureSpec.UNSPECIFIED);
         }
         child.measure(childWidthSpec, childHeightSpec);
     }
diff --git a/core/java/android/widget/ProgressBar.java b/core/java/android/widget/ProgressBar.java
index 24e9cbe..b59ae17 100644
--- a/core/java/android/widget/ProgressBar.java
+++ b/core/java/android/widget/ProgressBar.java
@@ -43,6 +43,7 @@
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.util.AttributeSet;
+import android.util.MathUtils;
 import android.util.Pools.SynchronizedPool;
 import android.view.Gravity;
 import android.view.RemotableViewMethod;
@@ -1341,23 +1342,22 @@
     }
 
     @android.view.RemotableViewMethod
-    synchronized void setProgress(int progress, boolean fromUser) {
+    synchronized boolean setProgress(int progress, boolean fromUser) {
         if (mIndeterminate) {
-            return;
+            // Not applicable.
+            return false;
         }
 
-        if (progress < 0) {
-            progress = 0;
+        progress = MathUtils.constrain(progress, 0, mMax);
+
+        if (progress == mProgress) {
+            // No change from current.
+            return false;
         }
 
-        if (progress > mMax) {
-            progress = mMax;
-        }
-
-        if (progress != mProgress) {
-            mProgress = progress;
-            refreshProgress(R.id.progress, mProgress, fromUser);
-        }
+        mProgress = progress;
+        refreshProgress(R.id.progress, mProgress, fromUser);
+        return true;
     }
 
     /**
diff --git a/core/java/android/widget/ScrollView.java b/core/java/android/widget/ScrollView.java
index b95c27d3..2026169 100644
--- a/core/java/android/widget/ScrollView.java
+++ b/core/java/android/widget/ScrollView.java
@@ -1238,7 +1238,8 @@
     }
 
     @Override
-    protected void measureChild(View child, int parentWidthMeasureSpec, int parentHeightMeasureSpec) {
+    protected void measureChild(View child, int parentWidthMeasureSpec,
+            int parentHeightMeasureSpec) {
         ViewGroup.LayoutParams lp = child.getLayoutParams();
 
         int childWidthMeasureSpec;
@@ -1247,7 +1248,8 @@
         childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec, mPaddingLeft
                 + mPaddingRight, lp.width);
 
-        childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
+        childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(
+                MeasureSpec.getSize(parentHeightMeasureSpec), MeasureSpec.UNSPECIFIED);
 
         child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
     }
@@ -1261,7 +1263,7 @@
                 mPaddingLeft + mPaddingRight + lp.leftMargin + lp.rightMargin
                         + widthUsed, lp.width);
         final int childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(
-                lp.topMargin + lp.bottomMargin, MeasureSpec.UNSPECIFIED);
+                MeasureSpec.getSize(parentHeightMeasureSpec), MeasureSpec.UNSPECIFIED);
 
         child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
     }
diff --git a/core/java/android/widget/SimpleMonthView.java b/core/java/android/widget/SimpleMonthView.java
index aa7f0b6..0249c22 100644
--- a/core/java/android/widget/SimpleMonthView.java
+++ b/core/java/android/widget/SimpleMonthView.java
@@ -26,7 +26,6 @@
 import android.graphics.Paint.Style;
 import android.graphics.Rect;
 import android.graphics.Typeface;
-import android.graphics.drawable.Drawable;
 import android.os.Bundle;
 import android.text.TextPaint;
 import android.text.format.DateFormat;
@@ -60,12 +59,6 @@
     private static final String DEFAULT_TITLE_FORMAT = "MMMMy";
     private static final String DAY_OF_WEEK_FORMAT = "EEEEE";
 
-    /** Virtual view ID for previous button. */
-    private static final int ITEM_ID_PREV = 0x101;
-
-    /** Virtual view ID for next button. */
-    private static final int ITEM_ID_NEXT = 0x100;
-
     private final TextPaint mMonthPaint = new TextPaint();
     private final TextPaint mDayOfWeekPaint = new TextPaint();
     private final TextPaint mDayPaint = new TextPaint();
@@ -87,14 +80,6 @@
     private final int mDesiredCellWidth;
     private final int mDesiredDaySelectorRadius;
 
-    // Next/previous drawables.
-    private final Drawable mPrevDrawable;
-    private final Drawable mNextDrawable;
-    private final Rect mPrevHitArea;
-    private final Rect mNextHitArea;
-    private final CharSequence mPrevContentDesc;
-    private final CharSequence mNextContentDesc;
-
     private CharSequence mTitle;
 
     private int mMonth;
@@ -137,9 +122,6 @@
     /** The day of month for the last (inclusive) enabled day. */
     private int mEnabledDayEnd = 31;
 
-    /** The number of week rows needed to display the current month. */
-    private int mNumWeeks = MAX_WEEKS_IN_MONTH;
-
     /** Optional listener for handling day click actions. */
     private OnDayClickListener mOnDayClickListener;
 
@@ -147,9 +129,6 @@
 
     private int mTouchedItem = -1;
 
-    private boolean mPrevEnabled;
-    private boolean mNextEnabled;
-
     public SimpleMonthView(Context context) {
         this(context, null);
     }
@@ -170,14 +149,8 @@
         mDesiredDayOfWeekHeight = res.getDimensionPixelSize(R.dimen.date_picker_day_of_week_height);
         mDesiredDayHeight = res.getDimensionPixelSize(R.dimen.date_picker_day_height);
         mDesiredCellWidth = res.getDimensionPixelSize(R.dimen.date_picker_day_width);
-        mDesiredDaySelectorRadius = res.getDimensionPixelSize(R.dimen.date_picker_day_selector_radius);
-
-        mPrevDrawable = context.getDrawable(R.drawable.ic_chevron_left);
-        mNextDrawable = context.getDrawable(R.drawable.ic_chevron_right);
-        mPrevHitArea = mPrevDrawable != null ? new Rect() : null;
-        mNextHitArea = mNextDrawable != null ? new Rect() : null;
-        mPrevContentDesc = res.getText(R.string.date_picker_prev_month_button);
-        mNextContentDesc = res.getText(R.string.date_picker_next_month_button);
+        mDesiredDaySelectorRadius = res.getDimensionPixelSize(
+                R.dimen.date_picker_day_selector_radius);
 
         // Set up accessibility components.
         mTouchHelper = new MonthViewTouchHelper(this);
@@ -193,18 +166,6 @@
         initPaints(res);
     }
 
-    public void setNextEnabled(boolean enabled) {
-        mNextEnabled = enabled;
-        mTouchHelper.invalidateRoot();
-        invalidate();
-    }
-
-    public void setPrevEnabled(boolean enabled) {
-        mPrevEnabled = enabled;
-        mTouchHelper.invalidateRoot();
-        invalidate();
-    }
-
     /**
      * Applies the specified text appearance resource to a paint, returning the
      * text color if one is set in the text appearance.
@@ -236,16 +197,16 @@
         return textColor;
     }
 
+    public int getMonthHeight() {
+        return mMonthHeight;
+    }
+
+    public int getCellWidth() {
+        return mCellWidth;
+    }
+
     public void setMonthTextAppearance(int resId) {
-        final ColorStateList monthColor = applyTextAppearance(mMonthPaint, resId);
-        if (monthColor != null) {
-            if (mPrevDrawable != null) {
-                mPrevDrawable.setTintList(monthColor);
-            }
-            if (mNextDrawable != null) {
-                mNextDrawable.setTintList(monthColor);
-            }
-        }
+        applyTextAppearance(mMonthPaint, resId);
 
         invalidate();
     }
@@ -360,7 +321,7 @@
         switch (event.getAction()) {
             case MotionEvent.ACTION_DOWN:
             case MotionEvent.ACTION_MOVE:
-                final int touchedItem = getItemAtLocation(x, y);
+                final int touchedItem = getDayAtLocation(x, y);
                 if (mTouchedItem != touchedItem) {
                     mTouchedItem = touchedItem;
                     invalidate();
@@ -368,8 +329,8 @@
                 break;
 
             case MotionEvent.ACTION_UP:
-                final int clickedItem = getItemAtLocation(x, y);
-                onItemClicked(clickedItem, true);
+                final int clickedDay = getDayAtLocation(x, y);
+                onDayClicked(clickedDay);
                 // Fall through.
             case MotionEvent.ACTION_CANCEL:
                 // Reset touched day on stream end.
@@ -389,7 +350,6 @@
         drawMonth(canvas);
         drawDaysOfWeek(canvas);
         drawDays(canvas);
-        drawButtons(canvas);
 
         canvas.translate(-paddingLeft, -paddingTop);
     }
@@ -482,16 +442,6 @@
         }
     }
 
-    private void drawButtons(Canvas canvas) {
-        if (mPrevEnabled && mPrevDrawable != null) {
-            mPrevDrawable.draw(canvas);
-        }
-
-        if (mNextEnabled && mNextDrawable != null) {
-            mNextDrawable.draw(canvas);
-        }
-    }
-
     private static boolean isValidDayOfWeek(int day) {
         return day >= Calendar.SUNDAY && day <= Calendar.SATURDAY;
     }
@@ -674,33 +624,6 @@
         mDaySelectorRadius = Math.min(mDesiredDaySelectorRadius,
                 Math.min(maxSelectorWidth, maxSelectorHeight));
 
-        // Vertically center the previous/next drawables within the month
-        // header, horizontally center within the day cell, then expand the
-        // hit area to ensure it's at least 48x48dp.
-        final Drawable prevDrawable = mPrevDrawable;
-        if (prevDrawable != null) {
-            final int dW = prevDrawable.getIntrinsicWidth();
-            final int dH = prevDrawable.getIntrinsicHeight();
-            final int iconTop = (monthHeight - dH) / 2;
-            final int iconLeft = (cellWidth - dW) / 2;
-
-            // Button bounds don't include padding, but hit area does.
-            prevDrawable.setBounds(iconLeft, iconTop, iconLeft + dW, iconTop + dH);
-            mPrevHitArea.set(0, 0, paddingLeft + cellWidth, paddingTop + monthHeight);
-        }
-
-        final Drawable nextDrawable = mNextDrawable;
-        if (nextDrawable != null) {
-            final int dW = nextDrawable.getIntrinsicWidth();
-            final int dH = nextDrawable.getIntrinsicHeight();
-            final int iconTop = (monthHeight - dH) / 2;
-            final int iconRight = paddedWidth - (cellWidth - dW) / 2;
-
-            // Button bounds don't include padding, but hit area does.
-            nextDrawable.setBounds(iconRight - dW, iconTop, iconRight, iconTop + dH);
-            mNextHitArea.set(paddedRight - cellWidth, 0, w, paddingTop + monthHeight);
-        }
-
         // Invalidate cached accessibility information.
         mTouchHelper.invalidateRoot();
     }
@@ -714,22 +637,15 @@
     }
 
     /**
-     * Calculates the day of the month or item identifier at the specified
-     * touch position. Returns the day of the month or -1 if the position
-     * wasn't in a valid day.
+     * Calculates the day of the month at the specified touch position. Returns
+     * the day of the month or -1 if the position wasn't in a valid day.
      *
      * @param x the x position of the touch event
      * @param y the y position of the touch event
-     * @return the day of the month at (x, y), an item identifier, or -1 if the
-     *         position wasn't in a valid day or item
+     * @return the day of the month at (x, y), or -1 if the position wasn't in
+     *         a valid day
      */
-    private int getItemAtLocation(int x, int y) {
-        if (mNextEnabled && mNextDrawable != null && mNextHitArea.contains(x, y)) {
-            return ITEM_ID_NEXT;
-        } else if (mPrevEnabled && mPrevDrawable != null && mPrevHitArea.contains(x, y)) {
-            return ITEM_ID_PREV;
-        }
-
+    private int getDayAtLocation(int x, int y) {
         final int paddedX = x - getPaddingLeft();
         if (paddedX < 0 || paddedX >= mPaddedWidth) {
             return -1;
@@ -755,22 +671,10 @@
     /**
      * Calculates the bounds of the specified day.
      *
-     * @param id the day of the month, or an item identifier
+     * @param id the day of the month
      * @param outBounds the rect to populate with bounds
      */
-    private boolean getBoundsForItem(int id, Rect outBounds) {
-        if (mNextEnabled && id == ITEM_ID_NEXT) {
-            if (mNextDrawable != null) {
-                outBounds.set(mNextHitArea);
-                return true;
-            }
-        } else if (mPrevEnabled && id == ITEM_ID_PREV) {
-            if (mPrevDrawable != null) {
-                outBounds.set(mPrevHitArea);
-                return true;
-            }
-        }
-
+    private boolean getBoundsForDay(int id, Rect outBounds) {
         if (id < 1 || id > mDaysInMonth) {
             return false;
         }
@@ -789,16 +693,8 @@
         final int top = getPaddingTop() + headerHeight + row * rowHeight;
 
         outBounds.set(left, top, left + colWidth, top + rowHeight);
-        return true;
-    }
 
-    /**
-     * Called when an item is clicked.
-     *
-     * @param id the day number or item identifier
-     */
-    private boolean onItemClicked(int id, boolean animate) {
-        return onNavigationClicked(id, animate) || onDayClicked(id);
+        return true;
     }
 
     /**
@@ -824,31 +720,6 @@
     }
 
     /**
-     * Called when the user clicks on a navigation button. Handles callbacks to
-     * the {@link OnDayClickListener} if one is set.
-     *
-     * @param id the item identifier
-     */
-    private boolean onNavigationClicked(int id, boolean animate) {
-        final int direction;
-        if (id == ITEM_ID_NEXT) {
-            direction = 1;
-        } else if (id == ITEM_ID_PREV) {
-            direction = -1;
-        } else {
-            return false;
-        }
-
-        if (mOnDayClickListener != null) {
-            mOnDayClickListener.onNavigationClick(this, direction, animate);
-        }
-
-        // This is a no-op if accessibility is turned off.
-        mTouchHelper.sendEventForVirtualView(id, AccessibilityEvent.TYPE_VIEW_CLICKED);
-        return true;
-    }
-
-    /**
      * Provides a virtual view hierarchy for interfacing with an accessibility
      * service.
      */
@@ -864,7 +735,7 @@
 
         @Override
         protected int getVirtualViewAt(float x, float y) {
-            final int day = getItemAtLocation((int) (x + 0.5f), (int) (y + 0.5f));
+            final int day = getDayAtLocation((int) (x + 0.5f), (int) (y + 0.5f));
             if (day >= 0) {
                 return day;
             }
@@ -873,14 +744,6 @@
 
         @Override
         protected void getVisibleVirtualViews(IntArray virtualViewIds) {
-            if (mNextEnabled && mNextDrawable != null) {
-                virtualViewIds.add(ITEM_ID_PREV);
-            }
-
-            if (mPrevEnabled && mPrevDrawable != null) {
-                virtualViewIds.add(ITEM_ID_NEXT);
-            }
-
             for (int day = 1; day <= mDaysInMonth; day++) {
                 virtualViewIds.add(day);
             }
@@ -888,12 +751,12 @@
 
         @Override
         protected void onPopulateEventForVirtualView(int virtualViewId, AccessibilityEvent event) {
-            event.setContentDescription(getItemDescription(virtualViewId));
+            event.setContentDescription(getDayDescription(virtualViewId));
         }
 
         @Override
         protected void onPopulateNodeForVirtualView(int virtualViewId, AccessibilityNodeInfo node) {
-            final boolean hasBounds = getBoundsForItem(virtualViewId, mTempRect);
+            final boolean hasBounds = getBoundsForDay(virtualViewId, mTempRect);
 
             if (!hasBounds) {
                 // The day is invalid, kill the node.
@@ -904,8 +767,8 @@
                 return;
             }
 
-            node.setText(getItemText(virtualViewId));
-            node.setContentDescription(getItemDescription(virtualViewId));
+            node.setText(getDayText(virtualViewId));
+            node.setContentDescription(getDayDescription(virtualViewId));
             node.setBoundsInParent(mTempRect);
             node.addAction(AccessibilityAction.ACTION_CLICK);
 
@@ -921,7 +784,7 @@
                 Bundle arguments) {
             switch (action) {
                 case AccessibilityNodeInfo.ACTION_CLICK:
-                    return onItemClicked(virtualViewId, false);
+                    return onDayClicked(virtualViewId);
             }
 
             return false;
@@ -930,15 +793,11 @@
         /**
          * Generates a description for a given virtual view.
          *
-         * @param id the day or item identifier to generate a description for
+         * @param id the day to generate a description for
          * @return a description of the virtual view
          */
-        private CharSequence getItemDescription(int id) {
-            if (id == ITEM_ID_NEXT) {
-                return mNextContentDesc;
-            } else if (id == ITEM_ID_PREV) {
-                return mPrevContentDesc;
-            } else if (id >= 1 && id <= mDaysInMonth) {
+        private CharSequence getDayDescription(int id) {
+            if (id >= 1 && id <= mDaysInMonth) {
                 mTempCalendar.set(mYear, mMonth, id);
                 return DateFormat.format(DATE_FORMAT, mTempCalendar.getTimeInMillis());
             }
@@ -949,13 +808,11 @@
         /**
          * Generates displayed text for a given virtual view.
          *
-         * @param id the day or item identifier to generate text for
+         * @param id the day to generate text for
          * @return the visible text of the virtual view
          */
-        private CharSequence getItemText(int id) {
-            if (id == ITEM_ID_NEXT || id == ITEM_ID_PREV) {
-                return null;
-            } else if (id >= 1 && id <= mDaysInMonth) {
+        private CharSequence getDayText(int id) {
+            if (id >= 1 && id <= mDaysInMonth) {
                 return Integer.toString(id);
             }
 
@@ -967,7 +824,6 @@
      * Handles callbacks when the user clicks on a time object.
      */
     public interface OnDayClickListener {
-        public void onDayClick(SimpleMonthView view, Calendar day);
-        public void onNavigationClick(SimpleMonthView view, int direction, boolean animate);
+        void onDayClick(SimpleMonthView view, Calendar day);
     }
 }
diff --git a/core/java/android/widget/Spinner.java b/core/java/android/widget/Spinner.java
index 3746ec6..095cc44 100644
--- a/core/java/android/widget/Spinner.java
+++ b/core/java/android/widget/Spinner.java
@@ -811,9 +811,9 @@
         View itemView = null;
         int itemType = 0;
         final int widthMeasureSpec =
-            MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
+            MeasureSpec.makeMeasureSpec(getMeasuredWidth(), MeasureSpec.UNSPECIFIED);
         final int heightMeasureSpec =
-            MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
+            MeasureSpec.makeMeasureSpec(getMeasuredHeight(), MeasureSpec.UNSPECIFIED);
 
         // Make sure the number of items we'll measure is capped. If it's a huge data set
         // with wildly varying sizes, oh well.
diff --git a/core/java/android/widget/TabWidget.java b/core/java/android/widget/TabWidget.java
index 9496e62..aa7168c 100644
--- a/core/java/android/widget/TabWidget.java
+++ b/core/java/android/widget/TabWidget.java
@@ -173,11 +173,12 @@
         }
 
         // First, measure with no constraint
-        final int unspecifiedWidth = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
+        final int width = MeasureSpec.getSize(widthMeasureSpec);
+        final int unspecifiedWidth = MeasureSpec.makeMeasureSpec(width, MeasureSpec.UNSPECIFIED);
         mImposedTabsHeight = -1;
         super.measureHorizontal(unspecifiedWidth, heightMeasureSpec);
 
-        int extraWidth = getMeasuredWidth() - MeasureSpec.getSize(widthMeasureSpec);
+        int extraWidth = getMeasuredWidth() - width;
         if (extraWidth > 0) {
             final int count = getChildCount();
 
diff --git a/core/java/android/widget/TableLayout.java b/core/java/android/widget/TableLayout.java
index 093bdcf..6fdd874 100644
--- a/core/java/android/widget/TableLayout.java
+++ b/core/java/android/widget/TableLayout.java
@@ -467,7 +467,7 @@
      */
     @Override
     void measureVertical(int widthMeasureSpec, int heightMeasureSpec) {
-        findLargestCells(widthMeasureSpec);
+        findLargestCells(widthMeasureSpec, heightMeasureSpec);
         shrinkAndStretchColumns(widthMeasureSpec);
 
         super.measureVertical(widthMeasureSpec, heightMeasureSpec);
@@ -479,7 +479,7 @@
      *
      * @param widthMeasureSpec the measure constraint imposed by our parent
      */
-    private void findLargestCells(int widthMeasureSpec) {
+    private void findLargestCells(int widthMeasureSpec, int heightMeasureSpec) {
         boolean firstRow = true;
 
         // find the maximum width for each column
@@ -502,7 +502,7 @@
                 final ViewGroup.LayoutParams layoutParams = row.getLayoutParams();
                 layoutParams.height = LayoutParams.WRAP_CONTENT;
 
-                final int[] widths = row.getColumnsWidths(widthMeasureSpec);
+                final int[] widths = row.getColumnsWidths(widthMeasureSpec, heightMeasureSpec);
                 final int newLength = widths.length;
                 // this is the first row, we just need to copy the values
                 if (firstRow) {
diff --git a/core/java/android/widget/TableRow.java b/core/java/android/widget/TableRow.java
index faf5b84..f73ee49 100644
--- a/core/java/android/widget/TableRow.java
+++ b/core/java/android/widget/TableRow.java
@@ -283,7 +283,7 @@
      *         column, in this row
      * {@hide}
      */
-    int[] getColumnsWidths(int widthMeasureSpec) {
+    int[] getColumnsWidths(int widthMeasureSpec, int heightMeasureSpec) {
         final int numColumns = getVirtualChildCount();
         if (mColumnWidths == null || numColumns != mColumnWidths.length) {
             mColumnWidths = new int[numColumns];
@@ -302,7 +302,9 @@
                             spec = getChildMeasureSpec(widthMeasureSpec, 0, LayoutParams.WRAP_CONTENT);
                             break;
                         case LayoutParams.MATCH_PARENT:
-                            spec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
+                            spec = MeasureSpec.makeMeasureSpec(
+                                    MeasureSpec.getSize(heightMeasureSpec),
+                                    MeasureSpec.UNSPECIFIED);
                             break;
                         default:
                             spec = MeasureSpec.makeMeasureSpec(layoutParams.width, MeasureSpec.EXACTLY);
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index b44a886..7328300 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -236,6 +236,9 @@
  * @attr ref android.R.styleable#TextView_elegantTextHeight
  * @attr ref android.R.styleable#TextView_letterSpacing
  * @attr ref android.R.styleable#TextView_fontFeatureSettings
+ * @attr ref android.R.styleable#TextView_breakStrategy
+ * @attr ref android.R.styleable#TextView_leftIndents
+ * @attr ref android.R.styleable#TextView_rightIndents
  */
 @RemoteView
 public class TextView extends View implements ViewTreeObserver.OnPreDrawListener {
@@ -551,6 +554,8 @@
     private float mSpacingAdd = 0.0f;
 
     private int mBreakStrategy;
+    private int[] mLeftIndents;
+    private int[] mRightIndents;
 
     private int mMaximum = Integer.MAX_VALUE;
     private int mMaxMode = LINES;
@@ -1146,6 +1151,17 @@
 
             case com.android.internal.R.styleable.TextView_breakStrategy:
                 mBreakStrategy = a.getInt(attr, Layout.BREAK_STRATEGY_SIMPLE);
+                break;
+
+            case com.android.internal.R.styleable.TextView_leftIndents:
+                TypedArray margins = res.obtainTypedArray(a.getResourceId(attr, View.NO_ID));
+                mLeftIndents = parseDimensionArray(margins);
+                break;
+
+            case com.android.internal.R.styleable.TextView_rightIndents:
+                margins = res.obtainTypedArray(a.getResourceId(attr, View.NO_ID));
+                mRightIndents = parseDimensionArray(margins);
+                break;
             }
         }
         a.recycle();
@@ -1421,6 +1437,17 @@
         }
     }
 
+    private int[] parseDimensionArray(TypedArray dimens) {
+        if (dimens == null) {
+            return null;
+        }
+        int[] result = new int[dimens.length()];
+        for (int i = 0; i < result.length; i++) {
+            result[i] = dimens.getDimensionPixelSize(i, 0);
+        }
+        return result;
+    }
+
     /**
      * @hide
      */
@@ -3019,6 +3046,51 @@
     }
 
     /**
+     * Set indents. Arguments are arrays holding an indent amount, one per line, measured in
+     * pixels. For lines past the last element in the array, the last element repeats.
+     *
+     * @param leftIndents array of indent values for left margin, in pixels
+     * @param rightIndents array of indent values for right margin, in pixels
+     *
+     * @see #getLeftIndents()
+     * @see #getRightIndents()
+     *
+     * @attr ref android.R.styleable#TextView_leftIndents
+     * @attr ref android.R.styleable#TextView_rightIndents
+     */
+    public void setIndents(@Nullable int[] leftIndents, @Nullable int[] rightIndents) {
+        mLeftIndents = leftIndents;
+        mRightIndents = rightIndents;
+        if (mLayout != null) {
+            nullLayouts();
+            requestLayout();
+            invalidate();
+        }
+    }
+
+    /**
+     * Get left indents. See {#link setMargins} for more details.
+     *
+     * @return left indents
+     * @see #setIndents(int[], int[])
+     * @attr ref android.R.styleable#TextView_leftIndents
+     */
+    public int[] getLeftIndents() {
+        return mLeftIndents;
+    }
+
+    /**
+     * Get right indents. See {#link setMargins} for more details.
+     *
+     * @return right indents
+     * @see #setIndents(int[], int[])
+     * @attr ref android.R.styleable#TextView_rightIndents
+     */
+    public int[] getRightIndents() {
+        return mRightIndents;
+    }
+
+    /**
      * Sets font feature settings.  The format is the same as the CSS
      * font-feature-settings attribute:
      * http://dev.w3.org/csswg/css-fonts/#propdef-font-feature-settings
@@ -6564,6 +6636,9 @@
                         .setSpacingAdd(mSpacingAdd)
                         .setIncludePad(mIncludePad)
                         .setBreakStrategy(mBreakStrategy);
+                if (mLeftIndents != null || mRightIndents != null) {
+                    builder.setIndents(mLeftIndents, mRightIndents);
+                }
                 if (shouldEllipsize) {
                     builder.setEllipsize(mEllipsize)
                             .setEllipsizedWidth(ellipsisWidth)
@@ -6652,6 +6727,9 @@
                     .setSpacingAdd(mSpacingAdd)
                     .setIncludePad(mIncludePad)
                     .setBreakStrategy(mBreakStrategy);
+            if (mLeftIndents != null || mRightIndents != null) {
+                builder.setIndents(mLeftIndents, mRightIndents);
+            }
             if (shouldEllipsize) {
                 builder.setEllipsize(effectiveEllipsize)
                         .setEllipsizedWidth(ellipsisWidth)
@@ -9124,6 +9202,7 @@
 
     void replaceSelectionWithText(CharSequence text) {
         ((Editable) mText).replace(getSelectionStart(), getSelectionEnd(), text);
+        mEditor.startSelectionActionModeWithSelection();
     }
 
     /**
diff --git a/core/java/com/android/internal/app/IBatteryStats.aidl b/core/java/com/android/internal/app/IBatteryStats.aidl
index 4f0e29e..7c5c565 100644
--- a/core/java/com/android/internal/app/IBatteryStats.aidl
+++ b/core/java/com/android/internal/app/IBatteryStats.aidl
@@ -95,9 +95,6 @@
     void noteWifiState(int wifiState, String accessPoint);
     void noteWifiSupplicantStateChanged(int supplState, boolean failedAuth);
     void noteWifiRssiChanged(int newRssi);
-    void noteBluetoothOn();
-    void noteBluetoothOff();
-    void noteBluetoothState(int bluetoothState);
     void noteFullWifiLockAcquired(int uid);
     void noteFullWifiLockReleased(int uid);
     void noteWifiScanStarted(int uid);
diff --git a/core/java/com/android/internal/os/BatteryStatsHelper.java b/core/java/com/android/internal/os/BatteryStatsHelper.java
index 59dbec6..a53d46c 100644
--- a/core/java/com/android/internal/os/BatteryStatsHelper.java
+++ b/core/java/com/android/internal/os/BatteryStatsHelper.java
@@ -136,6 +136,14 @@
                 profile.getAveragePower(PowerProfile.POWER_WIFI_CONTROLLER_TX) != 0;
     }
 
+    public static boolean checkHasBluetoothPowerReporting(BatteryStats stats,
+                                                          PowerProfile profile) {
+        return stats.hasBluetoothActivityReporting() &&
+                profile.getAveragePower(PowerProfile.POWER_BLUETOOTH_CONTROLLER_IDLE) != 0 &&
+                profile.getAveragePower(PowerProfile.POWER_BLUETOOTH_CONTROLLER_RX) != 0 &&
+                profile.getAveragePower(PowerProfile.POWER_BLUETOOTH_CONTROLLER_TX) != 0;
+    }
+
     public BatteryStatsHelper(Context context) {
         this(context, true);
     }
@@ -256,7 +264,8 @@
     }
 
     public static String makemAh(double power) {
-        if (power < .00001) return String.format("%.8f", power);
+        if (power == 0) return "0";
+        else if (power < .00001) return String.format("%.8f", power);
         else if (power < .0001) return String.format("%.7f", power);
         else if (power < .001) return String.format("%.6f", power);
         else if (power < .01) return String.format("%.5f", power);
@@ -342,7 +351,11 @@
         mWifiPowerCalculator.reset();
 
         if (mBluetoothPowerCalculator == null) {
-            mBluetoothPowerCalculator = new BluetoothPowerCalculator();
+            if (checkHasBluetoothPowerReporting(mStats, mPowerProfile)) {
+                mBluetoothPowerCalculator = new BluetoothPowerCalculator();
+            } else {
+                mBluetoothPowerCalculator = new BluetoothPowerCalculator();
+            }
         }
         mBluetoothPowerCalculator.reset();
 
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index 87605f6..405c861 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -19,8 +19,6 @@
 import android.annotation.Nullable;
 import android.app.ActivityManager;
 import android.bluetooth.BluetoothActivityEnergyInfo;
-import android.bluetooth.BluetoothDevice;
-import android.bluetooth.BluetoothHeadset;
 import android.content.Context;
 import android.content.Intent;
 import android.net.ConnectivityManager;
@@ -84,7 +82,6 @@
 import java.util.Calendar;
 import java.util.HashMap;
 import java.util.Iterator;
-import java.util.List;
 import java.util.Map;
 import java.util.concurrent.atomic.AtomicInteger;
 import java.util.concurrent.locks.ReentrantLock;
@@ -107,7 +104,7 @@
     private static final int MAGIC = 0xBA757475; // 'BATSTATS'
 
     // Current on-disk Parcel version
-    private static final int VERSION = 123 + (USE_OLD_HISTORY ? 1000 : 0);
+    private static final int VERSION = 125 + (USE_OLD_HISTORY ? 1000 : 0);
 
     // Maximum number of items we will record in the history.
     private static final int MAX_HISTORY_ITEMS = 2000;
@@ -176,7 +173,7 @@
     }
 
     public interface ExternalStatsSync {
-        void scheduleSync();
+        void scheduleSync(String reason);
     }
 
     public final MyHandler mHandler;
@@ -250,6 +247,8 @@
     int mNumHistoryTagChars = 0;
     int mHistoryBufferLastPos = -1;
     boolean mHistoryOverflow = false;
+    int mActiveHistoryStates = 0xffffffff;
+    int mActiveHistoryStates2 = 0xffffffff;
     long mLastHistoryElapsedRealtime = 0;
     long mTrackRunningHistoryElapsedRealtime = 0;
     long mTrackRunningHistoryUptime = 0;
@@ -313,7 +312,7 @@
 
     int mWakeLockNesting;
     boolean mWakeLockImportant;
-    boolean mRecordAllHistory;
+    public boolean mRecordAllHistory;
     boolean mNoAutoReset;
 
     int mScreenState = Display.STATE_UNKNOWN;
@@ -384,12 +383,6 @@
     final StopwatchTimer[] mWifiSignalStrengthsTimer =
             new StopwatchTimer[NUM_WIFI_SIGNAL_STRENGTH_BINS];
 
-    boolean mBluetoothOn;
-    StopwatchTimer mBluetoothOnTimer;
-
-    int mBluetoothState = -1;
-    final StopwatchTimer[] mBluetoothStateTimer = new StopwatchTimer[NUM_BLUETOOTH_STATES];
-
     int mMobileRadioPowerState = DataConnectionRealTimeInfo.DC_POWER_STATE_LOW;
     long mMobileRadioActiveStartTime;
     StopwatchTimer mMobileRadioActiveTimer;
@@ -398,8 +391,7 @@
     LongSamplingCounter mMobileRadioActiveUnknownTime;
     LongSamplingCounter mMobileRadioActiveUnknownCount;
 
-    /** Bluetooth headset object */
-    BluetoothHeadset mBtHeadset;
+    int mWifiRadioPowerState = DataConnectionRealTimeInfo.DC_POWER_STATE_LOW;
 
     /**
      * These provide time bases that discount the time the device is plugged
@@ -458,9 +450,6 @@
 
     long mLastWriteTime = 0; // Milliseconds
 
-    private int mBluetoothPingCount;
-    private int mBluetoothPingStart = -1;
-
     private int mPhoneServiceState = -1;
     private int mPhoneServiceStateRaw = -1;
     private int mPhoneSimStateRaw = -1;
@@ -1803,32 +1792,6 @@
         return kwlt;
     }
 
-    private int getCurrentBluetoothPingCount() {
-        if (mBtHeadset != null) {
-            List<BluetoothDevice> deviceList = mBtHeadset.getConnectedDevices();
-            if (deviceList.size() > 0) {
-                return mBtHeadset.getBatteryUsageHint(deviceList.get(0));
-            }
-        }
-        return -1;
-    }
-
-    public int getBluetoothPingCount() {
-        if (mBluetoothPingStart == -1) {
-            return mBluetoothPingCount;
-        } else if (mBtHeadset != null) {
-            return getCurrentBluetoothPingCount() - mBluetoothPingStart;
-        }
-        return 0;
-    }
-
-    public void setBtHeadset(BluetoothHeadset headset) {
-        if (headset != null && mBtHeadset == null && isOnBattery() && mBluetoothPingStart == -1) {
-            mBluetoothPingStart = getCurrentBluetoothPingCount();
-        }
-        mBtHeadset = headset;
-    }
-
     private int writeHistoryTag(HistoryTag tag) {
         Integer idxObj = mHistoryTagPool.get(tag);
         int idx;
@@ -2259,8 +2222,8 @@
         }
 
         final long timeDiff = (mHistoryBaseTime+elapsedRealtimeMs) - mHistoryLastWritten.time;
-        final int diffStates = mHistoryLastWritten.states^cur.states;
-        final int diffStates2 = mHistoryLastWritten.states2^cur.states2;
+        final int diffStates = mHistoryLastWritten.states^(cur.states&mActiveHistoryStates);
+        final int diffStates2 = mHistoryLastWritten.states2^(cur.states2&mActiveHistoryStates2);
         final int lastDiffStates = mHistoryLastWritten.states^mHistoryLastLastWritten.states;
         final int lastDiffStates2 = mHistoryLastWritten.states2^mHistoryLastLastWritten.states2;
         if (DEBUG) Slog.i(TAG, "ADD: tdelta=" + timeDiff + " diff="
@@ -2325,11 +2288,32 @@
                 return;
             }
 
+            // After overflow, we allow various bit-wise states to settle to 0.
+            boolean writeAnyway = false;
+            final int curStates = cur.states & HistoryItem.SETTLE_TO_ZERO_STATES
+                    & mActiveHistoryStates;
+            if (mHistoryLastWritten.states != curStates) {
+                // mActiveHistoryStates keeps track of which bits in .states are now being
+                // forced to 0.
+                int old = mActiveHistoryStates;
+                mActiveHistoryStates &= curStates | ~HistoryItem.SETTLE_TO_ZERO_STATES;
+                writeAnyway |= old != mActiveHistoryStates;
+            }
+            final int curStates2 = cur.states2 & HistoryItem.SETTLE_TO_ZERO_STATES2
+                    & mActiveHistoryStates2;
+            if (mHistoryLastWritten.states2 != curStates2) {
+                // mActiveHistoryStates2 keeps track of which bits in .states2 are now being
+                // forced to 0.
+                int old = mActiveHistoryStates2;
+                mActiveHistoryStates2 &= curStates2 | ~HistoryItem.SETTLE_TO_ZERO_STATES2;
+                writeAnyway |= old != mActiveHistoryStates2;
+            }
+
             // Once we've reached the maximum number of items, we only
             // record changes to the battery level and the most interesting states.
             // Once we've reached the maximum maximum number of items, we only
             // record changes to the battery level.
-            if (mHistoryLastWritten.batteryLevel == cur.batteryLevel &&
+            if (!writeAnyway && mHistoryLastWritten.batteryLevel == cur.batteryLevel &&
                     (dataSize >= MAX_MAX_HISTORY_BUFFER
                             || ((mHistoryLastWritten.states^cur.states)
                                     & HistoryItem.MOST_INTERESTING_STATES) == 0
@@ -2360,6 +2344,8 @@
         mHistoryBufferLastPos = mHistoryBuffer.dataPosition();
         mHistoryLastLastWritten.setTo(mHistoryLastWritten);
         mHistoryLastWritten.setTo(mHistoryBaseTime + elapsedRealtimeMs, cmd, cur);
+        mHistoryLastWritten.states &= mActiveHistoryStates;
+        mHistoryLastWritten.states2 &= mActiveHistoryStates2;
         writeHistoryDelta(mHistoryBuffer, mHistoryLastWritten, mHistoryLastLastWritten);
         mLastHistoryElapsedRealtime = elapsedRealtimeMs;
         cur.wakelockTag = null;
@@ -2411,8 +2397,8 @@
         // into one record.
         if (mHistoryEnd != null && mHistoryEnd.cmd == HistoryItem.CMD_UPDATE
                 && (mHistoryBaseTime+elapsedRealtimeMs) < (mHistoryEnd.time+1000)
-                && ((mHistoryEnd.states^cur.states)&mChangedStates) == 0
-                && ((mHistoryEnd.states2^cur.states2)&mChangedStates2) == 0) {
+                && ((mHistoryEnd.states^cur.states)&mChangedStates&mActiveHistoryStates) == 0
+                && ((mHistoryEnd.states2^cur.states2)&mChangedStates2&mActiveHistoryStates2) == 0) {
             // If the current is the same as the one before, then we no
             // longer need the entry.
             if (mHistoryLastEnd != null && mHistoryLastEnd.cmd == HistoryItem.CMD_UPDATE
@@ -2424,8 +2410,8 @@
                 mHistoryEnd = mHistoryLastEnd;
                 mHistoryLastEnd = null;
             } else {
-                mChangedStates |= mHistoryEnd.states^cur.states;
-                mChangedStates2 |= mHistoryEnd.states^cur.states2;
+                mChangedStates |= mHistoryEnd.states^(cur.states&mActiveHistoryStates);
+                mChangedStates2 |= mHistoryEnd.states^(cur.states2&mActiveHistoryStates2);
                 mHistoryEnd.setTo(mHistoryEnd.time, HistoryItem.CMD_UPDATE, cur);
             }
             return;
@@ -2447,7 +2433,7 @@
             if (mHistoryEnd != null && mHistoryEnd.batteryLevel
                     == cur.batteryLevel &&
                     (mNumHistoryItems >= MAX_MAX_HISTORY_ITEMS
-                            || ((mHistoryEnd.states^cur.states)
+                            || ((mHistoryEnd.states^(cur.states&mActiveHistoryStates))
                                     & HistoryItem.MOST_INTERESTING_STATES) == 0)) {
                 return;
             }
@@ -2456,7 +2442,7 @@
         addHistoryRecordLocked(elapsedRealtimeMs, HistoryItem.CMD_UPDATE);
     }
 
-    void addHistoryEventLocked(long elapsedRealtimeMs, long uptimeMs, int code,
+    public void addHistoryEventLocked(long elapsedRealtimeMs, long uptimeMs, int code,
             String name, int uid) {
         mHistoryCur.eventCode = code;
         mHistoryCur.eventTag = mHistoryCur.localEventTag;
@@ -2515,23 +2501,15 @@
         mNumHistoryTagChars = 0;
         mHistoryBufferLastPos = -1;
         mHistoryOverflow = false;
+        mActiveHistoryStates = 0xffffffff;
+        mActiveHistoryStates2 = 0xffffffff;
         mLastRecordedClockTime = 0;
         mLastRecordedClockRealtime = 0;
     }
 
     public void updateTimeBasesLocked(boolean unplugged, boolean screenOff, long uptime,
             long realtime) {
-        if (mOnBatteryTimeBase.setRunning(unplugged, uptime, realtime)) {
-            if (unplugged) {
-                // Track bt headset ping count
-                mBluetoothPingStart = getCurrentBluetoothPingCount();
-                mBluetoothPingCount = 0;
-            } else {
-                // Track bt headset ping count
-                mBluetoothPingCount = getBluetoothPingCount();
-                mBluetoothPingStart = -1;
-            }
-        }
+        mOnBatteryTimeBase.setRunning(unplugged, uptime, realtime);
 
         boolean unpluggedScreenOff = unplugged && screenOff;
         if (unpluggedScreenOff != mOnBatteryScreenOffTimeBase.isRunning()) {
@@ -3396,7 +3374,7 @@
         if (!mPhoneOn) {
             final long elapsedRealtime = SystemClock.elapsedRealtime();
             final long uptime = SystemClock.uptimeMillis();
-            mHistoryCur.states |= HistoryItem.STATE_PHONE_IN_CALL_FLAG;
+            mHistoryCur.states2 |= HistoryItem.STATE2_PHONE_IN_CALL_FLAG;
             if (DEBUG_HISTORY) Slog.v(TAG, "Phone on to: "
                     + Integer.toHexString(mHistoryCur.states));
             addHistoryRecordLocked(elapsedRealtime, uptime);
@@ -3409,7 +3387,7 @@
         if (mPhoneOn) {
             final long elapsedRealtime = SystemClock.elapsedRealtime();
             final long uptime = SystemClock.uptimeMillis();
-            mHistoryCur.states &= ~HistoryItem.STATE_PHONE_IN_CALL_FLAG;
+            mHistoryCur.states2 &= ~HistoryItem.STATE2_PHONE_IN_CALL_FLAG;
             if (DEBUG_HISTORY) Slog.v(TAG, "Phone off to: "
                     + Integer.toHexString(mHistoryCur.states));
             addHistoryRecordLocked(elapsedRealtime, uptime);
@@ -3626,7 +3604,7 @@
             addHistoryRecordLocked(elapsedRealtime, uptime);
             mWifiOn = true;
             mWifiOnTimer.startRunningLocked(elapsedRealtime);
-            scheduleSyncExternalStatsLocked();
+            scheduleSyncExternalStatsLocked("wifi-off");
         }
     }
 
@@ -3640,7 +3618,7 @@
             addHistoryRecordLocked(elapsedRealtime, uptime);
             mWifiOn = false;
             mWifiOnTimer.stopRunningLocked(elapsedRealtime);
-            scheduleSyncExternalStatsLocked();
+            scheduleSyncExternalStatsLocked("wifi-on");
         }
     }
 
@@ -3788,6 +3766,25 @@
         }
     }
 
+    public void noteWifiRadioPowerState(int powerState, long timestampNs) {
+        final long elapsedRealtime = SystemClock.elapsedRealtime();
+        final long uptime = SystemClock.uptimeMillis();
+        if (mWifiRadioPowerState != powerState) {
+            final boolean active =
+                    powerState == DataConnectionRealTimeInfo.DC_POWER_STATE_MEDIUM
+                            || powerState == DataConnectionRealTimeInfo.DC_POWER_STATE_HIGH;
+            if (active) {
+                mHistoryCur.states |= HistoryItem.STATE_WIFI_RADIO_ACTIVE_FLAG;
+            } else {
+                mHistoryCur.states &= ~HistoryItem.STATE_WIFI_RADIO_ACTIVE_FLAG;
+            }
+            if (DEBUG_HISTORY) Slog.v(TAG, "Wifi network active " + active + " to: "
+                    + Integer.toHexString(mHistoryCur.states));
+            addHistoryRecordLocked(elapsedRealtime, uptime);
+            mWifiRadioPowerState = powerState;
+        }
+    }
+
     public void noteWifiRunningLocked(WorkSource ws) {
         if (!mGlobalWifiRunning) {
             final long elapsedRealtime = SystemClock.elapsedRealtime();
@@ -3803,7 +3800,7 @@
                 int uid = mapUid(ws.get(i));
                 getUidStatsLocked(uid).noteWifiRunningLocked(elapsedRealtime);
             }
-            scheduleSyncExternalStatsLocked();
+            scheduleSyncExternalStatsLocked("wifi-running");
         } else {
             Log.w(TAG, "noteWifiRunningLocked -- called while WIFI running");
         }
@@ -3842,7 +3839,7 @@
                 int uid = mapUid(ws.get(i));
                 getUidStatsLocked(uid).noteWifiStoppedLocked(elapsedRealtime);
             }
-            scheduleSyncExternalStatsLocked();
+            scheduleSyncExternalStatsLocked("wifi-stopped");
         } else {
             Log.w(TAG, "noteWifiStoppedLocked -- called while WIFI not running");
         }
@@ -3857,7 +3854,7 @@
             }
             mWifiState = wifiState;
             mWifiStateTimer[wifiState].startRunningLocked(elapsedRealtime);
-            scheduleSyncExternalStatsLocked();
+            scheduleSyncExternalStatsLocked("wifi-state");
         }
     }
 
@@ -3919,46 +3916,6 @@
         }
     }
 
-    public void noteBluetoothOnLocked() {
-        if (!mBluetoothOn) {
-            final long elapsedRealtime = SystemClock.elapsedRealtime();
-            final long uptime = SystemClock.uptimeMillis();
-            mHistoryCur.states |= HistoryItem.STATE_BLUETOOTH_ON_FLAG;
-            if (DEBUG_HISTORY) Slog.v(TAG, "Bluetooth on to: "
-                    + Integer.toHexString(mHistoryCur.states));
-            addHistoryRecordLocked(elapsedRealtime, uptime);
-            mBluetoothOn = true;
-            mBluetoothOnTimer.startRunningLocked(elapsedRealtime);
-            scheduleSyncExternalStatsLocked();
-        }
-    }
-
-    public void noteBluetoothOffLocked() {
-        if (mBluetoothOn) {
-            final long elapsedRealtime = SystemClock.elapsedRealtime();
-            final long uptime = SystemClock.uptimeMillis();
-            mHistoryCur.states &= ~HistoryItem.STATE_BLUETOOTH_ON_FLAG;
-            if (DEBUG_HISTORY) Slog.v(TAG, "Bluetooth off to: "
-                    + Integer.toHexString(mHistoryCur.states));
-            addHistoryRecordLocked(elapsedRealtime, uptime);
-            mBluetoothOn = false;
-            mBluetoothOnTimer.stopRunningLocked(elapsedRealtime);
-            scheduleSyncExternalStatsLocked();
-        }
-    }
-
-    public void noteBluetoothStateLocked(int bluetoothState) {
-        if (DEBUG) Log.i(TAG, "Bluetooth state -> " + bluetoothState);
-        if (mBluetoothState != bluetoothState) {
-            final long elapsedRealtime = SystemClock.elapsedRealtime();
-            if (mBluetoothState >= 0) {
-                mBluetoothStateTimer[mBluetoothState].stopRunningLocked(elapsedRealtime);
-            }
-            mBluetoothState = bluetoothState;
-            mBluetoothStateTimer[bluetoothState].startRunningLocked(elapsedRealtime);
-        }
-    }
-
     int mWifiFullLockNesting = 0;
 
     public void noteFullWifiLockAcquiredLocked(int uid) {
@@ -4313,20 +4270,6 @@
         return mWifiSignalStrengthsTimer[strengthBin].getCountLocked(which);
     }
 
-    @Override public long getBluetoothOnTime(long elapsedRealtimeUs, int which) {
-        return mBluetoothOnTimer.getTotalTimeLocked(elapsedRealtimeUs, which);
-    }
-
-    @Override public long getBluetoothStateTime(int bluetoothState,
-            long elapsedRealtimeUs, int which) {
-        return mBluetoothStateTimer[bluetoothState].getTotalTimeLocked(
-                elapsedRealtimeUs, which);
-    }
-
-    @Override public int getBluetoothStateCount(int bluetoothState, int which) {
-        return mBluetoothStateTimer[bluetoothState].getCountLocked(which);
-    }
-
     @Override public boolean hasBluetoothActivityReporting() {
         return mHasBluetoothEnergyReporting;
     }
@@ -6780,10 +6723,6 @@
             mWifiSignalStrengthsTimer[i] = new StopwatchTimer(null, -800-i, null,
                     mOnBatteryTimeBase);
         }
-        mBluetoothOnTimer = new StopwatchTimer(null, -6, null, mOnBatteryTimeBase);
-        for (int i=0; i< NUM_BLUETOOTH_STATES; i++) {
-            mBluetoothStateTimer[i] = new StopwatchTimer(null, -500-i, null, mOnBatteryTimeBase);
-        }
         mAudioOnTimer = new StopwatchTimer(null, -7, null, mOnBatteryTimeBase);
         mVideoOnTimer = new StopwatchTimer(null, -8, null, mOnBatteryTimeBase);
         mFlashlightOnTimer = new StopwatchTimer(null, -9, null, mOnBatteryTimeBase);
@@ -7399,10 +7338,6 @@
         for (int i=0; i<NUM_WIFI_SIGNAL_STRENGTH_BINS; i++) {
             mWifiSignalStrengthsTimer[i].reset(false);
         }
-        mBluetoothOnTimer.reset(false);
-        for (int i=0; i< NUM_BLUETOOTH_STATES; i++) {
-            mBluetoothStateTimer[i].reset(false);
-        }
         for (int i=0; i< NUM_CONTROLLER_ACTIVITY_TYPES; i++) {
             mBluetoothActivityCounters[i].reset(false);
             mWifiActivityCounters[i].reset(false);
@@ -7727,16 +7662,12 @@
             mWifiActivityCounters[CONTROLLER_IDLE_TIME].addCountLocked(
                     info.getControllerIdleTimeMillis());
 
-            final double powerDrainMaMs;
-            if (mPowerProfile.getAveragePower(
-                    PowerProfile.POWER_WIFI_CONTROLLER_OPERATING_VOLTAGE) == 0) {
-                powerDrainMaMs = 0.0;
-            } else {
-                powerDrainMaMs = info.getControllerEnergyUsed()
-                        / mPowerProfile.getAveragePower(
-                        PowerProfile.POWER_WIFI_CONTROLLER_OPERATING_VOLTAGE);
+            final double opVoltage = mPowerProfile.getAveragePower(
+                    PowerProfile.POWER_WIFI_CONTROLLER_OPERATING_VOLTAGE);
+            if (opVoltage != 0) {
+                mWifiActivityCounters[CONTROLLER_POWER_DRAIN].addCountLocked(
+                        (long)(info.getControllerEnergyUsed() / opVoltage));
             }
-            mWifiActivityCounters[CONTROLLER_POWER_DRAIN].addCountLocked((long) powerDrainMaMs);
         }
     }
 
@@ -7824,8 +7755,13 @@
                     info.getControllerTxTimeMillis());
             mBluetoothActivityCounters[CONTROLLER_IDLE_TIME].addCountLocked(
                     info.getControllerIdleTimeMillis());
-            mBluetoothActivityCounters[CONTROLLER_POWER_DRAIN].addCountLocked(
-                    info.getControllerEnergyUsed());
+
+            final double opVoltage = mPowerProfile.getAveragePower(
+                    PowerProfile.POWER_BLUETOOTH_CONTROLLER_OPERATING_VOLTAGE);
+            if (opVoltage != 0) {
+                mBluetoothActivityCounters[CONTROLLER_POWER_DRAIN].addCountLocked(
+                        (long) (info.getControllerEnergyUsed() / opVoltage));
+            }
         }
     }
 
@@ -7871,9 +7807,9 @@
         if (mCharging != charging) {
             mCharging = charging;
             if (charging) {
-                mHistoryCur.states |= HistoryItem.STATE_CHARGING_FLAG;
+                mHistoryCur.states2 |= HistoryItem.STATE2_CHARGING_FLAG;
             } else {
-                mHistoryCur.states &= ~HistoryItem.STATE_CHARGING_FLAG;
+                mHistoryCur.states2 &= ~HistoryItem.STATE2_CHARGING_FLAG;
             }
             mHandler.sendEmptyMessage(MSG_REPORT_CHARGING);
             return true;
@@ -8039,9 +7975,9 @@
         }
     }
 
-    private void scheduleSyncExternalStatsLocked() {
+    private void scheduleSyncExternalStatsLocked(String reason) {
         if (mExternalSync != null) {
-            mExternalSync.scheduleSync();
+            mExternalSync.scheduleSync(reason);
         }
     }
 
@@ -8067,7 +8003,7 @@
                 }
             }
             // Always start out assuming charging, that will be updated later.
-            mHistoryCur.states |= HistoryItem.STATE_CHARGING_FLAG;
+            mHistoryCur.states2 |= HistoryItem.STATE2_CHARGING_FLAG;
             mHistoryCur.batteryStatus = (byte)status;
             mHistoryCur.batteryLevel = (byte)level;
             mMaxChargeStepLevel = mMinDischargeStepLevel =
@@ -8109,7 +8045,7 @@
 
                 // TODO(adamlesinski): Schedule the creation of a HistoryStepDetails record
                 // which will pull external stats.
-                scheduleSyncExternalStatsLocked();
+                scheduleSyncExternalStatsLocked("battery-level");
             }
             if (mHistoryCur.batteryStatus != status) {
                 mHistoryCur.batteryStatus = (byte)status;
@@ -8910,6 +8846,7 @@
         mMobileRadioActiveAdjustedTime.readSummaryFromParcelLocked(in);
         mMobileRadioActiveUnknownTime.readSummaryFromParcelLocked(in);
         mMobileRadioActiveUnknownCount.readSummaryFromParcelLocked(in);
+        mWifiRadioPowerState = DataConnectionRealTimeInfo.DC_POWER_STATE_LOW;
         mWifiOn = false;
         mWifiOnTimer.readSummaryFromParcelLocked(in);
         mGlobalWifiRunning = false;
@@ -8923,16 +8860,9 @@
         for (int i=0; i<NUM_WIFI_SIGNAL_STRENGTH_BINS; i++) {
             mWifiSignalStrengthsTimer[i].readSummaryFromParcelLocked(in);
         }
-        mBluetoothOn = false;
-        mBluetoothOnTimer.readSummaryFromParcelLocked(in);
-        for (int i=0; i< NUM_BLUETOOTH_STATES; i++) {
-            mBluetoothStateTimer[i].readSummaryFromParcelLocked(in);
-        }
-
         for (int i = 0; i < NUM_CONTROLLER_ACTIVITY_TYPES; i++) {
             mBluetoothActivityCounters[i].readSummaryFromParcelLocked(in);
         }
-
         for (int i = 0; i < NUM_CONTROLLER_ACTIVITY_TYPES; i++) {
             mWifiActivityCounters[i].readSummaryFromParcelLocked(in);
         }
@@ -9246,10 +9176,6 @@
         for (int i=0; i<NUM_WIFI_SIGNAL_STRENGTH_BINS; i++) {
             mWifiSignalStrengthsTimer[i].writeSummaryFromParcelLocked(out, NOWREAL_SYS);
         }
-        mBluetoothOnTimer.writeSummaryFromParcelLocked(out, NOWREAL_SYS);
-        for (int i=0; i< NUM_BLUETOOTH_STATES; i++) {
-            mBluetoothStateTimer[i].writeSummaryFromParcelLocked(out, NOWREAL_SYS);
-        }
         for (int i=0; i< NUM_CONTROLLER_ACTIVITY_TYPES; i++) {
             mBluetoothActivityCounters[i].writeSummaryFromParcelLocked(out);
         }
@@ -9542,6 +9468,7 @@
         mMobileRadioActiveAdjustedTime = new LongSamplingCounter(mOnBatteryTimeBase, in);
         mMobileRadioActiveUnknownTime = new LongSamplingCounter(mOnBatteryTimeBase, in);
         mMobileRadioActiveUnknownCount = new LongSamplingCounter(mOnBatteryTimeBase, in);
+        mWifiRadioPowerState = DataConnectionRealTimeInfo.DC_POWER_STATE_LOW;
         mWifiOn = false;
         mWifiOnTimer = new StopwatchTimer(null, -4, null, mOnBatteryTimeBase, in);
         mGlobalWifiRunning = false;
@@ -9558,17 +9485,9 @@
             mWifiSignalStrengthsTimer[i] = new StopwatchTimer(null, -800-i,
                     null, mOnBatteryTimeBase, in);
         }
-        mBluetoothOn = false;
-        mBluetoothOnTimer = new StopwatchTimer(null, -6, null, mOnBatteryTimeBase, in);
-        for (int i=0; i< NUM_BLUETOOTH_STATES; i++) {
-            mBluetoothStateTimer[i] = new StopwatchTimer(null, -500-i,
-                    null, mOnBatteryTimeBase, in);
-        }
-
         for (int i = 0; i < NUM_CONTROLLER_ACTIVITY_TYPES; i++) {
             mBluetoothActivityCounters[i] = new LongSamplingCounter(mOnBatteryTimeBase, in);
         }
-
         for (int i = 0; i < NUM_CONTROLLER_ACTIVITY_TYPES; i++) {
             mWifiActivityCounters[i] = new LongSamplingCounter(mOnBatteryTimeBase, in);
         }
@@ -9598,9 +9517,6 @@
         mChargeStepTracker.readFromParcel(in);
         mLastWriteTime = in.readLong();
 
-        mBluetoothPingCount = in.readInt();
-        mBluetoothPingStart = -1;
-
         mKernelWakelockStats.clear();
         int NKW = in.readInt();
         for (int ikw = 0; ikw < NKW; ikw++) {
@@ -9718,10 +9634,6 @@
         for (int i=0; i<NUM_WIFI_SIGNAL_STRENGTH_BINS; i++) {
             mWifiSignalStrengthsTimer[i].writeToParcel(out, uSecRealtime);
         }
-        mBluetoothOnTimer.writeToParcel(out, uSecRealtime);
-        for (int i=0; i< NUM_BLUETOOTH_STATES; i++) {
-            mBluetoothStateTimer[i].writeToParcel(out, uSecRealtime);
-        }
         for (int i=0; i< NUM_CONTROLLER_ACTIVITY_TYPES; i++) {
             mBluetoothActivityCounters[i].writeToParcel(out);
         }
@@ -9748,8 +9660,6 @@
         mChargeStepTracker.writeToParcel(out);
         out.writeLong(mLastWriteTime);
 
-        out.writeInt(getBluetoothPingCount());
-
         if (inclUids) {
             out.writeInt(mKernelWakelockStats.size());
             for (Map.Entry<String, SamplingTimer> ent : mKernelWakelockStats.entrySet()) {
@@ -9851,6 +9761,7 @@
             mMobileRadioActiveTimer.logState(pr, "  ");
             pr.println("*** Mobile network active adjusted timer:");
             mMobileRadioActiveAdjustedTime.logState(pr, "  ");
+            pr.println("*** mWifiRadioPowerState=" + mWifiRadioPowerState);
             pr.println("*** Wifi timer:");
             mWifiOnTimer.logState(pr, "  ");
             pr.println("*** WifiRunning timer:");
@@ -9867,12 +9778,6 @@
                 pr.println("*** Wifi signal strength #" + i + ":");
                 mWifiSignalStrengthsTimer[i].logState(pr, "  ");
             }
-            pr.println("*** Bluetooth timer:");
-            mBluetoothOnTimer.logState(pr, "  ");
-            for (int i=0; i< NUM_BLUETOOTH_STATES; i++) {
-                pr.println("*** Bluetooth active type #" + i + ":");
-                mBluetoothStateTimer[i].logState(pr, "  ");
-            }
             pr.println("*** Flashlight timer:");
             mFlashlightOnTimer.logState(pr, "  ");
         }
diff --git a/core/java/com/android/internal/os/WifiPowerCalculator.java b/core/java/com/android/internal/os/WifiPowerCalculator.java
index 4fb8b55..961b0df 100644
--- a/core/java/com/android/internal/os/WifiPowerCalculator.java
+++ b/core/java/com/android/internal/os/WifiPowerCalculator.java
@@ -71,7 +71,7 @@
         app.wifiRunningTimeMs = idleTimeMs + rxTimeMs + txTimeMs;
 
         double powerDrain = stats.getWifiControllerActivity(BatteryStats.CONTROLLER_POWER_DRAIN,
-                statsType) / (1000*60*60);
+                statsType) / (double)(1000*60*60);
         if (powerDrain == 0) {
             // Some controllers do not report power drain, so we can calculate it here.
             powerDrain = ((idleTimeMs * mIdleCurrentMa) + (txTimeMs * mTxCurrentMa)
diff --git a/core/java/com/android/internal/os/WifiPowerEstimator.java b/core/java/com/android/internal/os/WifiPowerEstimator.java
index 0172367..c4e2ef6 100644
--- a/core/java/com/android/internal/os/WifiPowerEstimator.java
+++ b/core/java/com/android/internal/os/WifiPowerEstimator.java
@@ -63,7 +63,7 @@
         mTotalAppWifiRunningTimeMs += app.wifiRunningTimeMs;
         final double wifiLockPower = (app.wifiRunningTimeMs * mWifiPowerOn) / (1000*60*60);
 
-        final long wifiScanTimeMs = u.getWifiScanTime(rawRealtimeUs, statsType);
+        final long wifiScanTimeMs = u.getWifiScanTime(rawRealtimeUs, statsType) / 1000;
         final double wifiScanPower = (wifiScanTimeMs * mWifiPowerScan) / (1000*60*60);
 
         double wifiBatchScanPower = 0;
diff --git a/core/java/com/android/internal/statusbar/IStatusBarService.aidl b/core/java/com/android/internal/statusbar/IStatusBarService.aidl
index 6cb839e..87e0603 100644
--- a/core/java/com/android/internal/statusbar/IStatusBarService.aidl
+++ b/core/java/com/android/internal/statusbar/IStatusBarService.aidl
@@ -27,6 +27,7 @@
     void expandNotificationsPanel();
     void collapsePanels();
     void disable(int what, IBinder token, String pkg);
+    void disableForUser(int what, IBinder token, String pkg, int userId);
     void setIcon(String slot, String iconPackage, int iconId, int iconLevel, String contentDescription);
     void setIconVisibility(String slot, boolean visible);
     void removeIcon(String slot);
diff --git a/core/java/com/android/internal/widget/ActionBarContextView.java b/core/java/com/android/internal/widget/ActionBarContextView.java
index 42d875d..2946456 100644
--- a/core/java/com/android/internal/widget/ActionBarContextView.java
+++ b/core/java/com/android/internal/widget/ActionBarContextView.java
@@ -367,7 +367,8 @@
 
         if (mTitleLayout != null && mCustomView == null) {
             if (mTitleOptional) {
-                final int titleWidthSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
+                final int titleWidthSpec = MeasureSpec.makeMeasureSpec(contentWidth,
+                        MeasureSpec.UNSPECIFIED);
                 mTitleLayout.measure(titleWidthSpec, childSpecHeight);
                 final int titleWidth = mTitleLayout.getMeasuredWidth();
                 final boolean titleFits = titleWidth <= availableWidth;
diff --git a/core/java/com/android/internal/widget/FloatingToolbar.java b/core/java/com/android/internal/widget/FloatingToolbar.java
index 2219ad1..1e96945 100644
--- a/core/java/com/android/internal/widget/FloatingToolbar.java
+++ b/core/java/com/android/internal/widget/FloatingToolbar.java
@@ -203,8 +203,8 @@
         if (mContentRect.top > mPopup.getHeight()) {
             y = mContentRect.top - mPopup.getHeight();
             mOverflowDirection = FloatingToolbarPopup.OVERFLOW_DIRECTION_UP;
-        } else if (mContentRect.top > getEstimatedToolbarHeight(mContext)) {
-            y = mContentRect.top - getEstimatedToolbarHeight(mContext);
+        } else if (mContentRect.top > mPopup.getToolbarHeightWithVerticalMargin()) {
+            y = mContentRect.top - mPopup.getToolbarHeightWithVerticalMargin();
             mOverflowDirection = FloatingToolbarPopup.OVERFLOW_DIRECTION_DOWN;
         } else {
             y = mContentRect.bottom;
@@ -264,7 +264,8 @@
         private final View mParent;
         private final PopupWindow mPopupWindow;
         private final ViewGroup mContentContainer;
-        private final int mPadding;
+        private final int mMarginHorizontal;
+        private final int mMarginVertical;
 
         private final Animation.AnimationListener mOnOverflowOpened =
                 new Animation.AnimationListener() {
@@ -365,7 +366,10 @@
                                             .TOUCHABLE_INSETS_REGION);
                                 }
                             });
-            mPadding = parent.getResources().getDimensionPixelSize(R.dimen.floating_toolbar_margin);
+            mMarginHorizontal = parent.getResources()
+                    .getDimensionPixelSize(R.dimen.floating_toolbar_horizontal_margin);
+            mMarginVertical = parent.getResources()
+                    .getDimensionPixelSize(R.dimen.floating_toolbar_vertical_margin);
         }
 
         /**
@@ -474,6 +478,10 @@
             return mContentContainer.getContext();
         }
 
+        int getToolbarHeightWithVerticalMargin() {
+            return getEstimatedToolbarHeight(mParent.getContext()) + mMarginVertical * 2;
+        }
+
         /**
          * Performs the "grow and fade in from the bottom" animation on the floating popup.
          */
@@ -506,7 +514,7 @@
 
             mMainPanel.fadeOut(true);
             Size overflowPanelSize = mOverflowPanel.measure();
-            final int targetWidth = getOverflowWidth(mParent.getContext());
+            final int targetWidth = overflowPanelSize.getWidth();
             final int targetHeight = overflowPanelSize.getHeight();
             final boolean morphUpwards = (mOverflowDirection == OVERFLOW_DIRECTION_UP);
             final int startWidth = mContentContainer.getWidth();
@@ -624,10 +632,14 @@
 
             // Make sure the main panel is at the correct position.
             if (mContentContainer.getChildAt(0) == mMainPanel.getView()) {
-                mContentContainer.setX(mPadding);
-                float y = mPadding;
+                float x = mPopupWindow.getWidth()
+                        - (mMainPanel.getView().getMeasuredWidth() + mMarginHorizontal);
+                mContentContainer.setX(x);
+
+                float y = mMarginVertical;
                 if  (mOverflowDirection == OVERFLOW_DIRECTION_UP) {
-                    y = getHeight() - getEstimatedToolbarHeight(mParent.getContext()) - mPadding;
+                    y = getHeight()
+                            - (mMainPanel.getView().getMeasuredHeight() + mMarginVertical);
                 }
                 mContentContainer.setY(y);
             }
@@ -661,8 +673,8 @@
                 width = Math.max(width, overflowPanelSize.getWidth());
                 height = Math.max(height, overflowPanelSize.getHeight());
             }
-            mPopupWindow.setWidth(width + mPadding * 2);
-            mPopupWindow.setHeight(height + mPadding * 2);
+            mPopupWindow.setWidth(width + mMarginHorizontal * 2);
+            mPopupWindow.setHeight(height + mMarginVertical * 2);
         }
 
         /**
@@ -748,22 +760,22 @@
                 final MenuItem menuItem = remainingMenuItems.peek();
                 Button menuItemButton = createMenuItemButton(mContext, menuItem);
 
-                // Adding additional left padding for the first button to even out button spacing.
+                // Adding additional start padding for the first button to even out button spacing.
                 if (isFirstItem) {
-                    menuItemButton.setPadding(
-                            2 * menuItemButton.getPaddingLeft(),
+                    menuItemButton.setPaddingRelative(
+                            (int) (1.5 * menuItemButton.getPaddingStart()),
                             menuItemButton.getPaddingTop(),
-                            menuItemButton.getPaddingRight(),
+                            menuItemButton.getPaddingEnd(),
                             menuItemButton.getPaddingBottom());
                     isFirstItem = false;
                 }
 
-                // Adding additional right padding for the last button to even out button spacing.
+                // Adding additional end padding for the last button to even out button spacing.
                 if (remainingMenuItems.size() == 1) {
-                    menuItemButton.setPadding(
-                            menuItemButton.getPaddingLeft(),
+                    menuItemButton.setPaddingRelative(
+                            menuItemButton.getPaddingStart(),
                             menuItemButton.getPaddingTop(),
-                            2 * menuItemButton.getPaddingRight(),
+                            (int) (1.5 * menuItemButton.getPaddingEnd()),
                             menuItemButton.getPaddingBottom());
                 }
 
@@ -836,10 +848,12 @@
         private final ViewGroup mBackButtonContainer;
         private final View mBackButton;
         private final ListView mListView;
+        private final TextView mListViewItemWidthCalculator;
         private final ViewFader mViewFader;
         private final Runnable mCloseOverflow;
 
         private MenuItem.OnMenuItemClickListener mOnMenuItemClickListener;
+        private int mOverflowWidth = 0;
 
         /**
          * Initializes a floating toolbar popup overflow view panel.
@@ -865,7 +879,7 @@
             mBackButtonContainer = new LinearLayout(context);
             mBackButtonContainer.addView(mBackButton);
 
-            mListView = createOverflowListView(context);
+            mListView = createOverflowListView();
             mListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
                 @Override
                 public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
@@ -878,6 +892,10 @@
 
             mContentView.addView(mListView);
             mContentView.addView(mBackButtonContainer);
+
+            mListViewItemWidthCalculator = createOverflowMenuItemButton(context);
+            mListViewItemWidthCalculator.setLayoutParams(new ViewGroup.LayoutParams(
+                    ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
         }
 
         /**
@@ -888,6 +906,7 @@
             overflowListViewAdapter.clear();
             overflowListViewAdapter.addAll(menuItems);
             setListViewHeight();
+            setOverflowWidth();
         }
 
         public void setOnMenuItemClickListener(MenuItem.OnMenuItemClickListener listener) {
@@ -943,7 +962,21 @@
             mListView.setLayoutParams(params);
         }
 
-        private static ListView createOverflowListView(final Context context) {
+        private int setOverflowWidth() {
+            for (int i = 0; i < mListView.getAdapter().getCount(); i++) {
+                MenuItem menuItem = (MenuItem) mListView.getAdapter().getItem(i);
+                Preconditions.checkNotNull(menuItem);
+                mListViewItemWidthCalculator.setText(menuItem.getTitle());
+                mListViewItemWidthCalculator.measure(
+                        MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
+                mOverflowWidth = Math.max(
+                        mListViewItemWidthCalculator.getMeasuredWidth(), mOverflowWidth);
+            }
+            return mOverflowWidth;
+        }
+
+        private ListView createOverflowListView() {
+            final Context context = mContentView.getContext();
             final ListView overflowListView = new ListView(context);
             overflowListView.setLayoutParams(new ViewGroup.LayoutParams(
                     ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
@@ -962,6 +995,7 @@
                             MenuItem menuItem = getItem(position);
                             menuButton.setText(menuItem.getTitle());
                             menuButton.setContentDescription(menuItem.getTitle());
+                            menuButton.setMinimumWidth(mOverflowWidth);
                             return menuButton;
                         }
                     };
@@ -1036,7 +1070,8 @@
     private static PopupWindow createPopupWindow(View content) {
         ViewGroup popupContentHolder = new LinearLayout(content.getContext());
         PopupWindow popupWindow = new PopupWindow(popupContentHolder);
-        popupWindow.setWindowLayoutType(WindowManager.LayoutParams.TYPE_APPLICATION_SUB_PANEL);
+        popupWindow.setWindowLayoutType(
+                WindowManager.LayoutParams.TYPE_APPLICATION_ABOVE_SUB_PANEL);
         popupWindow.setAnimationStyle(0);
         popupWindow.setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
         content.setLayoutParams(new ViewGroup.LayoutParams(
@@ -1077,11 +1112,6 @@
         return shrinkFadeOutFromBottomAnimation;
     }
 
-    private static int getOverflowWidth(Context context) {
-        return context.getResources()
-                .getDimensionPixelSize(R.dimen.floating_toolbar_overflow_width);
-    }
-
     private static int getEstimatedToolbarHeight(Context context) {
         return context.getResources().getDimensionPixelSize(R.dimen.floating_toolbar_height);
     }
diff --git a/core/java/com/android/internal/widget/LockPatternUtils.java b/core/java/com/android/internal/widget/LockPatternUtils.java
index 123d1ac..aa60eba 100644
--- a/core/java/com/android/internal/widget/LockPatternUtils.java
+++ b/core/java/com/android/internal/widget/LockPatternUtils.java
@@ -472,6 +472,8 @@
             updateEncryptionPassword(StorageManager.CRYPT_TYPE_DEFAULT, null);
         }
 
+        setCredentialRequiredToDecrypt(false);
+
         getDevicePolicyManager().setActivePasswordState(
                 DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED, 0, 0, 0, 0, 0, 0, 0, userHandle);
 
diff --git a/core/java/com/android/internal/widget/SlidingTab.java b/core/java/com/android/internal/widget/SlidingTab.java
index 65feab1..0066ed0 100644
--- a/core/java/com/android/internal/widget/SlidingTab.java
+++ b/core/java/com/android/internal/widget/SlidingTab.java
@@ -400,11 +400,13 @@
         /**
          * Ensure all the dependent widgets are measured.
          */
-        public void measure() {
-            tab.measure(View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED),
-                    View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED));
-            text.measure(View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED),
-                    View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED));
+        public void measure(int widthMeasureSpec, int heightMeasureSpec) {
+            int width = MeasureSpec.getSize(widthMeasureSpec);
+            int height = MeasureSpec.getSize(heightMeasureSpec);
+            tab.measure(View.MeasureSpec.makeMeasureSpec(width, View.MeasureSpec.UNSPECIFIED),
+                    View.MeasureSpec.makeMeasureSpec(height, View.MeasureSpec.UNSPECIFIED));
+            text.measure(View.MeasureSpec.makeMeasureSpec(width, View.MeasureSpec.UNSPECIFIED),
+                    View.MeasureSpec.makeMeasureSpec(height, View.MeasureSpec.UNSPECIFIED));
         }
 
         /**
@@ -491,8 +493,8 @@
             }
         }
 
-        mLeftSlider.measure();
-        mRightSlider.measure();
+        mLeftSlider.measure(widthMeasureSpec, heightMeasureSpec);
+        mRightSlider.measure(widthMeasureSpec, heightMeasureSpec);
         final int leftTabWidth = mLeftSlider.getTabWidth();
         final int rightTabWidth = mRightSlider.getTabWidth();
         final int leftTabHeight = mLeftSlider.getTabHeight();
diff --git a/core/jni/android_media_AudioRecord.cpp b/core/jni/android_media_AudioRecord.cpp
index c9b0e76..33db4a85 100644
--- a/core/jni/android_media_AudioRecord.cpp
+++ b/core/jni/android_media_AudioRecord.cpp
@@ -356,95 +356,58 @@
     android_media_AudioRecord_release(env, thiz);
 }
 
-
-// ----------------------------------------------------------------------------
-static jint android_media_AudioRecord_readInByteArray(JNIEnv *env,  jobject thiz,
-                                                        jbyteArray javaAudioData,
-                                                        jint offsetInBytes, jint sizeInBytes) {
-    jbyte* recordBuff = NULL;
-    // get the audio recorder from which we'll read new audio samples
-    sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz);
-    if (lpRecorder == NULL) {
-        ALOGE("Unable to retrieve AudioRecord object, can't record");
-        return 0;
-    }
-
-    if (!javaAudioData) {
-        ALOGE("Invalid Java array to store recorded audio, can't record");
-        return 0;
-    }
-
-    // get the pointer to where we'll record the audio
-    // NOTE: We may use GetPrimitiveArrayCritical() when the JNI implementation changes in such
-    // a way that it becomes much more efficient. When doing so, we will have to prevent the
-    // AudioSystem callback to be called while in critical section (in case of media server
-    // process crash for instance)
-    recordBuff = (jbyte *)env->GetByteArrayElements(javaAudioData, NULL);
-
-    if (recordBuff == NULL) {
-        ALOGE("Error retrieving destination for recorded audio data, can't record");
-        return 0;
-    }
-
-    // read the new audio data from the native AudioRecord object
-    ssize_t readSize = lpRecorder->read(recordBuff + offsetInBytes, sizeInBytes);
-    env->ReleaseByteArrayElements(javaAudioData, recordBuff, 0);
-
-    if (readSize < 0) {
-        readSize = (jint)AUDIO_JAVA_INVALID_OPERATION;
-    }
-    return (jint) readSize;
+// overloaded JNI array helper functions
+static inline
+jbyte *envGetArrayElements(JNIEnv *env, jbyteArray array, jboolean *isCopy) {
+    return env->GetByteArrayElements(array, isCopy);
 }
 
-// ----------------------------------------------------------------------------
-static jint android_media_AudioRecord_readInShortArray(JNIEnv *env,  jobject thiz,
-                                                        jshortArray javaAudioData,
-                                                        jint offsetInShorts, jint sizeInShorts) {
-
-    jshort* recordBuff = NULL;
-    // get the audio recorder from which we'll read new audio samples
-    sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz);
-    if (lpRecorder == NULL) {
-        ALOGE("Unable to retrieve AudioRecord object, can't record");
-        return 0;
-    }
-
-    if (!javaAudioData) {
-        ALOGE("Invalid Java array to store recorded audio, can't record");
-        return 0;
-    }
-
-    // get the pointer to where we'll record the audio
-    // NOTE: We may use GetPrimitiveArrayCritical() when the JNI implementation changes in such
-    // a way that it becomes much more efficient. When doing so, we will have to prevent the
-    // AudioSystem callback to be called while in critical section (in case of media server
-    // process crash for instance)
-    recordBuff = (jshort *)env->GetShortArrayElements(javaAudioData, NULL);
-
-    if (recordBuff == NULL) {
-        ALOGE("Error retrieving destination for recorded audio data, can't record");
-        return 0;
-    }
-
-    // read the new audio data from the native AudioRecord object
-    const size_t sizeInBytes = sizeInShorts * sizeof(short);
-    ssize_t readSize = lpRecorder->read(recordBuff + offsetInShorts, sizeInBytes);
-
-    env->ReleaseShortArrayElements(javaAudioData, recordBuff, 0);
-
-    if (readSize < 0) {
-        readSize = (jint)AUDIO_JAVA_INVALID_OPERATION;
-    } else {
-        readSize /= sizeof(short);
-    }
-    return (jint) readSize;
+static inline
+void envReleaseArrayElements(JNIEnv *env, jbyteArray array, jbyte *elems, jint mode) {
+    env->ReleaseByteArrayElements(array, elems, mode);
 }
 
-// ----------------------------------------------------------------------------
-static jint android_media_AudioRecord_readInFloatArray(JNIEnv *env,  jobject thiz,
-                                                        jfloatArray javaAudioData,
-                                                        jint offsetInFloats, jint sizeInFloats,
-                                                        jboolean isReadBlocking) {
+static inline
+jshort *envGetArrayElements(JNIEnv *env, jshortArray array, jboolean *isCopy) {
+    return env->GetShortArrayElements(array, isCopy);
+}
+
+static inline
+void envReleaseArrayElements(JNIEnv *env, jshortArray array, jshort *elems, jint mode) {
+    env->ReleaseShortArrayElements(array, elems, mode);
+}
+
+static inline
+jfloat *envGetArrayElements(JNIEnv *env, jfloatArray array, jboolean *isCopy) {
+    return env->GetFloatArrayElements(array, isCopy);
+}
+
+static inline
+void envReleaseArrayElements(JNIEnv *env, jfloatArray array, jfloat *elems, jint mode) {
+    env->ReleaseFloatArrayElements(array, elems, mode);
+}
+
+static inline
+jint interpretReadSizeError(ssize_t readSize) {
+    ALOGE_IF(readSize != WOULD_BLOCK, "Error %zd during AudioRecord native read", readSize);
+    switch (readSize) {
+    case WOULD_BLOCK:
+        return (jint)0;
+    case BAD_VALUE:
+        return (jint)AUDIO_JAVA_BAD_VALUE;
+    default:
+        // may be possible for other errors such as
+        // NO_INIT to happen if restoreRecord_l fails.
+    case INVALID_OPERATION:
+        return (jint)AUDIO_JAVA_INVALID_OPERATION;
+    }
+}
+
+template <typename T>
+static jint android_media_AudioRecord_readInArray(JNIEnv *env,  jobject thiz,
+                                                  T javaAudioData,
+                                                  jint offsetInSamples, jint sizeInSamples,
+                                                  jboolean isReadBlocking) {
     // get the audio recorder from which we'll read new audio samples
     sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz);
     if (lpRecorder == NULL) {
@@ -453,76 +416,68 @@
     }
 
     if (javaAudioData == NULL) {
-         ALOGE("Invalid Java array to store recorded audio");
-         return (jint)AUDIO_JAVA_BAD_VALUE;
-     }
+        ALOGE("Invalid Java array to store recorded audio");
+        return (jint)AUDIO_JAVA_BAD_VALUE;
+    }
 
-    // get the pointer to where we'll record the audio
     // NOTE: We may use GetPrimitiveArrayCritical() when the JNI implementation changes in such
     // a way that it becomes much more efficient. When doing so, we will have to prevent the
     // AudioSystem callback to be called while in critical section (in case of media server
     // process crash for instance)
-    jfloat *recordBuff = (jfloat *)env->GetFloatArrayElements(javaAudioData, NULL);
+
+    // get the pointer to where we'll record the audio
+    auto *recordBuff = envGetArrayElements(env, javaAudioData, NULL);
     if (recordBuff == NULL) {
         ALOGE("Error retrieving destination for recorded audio data");
         return (jint)AUDIO_JAVA_BAD_VALUE;
     }
 
     // read the new audio data from the native AudioRecord object
-    const size_t sizeInBytes = sizeInFloats * sizeof(float);
-    ssize_t readSize = lpRecorder->read(recordBuff + offsetInFloats, sizeInBytes);
+    const size_t sizeInBytes = sizeInSamples * sizeof(*recordBuff);
+    ssize_t readSize = lpRecorder->read(
+            recordBuff + offsetInSamples, sizeInBytes, isReadBlocking == JNI_TRUE /* blocking */);
 
-    env->ReleaseFloatArrayElements(javaAudioData, recordBuff, 0);
+    envReleaseArrayElements(env, javaAudioData, recordBuff, 0);
 
     if (readSize < 0) {
-        ALOGE_IF(readSize != WOULD_BLOCK, "Error %zd during AudioRecord native read", readSize);
-        switch (readSize) {
-        case WOULD_BLOCK:
-            return (jint)0;
-        case BAD_VALUE:
-            return (jint)AUDIO_JAVA_BAD_VALUE;
-        default:
-            // may be possible for other errors such as
-            // NO_INIT to happen if restoreRecord_l fails.
-        case INVALID_OPERATION:
-            return (jint)AUDIO_JAVA_INVALID_OPERATION;
-        }
+        return interpretReadSizeError(readSize);
     }
-    return (jint)(readSize / sizeof(float));
+    return (jint)(readSize / sizeof(*recordBuff));
 }
 
 // ----------------------------------------------------------------------------
 static jint android_media_AudioRecord_readInDirectBuffer(JNIEnv *env,  jobject thiz,
-                                                  jobject jBuffer, jint sizeInBytes) {
+                                                         jobject jBuffer, jint sizeInBytes,
+                                                         jboolean isReadBlocking) {
     // get the audio recorder from which we'll read new audio samples
     sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz);
     if (lpRecorder==NULL)
-        return 0;
+        return (jint)AUDIO_JAVA_INVALID_OPERATION;
 
     // direct buffer and direct access supported?
     long capacity = env->GetDirectBufferCapacity(jBuffer);
     if (capacity == -1) {
         // buffer direct access is not supported
         ALOGE("Buffer direct access is not supported, can't record");
-        return 0;
+        return (jint)AUDIO_JAVA_BAD_VALUE;
     }
     //ALOGV("capacity = %ld", capacity);
     jbyte* nativeFromJavaBuf = (jbyte*) env->GetDirectBufferAddress(jBuffer);
     if (nativeFromJavaBuf==NULL) {
         ALOGE("Buffer direct access is not supported, can't record");
-        return 0;
+        return (jint)AUDIO_JAVA_BAD_VALUE;
     }
 
     // read new data from the recorder
     ssize_t readSize = lpRecorder->read(nativeFromJavaBuf,
-                                   capacity < sizeInBytes ? capacity : sizeInBytes);
+                                        capacity < sizeInBytes ? capacity : sizeInBytes,
+                                        isReadBlocking == JNI_TRUE /* blocking */);
     if (readSize < 0) {
-        readSize = (jint)AUDIO_JAVA_INVALID_OPERATION;
+        return interpretReadSizeError(readSize);
     }
     return (jint)readSize;
 }
 
-
 // ----------------------------------------------------------------------------
 static jint android_media_AudioRecord_get_native_frame_count(JNIEnv *env,  jobject thiz) {
     sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz);
@@ -633,12 +588,15 @@
     {"native_finalize",      "()V",    (void *)android_media_AudioRecord_finalize},
     {"native_release",       "()V",    (void *)android_media_AudioRecord_release},
     {"native_read_in_byte_array",
-                             "([BII)I", (void *)android_media_AudioRecord_readInByteArray},
+                             "([BIIZ)I",
+                                     (void *)android_media_AudioRecord_readInArray<jbyteArray>},
     {"native_read_in_short_array",
-                             "([SII)I", (void *)android_media_AudioRecord_readInShortArray},
+                             "([SIIZ)I",
+                                     (void *)android_media_AudioRecord_readInArray<jshortArray>},
     {"native_read_in_float_array",
-                             "([FIIZ)I", (void *)android_media_AudioRecord_readInFloatArray},
-    {"native_read_in_direct_buffer","(Ljava/lang/Object;I)I",
+                             "([FIIZ)I",
+                                     (void *)android_media_AudioRecord_readInArray<jfloatArray>},
+    {"native_read_in_direct_buffer","(Ljava/lang/Object;IZ)I",
                                        (void *)android_media_AudioRecord_readInDirectBuffer},
     {"native_get_native_frame_count",
                              "()I",    (void *)android_media_AudioRecord_get_native_frame_count},
diff --git a/core/jni/android_media_AudioTrack.cpp b/core/jni/android_media_AudioTrack.cpp
index 1f688e1..8d3a9aa 100644
--- a/core/jni/android_media_AudioTrack.cpp
+++ b/core/jni/android_media_AudioTrack.cpp
@@ -510,14 +510,47 @@
     android_media_AudioTrack_release(env, thiz);
 }
 
+// overloaded JNI array helper functions (same as in android_media_AudioRecord)
+static inline
+jbyte *envGetArrayElements(JNIEnv *env, jbyteArray array, jboolean *isCopy) {
+    return env->GetByteArrayElements(array, isCopy);
+}
+
+static inline
+void envReleaseArrayElements(JNIEnv *env, jbyteArray array, jbyte *elems, jint mode) {
+    env->ReleaseByteArrayElements(array, elems, mode);
+}
+
+static inline
+jshort *envGetArrayElements(JNIEnv *env, jshortArray array, jboolean *isCopy) {
+    return env->GetShortArrayElements(array, isCopy);
+}
+
+static inline
+void envReleaseArrayElements(JNIEnv *env, jshortArray array, jshort *elems, jint mode) {
+    env->ReleaseShortArrayElements(array, elems, mode);
+}
+
+static inline
+jfloat *envGetArrayElements(JNIEnv *env, jfloatArray array, jboolean *isCopy) {
+    return env->GetFloatArrayElements(array, isCopy);
+}
+
+static inline
+void envReleaseArrayElements(JNIEnv *env, jfloatArray array, jfloat *elems, jint mode) {
+    env->ReleaseFloatArrayElements(array, elems, mode);
+}
+
 // ----------------------------------------------------------------------------
-jint writeToTrack(const sp<AudioTrack>& track, jint audioFormat, const jbyte* data,
-                  jint offsetInBytes, jint sizeInBytes, bool blocking = true) {
+template <typename T>
+static jint writeToTrack(const sp<AudioTrack>& track, jint audioFormat, const T *data,
+                         jint offsetInSamples, jint sizeInSamples, bool blocking) {
     // give the data to the native AudioTrack object (the data starts at the offset)
     ssize_t written = 0;
     // regular write() or copy the data to the AudioTrack's shared memory?
+    size_t sizeInBytes = sizeInSamples * sizeof(T);
     if (track->sharedBuffer() == 0) {
-        written = track->write(data + offsetInBytes, sizeInBytes, blocking);
+        written = track->write(data + offsetInSamples, sizeInBytes, blocking);
         // for compatibility with earlier behavior of write(), return 0 in this case
         if (written == (ssize_t) WOULD_BLOCK) {
             written = 0;
@@ -527,55 +560,59 @@
         if ((size_t)sizeInBytes > track->sharedBuffer()->size()) {
             sizeInBytes = track->sharedBuffer()->size();
         }
-        memcpy(track->sharedBuffer()->pointer(), data + offsetInBytes, sizeInBytes);
+        memcpy(track->sharedBuffer()->pointer(), data + offsetInSamples, sizeInBytes);
         written = sizeInBytes;
     }
+    if (written > 0) {
+        return written / sizeof(T);
+    }
+    // for compatibility, error codes pass through unchanged
     return written;
 }
 
 // ----------------------------------------------------------------------------
-static jint android_media_AudioTrack_write_byte(JNIEnv *env,  jobject thiz,
-                                                  jbyteArray javaAudioData,
-                                                  jint offsetInBytes, jint sizeInBytes,
-                                                  jint javaAudioFormat,
-                                                  jboolean isWriteBlocking) {
-    //ALOGV("android_media_AudioTrack_write_byte(offset=%d, sizeInBytes=%d) called",
-    //    offsetInBytes, sizeInBytes);
+template <typename T>
+static jint android_media_AudioTrack_writeArray(JNIEnv *env, jobject thiz,
+                                                T javaAudioData,
+                                                jint offsetInSamples, jint sizeInSamples,
+                                                jint javaAudioFormat,
+                                                jboolean isWriteBlocking) {
+    //ALOGV("android_media_AudioTrack_writeArray(offset=%d, sizeInSamples=%d) called",
+    //        offsetInSamples, sizeInSamples);
     sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
     if (lpTrack == NULL) {
         jniThrowException(env, "java/lang/IllegalStateException",
             "Unable to retrieve AudioTrack pointer for write()");
-        return 0;
+        return (jint)AUDIO_JAVA_INVALID_OPERATION;
     }
 
-    // get the pointer for the audio data from the java array
+    if (javaAudioData == NULL) {
+        ALOGE("NULL java array of audio data to play");
+        return (jint)AUDIO_JAVA_BAD_VALUE;
+    }
+
     // NOTE: We may use GetPrimitiveArrayCritical() when the JNI implementation changes in such
     // a way that it becomes much more efficient. When doing so, we will have to prevent the
     // AudioSystem callback to be called while in critical section (in case of media server
     // process crash for instance)
-    jbyte* cAudioData = NULL;
-    if (javaAudioData) {
-        cAudioData = (jbyte *)env->GetByteArrayElements(javaAudioData, NULL);
-        if (cAudioData == NULL) {
-            ALOGE("Error retrieving source of audio data to play, can't play");
-            return 0; // out of memory or no data to load
-        }
-    } else {
-        ALOGE("NULL java array of audio data to play, can't play");
-        return 0;
+
+    // get the pointer for the audio data from the java array
+    auto cAudioData = envGetArrayElements(env, javaAudioData, NULL);
+    if (cAudioData == NULL) {
+        ALOGE("Error retrieving source of audio data to play");
+        return (jint)AUDIO_JAVA_BAD_VALUE; // out of memory or no data to load
     }
 
-    jint written = writeToTrack(lpTrack, javaAudioFormat, cAudioData, offsetInBytes, sizeInBytes,
-            isWriteBlocking == JNI_TRUE /* blocking */);
+    jint samplesWritten = writeToTrack(lpTrack, javaAudioFormat, cAudioData,
+            offsetInSamples, sizeInSamples, isWriteBlocking == JNI_TRUE /* blocking */);
 
-    env->ReleaseByteArrayElements(javaAudioData, cAudioData, 0);
+    envReleaseArrayElements(env, javaAudioData, cAudioData, 0);
 
-    //ALOGV("write wrote %d (tried %d) bytes in the native AudioTrack with offset %d",
-    //     (int)written, (int)(sizeInBytes), (int)offsetInBytes);
-    return written;
+    //ALOGV("write wrote %d (tried %d) samples in the native AudioTrack with offset %d",
+    //        (int)samplesWritten, (int)(sizeInSamples), (int)offsetInSamples);
+    return samplesWritten;
 }
 
-
 // ----------------------------------------------------------------------------
 static jint android_media_AudioTrack_write_native_bytes(JNIEnv *env,  jobject thiz,
         jbyteArray javaBytes, jint byteOffset, jint sizeInBytes,
@@ -586,7 +623,7 @@
     if (lpTrack == NULL) {
         jniThrowException(env, "java/lang/IllegalStateException",
                 "Unable to retrieve AudioTrack pointer for write()");
-        return 0;
+        return (jint)AUDIO_JAVA_INVALID_OPERATION;
     }
 
     ScopedBytesRO bytes(env, javaBytes);
@@ -602,90 +639,6 @@
 }
 
 // ----------------------------------------------------------------------------
-static jint android_media_AudioTrack_write_short(JNIEnv *env,  jobject thiz,
-                                                  jshortArray javaAudioData,
-                                                  jint offsetInShorts, jint sizeInShorts,
-                                                  jint javaAudioFormat) {
-
-    //ALOGV("android_media_AudioTrack_write_short(offset=%d, sizeInShorts=%d) called",
-    //    offsetInShorts, sizeInShorts);
-    sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
-    if (lpTrack == NULL) {
-        jniThrowException(env, "java/lang/IllegalStateException",
-            "Unable to retrieve AudioTrack pointer for write()");
-        return 0;
-    }
-
-    // get the pointer for the audio data from the java array
-    // NOTE: We may use GetPrimitiveArrayCritical() when the JNI implementation changes in such
-    // a way that it becomes much more efficient. When doing so, we will have to prevent the
-    // AudioSystem callback to be called while in critical section (in case of media server
-    // process crash for instance)
-    jshort* cAudioData = NULL;
-    if (javaAudioData) {
-        cAudioData = (jshort *)env->GetShortArrayElements(javaAudioData, NULL);
-        if (cAudioData == NULL) {
-            ALOGE("Error retrieving source of audio data to play, can't play");
-            return 0; // out of memory or no data to load
-        }
-    } else {
-        ALOGE("NULL java array of audio data to play, can't play");
-        return 0;
-    }
-    jint written = writeToTrack(lpTrack, javaAudioFormat, (jbyte *)cAudioData,
-                                offsetInShorts * sizeof(short), sizeInShorts * sizeof(short),
-            true /*blocking write, legacy behavior*/);
-    env->ReleaseShortArrayElements(javaAudioData, cAudioData, 0);
-
-    if (written > 0) {
-        written /= sizeof(short);
-    }
-    //ALOGV("write wrote %d (tried %d) shorts in the native AudioTrack with offset %d",
-    //     (int)written, (int)(sizeInShorts), (int)offsetInShorts);
-
-    return written;
-}
-
-
-// ----------------------------------------------------------------------------
-static jint android_media_AudioTrack_write_float(JNIEnv *env,  jobject thiz,
-                                                  jfloatArray javaAudioData,
-                                                  jint offsetInFloats, jint sizeInFloats,
-                                                  jint javaAudioFormat,
-                                                  jboolean isWriteBlocking) {
-
-    sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
-    if (lpTrack == NULL) {
-        jniThrowException(env, "java/lang/IllegalStateException",
-            "Unable to retrieve AudioTrack pointer for write()");
-        return 0;
-    }
-
-    jfloat* cAudioData = NULL;
-    if (javaAudioData) {
-        cAudioData = (jfloat *)env->GetFloatArrayElements(javaAudioData, NULL);
-        if (cAudioData == NULL) {
-            ALOGE("Error retrieving source of audio data to play, can't play");
-            return 0; // out of memory or no data to load
-        }
-    } else {
-        ALOGE("NULL java array of audio data to play, can't play");
-        return 0;
-    }
-    jint written = writeToTrack(lpTrack, javaAudioFormat, (jbyte *)cAudioData,
-                                offsetInFloats * sizeof(float), sizeInFloats * sizeof(float),
-                                isWriteBlocking == JNI_TRUE /* blocking */);
-    env->ReleaseFloatArrayElements(javaAudioData, cAudioData, 0);
-
-    if (written > 0) {
-        written /= sizeof(float);
-    }
-
-    return written;
-}
-
-
-// ----------------------------------------------------------------------------
 static jint android_media_AudioTrack_get_native_frame_count(JNIEnv *env,  jobject thiz) {
     sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
     if (lpTrack == NULL) {
@@ -957,6 +910,13 @@
     return nativeToJavaStatus( lpTrack->attachAuxEffect(effectId) );
 }
 
+static jboolean android_media_AudioTrack_setOutputDevice(
+                JNIEnv *env,  jobject thiz, jint device_id) {
+
+    sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
+    return lpTrack->setOutputDevice(device_id) == NO_ERROR;
+}
+
 // ----------------------------------------------------------------------------
 // ----------------------------------------------------------------------------
 static JNINativeMethod gMethods[] = {
@@ -969,12 +929,12 @@
                                          (void *)android_media_AudioTrack_setup},
     {"native_finalize",      "()V",      (void *)android_media_AudioTrack_finalize},
     {"native_release",       "()V",      (void *)android_media_AudioTrack_release},
-    {"native_write_byte",    "([BIIIZ)I",(void *)android_media_AudioTrack_write_byte},
+    {"native_write_byte",    "([BIIIZ)I",(void *)android_media_AudioTrack_writeArray<jbyteArray>},
     {"native_write_native_bytes",
                              "(Ljava/lang/Object;IIIZ)I",
                                          (void *)android_media_AudioTrack_write_native_bytes},
-    {"native_write_short",   "([SIII)I", (void *)android_media_AudioTrack_write_short},
-    {"native_write_float",   "([FIIIZ)I",(void *)android_media_AudioTrack_write_float},
+    {"native_write_short",   "([SIIIZ)I",(void *)android_media_AudioTrack_writeArray<jshortArray>},
+    {"native_write_float",   "([FIIIZ)I",(void *)android_media_AudioTrack_writeArray<jfloatArray>},
     {"native_setVolume",     "(FF)V",    (void *)android_media_AudioTrack_set_volume},
     {"native_get_native_frame_count",
                              "()I",      (void *)android_media_AudioTrack_get_native_frame_count},
@@ -1002,6 +962,8 @@
                              "(F)I",     (void *)android_media_AudioTrack_setAuxEffectSendLevel},
     {"native_attachAuxEffect",
                              "(I)I",     (void *)android_media_AudioTrack_attachAuxEffect},
+    {"native_setOutputDevice", "(I)Z",
+                             (void *)android_media_AudioTrack_setOutputDevice},
 };
 
 
diff --git a/core/jni/android_text_StaticLayout.cpp b/core/jni/android_text_StaticLayout.cpp
index 87c58d6..5e73ef2 100644
--- a/core/jni/android_text_StaticLayout.cpp
+++ b/core/jni/android_text_StaticLayout.cpp
@@ -132,6 +132,13 @@
     }
 }
 
+static void nSetIndents(JNIEnv* env, jclass, jlong nativePtr, jintArray indents) {
+    ScopedIntArrayRO indentArr(env, indents);
+    std::vector<float> indentVec(indentArr.get(), indentArr.get() + indentArr.size());
+    LineBreaker* b = reinterpret_cast<LineBreaker*>(nativePtr);
+    b->setIndents(indentVec);
+}
+
 // Basically similar to Paint.getTextRunAdvances but with C++ interface
 static jfloat nAddStyleRun(JNIEnv* env, jclass, jlong nativePtr,
         jlong nativePaint, jlong nativeTypeface, jint start, jint end, jboolean isRtl) {
@@ -171,6 +178,7 @@
     {"nLoadHyphenator", "(Ljava/lang/String;)J", (void*) nLoadHyphenator},
     {"nSetLocale", "(JLjava/lang/String;J)V", (void*) nSetLocale},
     {"nSetupParagraph", "(J[CIFIF[III)V", (void*) nSetupParagraph},
+    {"nSetIndents", "(J[I)V", (void*) nSetIndents},
     {"nAddStyleRun", "(JJJIIZ)F", (void*) nAddStyleRun},
     {"nAddMeasuredRun", "(JII[F)V", (void*) nAddMeasuredRun},
     {"nAddReplacementRun", "(JIIF)V", (void*) nAddReplacementRun},
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 13877fb..ec57eba 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -517,13 +517,6 @@
         android:description="@string/permdesc_accessCoarseLocation"
         android:protectionLevel="dangerous" />
 
-    <!-- Allows an application to create mock location providers for testing. -->
-    <permission android:name="android.permission.ACCESS_MOCK_LOCATION"
-        android:permissionGroup="android.permission-group.LOCATION"
-        android:label="@string/permlab_accessMockLocation"
-        android:description="@string/permdesc_accessMockLocation"
-        android:protectionLevel="dangerous" />
-
     <!-- ====================================================================== -->
     <!-- Permissions for accessing the device telephony                         -->
     <!-- ====================================================================== -->
@@ -734,12 +727,6 @@
     <!-- =============================================================== -->
     <eat-comment />
 
-
-    <!-- =============================================================== -->
-    <!-- Permissions for accessing the user dictionary                   -->
-    <!-- =============================================================== -->
-    <eat-comment />
-
     <!-- Used for permissions that provide access to the user
          calendar to create / view events.-->
     <permission-group android:name="android.permission-group.USER_DICTIONARY"
@@ -848,6 +835,12 @@
         android:protectionLevel="signature|system" />
     <uses-permission android:name="android.permission.LOCATION_HARDWARE"/>
 
+    <!-- Allows an application to create mock location providers for testing. -->
+    <permission android:name="android.permission.ACCESS_MOCK_LOCATION"
+        android:label="@string/permlab_accessMockLocation"
+        android:description="@string/permdesc_accessMockLocation"
+        android:protectionLevel="normal" />
+
     <!-- ======================================= -->
     <!-- Permissions for accessing networks -->
     <!-- ======================================= -->
@@ -2408,6 +2401,15 @@
                 android:permissionGroup="android.permission-group.SYSTEM_TOOLS"
                 android:protectionLevel="signature" />
 
+    <!-- The system process that pulls carrier configuration from carrier apps will
+         have this permission. Carrier apps that provide
+         {@link android.service.carrier.CarrierConfigService} should require this
+         permission for clients binding to their service. -->
+    <permission android:name="android.permission.BIND_CARRIER_CONFIG_SERVICE"
+        android:label="@string/permlab_bindCarrierConfigService"
+        android:description="@string/permdesc_bindCarrierConfigService"
+        android:protectionLevel="signature|system" />
+
     <!-- The system process is explicitly the only one allowed to launch the
          confirmation UI for full backup/restore -->
     <uses-permission android:name="android.permission.CONFIRM_FULL_BACKUP"/>
diff --git a/core/res/res/color/ratingbar_background_material.xml b/core/res/res/color/ratingbar_background_material.xml
index e6f7488..5af6de1 100644
--- a/core/res/res/color/ratingbar_background_material.xml
+++ b/core/res/res/color/ratingbar_background_material.xml
@@ -20,6 +20,10 @@
         android:color="?attr/colorControlActivated"
         android:alpha="?attr/disabledAlpha" />
     <item
+        android:state_focused="true"
+        android:color="?attr/colorControlActivated"
+        android:alpha="?attr/disabledAlpha" />
+    <item
         android:color="?attr/colorControlNormal"
         android:alpha="?attr/disabledAlpha" />
 </selector>
diff --git a/core/res/res/drawable-hdpi/ic_audio_bt_alpha.png b/core/res/res/drawable-hdpi/ic_audio_bt_alpha.png
deleted file mode 100644
index 597c384..0000000
--- a/core/res/res/drawable-hdpi/ic_audio_bt_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_audio_bt_mute_alpha.png b/core/res/res/drawable-hdpi/ic_audio_bt_mute_alpha.png
deleted file mode 100644
index 298db92..0000000
--- a/core/res/res/drawable-hdpi/ic_audio_bt_mute_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_audio_phone_am_alpha.png b/core/res/res/drawable-hdpi/ic_audio_phone_am_alpha.png
deleted file mode 100644
index 8a7d67a..0000000
--- a/core/res/res/drawable-hdpi/ic_audio_phone_am_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_audio_bt_alpha.png b/core/res/res/drawable-mdpi/ic_audio_bt_alpha.png
deleted file mode 100644
index 282c643..0000000
--- a/core/res/res/drawable-mdpi/ic_audio_bt_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_audio_bt_mute_alpha.png b/core/res/res/drawable-mdpi/ic_audio_bt_mute_alpha.png
deleted file mode 100644
index f734c1c..0000000
--- a/core/res/res/drawable-mdpi/ic_audio_bt_mute_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_audio_phone_am_alpha.png b/core/res/res/drawable-mdpi/ic_audio_phone_am_alpha.png
deleted file mode 100644
index beda721..0000000
--- a/core/res/res/drawable-mdpi/ic_audio_phone_am_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_audio_bt_alpha.png b/core/res/res/drawable-xhdpi/ic_audio_bt_alpha.png
deleted file mode 100644
index b8aa083..0000000
--- a/core/res/res/drawable-xhdpi/ic_audio_bt_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_audio_bt_mute_alpha.png b/core/res/res/drawable-xhdpi/ic_audio_bt_mute_alpha.png
deleted file mode 100644
index 93a2481..0000000
--- a/core/res/res/drawable-xhdpi/ic_audio_bt_mute_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_audio_phone_am_alpha.png b/core/res/res/drawable-xhdpi/ic_audio_phone_am_alpha.png
deleted file mode 100644
index 2a04619..0000000
--- a/core/res/res/drawable-xhdpi/ic_audio_phone_am_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/ic_audio_bt_alpha.png b/core/res/res/drawable-xxhdpi/ic_audio_bt_alpha.png
deleted file mode 100755
index 140edac..0000000
--- a/core/res/res/drawable-xxhdpi/ic_audio_bt_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/ic_audio_bt_mute_alpha.png b/core/res/res/drawable-xxhdpi/ic_audio_bt_mute_alpha.png
deleted file mode 100644
index 97829b4..0000000
--- a/core/res/res/drawable-xxhdpi/ic_audio_bt_mute_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/ic_audio_phone_am_alpha.png b/core/res/res/drawable-xxhdpi/ic_audio_phone_am_alpha.png
deleted file mode 100644
index 1fd54a1..0000000
--- a/core/res/res/drawable-xxhdpi/ic_audio_phone_am_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable/btn_colored_material.xml b/core/res/res/drawable/btn_colored_material.xml
new file mode 100644
index 0000000..81cbe39
--- /dev/null
+++ b/core/res/res/drawable/btn_colored_material.xml
@@ -0,0 +1,35 @@
+<?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.
+-->
+
+<inset xmlns:android="http://schemas.android.com/apk/res/android"
+       android:insetLeft="@dimen/button_inset_horizontal_material"
+       android:insetTop="@dimen/button_inset_vertical_material"
+       android:insetRight="@dimen/button_inset_horizontal_material"
+       android:insetBottom="@dimen/button_inset_vertical_material">
+    <ripple android:color="?attr/colorControlHighlight">
+        <item>
+            <shape android:shape="rectangle"
+                   android:tint="@color/btn_colored_material">
+                <corners android:radius="@dimen/control_corner_material" />
+                <solid android:color="@color/white" />
+                <padding android:left="@dimen/button_padding_horizontal_material"
+                         android:top="@dimen/button_padding_vertical_material"
+                         android:right="@dimen/button_padding_horizontal_material"
+                         android:bottom="@dimen/button_padding_vertical_material" />
+            </shape>
+        </item>
+    </ripple>
+</inset>
diff --git a/core/res/res/drawable/ic_audio_bt.xml b/core/res/res/drawable/ic_audio_bt.xml
deleted file mode 100644
index 4f5af3d..0000000
--- a/core/res/res/drawable/ic_audio_bt.xml
+++ /dev/null
@@ -1,19 +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.
--->
-
-<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
-    android:src="@drawable/ic_audio_bt_alpha"
-    android:tint="?attr/colorControlNormal" />
diff --git a/core/res/res/drawable/ic_audio_bt_mute.xml b/core/res/res/drawable/ic_audio_bt_mute.xml
deleted file mode 100644
index d2004c0..0000000
--- a/core/res/res/drawable/ic_audio_bt_mute.xml
+++ /dev/null
@@ -1,19 +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.
--->
-
-<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
-    android:src="@drawable/ic_audio_bt_mute_alpha"
-    android:tint="?attr/colorControlNormal" />
diff --git a/core/res/res/drawable/ic_audio_media.xml b/core/res/res/drawable/ic_audio_media.xml
new file mode 100644
index 0000000..a453b3db
--- /dev/null
+++ b/core/res/res/drawable/ic_audio_media.xml
@@ -0,0 +1,27 @@
+<!--
+     Copyright (C) 2015 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:height="32.0dp"
+    android:viewportHeight="24.0"
+    android:viewportWidth="24.0"
+    android:width="32.0dp" >
+
+    <path
+        android:fillColor="?android:attr/colorControlNormal"
+        android:pathData="M12.0,3.0l0.0,9.28c-0.47,-0.17 -0.97,-0.28 -1.5,-0.28C8.01,12.0 6.0,14.01 6.0,16.5S8.01,21.0 10.5,21.0c2.31,0.0 4.2,-1.75 4.45,-4.0L15.0,17.0L15.0,6.0l4.0,0.0L19.0,3.0l-7.0,0.0z" />
+
+</vector>
+
diff --git a/core/res/res/drawable/ic_audio_media_mute.xml b/core/res/res/drawable/ic_audio_media_mute.xml
new file mode 100644
index 0000000..2e7f6dc
--- /dev/null
+++ b/core/res/res/drawable/ic_audio_media_mute.xml
@@ -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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:height="32.0dp"
+    android:viewportHeight="24.0"
+    android:viewportWidth="24.0"
+    android:width="32.0dp" >
+
+    <path
+        android:fillColor="?android:attr/colorControlNormal"
+        android:pathData="M15.0,6.0l4.0,0.0L19.0,3.0l-7.0,0.0l0.0,5.6l3.0,3.0C15.0,8.8 15.0,6.0 15.0,6.0z" />
+    <path
+        android:fillColor="?android:attr/colorControlNormal"
+        android:pathData="M4.8,3.9L3.5,5.1l6.9,6.9C8.0,12.1 6.0,14.0 6.0,16.5C6.0,19.0 8.0,21.0 10.5,21.0c2.7,0.0 4.5,-2.3 4.5,-4.3c0.0,0.0 0.0,-0.1 0.0,-0.1l4.0,4.0l1.3,-1.3L4.8,3.9z" />
+
+</vector>
+
diff --git a/core/res/res/drawable/ic_audio_phone.xml b/core/res/res/drawable/ic_audio_phone.xml
deleted file mode 100644
index 1bab863..0000000
--- a/core/res/res/drawable/ic_audio_phone.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
- * Copyright 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.
- */
--->
-
-<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
-    android:src="@drawable/ic_audio_phone_am_alpha"
-    android:autoMirrored="true"
-    android:tint="?attr/colorControlNormal" />
diff --git a/core/res/res/layout/date_picker_header_material.xml b/core/res/res/layout/date_picker_header_material.xml
index bda7de9..8125544 100644
--- a/core/res/res/layout/date_picker_header_material.xml
+++ b/core/res/res/layout/date_picker_header_material.xml
@@ -45,6 +45,7 @@
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
         android:textAppearance="@style/TextAppearance.Material.DatePicker.DateLabel"
+        android:gravity="start"
         android:maxLines="2"
         android:ellipsize="none"
         tools:text="Thu, Sep 30"
diff --git a/core/res/res/layout/day_picker_content_material.xml b/core/res/res/layout/day_picker_content_material.xml
new file mode 100644
index 0000000..1852bfa
--- /dev/null
+++ b/core/res/res/layout/day_picker_content_material.xml
@@ -0,0 +1,49 @@
+<?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.
+-->
+
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+             android:layout_width="match_parent"
+             android:layout_height="match_parent">
+
+    <android.widget.DayPickerViewPager
+        android:id="@+id/day_picker_view_pager"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent" />
+
+    <ImageButton
+        android:id="@+id/prev"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:minWidth="48dp"
+        android:minHeight="48dp"
+        android:src="@drawable/ic_chevron_left"
+        android:background="?attr/selectableItemBackgroundBorderless"
+        android:contentDescription="@string/date_picker_prev_month_button"
+        android:visibility="invisible" />
+
+    <ImageButton
+        android:id="@+id/next"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:minWidth="48dp"
+        android:minHeight="48dp"
+        android:src="@drawable/ic_chevron_right"
+        android:background="?attr/selectableItemBackgroundBorderless"
+        android:contentDescription="@string/date_picker_next_month_button"
+        android:visibility="invisible" />
+
+</FrameLayout>
diff --git a/core/res/res/layout/floating_popup_container.xml b/core/res/res/layout/floating_popup_container.xml
index f247919..e1af94c 100644
--- a/core/res/res/layout/floating_popup_container.xml
+++ b/core/res/res/layout/floating_popup_container.xml
@@ -19,7 +19,9 @@
     android:orientation="horizontal"
     android:layout_width="wrap_content"
     android:layout_height="@dimen/floating_toolbar_height"
+    android:padding="0dp"
+    android:layout_margin="0dp"
     android:elevation="2dp"
     android:focusable="true"
     android:focusableInTouchMode="true"
-    android:background="@android:color/background_light" />
+    android:background="@color/floating_toolbar_background_color"/>
diff --git a/core/res/res/layout/floating_popup_menu_button.xml b/core/res/res/layout/floating_popup_menu_button.xml
index 9fa13bd..70227fa 100644
--- a/core/res/res/layout/floating_popup_menu_button.xml
+++ b/core/res/res/layout/floating_popup_menu_button.xml
@@ -19,13 +19,15 @@
     android:layout_width="wrap_content"
     android:layout_height="match_parent"
     android:minWidth="@dimen/floating_toolbar_menu_button_side_padding"
-    android:paddingLeft="@dimen/floating_toolbar_menu_button_side_padding"
-    android:paddingRight="@dimen/floating_toolbar_menu_button_side_padding"
+    android:paddingStart="@dimen/floating_toolbar_menu_button_side_padding"
+    android:paddingEnd="@dimen/floating_toolbar_menu_button_side_padding"
     android:paddingTop="0dp"
     android:paddingBottom="0dp"
+    android:layout_margin="0dp"
     android:singleLine="true"
     android:ellipsize="end"
     android:fontFamily="sans-serif"
     android:textSize="@dimen/floating_toolbar_text_size"
     android:textAllCaps="true"
-    android:background="?attr/selectableItemBackground" />
\ No newline at end of file
+    android:textColor="@color/floating_toolbar_text_color"
+    android:background="?attr/selectableItemBackground" />
diff --git a/core/res/res/layout/floating_popup_overflow_list_item b/core/res/res/layout/floating_popup_overflow_list_item
index 9294f3b..c0db1bd 100644
--- a/core/res/res/layout/floating_popup_overflow_list_item
+++ b/core/res/res/layout/floating_popup_overflow_list_item
@@ -22,12 +22,14 @@
     android:gravity="center_vertical"
     android:minWidth="@dimen/floating_toolbar_menu_button_side_padding"
     android:minHeight="@dimen/floating_toolbar_height"
-    android:paddingLeft="@dimen/floating_toolbar_menu_button_side_padding"
-    android:paddingRight="@dimen/floating_toolbar_menu_button_side_padding"
+    android:paddingStart="@dimen/floating_toolbar_overflow_side_padding"
+    android:paddingEnd="@dimen/floating_toolbar_overflow_side_padding"
     android:paddingTop="0dp"
     android:paddingBottom="0dp"
+    android:layout_margin="0dp"
     android:singleLine="true"
     android:ellipsize="end"
     android:fontFamily="sans-serif"
     android:textSize="@dimen/floating_toolbar_text_size"
+    android:textColor="@color/floating_toolbar_text_color"
     android:textAllCaps="true" />
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index a623d0cb..674c695 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -4322,6 +4322,10 @@
             <!-- Line breaking stratgegy balances line lengths. -->
             <enum name="balanced" value="2" />
         </attr>
+        <!-- Array of indents, one dimension value per line, left side. -->
+        <attr name="leftIndents" format="reference" />
+        <!-- Array of indents, one dimension value per line, right side. -->
+        <attr name="rightIndents" format="reference" />
     </declare-styleable>
     <declare-styleable name="TextViewAppearance">
         <!-- Base text color, typeface, size, and style. -->
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index 5ffe57e..12d7e60 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -501,9 +501,16 @@
     <attr name="excludeFromRecents" format="boolean" />
 
     <!-- Specify that an Activity should be shown over the lock screen and,
-         in a multiuser environment, across all users' windows -->
+         in a multiuser environment, across all users' windows.
+         @deprecated use {@link android.R.attr#showForAllUsers} instead. -->
     <attr name="showOnLockScreen" format="boolean" />
 
+    <!-- Specify that an Activity should be shown even if the current/foreground user
+         is different from the user of the Activity. This will also force the
+         <code>android.view.LayoutParams.FLAG_SHOW_WHEN_LOCKED</code> flag
+         to be set for all windows of this activity -->
+    <attr name="showForAllUsers" format="boolean" />
+
     <!-- Specify the authorities under which this content provider can be
          found.  Multiple authorities may be supplied by separating them
          with a semicolon.  Authority names should use a Java-style naming
@@ -907,10 +914,11 @@
          what gets persisted. -->
     <attr name="persistableMode">
         <!-- The default. If this activity forms the root of a task then that task will be
-             persisted across reboots but only the launching intent will be used. All
-             activities above this activity in the task will not be persisted. In addition
-             this activity will not be passed a PersistableBundle into which it could have
-             stored its state. -->
+             persisted across reboots but only the launching intent will be used. If the task
+             relinquishes its identity then the intent used is that of the topmost inherited
+             identity. All activities above this activity in the task will not be persisted.
+             In addition this activity will not be passed a PersistableBundle into which it
+             could have stored its state. -->
         <enum name="persistRootOnly" value="0" />
         <!-- If this activity forms the root of a task then that task will not be persisted
              across reboots -->
@@ -1038,6 +1046,47 @@
          activity. -->
     <attr name="resizeableActivity" format="boolean" />
 
+    <!-- This value indicates how tasks rooted at this activity will behave in lockTask mode.
+         While in lockTask mode the system will not launch non-permitted tasks until
+         lockTask mode is disabled.
+         <p>While in lockTask mode with multiple permitted tasks running, each launched task is
+         permitted to finish, transitioning to the previous locked task, until there is only one
+         task remaining. At that point the last task running is not permitted to finish. -->
+    <attr name="lockTaskMode">
+        <!-- This is the default value. Tasks will not launch into lockTask mode but can be
+             placed there by calling {@link android.app.Activity#startLockTask}. If a task with
+             this mode has been whitelisted using {@link
+             android.app.admin.DevicePolicyManager#setLockTaskPackages} then calling startLockTask
+             will enter lockTask mode immediately, otherwise the user will be presented with a
+             dialog to approve entering lockTask mode.
+             <p>If the system is already in lockTask mode when a new task rooted at this activity
+             is launched that task will or will not start depending on whether the package of this
+             activity has been whitelisted.
+             <p>Tasks rooted at this activity can only exit lockTask mode using stopLockTask(). -->
+        <enum name="lockTaskModeDefault" value="0"/>
+        <!-- Tasks will not launch into lockTask mode and cannot be placed there using
+             {@link android.app.Activity#startLockTask} or be pinned from the Overview screen.
+             If the system is already in lockTask mode when a new task rooted at this activity is
+             launched that task will not be started.
+             <p>Note: This mode is only available to system and privileged applications.
+             Non-privileged apps with this value will be treated as lockTaskModeDefault.
+             -->
+        <enum name="lockTaskModeNever" value="1"/>
+        <!-- Tasks rooted at this activity will always launch into lockTask mode. If the system is
+             already in lockTask mode when this task is launched then the new task will be launched
+             on top of the current task. Tasks launched in this mode are capable of exiting
+             lockTask mode using finish(), whereas tasks entering lockTask mode using
+             startLockTask() must use stopLockTask() to exit.
+             <p>Note: This mode is only available to system and privileged applications.
+             Non-privileged apps with this value will be treated as lockTaskModeDefault.
+             -->
+        <enum name="lockTaskModeAlways" value="2"/>
+        <!-- If the DevicePolicyManager (DPM) authorizes this package ({@link
+             android.app.admin.DevicePolicyManager#setLockTaskPackages}) then this mode is
+             identical to lockTaskModeAlways. If the DPM does not authorize this package then this
+             mode is identical to lockTaskModeDefault. -->
+        <enum name="lockTaskModeIfWhitelisted" value="3"/>
+    </attr>
     <!-- When set installer will extract native libraries. If set to false
          libraries in the apk must be stored and page-aligned.  -->
     <attr name="extractNativeLibs" format="boolean"/>
@@ -1684,7 +1733,7 @@
          {@link android.app.Activity} class that is available
          as part of the package's application components, implementing
          a part of the application's user interface.
-         
+
          <p>Zero or more {@link #AndroidManifestIntentFilter intent-filter}
          tags can be included inside of an activity, to specify the Intents
          that it can handle.  If none are specified, the activity can
@@ -1719,6 +1768,7 @@
         <attr name="alwaysRetainTaskState" />
         <attr name="stateNotNeeded" />
         <attr name="excludeFromRecents" />
+        <!-- @deprecated use {@link android.R.attr#showForAllUsers} instead. -->
         <attr name="showOnLockScreen" />
         <!-- Specify whether the activity is enabled or not (that is, can be instantiated by the system).
              It can also be specified for an application as a whole, in which case a value of "false"
@@ -1746,12 +1796,14 @@
         <attr name="relinquishTaskIdentity" />
         <attr name="resumeWhilePausing" />
         <attr name="resizeableActivity" />
+        <attr name="lockTaskMode" />
+        <attr name="showForAllUsers" />
     </declare-styleable>
-    
+
     <!-- The <code>activity-alias</code> tag declares a new
          name for an existing {@link #AndroidManifestActivity activity}
          tag.
-         
+
          <p>Zero or more {@link #AndroidManifestIntentFilter intent-filter}
          tags can be included inside of an activity-alias, to specify the Intents
          that it can handle.  If none are specified, the activity can
@@ -1769,7 +1821,7 @@
              must be in the same manifest as the alias, and have been defined
              in that manifest before the alias here.  This must use a Java-style
              naming convention to ensure the name is unique, for example
-             "com.mycompany.MyName". -->  
+             "com.mycompany.MyName". -->
         <attr name="targetActivity" format="string" />
         <attr name="label" />
         <attr name="description" />
@@ -1785,7 +1837,7 @@
         <attr name="exported" />
         <attr name="parentActivityName" />
     </declare-styleable>
-    
+
     <!-- The <code>meta-data</code> tag is used to attach additional
          arbitrary data to an application component.  The data can later
          be retrieved programmatically from the
diff --git a/core/res/res/values/colors.xml b/core/res/res/values/colors.xml
index b9825c5..f1d2242 100644
--- a/core/res/res/values/colors.xml
+++ b/core/res/res/values/colors.xml
@@ -174,4 +174,8 @@
     <color name="Pink_800">#ffad1457</color>
     <color name="Red_700">#ffc53929</color>
     <color name="Red_800">#ffb93221</color>
+
+    <!-- Floating toolbar colors -->
+    <color name="floating_toolbar_text_color">#DD000000</color>
+    <color name="floating_toolbar_background_color">#FAFAFA</color>
 </resources>
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 8c78e74..07f8c60 100755
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -423,6 +423,9 @@
          point on the move. A value of 0 means no periodic scans will be used in the framework. -->
     <integer translatable="false" name="config_wifi_framework_scan_interval">300000</integer>
 
+    <!-- Integer indicating disconnect mode scan interval in milliseconds -->
+    <integer translatable="false" name="config_wifi_disconnected_scan_interval">15000</integer>
+
     <!-- Integer indicating associated partial scan interval in milliseconds -->
     <integer translatable="false" name="config_wifi_framework_associated_scan_interval">20000</integer>
 
@@ -472,6 +475,9 @@
     <!-- Wifi driver supports batched scan -->
     <bool translatable="false" name="config_wifi_batched_scan_supported">false</bool>
 
+    <!-- Wifi HAL supported PNO -->
+    <bool translatable="false" name="config_wifi_hal_pno_enable">false</bool>
+
     <!-- Idle Receive current for wifi radio. 0 by default-->
     <integer translatable="false" name="config_wifi_idle_receive_cur_ma">1</integer>
 
@@ -1962,8 +1968,10 @@
         string that's stored in 8-bit unpacked format) characters.-->
     <bool translatable="false" name="config_sms_decode_gsm_8bit_data">false</bool>
 
-    <!-- Package name providing WebView implementation. -->
-    <string name="config_webViewPackageName" translatable="false">com.android.webview</string>
+    <!-- List of package names (ordered by preference) providing WebView implementations. -->
+    <string-array name="config_webViewPackageNames" translatable="false">
+      <item>com.android.webview</item>
+    </string-array>
 
     <!-- If EMS is not supported, framework breaks down EMS into single segment SMS
          and adds page info " x/y". This config is used to set which carrier doesn't
diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml
index 100b161..bbba712 100644
--- a/core/res/res/values/dimens.xml
+++ b/core/res/res/values/dimens.xml
@@ -387,12 +387,14 @@
 
      <!-- Floating toolbar dimensions -->
      <dimen name="floating_toolbar_height">48dp</dimen>
-     <dimen name="floating_toolbar_menu_button_side_padding">8dp</dimen>
+     <dimen name="floating_toolbar_menu_button_side_padding">16dp</dimen>
+     <dimen name="floating_toolbar_overflow_side_padding">18dp</dimen>
      <dimen name="floating_toolbar_text_size">14sp</dimen>
      <dimen name="floating_toolbar_menu_button_minimum_width">48dp</dimen>
-     <dimen name="floating_toolbar_default_width">250dp</dimen>
-     <dimen name="floating_toolbar_minimum_overflow_height">192dp</dimen>
-     <dimen name="floating_toolbar_overflow_width">130dp</dimen>
-     <dimen name="floating_toolbar_margin">2dp</dimen>
+     <dimen name="floating_toolbar_default_width">264dp</dimen>
+     <dimen name="floating_toolbar_minimum_overflow_height">144dp</dimen>
+     <dimen name="floating_toolbar_horizontal_margin">16dp</dimen>
+     <dimen name="floating_toolbar_vertical_margin">8dp</dimen>
+
      <dimen name="chooser_grid_padding">0dp</dimen>
 </resources>
diff --git a/core/res/res/values/dimens_material.xml b/core/res/res/values/dimens_material.xml
index 937e92ad..92d5aa1 100644
--- a/core/res/res/values/dimens_material.xml
+++ b/core/res/res/values/dimens_material.xml
@@ -139,7 +139,7 @@
     <dimen name="timepicker_selector_radius">20dp</dimen>
     <dimen name="timepicker_selector_stroke">2dp</dimen>
     <dimen name="timepicker_center_dot_radius">3dp</dimen>
-    <dimen name="timepicker_selector_dot_radius">3dp</dimen>
+    <dimen name="timepicker_selector_dot_radius">2dp</dimen>
     <dimen name="timepicker_text_inset_normal">22dp</dimen>
     <dimen name="timepicker_text_inset_inner">58dp</dimen>
     <dimen name="timepicker_text_size_normal">16sp</dimen>
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index c2f2c6d..208afb7d 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -2641,6 +2641,7 @@
   <public type="style" name="Theme.Material.DayNight.Panel" />
   <public type="style" name="Theme.Material.Light.LightStatusBar" />
   <public type="style" name="ThemeOverlay.Material.Dialog" />
+  <public type="style" name="TextAppearance.Material.Widget.Button.Inverse" />
 
   <public type="id" name="pasteAsPlainText" />
   <public type="id" name="undo" />
@@ -2663,4 +2664,11 @@
 
   <!-- Animation -->
   <public type="attr" name="durationScaleHint" />
+
+  <public type="attr" name="lockTaskMode" />
+
+  <public type="attr" name="leftIndents" />
+  <public type="attr" name="rightIndents" />
+
+  <public type="attr" name="showForAllUsers" />
 </resources>
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 9ec5221..59366cc 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -1323,6 +1323,108 @@
     <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
     <string name="permdesc_use_sip">Allows the app to make and receive SIP calls.</string>
 
+    <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
+    <string name="permlab_register_sim_subscription">register new telecom SIM connections</string>
+    <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
+    <string name="permdesc_register_sim_subscription">Allows the app to register new telecom SIM connections.</string>
+
+    <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
+    <string name="permlab_register_call_provider">register new telecom connections</string>
+    <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
+    <string name="permdesc_register_call_provider">Allows the app to register new telecom connections.</string>
+
+    <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
+    <string name="permlab_connection_manager">manage telecom connections</string>
+    <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
+    <string name="permdesc_connection_manager">Allows the app to manage telecom connections.</string>
+
+    <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
+    <string name="permlab_bind_incall_service">interact with in-call screen</string>
+    <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
+    <string name="permdesc_bind_incall_service">Allows the app to control when and how the user sees the in-call screen.</string>
+
+    <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
+    <string name="permlab_bind_connection_service">interact with telephony services</string>
+    <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
+    <string name="permdesc_bind_connection_service">Allows the app to interact with telephony services to make/receive calls.</string>
+
+    <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
+    <string name="permlab_control_incall_experience">provide an in-call user experience</string>
+    <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
+    <string name="permdesc_control_incall_experience">Allows the app to provide an in-call user experience.</string>
+
+    <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
+    <string name="permlab_readNetworkUsageHistory">read historical network usage</string>
+    <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
+    <string name="permdesc_readNetworkUsageHistory">Allows the app to read historical network usage for specific networks and apps.</string>
+
+    <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
+    <string name="permlab_manageNetworkPolicy">manage network policy</string>
+    <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
+    <string name="permdesc_manageNetworkPolicy">Allows the app to manage network policies and define app-specific rules.</string>
+
+    <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
+    <string name="permlab_modifyNetworkAccounting">modify network usage accounting</string>
+    <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
+    <string name="permdesc_modifyNetworkAccounting">Allows the app to modify how network usage is accounted against apps. Not for use by normal apps.</string>
+
+    <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
+    <string name="permlab_accessNotifications">access notifications</string>
+    <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
+    <string name="permdesc_accessNotifications">Allows the app to retrieve, examine, and clear notifications, including those posted by other apps.</string>
+
+    <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
+    <string name="permlab_bindNotificationListenerService">bind to a notification listener service</string>
+    <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
+    <string name="permdesc_bindNotificationListenerService">Allows the holder to bind to the top-level interface of a notification listener service. Should never be needed for normal apps.</string>
+
+    <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
+    <string name="permlab_bindConditionProviderService">bind to a condition provider service</string>
+    <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
+    <string name="permdesc_bindConditionProviderService">Allows the holder to bind to the top-level interface of a condition provider service. Should never be needed for normal apps.</string>
+
+    <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
+    <string name="permlab_bindDreamService">bind to a dream service</string>
+    <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
+    <string name="permdesc_bindDreamService">Allows the holder to bind to the top-level interface of a dream service. Should never be needed for normal apps.</string>
+
+    <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
+    <string name="permlab_invokeCarrierSetup">invoke the carrier-provided configuration app</string>
+    <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
+    <string name="permdesc_invokeCarrierSetup">Allows the holder to invoke the carrier-provided configuration app. Should never be needed for normal apps.</string>
+
+    <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
+    <string name="permlab_accessNetworkConditions">listen for observations on network conditions</string>
+    <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
+    <string name="permdesc_accessNetworkConditions">Allows an application to listen for observations on network conditions. Should never be needed for normal apps.</string>
+
+    <string name="permlab_setInputCalibration">change input device calibration</string>
+    <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
+    <string name="permdesc_setInputCalibration">Allows the app to modify the calibration parameters of the touch screen. Should never be needed for normal apps.</string>
+
+    <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
+    <string name="permlab_accessDrmCertificates">access DRM certificates</string>
+    <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
+    <string name="permdesc_accessDrmCertificates">Allows an application to provision and use DRM certficates. Should never be needed for normal apps.</string>
+
+    <string name="permlab_handoverStatus">Receive Android Beam transfer status</string>
+    <string name="permdesc_handoverStatus">Allows this application to receive information about current Android Beam transfers</string>
+
+    <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
+    <string name="permlab_removeDrmCertificates">remove DRM certificates</string>
+    <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
+    <string name="permdesc_removeDrmCertificates">Allows an application to remove DRM certficates. Should never be needed for normal apps.</string>
+
+    <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
+    <string name="permlab_bindCarrierMessagingService">bind to a carrier messaging service</string>
+    <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
+    <string name="permdesc_bindCarrierMessagingService">Allows the holder to bind to the top-level interface of a carrier messaging service. Should never be needed for normal apps.</string>
+
+    <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
+    <string name="permlab_bindCarrierConfigService">bind to a carrier config service</string>
+    <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
+    <string name="permdesc_bindCarrierConfigService">Allows the holder to bind to a carrier config service. Should never be needed for normal apps.</string>
+
     <!-- Policy administration -->
 
     <!-- Title of policy access to limiting the user's password choices -->
@@ -3939,7 +4041,7 @@
     <string name="toolbar_collapse_description">Collapse</string>
 
     <!-- Zen mode - feature name. [CHAR LIMIT=40] -->
-    <string name="zen_mode_feature_name">Block interruptions</string>
+    <string name="zen_mode_feature_name">Do not disturb</string>
 
     <!-- Zen mode - downtime legacy feature name. [CHAR LIMIT=40] -->
     <string name="zen_mode_downtime_feature_name">Downtime</string>
diff --git a/core/res/res/values/styles_material.xml b/core/res/res/values/styles_material.xml
index 88cac72..b874f63 100644
--- a/core/res/res/values/styles_material.xml
+++ b/core/res/res/values/styles_material.xml
@@ -289,6 +289,10 @@
     <style name="TextAppearance.Material.Widget"/>
     <style name="TextAppearance.Material.Widget.Button" parent="TextAppearance.Material.Button" />
 
+    <style name="TextAppearance.Material.Widget.Button.Inverse">
+        <item name="textColor">?attr/textColorPrimaryInverse</item>
+    </style>
+
     <style name="TextAppearance.Material.Widget.EditText">
         <item name="textColor">?attr/textColorPrimaryInverse</item>
         <item name="textColorHint">?attr/textColorHintInverse</item>
@@ -461,7 +465,8 @@
 
     <!-- Colored bordered ink button -->
     <style name="Widget.Material.Button.Colored">
-        <item name="backgroundTint">@color/btn_colored_material</item>
+        <item name="background">@drawable/btn_colored_material</item>
+        <item name="textAppearance">@style/TextAppearance.Material.Widget.Button.Inverse</item>
     </style>
 
     <!-- Small bordered ink button -->
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index e5b1cb5..98c1b8f 100755
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -378,7 +378,9 @@
   <java-symbol type="integer" name="config_shortPressOnSleepBehavior" />
   <java-symbol type="integer" name="config_wifi_framework_scan_interval" />
   <java-symbol type="integer" name="config_wifi_supplicant_scan_interval" />
+  <java-symbol type="integer" name="config_wifi_disconnected_scan_interval" />
   <java-symbol type="integer" name="config_wifi_scan_interval_p2p_connected" />
+  <java-symbol type="bool" name="config_wifi_hal_pno_enable" />
   <java-symbol type="integer" name="db_connection_pool_size" />
   <java-symbol type="integer" name="db_journal_size_limit" />
   <java-symbol type="integer" name="db_wal_autocheckpoint" />
@@ -1163,11 +1165,10 @@
   <java-symbol type="drawable" name="expander_open_holo_dark" />
   <java-symbol type="drawable" name="ic_audio_alarm" />
   <java-symbol type="drawable" name="ic_audio_alarm_mute" />
-  <java-symbol type="drawable" name="ic_audio_bt" />
-  <java-symbol type="drawable" name="ic_audio_bt_mute" />
+  <java-symbol type="drawable" name="ic_audio_media" />
+  <java-symbol type="drawable" name="ic_audio_media_mute" />
   <java-symbol type="drawable" name="ic_audio_notification" />
   <java-symbol type="drawable" name="ic_audio_notification_mute" />
-  <java-symbol type="drawable" name="ic_audio_phone" />
   <java-symbol type="drawable" name="ic_audio_ring_notif" />
   <java-symbol type="drawable" name="ic_audio_ring_notif_mute" />
   <java-symbol type="drawable" name="ic_audio_ring_notif_vibrate" />
@@ -1995,7 +1996,7 @@
   <java-symbol type="attr" name="actionModeWebSearchDrawable" />
   <java-symbol type="string" name="websearch" />
   <java-symbol type="drawable" name="ic_media_video_poster" />
-  <java-symbol type="string" name="config_webViewPackageName" />
+  <java-symbol type="array" name="config_webViewPackageNames" />
 
   <!-- From SubtitleView -->
   <java-symbol type="dimen" name="subtitle_corner_radius" />
@@ -2223,12 +2224,13 @@
   <java-symbol type="layout" name="floating_popup_overflow_list_item" />
   <java-symbol type="dimen" name="floating_toolbar_height" />
   <java-symbol type="dimen" name="floating_toolbar_menu_button_side_padding" />
+  <java-symbol type="dimen" name="floating_toolbar_overflow_side_padding" />
   <java-symbol type="dimen" name="floating_toolbar_text_size" />
   <java-symbol type="dimen" name="floating_toolbar_menu_button_minimum_width" />
   <java-symbol type="dimen" name="floating_toolbar_default_width" />
   <java-symbol type="dimen" name="floating_toolbar_minimum_overflow_height" />
-  <java-symbol type="dimen" name="floating_toolbar_overflow_width" />
-  <java-symbol type="dimen" name="floating_toolbar_margin" />
+  <java-symbol type="dimen" name="floating_toolbar_horizontal_margin" />
+  <java-symbol type="dimen" name="floating_toolbar_vertical_margin" />
 
   <java-symbol type="drawable" name="ic_chevron_left" />
   <java-symbol type="drawable" name="ic_chevron_right" />
@@ -2240,4 +2242,6 @@
   <java-symbol type="layout" name="chooser_grid" />
   <java-symbol type="layout" name="resolve_grid_item" />
   <java-symbol type="id" name="title_icon" />
+  <java-symbol type="id" name="day_picker_view_pager" />
+  <java-symbol type="layout" name="day_picker_content_material" />
 </resources>
diff --git a/core/tests/coretests/apks/install_jni_lib_open_from_apk/Android.mk b/core/tests/coretests/apks/install_jni_lib_open_from_apk/Android.mk
index 5fa2405..6ee6ffa 100644
--- a/core/tests/coretests/apks/install_jni_lib_open_from_apk/Android.mk
+++ b/core/tests/coretests/apks/install_jni_lib_open_from_apk/Android.mk
@@ -5,7 +5,6 @@
 
 LOCAL_PACKAGE_NAME := install_jni_lib_open_from_apk
 
-LOCAL_JNI_SHARED_LIBRARIES_ZIP_OPTIONS := -0
 LOCAL_PAGE_ALIGN_JNI_SHARED_LIBRARIES := true
 
 include $(FrameworkCoreTests_BUILD_PACKAGE)
diff --git a/docs/html/guide/topics/manifest/application-element.jd b/docs/html/guide/topics/manifest/application-element.jd
index b5af9c3..d6ad656 100644
--- a/docs/html/guide/topics/manifest/application-element.jd
+++ b/docs/html/guide/topics/manifest/application-element.jd
@@ -32,6 +32,7 @@
              android:<a href="#testOnly">testOnly</a>=["true" | "false"]
              android:<a href="#theme">theme</a>="<i>resource or theme</i>"
              android:<a href="#uioptions">uiOptions</a>=["none" | "splitActionBarWhenNarrow"]
+             android:<a href="#usesCleartextTraffic">usesCleartextTraffic</a>=["true" | "false"]
              android:<a href="#vmSafeMode">vmSafeMode</a>=["true" | "false"] &gt;
     . . .
 &lt;/application&gt;</pre></dd>
@@ -446,6 +447,32 @@
   <p>This attribute was added in API level 14.</p>
 </dd>
 
+<dt><a name="usesCleartextTraffic"></a>{@code android:usesCleartextTraffic}</dt>
+<dd>Indicates whether the app intends to use cleartext network traffic, such as cleartext HTTP.
+The default value is {@code "true"}.
+
+<p>When the attribute is set to {@code "false"}, platform components (for example, HTTP and FTP
+stacks, {@link android.webkit.WebView}, {@link android.app.DownloadManager},
+{@link android.media.MediaPlayer}) will refuse the app's requests to use cleartext traffic.
+Third-party libraries are strongly encouraged to honor this setting as well. The key reason for
+avoiding cleartext traffic is the lack of confidentiality, authenticity, and protections against
+tampering: a network attacker can eavesdrop on transmitted data and also modify it without being
+detected.
+
+<p>This flag is honored on a best effort basis because it's impossible to prevent all cleartext
+traffic from Android applications given the level of access provided to them. For example, there's
+no expectation that the {@link java.net.Socket} API will honor this flag because it cannot
+determine whether its traffic is in cleartext. However, most network traffic from applications is
+handled by higher-level network stacks/components which can honor this flag by either reading it
+from {@link android.content.pm.ApplicationInfo#flags ApplicationInfo.flags} or
+{@link android.security.NetworkSecurityPolicy#isCleartextTrafficPermitted() NetworkSecurityPolicy.isCleartextTrafficPermitted()}.
+
+<p>During app development, StrictMode can be used to identify any cleartext traffic from the app: see
+{@link android.os.StrictMode.VmPolicy.Builder#detectCleartextNetwork() StrictMode.VmPolicy.Builder.detectCleartextNetwork()}.
+
+<p>This attribute was added in API level 23.</p>
+</dd>
+
 <dt><a name="vmSafeMode"></a>{@code android:vmSafeMode}</dt>
 <dd>Indicates whether the app would like the virtual machine (VM) to operate
 in safe mode. The default value is {@code "false"}.
diff --git a/docs/html/images/tools/hierarchicalviewer-icon.png b/docs/html/images/tools/hierarchicalviewer-icon.png
new file mode 100644
index 0000000..061f952
--- /dev/null
+++ b/docs/html/images/tools/hierarchicalviewer-icon.png
Binary files differ
diff --git a/docs/html/images/tools/studio-DDMS-open-perspective-icon.png b/docs/html/images/tools/studio-DDMS-open-perspective-icon.png
new file mode 100644
index 0000000..f8e6d1a
--- /dev/null
+++ b/docs/html/images/tools/studio-DDMS-open-perspective-icon.png
Binary files differ
diff --git a/docs/html/images/tools/studio-gradle-panel.png b/docs/html/images/tools/studio-gradle-panel.png
deleted file mode 100644
index 4a76a8d..0000000
--- a/docs/html/images/tools/studio-gradle-panel.png
+++ /dev/null
Binary files differ
diff --git a/docs/html/images/tools/studio-gradle-tab.png b/docs/html/images/tools/studio-gradle-tab.png
deleted file mode 100644
index b0f302c..0000000
--- a/docs/html/images/tools/studio-gradle-tab.png
+++ /dev/null
Binary files differ
diff --git a/docs/html/tools/building/building-studio.jd b/docs/html/tools/building/building-studio.jd
index 68800da..4431194 100644
--- a/docs/html/tools/building/building-studio.jd
+++ b/docs/html/tools/building/building-studio.jd
@@ -67,26 +67,13 @@
 <a href="{@docRoot}sdk/installing/studio-build.html">Build System</a> guide.</p>
 
 <p>To view the list of all available build tasks in Android Studio, click <strong>Gradle</strong>
-on the right side of the IDE window. The <em>Gradle tasks</em> panel appears as shown in
-figure 2. Double-click any build task to run it in Android Studio. To hide the <em>Gradle tasks</em>
-panel, click <strong>Gradle</strong> again.</p>
+on the right side of the IDE window. The <em>Gradle tasks</em> panel appears.</p>
 
-<img src="{@docRoot}images/tools/studio-gradle-panel.png" alt="" />
-<p class="img-caption"><strong>Figure 2.</strong> The list of build tasks in Android Studio.</p>
 
 <h3 id="buildRelease">Build a release version</h3>
 
-<p>You can now build the release version of your application for distribution. To build it from Android
-Studio:</p>
-
-<ol>
-    <li>Click <strong>Gradle</strong> on the right side of the IDE window.</li>
-    <li>On the <em>All tasks</em> section of the sidebar that appears, expand
-        <strong>BuildSystemExample</strong>.</li>
-    <li>Expand <strong>:app</strong> and double-click <strong>assembleRelease</strong>.</li>
-</ol>
-
-<p>You can use this procedure to invoke any build task from Android Studio.</p>
+<p>You can now use the <strong>Build</strong> menu options to build the release version of your
+application for distribution. </p>
 
 <p>The build generates an APK for each build variant:
 the <code>app/build/apk/</code> directory contains packages named
diff --git a/docs/html/tools/building/configuring-gradle.jd b/docs/html/tools/building/configuring-gradle.jd
index 8379508..7cca5b4 100644
--- a/docs/html/tools/building/configuring-gradle.jd
+++ b/docs/html/tools/building/configuring-gradle.jd
@@ -470,7 +470,11 @@
 <li>fullRelease</li>
 </ul>
 
-<p>To build this example, invoke the <code>assemble</code> task from Android Studio or from the
-command line.</p>
+<p>To build this example, click the <strong>Build</strong> menu option in Android Studio or invoke
+the <code>assemble</code> task from the command line. </p>
+
+<p class="note"><strong>Note:</strong> The <strong>Build &gt; Make Project</strong> option compiles
+all the source files in the entire project that have been modified since the last compilation. The
+<strong>Build &gt; Rebuild Project</strong> option recomplies all the source files in the project.</p>
 
 <p>Separate output folders are created for each build variant. </p>
diff --git a/docs/html/tools/building/plugin-for-gradle.jd b/docs/html/tools/building/plugin-for-gradle.jd
index 54a03fd..a497c1b 100644
--- a/docs/html/tools/building/plugin-for-gradle.jd
+++ b/docs/html/tools/building/plugin-for-gradle.jd
@@ -15,7 +15,7 @@
 <li><a href="{@docRoot}sdk/installing/studio-build.html">
 Build System Overview</a></li>
 <li><a href="{@docRoot}tools/building/index.html">
-Buidling and Running</a></li>
+Building and Running</a></li>
 <li><a href="{@docRoot}tools/building/building-studio.html">
 Building and Running from Android Studio</a></li>
 </ul>
@@ -273,16 +273,9 @@
    <dd><p>Performs the clean.</p></dd>
 </dl>
 
-<p>The Android plugin provides additional tasks for <em>connectedCheck</em> and <em>deviceCheck</em>
+<p>The Android plugin provides the <em>connectedCheck</em> and <em>deviceCheck</em> tasks
 for checks run on connected, emulated, and remote devices. Gradle tasks can be viewed by clicking
-the Gradle tab</a> in the right margin.
-<img src="{@docRoot}images/tools/studio-gradle-tab.png"></p>
-<p class="img-caption"><strong>Figure 1:</strong> Gradle tab</p>
-
-<p>Running a top-level task, runs all the dependent tasks. For example, the <em>assemble</em> task
-has dependent tasks for <em>assembleDebug</em> and <em>assembleRelease</em> to make the debug and
-release APKs. The <em>assemble</em> task depends on these tasks so calling it builds both APKs.
-These tasks can also be called independently to build the debug or release APK separately. </p>
+the Gradle tab</a> in the right margin.</p>
 
 <p>You can view the list of available tasks and invoke any task from Android Studio and from
 the command line, as described in
diff --git a/docs/html/tools/help/hierarchy-viewer.jd b/docs/html/tools/help/hierarchy-viewer.jd
index 4a346e0..da4cc1e 100644
--- a/docs/html/tools/help/hierarchy-viewer.jd
+++ b/docs/html/tools/help/hierarchy-viewer.jd
@@ -3,16 +3,27 @@
 parent.link=index.html
 @jd:body
 
-<p>Hierarchy Viewer allows you to debug and optimize your user 
-interface. It provides a visual representation of the layout's View hierarchy 
-(the Layout View) and a magnified inspector of the display (the Pixel Perfect View). 
+<p>The Hierarchy Viewer allows you to debug and optimize your user
+interface. It provides a visual representation of the layout's View hierarchy
+(the Layout View) and a magnified inspector of the display (the Pixel Perfect View). </p>
+
+<p>To start the Hierarchy Viewer, do one of the following: </p>
+
+<ul>
+<li> From Android Studio, choose <strong>Tools &gt; Android Device Monitor</strong> or click the
+Android Device Monitor icon
+<img src="{@docRoot}images/tools/hierarchicalviewer-icon.png" alt=""/>. Click the Open Perspectives
+icon <img src="{@docRoot}images/tools/studio-DDMS-open-perspective-icon.png" alt=""/> and select
+<strong>Hierarchy View</strong>. </li>
+<li>From the SDK <code>tools/</code> directory, enter:
+  <pre>monitor</pre> </li>
+</ul>
+
+<p>For more information on how to use the Hierarchy Viewer, see
+<a href="{@docRoot}tools/debugging/debugging-ui.html">Optimizing Your UI</a>.
 </p>
 
-<p>To start Hierarchy Viewer, enter the following command from the SDK <code>tools/</code> directory:</p>
-  <pre>hierarchyviewer</pre>
-</ol>
+<p class="note"><strong>Note:</strong> The command line version of Hierarchy Viewer has been
+deprecated. </p>
 
-<p>For more information on how to use Hierarchy Viewer, see 
-<a href="{@docRoot}tools/debugging/debugging-ui.html">Debugging and Profiling UIs</a>
-</p>
 
diff --git a/docs/html/tools/help/hprof-conv.jd b/docs/html/tools/help/hprof-conv.jd
index f96def2..982f337 100644
--- a/docs/html/tools/help/hprof-conv.jd
+++ b/docs/html/tools/help/hprof-conv.jd
@@ -8,9 +8,13 @@
 generated by the Android SDK tools to a standard format so you
 can view the file in a profiling tool of your choice. </p>
 
-<pre> hprof-conv &lt;infile&gt; &lt;outfile&gt;</pre>
+<pre> hprof-conv [-z] &lt;infile&gt; &lt;outfile&gt;</pre>
 
 <p>
 You can use "-" for <code>&lt;infile&gt;</code> or <code>&lt;outfile&gt;</code>
 to specify stdin or stdout.
 </p>
+
+<p>
+You can use "-z" to filter out zygote allocations shared by all applications.
+</p>
diff --git a/graphics/java/android/graphics/drawable/Drawable.java b/graphics/java/android/graphics/drawable/Drawable.java
index e8b8c77..22ff3e74 100644
--- a/graphics/java/android/graphics/drawable/Drawable.java
+++ b/graphics/java/android/graphics/drawable/Drawable.java
@@ -572,7 +572,7 @@
      * Specifies a tint blending mode for this drawable.
      * <p>
      * Defines how this drawable's tint color should be blended into the drawable
-     * before it is drawn to screen. Default tint mode is {@link PorterDuff.Mode#MULTIPLY}.
+     * before it is drawn to screen. Default tint mode is {@link PorterDuff.Mode#SRC_IN}.
      * </p>
      * <p class="note"><strong>Note:</strong> Setting a color filter via
      * {@link #setColorFilter(ColorFilter)} or
diff --git a/keystore/java/android/security/AndroidKeyStore.java b/keystore/java/android/security/AndroidKeyStore.java
index b9317741..c259c25 100644
--- a/keystore/java/android/security/AndroidKeyStore.java
+++ b/keystore/java/android/security/AndroidKeyStore.java
@@ -55,6 +55,7 @@
 import java.util.Enumeration;
 import java.util.HashSet;
 import java.util.Iterator;
+import java.util.List;
 import java.util.Set;
 
 import javax.crypto.SecretKey;
@@ -116,11 +117,15 @@
                 throw new UnrecoverableKeyException("Key algorithm unknown");
             }
 
-            int keymasterDigest =
-                    keyCharacteristics.hwEnforced.getInt(KeymasterDefs.KM_TAG_DIGEST, -1);
-            if (keymasterDigest == -1) {
-                keymasterDigest =
-                        keyCharacteristics.swEnforced.getInt(KeymasterDefs.KM_TAG_DIGEST, -1);
+            List<Integer> keymasterDigests =
+                    keyCharacteristics.getInts(KeymasterDefs.KM_TAG_DIGEST);
+            int keymasterDigest;
+            if (keymasterDigests.isEmpty()) {
+                keymasterDigest = -1;
+            } else {
+                // More than one digest can be permitted for this key. Use the first one to form the
+                // JCA key algorithm name.
+                keymasterDigest = keymasterDigests.get(0);
             }
 
             String keyAlgorithmString;
@@ -530,6 +535,12 @@
             args.addInt(KeymasterDefs.KM_TAG_USER_AUTH_TYPE,
                     KeyStoreKeyProperties.UserAuthenticator.allToKeymaster(
                             params.getUserAuthenticators()));
+            long secureUserId = GateKeeper.getSecureUserId();
+            if (secureUserId == 0) {
+                throw new IllegalStateException("Secure lock screen must be enabled"
+                        + " to import keys requiring user authentication");
+            }
+            args.addLong(KeymasterDefs.KM_TAG_USER_SECURE_ID, secureUserId);
         }
         if (params.isInvalidatedOnNewFingerprintEnrolled()) {
             // TODO: Add the invalidate on fingerprint enrolled constraint once Keymaster supports
diff --git a/keystore/java/android/security/GateKeeper.java b/keystore/java/android/security/GateKeeper.java
new file mode 100644
index 0000000..c9f06e9
--- /dev/null
+++ b/keystore/java/android/security/GateKeeper.java
@@ -0,0 +1,30 @@
+package android.security;
+
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.os.UserHandle;
+import android.service.gatekeeper.IGateKeeperService;
+
+/**
+ * Convenience class for accessing the gatekeeper service.
+ *
+ * @hide
+ */
+public abstract class GateKeeper {
+
+    private GateKeeper() {}
+
+    public static IGateKeeperService getService() {
+        return IGateKeeperService.Stub.asInterface(
+                ServiceManager.getService("android.service.gatekeeper.IGateKeeperService"));
+    }
+
+    public static long getSecureUserId() throws IllegalStateException {
+        try {
+            return GateKeeper.getService().getSecureUserId(UserHandle.myUserId());
+        } catch (RemoteException e) {
+            throw new IllegalStateException(
+                    "Failed to obtain secure user ID from gatekeeper", e);
+        }
+    }
+}
diff --git a/keystore/java/android/security/KeyStoreCipherSpi.java b/keystore/java/android/security/KeyStoreCipherSpi.java
index 7bc6378..37e00b2 100644
--- a/keystore/java/android/security/KeyStoreCipherSpi.java
+++ b/keystore/java/android/security/KeyStoreCipherSpi.java
@@ -547,18 +547,12 @@
             if (mIvRequired) {
                 // IV is needed
                 if ((mIv == null) && (mEncrypting)) {
-                    // TODO: Switch to keymaster-generated IV code below once keymaster supports
-                    // that.
-                    // IV is needed but was not provided by the caller -- generate an IV.
-                    mIv = new byte[mBlockSizeBytes];
-                    SecureRandom rng = (mRng != null) ? mRng : new SecureRandom();
-                    rng.nextBytes(mIv);
-//                    // IV was not provided by the caller and thus will be generated by keymaster.
-//                    // Mix in some additional entropy from the provided SecureRandom.
-//                    if (mRng != null) {
-//                        mAdditionalEntropyForBegin = new byte[mBlockSizeBytes];
-//                        mRng.nextBytes(mAdditionalEntropyForBegin);
-//                    }
+                    // IV was not provided by the caller and thus will be generated by keymaster.
+                    // Mix in some additional entropy from the provided SecureRandom.
+                    if (mRng != null) {
+                        mAdditionalEntropyForBegin = new byte[mBlockSizeBytes];
+                        mRng.nextBytes(mAdditionalEntropyForBegin);
+                    }
                 }
             }
         }
diff --git a/keystore/java/android/security/KeyStoreKeyGeneratorSpi.java b/keystore/java/android/security/KeyStoreKeyGeneratorSpi.java
index 72c485a..d1abe12 100644
--- a/keystore/java/android/security/KeyStoreKeyGeneratorSpi.java
+++ b/keystore/java/android/security/KeyStoreKeyGeneratorSpi.java
@@ -167,6 +167,12 @@
             args.addInt(KeymasterDefs.KM_TAG_USER_AUTH_TYPE,
                     KeyStoreKeyProperties.UserAuthenticator.allToKeymaster(
                             spec.getUserAuthenticators()));
+            long secureUserId = GateKeeper.getSecureUserId();
+            if (secureUserId == 0) {
+                throw new IllegalStateException("Secure lock screen must be enabled"
+                        + " to generate keys requiring user authentication");
+            }
+            args.addLong(KeymasterDefs.KM_TAG_USER_SECURE_ID, secureUserId);
         }
         if (spec.isInvalidatedOnNewFingerprintEnrolled()) {
             // TODO: Add the invalidate on fingerprint enrolled constraint once Keymaster supports
diff --git a/keystore/java/android/security/KeyStoreKeyProperties.java b/keystore/java/android/security/KeyStoreKeyProperties.java
index b1f330f..206103f 100644
--- a/keystore/java/android/security/KeyStoreKeyProperties.java
+++ b/keystore/java/android/security/KeyStoreKeyProperties.java
@@ -217,7 +217,7 @@
     }
 
     @Retention(RetentionPolicy.SOURCE)
-    @IntDef({Origin.GENERATED, Origin.IMPORTED})
+    @IntDef({Origin.GENERATED, Origin.IMPORTED, Origin.UNKNOWN})
     public @interface OriginEnum {}
 
     /**
@@ -233,14 +233,22 @@
         public static final int IMPORTED = 1 << 1;
 
         /**
+         * Origin of the key is unknown. This can occur only for keys backed by an old TEE
+         * implementation which does not record origin information.
+         */
+        public static final int UNKNOWN = 1 << 2;
+
+        /**
          * @hide
          */
         public static @OriginEnum int fromKeymaster(int origin) {
             switch (origin) {
-                case KeymasterDefs.KM_ORIGIN_HARDWARE:
+                case KeymasterDefs.KM_ORIGIN_GENERATED:
                     return GENERATED;
                 case KeymasterDefs.KM_ORIGIN_IMPORTED:
                     return IMPORTED;
+                case KeymasterDefs.KM_ORIGIN_UNKNOWN:
+                    return UNKNOWN;
                 default:
                     throw new IllegalArgumentException("Unknown origin: " + origin);
             }
diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp
index aa722d0..d06534e 100644
--- a/libs/hwui/OpenGLRenderer.cpp
+++ b/libs/hwui/OpenGLRenderer.cpp
@@ -1009,12 +1009,13 @@
         TextureVertex::set(mesh++, r->left, r->bottom, u1, v2);
         TextureVertex::set(mesh++, r->right, r->bottom, u2, v2);
     }
+    Rect modelRect = Rect(rect.getWidth(), rect.getHeight());
     Glop glop;
     GlopBuilder(mRenderState, mCaches, &glop)
             .setMeshTexturedIndexedQuads(&quadVertices[0], count * 6)
             .setFillLayer(layer->getTexture(), layer->getColorFilter(), getLayerAlpha(layer), layer->getMode(), Blend::ModeOrderSwap::NoSwap)
             .setTransform(currentSnapshot()->getOrthoMatrix(), *currentTransform(), false)
-            .setModelViewOffsetRectSnap(0, 0, rect)
+            .setModelViewOffsetRectSnap(rect.left, rect.top, modelRect)
             .setRoundRectClipState(currentSnapshot()->roundRectClipState)
             .build();
     DRAW_DOUBLE_STENCIL_IF(!layer->hasDrawnSinceUpdate, renderGlop(glop));
diff --git a/libs/hwui/SkiaCanvasProxy.cpp b/libs/hwui/SkiaCanvasProxy.cpp
index ec1bb90..8a6c8c5 100644
--- a/libs/hwui/SkiaCanvasProxy.cpp
+++ b/libs/hwui/SkiaCanvasProxy.cpp
@@ -289,6 +289,7 @@
     //       but even more conservative bounds if this  is too slow.
     SkRect bounds;
     glyphs.paint.measureText(glyphs.glyphIDs, glyphs.count << 1, &bounds);
+    bounds.offset(x, y);
 
     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,
diff --git a/location/java/android/location/GpsMeasurement.java b/location/java/android/location/GpsMeasurement.java
index 05bcf79..df128c9 100644
--- a/location/java/android/location/GpsMeasurement.java
+++ b/location/java/android/location/GpsMeasurement.java
@@ -80,6 +80,8 @@
     private static final int HAS_TIME_FROM_LAST_BIT = (1<<14);
     private static final int HAS_DOPPLER_SHIFT = (1<<15);
     private static final int HAS_DOPPLER_SHIFT_UNCERTAINTY = (1<<16);
+    private static final int HAS_USED_IN_FIX = (1<<17);
+    private static final int GPS_MEASUREMENT_HAS_UNCORRECTED_PSEUDORANGE_RATE = (1<<18);
 
     /**
      * The indicator is not available or it is unknown.
@@ -137,10 +139,17 @@
     public static final short STATE_TOW_DECODED = (1<<3);
 
     /**
+     * The state of the GPS receiver contains millisecond ambiguity.
+     *
+     * @hide
+     */
+    public static final short STATE_MSEC_AMBIGUOUS = (1<<4);
+
+    /**
      * All the GPS receiver state flags.
      */
-    private static final short STATE_ALL =
-            STATE_CODE_LOCK | STATE_BIT_SYNC | STATE_SUBFRAME_SYNC | STATE_TOW_DECODED;
+    private static final short STATE_ALL = STATE_CODE_LOCK | STATE_BIT_SYNC | STATE_SUBFRAME_SYNC
+            | STATE_TOW_DECODED | STATE_MSEC_AMBIGUOUS;
 
     /**
      * The state of the 'Accumulated Delta Range' is invalid or unknown.
@@ -295,6 +304,9 @@
         if ((mState & STATE_TOW_DECODED) == STATE_TOW_DECODED) {
             builder.append("TowDecoded|");
         }
+        if ((mState & STATE_MSEC_AMBIGUOUS) == STATE_MSEC_AMBIGUOUS) {
+            builder.append("MsecAmbiguous");
+        }
         int remainingStates = mState & ~STATE_ALL;
         if (remainingStates > 0) {
             builder.append("Other(");
@@ -361,6 +373,15 @@
     /**
      * Gets the Pseudorange rate at the timestamp in m/s.
      * The reported value includes {@link #getPseudorangeRateUncertaintyInMetersPerSec()}.
+     *
+     * The correction of a given Pseudorange Rate value includes corrections from receiver and
+     * satellite clock frequency errors.
+     * {@link #isPseudorangeRateCorrected()} identifies the type of value reported.
+     *
+     * A positive 'uncorrected' value indicates that the SV is moving away from the receiver.
+     * The sign of the 'uncorrected' Pseudorange Rate and its relation to the sign of
+     * {@link #getDopplerShiftInHz()} is given by the equation:
+     *      pseudorange rate = -k * doppler shift   (where k is a constant)
      */
     public double getPseudorangeRateInMetersPerSec() {
         return mPseudorangeRateInMetersPerSec;
@@ -374,6 +395,18 @@
     }
 
     /**
+     * See {@link #getPseudorangeRateInMetersPerSec()} for more details.
+     *
+     * @return {@code true} if {@link #getPseudorangeRateInMetersPerSec()} contains a corrected
+     *         value, {@code false} if it contains an uncorrected value.
+     *
+     * @hide
+     */
+    public boolean isPseudorangeRateCorrected() {
+        return !isFlagSet(GPS_MEASUREMENT_HAS_UNCORRECTED_PSEUDORANGE_RATE);
+    }
+
+    /**
      * Gets the pseudorange's rate uncertainty (1-Sigma) in m/s.
      * The uncertainty is represented as an absolute (single sided) value.
      */
@@ -437,6 +470,11 @@
      * The reported value includes {@link #getAccumulatedDeltaRangeUncertaintyInMeters()}.
      *
      * The availability of the value is represented by {@link #getAccumulatedDeltaRangeState()}.
+     *
+     * A positive value indicates that the SV is moving away from the receiver.
+     * The sign of {@link #getAccumulatedDeltaRangeInMeters()} and its relation to the sign of
+     * {@link #getCarrierPhase()} is given by the equation:
+     *          accumulated delta range = -k * carrier phase    (where k is a constant)
      */
     public double getAccumulatedDeltaRangeInMeters() {
         return mAccumulatedDeltaRangeInMeters;
@@ -452,6 +490,8 @@
     /**
      * Gets the accumulated delta range's uncertainty (1-Sigma) in meters.
      * The uncertainty is represented as an absolute (single sided) value.
+     *
+     * The status of the value is represented by {@link #getAccumulatedDeltaRangeState()}.
      */
     public double getAccumulatedDeltaRangeUncertaintyInMeters() {
         return mAccumulatedDeltaRangeUncertaintyInMeters;
@@ -460,7 +500,7 @@
     /**
      * Sets the accumulated delta range's uncertainty (1-sigma) in meters.
      *
-     * The availability of the value is represented by {@link #getAccumulatedDeltaRangeState()}.
+     * The status of the value is represented by {@link #getAccumulatedDeltaRangeState()}.
      */
     public void setAccumulatedDeltaRangeUncertaintyInMeters(double value) {
         mAccumulatedDeltaRangeUncertaintyInMeters = value;
@@ -1235,6 +1275,10 @@
                 mPseudorangeRateInMetersPerSec,
                 "PseudorangeRateUncertaintyInMetersPerSec",
                 mPseudorangeRateUncertaintyInMetersPerSec));
+        builder.append(String.format(
+                format,
+                "PseudorangeRateIsCorrected",
+                isPseudorangeRateCorrected()));
 
         builder.append(String.format(
                 format,
diff --git a/location/java/android/location/GpsNavigationMessage.java b/location/java/android/location/GpsNavigationMessage.java
index b893f3f..5b12a61 100644
--- a/location/java/android/location/GpsNavigationMessage.java
+++ b/location/java/android/location/GpsNavigationMessage.java
@@ -60,6 +60,28 @@
      */
     public static final byte TYPE_CNAV2 = 4;
 
+    /**
+     * The Navigation Message Status is 'unknown'.
+     *
+     * @hide
+     */
+    public static final short STATUS_UNKNOWN = 0;
+
+    /**
+     * The Navigation Message was received without any parity error in its navigation words.
+     *
+     * @hide
+     */
+    public static final short STATUS_PARITY_PASSED = (1<<0);
+
+    /**
+     * The Navigation Message was received with words that failed parity check, but the receiver was
+     * able to correct those words.
+     *
+     * @hide
+     */
+    public static final short STATUS_PARITY_REBUILT = (1<<1);
+
     // End enumerations in sync with gps.h
 
     private byte mType;
@@ -67,6 +89,7 @@
     private short mMessageId;
     private short mSubmessageId;
     private byte[] mData;
+    private short mStatus;
 
     GpsNavigationMessage() {
         initialize();
@@ -81,6 +104,7 @@
         mMessageId = navigationMessage.mMessageId;
         mSubmessageId = navigationMessage.mSubmessageId;
         mData = navigationMessage.mData;
+        mStatus = navigationMessage.mStatus;
     }
 
     /**
@@ -194,6 +218,41 @@
         mData = value;
     }
 
+    /**
+     * Gets the Status of the navigation message contained in the object.
+     *
+     * @hide
+     */
+    public short getStatus() {
+        return mStatus;
+    }
+
+    /**
+     * Sets the status of the navigation message.
+     *
+     * @hide
+     */
+    public void setStatus(short value) {
+        mStatus = value;
+    }
+
+    /**
+     * Gets a string representation of the 'status'.
+     * For internal and logging use only.
+     */
+    private String getStatusString() {
+        switch (mStatus) {
+            case STATUS_UNKNOWN:
+                return "Unknown";
+            case STATUS_PARITY_PASSED:
+                return "ParityPassed";
+            case STATUS_PARITY_REBUILT:
+                return "ParityRebuilt";
+            default:
+                return "<Invalid:" + mStatus + ">";
+        }
+    }
+
     public static final Creator<GpsNavigationMessage> CREATOR =
             new Creator<GpsNavigationMessage>() {
         @Override
@@ -210,6 +269,13 @@
             parcel.readByteArray(data);
             navigationMessage.setData(data);
 
+            if (parcel.dataAvail() >= Integer.SIZE) {
+                int status = parcel.readInt();
+                navigationMessage.setStatus((short) status);
+            } else {
+                navigationMessage.setStatus(STATUS_UNKNOWN);
+            }
+
             return navigationMessage;
         }
 
@@ -226,6 +292,7 @@
         parcel.writeInt(mSubmessageId);
         parcel.writeInt(mData.length);
         parcel.writeByteArray(mData);
+        parcel.writeInt(mStatus);
     }
 
     @Override
@@ -240,6 +307,7 @@
 
         builder.append(String.format(format, "Type", getTypeString()));
         builder.append(String.format(format, "Prn", mPrn));
+        builder.append(String.format(format, "Status", getStatusString()));
         builder.append(String.format(format, "MessageId", mMessageId));
         builder.append(String.format(format, "SubmessageId", mSubmessageId));
 
@@ -261,5 +329,6 @@
         mMessageId = -1;
         mSubmessageId = -1;
         mData = EMPTY_ARRAY;
+        mStatus = STATUS_UNKNOWN;
     }
 }
diff --git a/media/java/android/media/AudioDevice.java b/media/java/android/media/AudioDeviceInfo.java
similarity index 80%
rename from media/java/android/media/AudioDevice.java
rename to media/java/android/media/AudioDeviceInfo.java
index df4d60d..d58b1d1 100644
--- a/media/java/android/media/AudioDevice.java
+++ b/media/java/android/media/AudioDeviceInfo.java
@@ -20,9 +20,8 @@
 
 /**
  * Class to provide information about the audio devices.
- * @hide
  */
-public class AudioDevice {
+public class AudioDeviceInfo {
 
     /**
      * A device type associated with an unknown or uninitialized device.
@@ -42,7 +41,7 @@
      */
     public static final int TYPE_WIRED_HEADSET    = 3;
     /**
-     * A device type describing a pair of wired headphones .
+     * A device type describing a pair of wired headphones.
      */
     public static final int TYPE_WIRED_HEADPHONES = 4;
     /**
@@ -54,7 +53,7 @@
      */
     public static final int TYPE_LINE_DIGITAL     = 6;
     /**
-     * A device type describing a Bluetooth device typically used for telephony .
+     * A device type describing a Bluetooth device typically used for telephony.
      */
     public static final int TYPE_BLUETOOTH_SCO    = 7;
     /**
@@ -106,46 +105,92 @@
      */
     public static final int TYPE_AUX_LINE         = 19;
 
-    AudioDevicePortConfig mConfig;
+    private final AudioDevicePort mPort;
 
-    AudioDevice(AudioDevicePortConfig config) {
-        mConfig = new AudioDevicePortConfig(config);
+    AudioDeviceInfo(AudioDevicePort port) {
+       mPort = port;
     }
 
     /**
      * @hide
-     * CANDIDATE FOR PUBLIC API
-     * @return
+     * @return The internal device ID.
      */
-    public boolean isInputDevice() {
-        return (mConfig.port().role() == AudioPort.ROLE_SOURCE);
+    public int getId() {
+        return mPort.handle().id();
     }
 
     /**
-     * @hide
-     * CANDIDATE FOR PUBLIC API
-     * @return
+     * @return The human-readable name of the audio device.
      */
-    public boolean isOutputDevice() {
-        return (mConfig.port().role() == AudioPort.ROLE_SINK);
+    public String getName() {
+        return mPort.name();
     }
 
     /**
-     * @hide
-     * CANDIDATE FOR PUBLIC API
-     * @return
+     * @return The "address" string of the device. This generally contains device-specific
+     * parameters.
      */
-    public int getDeviceType() {
-        return INT_TO_EXT_DEVICE_MAPPING.get(mConfig.port().type(), TYPE_UNKNOWN);
-    }
-
-    /**
-     * @hide
-     * CANDIDATE FOR PUBLIC API
-     * @return
-     */
+    // TODO Is there a compelling reason to expose this?
     public String getAddress() {
-        return mConfig.port().address();
+        return mPort.address();
+    }
+
+   /**
+     * @return true if the audio device is a source for audio data (e.e an input).
+     */
+    public boolean isSource() {
+        return mPort.role() == AudioPort.ROLE_SOURCE;
+    }
+
+    /**
+     * @return true if the audio device is a sink for audio data (i.e. an output).
+     */
+    public boolean isSink() {
+        return mPort.role() == AudioPort.ROLE_SINK;
+    }
+
+    /**
+     * @return An array of sample rates supported by the audio device.
+     */
+    public int[] getSampleRates() {
+        return mPort.samplingRates();
+    }
+
+    /**
+     * @return An array of channel masks supported by the audio device (defined in
+     * AudioFormat.java).
+     */
+    public int[] getChannelMasks() {
+        return mPort.channelMasks();
+    }
+
+    /**
+     * @return An array of channel counts supported by the audio device.
+     */
+    public int[] getChannelCounts() {
+        int[] masks = getChannelMasks();
+        int[] counts = new int[masks.length];
+        for (int mask_index = 0; mask_index < masks.length; mask_index++) {
+            counts[mask_index] = isSink()
+                    ? AudioFormat.channelCountFromOutChannelMask(masks[mask_index])
+                    : AudioFormat.channelCountFromInChannelMask(masks[mask_index]);
+        }
+        return counts;
+    }
+
+    /**
+     * @return An array of audio format IDs supported by the audio device (defined in
+     * AudioFormat.java)
+     */
+    public int[] getFormats() {
+        return mPort.formats();
+    }
+
+   /**
+     * @return The device type identifier of the audio device (i.e. TYPE_BUILTIN_SPEAKER).
+     */
+    public int getType() {
+        return INT_TO_EXT_DEVICE_MAPPING.get(mPort.type(), TYPE_UNKNOWN);
     }
 
     /** @hide */
diff --git a/media/java/android/media/AudioDevicesManager.java b/media/java/android/media/AudioDevicesManager.java
index ee11eef..ca238d7 100644
--- a/media/java/android/media/AudioDevicesManager.java
+++ b/media/java/android/media/AudioDevicesManager.java
@@ -17,24 +17,54 @@
 package android.media;
 
 import android.content.Context;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.util.ArrayMap;
+import android.util.Pair;
 import android.util.Slog;
 
 import java.util.ArrayList;
+import java.util.Collection;
+import java.util.concurrent.CopyOnWriteArrayList;
 import java.util.HashMap;
 import java.util.Iterator;
 
-/** @hide
- * API candidate
+/**
+ * AudioDevicesManager implements the Android Media Audio device enumeration and notification
+ * functionality.  This functionality is in two comlementary parts.
+ * <ol>
+ * <li>{@link AudioDevicesManager#listDevices(int)} gets the list of current audio devices
+ * </li>
+ * <li>{@link AudioDevicesManager#addOnAudioDeviceConnectionListener(OnAudioDeviceConnectionListener, android.os.Handler)}
+ *  provides a mechanism for applications to be informed of audio device connect/disconnect events.
+ * </li>
+ * </ol>
  */
 public class AudioDevicesManager {
+
     private static String TAG = "AudioDevicesManager";
-    private static boolean DEBUG = true;
+
+    private static boolean DEBUG = false;
 
     private AudioManager mAudioManager = null;
+
     private OnAmPortUpdateListener mPortListener = null;
 
-    /*
-     * Enum/Selection API
+    /**
+     * The message sent to apps when the contents of the device list changes if they provide
+     * a {#link Handler} object to addOnAudioDeviceConnectionListener().
+     */
+    private final static int MSG_DEVICES_LIST_CHANGE = 0;
+
+    private ArrayMap<OnAudioDeviceConnectionListener, NativeEventHandlerDelegate>
+        mDeviceConnectionListeners =
+            new ArrayMap<OnAudioDeviceConnectionListener, NativeEventHandlerDelegate>();
+
+    /**
+     * @hide
+     * The AudioDevicesManager class is used to enumerate the physical audio devices connected
+     * to the system.  See also {@link AudioDeviceInfo}.
      */
     public AudioDevicesManager(Context context) {
         mAudioManager = (AudioManager)context.getSystemService(Context.AUDIO_SERVICE);
@@ -42,214 +72,120 @@
         mAudioManager.registerAudioPortUpdateListener(mPortListener);
     }
 
-    /** @hide
-     * API candidate
+    /**
+     * Specifies to the {@link AudioDevicesManager#listDevices(int)} method to include
+     * source (i.e. input) audio devices.
      */
-    //TODO Merge this class into android.media.AudioDevice
-    public class AudioDeviceInfo {
-        private AudioDevicePort mPort = null;
+    public static final int LIST_DEVICES_INPUTS    = 0x0001;
 
-        /** @hide */
-        /* package */ AudioDeviceInfo(AudioDevicePort port) {
-            mPort = port;
-        }
+    /**
+     * Specifies to the {@link AudioDevicesManager#listDevices(int)} method to include
+     * sink (i.e. output) audio devices.
+     */
+    public static final int LIST_DEVICES_OUTPUTS   = 0x0002;
 
-        public int getId() { return mPort.handle().id(); }
-
-        public String getName() { return mPort.name(); }
-
-        public int getType() {
-            return mPort.type();
-        }
-
-        public String getAddress() {
-            return mPort.address();
-        }
-
-        public int getRole() { return mPort.role(); }
-
-        public int[] getSampleRates() { return mPort.samplingRates(); }
-
-        public int[] getChannelMasks() { return mPort.channelMasks(); }
-
-        public int[] getChannelCounts() {
-            int[] masks = getChannelMasks();
-            int[] counts = new int[masks.length];
-            for (int mask_index = 0; mask_index < masks.length; mask_index++) {
-                counts[mask_index] = getRole() == AudioPort.ROLE_SINK
-                        ? AudioFormat.channelCountFromOutChannelMask(masks[mask_index])
-                        : AudioFormat.channelCountFromInChannelMask(masks[mask_index]);
-            }
-            return counts;
-        }
-
-        /* The format IDs are in AudioFormat.java */
-        public int[] getFormats() { return mPort.formats(); }
-
-        public String toString() { return "" + getId() + " - " + getName(); }
-    }
-
-    /** @hide */
-    public static final int LIST_DEVICES_OUTPUTS   = 0x0001;
-    /** @hide */
-    public static final int LIST_DEVICES_INPUTS    = 0x0002;
-    /** @hide */
-    public static final int LIST_DEVICES_BUILTIN   = 0x0004;
-    /** @hide */
-    public static final int LIST_DEVICES_USB       = 0x0008;
-    // TODO implement the semantics for these.
-    /** @hide */
-    public static final int LIST_DEVICES_WIRED     = 0x0010;
-    /** @hide */
-    public static final int LIST_DEVICES_UNWIRED   = 0x0020;
-
-    /** @hide */
+    /**
+     * Specifies to the {@link AudioDevicesManager#listDevices(int)} method to include both
+     * source and sink devices.
+     */
     public static final int LIST_DEVICES_ALL = LIST_DEVICES_OUTPUTS | LIST_DEVICES_INPUTS;
 
+    /**
+     * Determines if a given AudioDevicePort meets the specified filter criteria.
+     * @param port  The port to test.
+     * @param flags A set of bitflags specifying the criteria to test.
+     * @see {@link LIST_DEVICES_OUTPUTS} and {@link LIST_DEVICES_INPUTS}
+     **/
     private boolean checkFlags(AudioDevicePort port, int flags) {
-        // Inputs / Outputs
-        boolean passed =
-                port.role() == AudioPort.ROLE_SINK && (flags & LIST_DEVICES_OUTPUTS) != 0 ||
-                port.role() == AudioPort.ROLE_SOURCE && (flags & LIST_DEVICES_INPUTS) != 0;
-
-        // USB
-        if (passed && (flags & LIST_DEVICES_USB) != 0) {
-            int role = port.role();
-            int type = port.type();
-            Slog.i(TAG, "  role:" + role + " type:0x" + Integer.toHexString(type));
-            passed =
-                (role == AudioPort.ROLE_SINK && (type & AudioSystem.DEVICE_OUT_ALL_USB) != 0) ||
-                (role == AudioPort.ROLE_SOURCE && (type & AudioSystem.DEVICE_IN_ALL_USB) != 0);
-        }
-
-        return passed;
+        return port.role() == AudioPort.ROLE_SINK && (flags & LIST_DEVICES_OUTPUTS) != 0 ||
+               port.role() == AudioPort.ROLE_SOURCE && (flags & LIST_DEVICES_INPUTS) != 0;
     }
 
-    /** @hide */
-    public ArrayList<AudioDeviceInfo> listDevices(int flags) {
-        Slog.i(TAG, "AudioManager.listDevices(" + Integer.toHexString(flags) + ")");
-
+    /**
+     * Generates a list of AudioDeviceInfo objects corresponding to the audio devices currently
+     * connected to the system and meeting the criteria specified in the <code>flags</code>
+     * parameter.
+     * @param flags A set of bitflags specifying the criteria to test.
+     * @see {@link LIST_DEVICES_OUTPUTS}, {@link LIST_DEVICES_INPUTS} and {@link LIST_DEVICES_ALL}.
+     * @return A (possibly zero-length) array of AudioDeviceInfo objects.
+     */
+    public AudioDeviceInfo[] listDevices(int flags) {
         ArrayList<AudioDevicePort> ports = new ArrayList<AudioDevicePort>();
         int status = mAudioManager.listAudioDevicePorts(ports);
+        if (status != AudioManager.SUCCESS) {
+            // fail and bail!
+            return new AudioDeviceInfo[0];
+        }
 
-        Slog.i(TAG, "  status:" + status + " numPorts:" + ports.size());
-
-        ArrayList<AudioDeviceInfo> deviceList = new ArrayList<AudioDeviceInfo>();
-
-        if (status == AudioManager.SUCCESS) {
-            deviceList = new ArrayList<AudioDeviceInfo>();
-             for (AudioDevicePort port : ports) {
-                if (checkFlags(port, flags)) {
-                    deviceList.add(new AudioDeviceInfo(port));
-                }
+        // figure out how many AudioDeviceInfo we need space for
+        int numRecs = 0;
+        for (AudioDevicePort port : ports) {
+            if (checkFlags(port, flags)) {
+                numRecs++;
             }
         }
+
+        // Now load them up
+        AudioDeviceInfo[] deviceList = new AudioDeviceInfo[numRecs];
+        int slot = 0;
+        for (AudioDevicePort port : ports) {
+            if (checkFlags(port, flags)) {
+                deviceList[slot++] = new AudioDeviceInfo(port);
+            }
+        }
+
         return deviceList;
     }
 
-    private ArrayList<OnAudioDeviceConnectionListener> mDeviceConnectionListeners =
-            new ArrayList<OnAudioDeviceConnectionListener>();
-
-    private HashMap<Integer, AudioPort> mCurrentPortlist =
-            new HashMap<Integer, AudioPort>();
-
-    private ArrayList<AudioDeviceInfo> calcAddedDevices(AudioPort[] portList) {
-        ArrayList<AudioDeviceInfo> addedDevices = new  ArrayList<AudioDeviceInfo>();
-        synchronized(mCurrentPortlist) {
-            for(int portIndex = 0; portIndex < portList.length; portIndex++) {
-                if (portList[portIndex] instanceof AudioDevicePort) {
-                    if (!mCurrentPortlist.containsKey(portList[portIndex].handle().id())) {
-                        addedDevices.add(new AudioDeviceInfo((AudioDevicePort)portList[portIndex]));
-                    }
-                }
+    /**
+     * Adds an {@link OnAudioDeviceConnectionListener} to receive notifications of changes
+     * to the set of connected audio devices.
+     */
+    public void addOnAudioDeviceConnectionListener(OnAudioDeviceConnectionListener listener,
+            android.os.Handler handler) {
+        if (listener != null && !mDeviceConnectionListeners.containsKey(listener)) {
+            synchronized (mDeviceConnectionListeners) {
+                mDeviceConnectionListeners.put(
+                    listener, new NativeEventHandlerDelegate(listener, handler));
             }
         }
-        return addedDevices;
-    }
-
-    private boolean hasPortId(AudioPort[] portList, int id) {
-        for(int portIndex = 0; portIndex < portList.length; portIndex++) {
-            if (portList[portIndex].handle().id() == id) {
-                return true;
-            }
-        }
-        return false;
-    }
-
-    private ArrayList<AudioDeviceInfo> calcRemovedDevices(AudioPort[] portList) {
-        ArrayList<AudioDeviceInfo> removedDevices = new  ArrayList<AudioDeviceInfo>();
-
-        synchronized (mCurrentPortlist) {
-            Iterator it = mCurrentPortlist.entrySet().iterator();
-            while (it.hasNext()) {
-                HashMap.Entry pairs = (HashMap.Entry)it.next();
-                if (pairs.getValue() instanceof AudioDevicePort) {
-                    if (!hasPortId(portList, ((Integer)pairs.getKey()).intValue())) {
-                        removedDevices.add(new AudioDeviceInfo((AudioDevicePort)pairs.getValue()));
-                    }
-                }
-            }
-        }
-        return removedDevices;
-    }
-
-    private void buildCurrentDevicesList(AudioPort[] portList) {
-        synchronized (mCurrentPortlist) {
-            mCurrentPortlist.clear();
-            for (int portIndex = 0; portIndex < portList.length; portIndex++) {
-                if (portList[portIndex] instanceof AudioDevicePort) {
-                    mCurrentPortlist.put(portList[portIndex].handle().id(),
-                                         (AudioDevicePort)portList[portIndex]);
-                }
-            }
-        }
-    }
-
-    /** @hide */
-    public void addDeviceConnectionListener(OnAudioDeviceConnectionListener listener) {
-        synchronized (mDeviceConnectionListeners) {
-            mDeviceConnectionListeners.add(listener);
-        }
-    }
-
-    /** @hide */
-    public void removeDeviceConnectionListener(OnAudioDeviceConnectionListener listener) {
-        synchronized (mDeviceConnectionListeners) {
-            mDeviceConnectionListeners.remove(listener);
-        }
     }
 
     /**
-     * @hide
+     * Removes an {@link OnAudioDeviceConnectionListener} which has been previously registered
+     * to receive notifications of changes to the set of connected audio devices.
+     */
+    public void removeOnAudioDeviceConnectionListener(OnAudioDeviceConnectionListener listener) {
+        synchronized (mDeviceConnectionListeners) {
+            if (mDeviceConnectionListeners.containsKey(listener)) {
+                mDeviceConnectionListeners.remove(listener);
+            }
+        }
+    }
+
+    /**
+     * Sends device list change notification to all listeners.
+     */
+    private void broadcastDeviceListChange() {
+        Collection<NativeEventHandlerDelegate> values;
+        synchronized (mDeviceConnectionListeners) {
+            values = mDeviceConnectionListeners.values();
+        }
+        for(NativeEventHandlerDelegate delegate : values) {
+            Handler handler = delegate.getHandler();
+            if (handler != null) {
+                handler.sendEmptyMessage(MSG_DEVICES_LIST_CHANGE);
+            }
+        }
+    }
+
+    /**
+     * Handles Port list update notifications from the AudioManager
      */
     private class OnAmPortUpdateListener implements AudioManager.OnAudioPortUpdateListener {
         static final String TAG = "OnAmPortUpdateListener";
         public void onAudioPortListUpdate(AudioPort[] portList) {
-            Slog.i(TAG, "onAudioPortListUpdate() " + portList.length + " ports.");
-            ArrayList<AudioDeviceInfo> addedDevices = calcAddedDevices(portList);
-            ArrayList<AudioDeviceInfo> removedDevices = calcRemovedDevices(portList);
-
-            ArrayList<OnAudioDeviceConnectionListener> listeners = null;
-            synchronized (mDeviceConnectionListeners) {
-                listeners =
-                        new ArrayList<OnAudioDeviceConnectionListener>(mDeviceConnectionListeners);
-            }
-
-            // Connect
-            if (addedDevices.size() != 0) {
-                for (OnAudioDeviceConnectionListener listener : listeners) {
-                    listener.onConnect(addedDevices);
-                }
-            }
-
-            // Disconnect?
-            if (removedDevices.size() != 0) {
-                for (OnAudioDeviceConnectionListener listener : listeners) {
-                    listener.onDisconnect(removedDevices);
-                }
-            }
-
-            buildCurrentDevicesList(portList);
+            broadcastDeviceListChange();
         }
 
         /**
@@ -257,14 +193,70 @@
          * @param patchList the updated list of audio patches
          */
         public void onAudioPatchListUpdate(AudioPatch[] patchList) {
-            Slog.i(TAG, "onAudioPatchListUpdate() " + patchList.length + " patches.");
+            if (DEBUG) {
+                Slog.d(TAG, "onAudioPatchListUpdate() " + patchList.length + " patches.");
+            }
         }
 
         /**
          * Callback method called when the mediaserver dies
          */
         public void onServiceDied() {
-            Slog.i(TAG, "onServiceDied()");
+            if (DEBUG) {
+                Slog.i(TAG, "onServiceDied()");
+            }
+
+            broadcastDeviceListChange();
+        }
+    }
+
+    //---------------------------------------------------------
+    // Inner classes
+    //--------------------
+    /**
+     * Helper class to handle the forwarding of native events to the appropriate listener
+     * (potentially) handled in a different thread.
+     */
+    private class NativeEventHandlerDelegate {
+        private final Handler mHandler;
+
+        NativeEventHandlerDelegate(final OnAudioDeviceConnectionListener listener,
+                                   Handler handler) {
+            // find the looper for our new event handler
+            Looper looper;
+            if (handler != null) {
+                looper = handler.getLooper();
+            } else {
+                // no given handler, use the looper the addListener call was called in
+                looper = Looper.getMainLooper();
+            }
+
+            // construct the event handler with this looper
+            if (looper != null) {
+                // implement the event handler delegate
+                mHandler = new Handler(looper) {
+                    @Override
+                    public void handleMessage(Message msg) {
+                        switch(msg.what) {
+                        case MSG_DEVICES_LIST_CHANGE:
+                            // call the OnAudioDeviceConnectionListener
+                            if (listener != null) {
+                                listener.onAudioDeviceConnection();
+                            }
+                            break;
+                        default:
+                            Slog.e(TAG, "Unknown native event type: " + msg.what);
+                            break;
+                        }
+                    }
+                };
+            } else {
+                mHandler = null;
+            }
+        }
+
+        Handler getHandler() {
+            return mHandler;
         }
     }
 }
diff --git a/media/java/android/media/AudioRecord.java b/media/java/android/media/AudioRecord.java
index 9e1e055..3e771f4 100644
--- a/media/java/android/media/AudioRecord.java
+++ b/media/java/android/media/AudioRecord.java
@@ -901,21 +901,48 @@
      *    the parameters don't resolve to valid data and indexes.
      *    The number of bytes will not exceed sizeInBytes.
      */
-    public int read(byte[] audioData, int offsetInBytes, int sizeInBytes) {
-        if (mState != STATE_INITIALIZED) {
+    public int read(@NonNull byte[] audioData, int offsetInBytes, int sizeInBytes) {
+        return read(audioData, offsetInBytes, sizeInBytes, READ_BLOCKING);
+    }
+
+    /**
+     * Reads audio data from the audio hardware for recording into a byte array.
+     * The format specified in the AudioRecord constructor should be
+     * {@link AudioFormat#ENCODING_PCM_8BIT} to correspond to the data in the array.
+     * @param audioData the array to which the recorded audio data is written.
+     * @param offsetInBytes index in audioData from which the data is written expressed in bytes.
+     * @param sizeInBytes the number of requested bytes.
+     * @param readMode one of {@link #READ_BLOCKING}, {@link #READ_NON_BLOCKING}.
+     *     <br>With {@link #READ_BLOCKING}, the read will block until all the requested data
+     *     is read.
+     *     <br>With {@link #READ_NON_BLOCKING}, the read will return immediately after
+     *     reading as much audio data as possible without blocking.
+     * @return the number of bytes that were read or {@link #ERROR_INVALID_OPERATION}
+     *    if the object wasn't properly initialized, or {@link #ERROR_BAD_VALUE} if
+     *    the parameters don't resolve to valid data and indexes.
+     *    The number of bytes will not exceed sizeInBytes.
+     */
+    public int read(@NonNull byte[] audioData, int offsetInBytes, int sizeInBytes,
+            @ReadMode int readMode) {
+        if (mState != STATE_INITIALIZED  || mAudioFormat == AudioFormat.ENCODING_PCM_FLOAT) {
             return ERROR_INVALID_OPERATION;
         }
 
+        if ((readMode != READ_BLOCKING) && (readMode != READ_NON_BLOCKING)) {
+            Log.e(TAG, "AudioRecord.read() called with invalid blocking mode");
+            return ERROR_BAD_VALUE;
+        }
+
         if ( (audioData == null) || (offsetInBytes < 0 ) || (sizeInBytes < 0)
                 || (offsetInBytes + sizeInBytes < 0)  // detect integer overflow
                 || (offsetInBytes + sizeInBytes > audioData.length)) {
             return ERROR_BAD_VALUE;
         }
 
-        return native_read_in_byte_array(audioData, offsetInBytes, sizeInBytes);
+        return native_read_in_byte_array(audioData, offsetInBytes, sizeInBytes,
+                readMode == READ_BLOCKING);
     }
 
-
     /**
      * Reads audio data from the audio hardware for recording into a short array.
      * The format specified in the AudioRecord constructor should be
@@ -928,18 +955,46 @@
      *    the parameters don't resolve to valid data and indexes.
      *    The number of shorts will not exceed sizeInShorts.
      */
-    public int read(short[] audioData, int offsetInShorts, int sizeInShorts) {
-        if (mState != STATE_INITIALIZED) {
+    public int read(@NonNull short[] audioData, int offsetInShorts, int sizeInShorts) {
+        return read(audioData, offsetInShorts, sizeInShorts, READ_BLOCKING);
+    }
+
+    /**
+     * Reads audio data from the audio hardware for recording into a short array.
+     * The format specified in the AudioRecord constructor should be
+     * {@link AudioFormat#ENCODING_PCM_16BIT} to correspond to the data in the array.
+     * @param audioData the array to which the recorded audio data is written.
+     * @param offsetInShorts index in audioData from which the data is written expressed in shorts.
+     * @param sizeInShorts the number of requested shorts.
+     * @param readMode one of {@link #READ_BLOCKING}, {@link #READ_NON_BLOCKING}.
+     *     <br>With {@link #READ_BLOCKING}, the read will block until all the requested data
+     *     is read.
+     *     <br>With {@link #READ_NON_BLOCKING}, the read will return immediately after
+     *     reading as much audio data as possible without blocking.
+     * @return the number of shorts that were read or {@link #ERROR_INVALID_OPERATION}
+     *    if the object wasn't properly initialized, or {@link #ERROR_BAD_VALUE} if
+     *    the parameters don't resolve to valid data and indexes.
+     *    The number of shorts will not exceed sizeInShorts.
+     */
+    public int read(@NonNull short[] audioData, int offsetInShorts, int sizeInShorts,
+            @ReadMode int readMode) {
+        if (mState != STATE_INITIALIZED || mAudioFormat == AudioFormat.ENCODING_PCM_FLOAT) {
             return ERROR_INVALID_OPERATION;
         }
 
+        if ((readMode != READ_BLOCKING) && (readMode != READ_NON_BLOCKING)) {
+            Log.e(TAG, "AudioRecord.read() called with invalid blocking mode");
+            return ERROR_BAD_VALUE;
+        }
+
         if ( (audioData == null) || (offsetInShorts < 0 ) || (sizeInShorts < 0)
                 || (offsetInShorts + sizeInShorts < 0)  // detect integer overflow
                 || (offsetInShorts + sizeInShorts > audioData.length)) {
             return ERROR_BAD_VALUE;
         }
 
-        return native_read_in_short_array(audioData, offsetInShorts, sizeInShorts);
+        return native_read_in_short_array(audioData, offsetInShorts, sizeInShorts,
+                readMode == READ_BLOCKING);
     }
 
     /**
@@ -950,22 +1005,33 @@
      * @param offsetInFloats index in audioData from which the data is written.
      * @param sizeInFloats the number of requested floats.
      * @param readMode one of {@link #READ_BLOCKING}, {@link #READ_NON_BLOCKING}.
-     *     <BR>With {@link #READ_BLOCKING}, the read will block until all the requested data
+     *     <br>With {@link #READ_BLOCKING}, the read will block until all the requested data
      *     is read.
-     *     <BR>With {@link #READ_NON_BLOCKING}, the read will return immediately after
+     *     <br>With {@link #READ_NON_BLOCKING}, the read will return immediately after
      *     reading as much audio data as possible without blocking.
      * @return the number of floats that were read or {@link #ERROR_INVALID_OPERATION}
      *    if the object wasn't properly initialized, or {@link #ERROR_BAD_VALUE} if
      *    the parameters don't resolve to valid data and indexes.
      *    The number of floats will not exceed sizeInFloats.
      */
-    public int read(float[] audioData, int offsetInFloats, int sizeInFloats,
+    public int read(@NonNull float[] audioData, int offsetInFloats, int sizeInFloats,
             @ReadMode int readMode) {
-        if (mState != STATE_INITIALIZED) {
+        if (mState == STATE_UNINITIALIZED) {
+            Log.e(TAG, "AudioRecord.read() called in invalid state STATE_UNINITIALIZED");
             return ERROR_INVALID_OPERATION;
         }
 
-        if ( (audioData == null) || (offsetInFloats < 0 ) || (sizeInFloats < 0)
+        if (mAudioFormat != AudioFormat.ENCODING_PCM_FLOAT) {
+            Log.e(TAG, "AudioRecord.read(float[] ...) requires format ENCODING_PCM_FLOAT");
+            return ERROR_INVALID_OPERATION;
+        }
+
+        if ((readMode != READ_BLOCKING) && (readMode != READ_NON_BLOCKING)) {
+            Log.e(TAG, "AudioRecord.read() called with invalid blocking mode");
+            return ERROR_BAD_VALUE;
+        }
+
+        if ((audioData == null) || (offsetInFloats < 0) || (sizeInFloats < 0)
                 || (offsetInFloats + sizeInFloats < 0)  // detect integer overflow
                 || (offsetInFloats + sizeInFloats > audioData.length)) {
             return ERROR_BAD_VALUE;
@@ -992,19 +1058,49 @@
      *    The number of bytes will not exceed sizeInBytes.
      *    The number of bytes read will truncated to be a multiple of the frame size.
      */
-    public int read(ByteBuffer audioBuffer, int sizeInBytes) {
+    public int read(@NonNull ByteBuffer audioBuffer, int sizeInBytes) {
+        return read(audioBuffer, sizeInBytes, READ_BLOCKING);
+    }
+
+    /**
+     * Reads audio data from the audio hardware for recording into a direct buffer. If this buffer
+     * is not a direct buffer, this method will always return 0.
+     * Note that the value returned by {@link java.nio.Buffer#position()} on this buffer is
+     * unchanged after a call to this method.
+     * The representation of the data in the buffer will depend on the format specified in
+     * the AudioRecord constructor, and will be native endian.
+     * @param audioBuffer the direct buffer to which the recorded audio data is written.
+     * @param sizeInBytes the number of requested bytes. It is recommended but not enforced
+     *    that the number of bytes requested be a multiple of the frame size (sample size in
+     *    bytes multiplied by the channel count).
+     * @param readMode one of {@link #READ_BLOCKING}, {@link #READ_NON_BLOCKING}.
+     *     <br>With {@link #READ_BLOCKING}, the read will block until all the requested data
+     *     is read.
+     *     <br>With {@link #READ_NON_BLOCKING}, the read will return immediately after
+     *     reading as much audio data as possible without blocking.
+     * @return the number of bytes that were read or {@link #ERROR_INVALID_OPERATION}
+     *    if the object wasn't properly initialized, or {@link #ERROR_BAD_VALUE} if
+     *    the parameters don't resolve to valid data and indexes.
+     *    The number of bytes will not exceed sizeInBytes.
+     *    The number of bytes read will truncated to be a multiple of the frame size.
+     */
+    public int read(@NonNull ByteBuffer audioBuffer, int sizeInBytes, @ReadMode int readMode) {
         if (mState != STATE_INITIALIZED) {
             return ERROR_INVALID_OPERATION;
         }
 
+        if ((readMode != READ_BLOCKING) && (readMode != READ_NON_BLOCKING)) {
+            Log.e(TAG, "AudioRecord.read() called with invalid blocking mode");
+            return ERROR_BAD_VALUE;
+        }
+
         if ( (audioBuffer == null) || (sizeInBytes < 0) ) {
             return ERROR_BAD_VALUE;
         }
 
-        return native_read_in_direct_buffer(audioBuffer, sizeInBytes);
+        return native_read_in_direct_buffer(audioBuffer, sizeInBytes, readMode == READ_BLOCKING);
     }
 
-
     //--------------------------------------------------------------------------
     // Initialization / configuration
     //--------------------
@@ -1186,15 +1282,16 @@
     private native final void native_stop();
 
     private native final int native_read_in_byte_array(byte[] audioData,
-            int offsetInBytes, int sizeInBytes);
+            int offsetInBytes, int sizeInBytes, boolean isBlocking);
 
     private native final int native_read_in_short_array(short[] audioData,
-            int offsetInShorts, int sizeInShorts);
+            int offsetInShorts, int sizeInShorts, boolean isBlocking);
 
     private native final int native_read_in_float_array(float[] audioData,
             int offsetInFloats, int sizeInFloats, boolean isBlocking);
 
-    private native final int native_read_in_direct_buffer(Object jBuffer, int sizeInBytes);
+    private native final int native_read_in_direct_buffer(Object jBuffer,
+            int sizeInBytes, boolean isBlocking);
 
     private native final int native_get_native_frame_count();
 
diff --git a/media/java/android/media/AudioTrack.java b/media/java/android/media/AudioTrack.java
index fac69ea..44455fa 100644
--- a/media/java/android/media/AudioTrack.java
+++ b/media/java/android/media/AudioTrack.java
@@ -1540,6 +1540,8 @@
     /**
      * Writes the audio data to the audio sink for playback (streaming mode),
      * or copies audio data for later playback (static buffer mode).
+     * The format specified in the AudioTrack constructor should be
+     * {@link AudioFormat#ENCODING_PCM_8BIT} to correspond to the data in the array.
      * In streaming mode, will block until all data has been written to the audio sink.
      * In static buffer mode, copies the data to the buffer starting at offset 0.
      * Note that the actual playback of this data might occur after this function
@@ -1556,13 +1558,49 @@
      *    {@link AudioManager#ERROR_DEAD_OBJECT} if the AudioTrack is not valid anymore and
      *    needs to be recreated.
      */
+    public int write(@NonNull byte[] audioData, int offsetInBytes, int sizeInBytes) {
+        return write(audioData, offsetInBytes, sizeInBytes, WRITE_BLOCKING);
+    }
 
-    public int write(byte[] audioData, int offsetInBytes, int sizeInBytes) {
+    /**
+     * Writes the audio data to the audio sink for playback (streaming mode),
+     * or copies audio data for later playback (static buffer mode).
+     * The format specified in the AudioTrack constructor should be
+     * {@link AudioFormat#ENCODING_PCM_8BIT} to correspond to the data in the array.
+     * In streaming mode, will block until all data has been written to the audio sink.
+     * In static buffer mode, copies the data to the buffer starting at offset 0.
+     * Note that the actual playback of this data might occur after this function
+     * returns. This function is thread safe with respect to {@link #stop} calls,
+     * in which case all of the specified data might not be written to the audio sink.
+     *
+     * @param audioData the array that holds the data to play.
+     * @param offsetInBytes the offset expressed in bytes in audioData where the data to play
+     *    starts.
+     * @param sizeInBytes the number of bytes to read in audioData after the offset.
+     * @param writeMode one of {@link #WRITE_BLOCKING}, {@link #WRITE_NON_BLOCKING}. It has no
+     *     effect in static mode.
+     *     <br>With {@link #WRITE_BLOCKING}, the write will block until all data has been written
+     *         to the audio sink.
+     *     <br>With {@link #WRITE_NON_BLOCKING}, the write will return immediately after
+     *     queuing as much audio data for playback as possible without blocking.
+     * @return the number of bytes that were written or {@link #ERROR_INVALID_OPERATION}
+     *    if the object wasn't properly initialized, or {@link #ERROR_BAD_VALUE} if
+     *    the parameters don't resolve to valid data and indexes, or
+     *    {@link AudioManager#ERROR_DEAD_OBJECT} if the AudioTrack is not valid anymore and
+     *    needs to be recreated.
+     */
+    public int write(@NonNull byte[] audioData, int offsetInBytes, int sizeInBytes,
+            @WriteMode int writeMode) {
 
         if (mState == STATE_UNINITIALIZED || mAudioFormat == AudioFormat.ENCODING_PCM_FLOAT) {
             return ERROR_INVALID_OPERATION;
         }
 
+        if ((writeMode != WRITE_BLOCKING) && (writeMode != WRITE_NON_BLOCKING)) {
+            Log.e(TAG, "AudioTrack.write() called with invalid blocking mode");
+            return ERROR_BAD_VALUE;
+        }
+
         if ( (audioData == null) || (offsetInBytes < 0 ) || (sizeInBytes < 0)
                 || (offsetInBytes + sizeInBytes < 0)    // detect integer overflow
                 || (offsetInBytes + sizeInBytes > audioData.length)) {
@@ -1570,7 +1608,7 @@
         }
 
         int ret = native_write_byte(audioData, offsetInBytes, sizeInBytes, mAudioFormat,
-                true /*isBlocking*/);
+                writeMode == WRITE_BLOCKING);
 
         if ((mDataLoadMode == MODE_STATIC)
                 && (mState == STATE_NO_STATIC_DATA)
@@ -1582,10 +1620,11 @@
         return ret;
     }
 
-
     /**
      * Writes the audio data to the audio sink for playback (streaming mode),
      * or copies audio data for later playback (static buffer mode).
+     * The format specified in the AudioTrack constructor should be
+     * {@link AudioFormat#ENCODING_PCM_16BIT} to correspond to the data in the array.
      * In streaming mode, will block until all data has been written to the audio sink.
      * In static buffer mode, copies the data to the buffer starting at offset 0.
      * Note that the actual playback of this data might occur after this function
@@ -1602,20 +1641,57 @@
      *    {@link AudioManager#ERROR_DEAD_OBJECT} if the AudioTrack is not valid anymore and
      *    needs to be recreated.
      */
+    public int write(@NonNull short[] audioData, int offsetInShorts, int sizeInShorts) {
+        return write(audioData, offsetInShorts, sizeInShorts, WRITE_BLOCKING);
+    }
 
-    public int write(short[] audioData, int offsetInShorts, int sizeInShorts) {
+    /**
+     * Writes the audio data to the audio sink for playback (streaming mode),
+     * or copies audio data for later playback (static buffer mode).
+     * The format specified in the AudioTrack constructor should be
+     * {@link AudioFormat#ENCODING_PCM_16BIT} to correspond to the data in the array.
+     * In streaming mode, will block until all data has been written to the audio sink.
+     * In static buffer mode, copies the data to the buffer starting at offset 0.
+     * Note that the actual playback of this data might occur after this function
+     * returns. This function is thread safe with respect to {@link #stop} calls,
+     * in which case all of the specified data might not be written to the audio sink.
+     *
+     * @param audioData the array that holds the data to play.
+     * @param offsetInShorts the offset expressed in shorts in audioData where the data to play
+     *     starts.
+     * @param sizeInShorts the number of shorts to read in audioData after the offset.
+     * @param writeMode one of {@link #WRITE_BLOCKING}, {@link #WRITE_NON_BLOCKING}. It has no
+     *     effect in static mode.
+     *     <br>With {@link #WRITE_BLOCKING}, the write will block until all data has been written
+     *         to the audio sink.
+     *     <br>With {@link #WRITE_NON_BLOCKING}, the write will return immediately after
+     *     queuing as much audio data for playback as possible without blocking.
+     * @return the number of shorts that were written or {@link #ERROR_INVALID_OPERATION}
+     *    if the object wasn't properly initialized, or {@link #ERROR_BAD_VALUE} if
+     *    the parameters don't resolve to valid data and indexes, or
+     *    {@link AudioManager#ERROR_DEAD_OBJECT} if the AudioTrack is not valid anymore and
+     *    needs to be recreated.
+     */
+    public int write(@NonNull short[] audioData, int offsetInShorts, int sizeInShorts,
+            @WriteMode int writeMode) {
 
         if (mState == STATE_UNINITIALIZED || mAudioFormat == AudioFormat.ENCODING_PCM_FLOAT) {
             return ERROR_INVALID_OPERATION;
         }
 
+        if ((writeMode != WRITE_BLOCKING) && (writeMode != WRITE_NON_BLOCKING)) {
+            Log.e(TAG, "AudioTrack.write() called with invalid blocking mode");
+            return ERROR_BAD_VALUE;
+        }
+
         if ( (audioData == null) || (offsetInShorts < 0 ) || (sizeInShorts < 0)
                 || (offsetInShorts + sizeInShorts < 0)  // detect integer overflow
                 || (offsetInShorts + sizeInShorts > audioData.length)) {
             return ERROR_BAD_VALUE;
         }
 
-        int ret = native_write_short(audioData, offsetInShorts, sizeInShorts, mAudioFormat);
+        int ret = native_write_short(audioData, offsetInShorts, sizeInShorts, mAudioFormat,
+                writeMode == WRITE_BLOCKING);
 
         if ((mDataLoadMode == MODE_STATIC)
                 && (mState == STATE_NO_STATIC_DATA)
@@ -1627,10 +1703,11 @@
         return ret;
     }
 
-
     /**
      * Writes the audio data to the audio sink for playback (streaming mode),
      * or copies audio data for later playback (static buffer mode).
+     * The format specified in the AudioTrack constructor should be
+     * {@link AudioFormat#ENCODING_PCM_FLOAT} to correspond to the data in the array.
      * In static buffer mode, copies the data to the buffer starting at offset 0,
      * and the write mode is ignored.
      * In streaming mode, the blocking behavior will depend on the write mode.
@@ -1654,9 +1731,9 @@
      * @param sizeInFloats the number of floats to read in audioData after the offset.
      * @param writeMode one of {@link #WRITE_BLOCKING}, {@link #WRITE_NON_BLOCKING}. It has no
      *     effect in static mode.
-     *     <BR>With {@link #WRITE_BLOCKING}, the write will block until all data has been written
+     *     <br>With {@link #WRITE_BLOCKING}, the write will block until all data has been written
      *         to the audio sink.
-     *     <BR>With {@link #WRITE_NON_BLOCKING}, the write will return immediately after
+     *     <br>With {@link #WRITE_NON_BLOCKING}, the write will return immediately after
      *     queuing as much audio data for playback as possible without blocking.
      * @return the number of floats that were written, or {@link #ERROR_INVALID_OPERATION}
      *    if the object wasn't properly initialized, or {@link #ERROR_BAD_VALUE} if
@@ -1664,7 +1741,7 @@
      *    {@link AudioManager#ERROR_DEAD_OBJECT} if the AudioTrack is not valid anymore and
      *    needs to be recreated.
      */
-    public int write(float[] audioData, int offsetInFloats, int sizeInFloats,
+    public int write(@NonNull float[] audioData, int offsetInFloats, int sizeInFloats,
             @WriteMode int writeMode) {
 
         if (mState == STATE_UNINITIALIZED) {
@@ -1727,7 +1804,7 @@
      *     {@link AudioManager#ERROR_DEAD_OBJECT} if the AudioTrack is not valid anymore and
      *     needs to be recreated.
      */
-    public int write(ByteBuffer audioData, int sizeInBytes,
+    public int write(@NonNull ByteBuffer audioData, int sizeInBytes,
             @WriteMode int writeMode) {
 
         if (mState == STATE_UNINITIALIZED) {
@@ -1851,6 +1928,39 @@
         return err == 0 ? SUCCESS : ERROR;
     }
 
+    //--------------------------------------------------------------------------
+    // Explicit Routing
+    //--------------------
+    private AudioDeviceInfo mPreferredDevice = null;
+
+    /**
+     * Specifies an audio device (via and {@link AudioDeviceInfo} object) to route
+     * the output from this AudioTrack.
+     * @param deviceSpec The {@link AudioDeviceInfo} specifying the physical audio device.
+     *  If deviceSpec is null, default routing is restored.
+     * @return true if succesful, false if the specified {@link AudioDeviceInfo} is non-null and
+     * does not correspond to a valid audio output device.
+     */
+    public boolean setPreferredOutputDevice(AudioDeviceInfo deviceSpec) {
+        // Do some validation....
+        if (deviceSpec != null && !deviceSpec.isSink()) {
+            return false;
+        }
+
+        mPreferredDevice = deviceSpec;
+        int routingDeviceId = mPreferredDevice != null ? deviceSpec.getId() : 0;
+
+        return native_setOutputDevice(routingDeviceId);
+    }
+
+    /**
+     * Returns the selected output specified by {@link #setPreferredOutputDevice}. Note that this
+     * is not guarenteed to correspond to the actual device being used for playback.
+     */
+    public AudioDeviceInfo getPreferredOutputDevice() {
+        return mPreferredDevice;
+    }
+
     //---------------------------------------------------------
     // Interface definitions
     //--------------------
@@ -1984,7 +2094,8 @@
                                                boolean isBlocking);
 
     private native final int native_write_short(short[] audioData,
-                                                int offsetInShorts, int sizeInShorts, int format);
+                                                int offsetInShorts, int sizeInShorts, int format,
+                                                boolean isBlocking);
 
     private native final int native_write_float(float[] audioData,
                                                 int offsetInFloats, int sizeInFloats, int format,
@@ -2027,6 +2138,8 @@
     private native final int native_attachAuxEffect(int effectId);
     private native final int native_setAuxEffectSendLevel(float level);
 
+    private native final boolean native_setOutputDevice(int deviceId);
+
     //---------------------------------------------------------
     // Utility methods
     //------------------
diff --git a/media/java/android/media/DataSource.java b/media/java/android/media/DataSource.java
deleted file mode 100644
index 347bd5f..0000000
--- a/media/java/android/media/DataSource.java
+++ /dev/null
@@ -1,43 +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 android.media;
-
-import java.io.Closeable;
-
-/**
- * An abstraction for a media data source, e.g. a file or an http stream
- * {@hide}
- */
-public interface DataSource extends Closeable {
-    /**
-     * Reads data from the data source at the requested position
-     *
-     * @param offset where in the source to read
-     * @param buffer the buffer to read the data into
-     * @param size how many bytes to read
-     * @return the number of bytes read, or -1 if there was an error
-     */
-    public int readAt(long offset, byte[] buffer, int size);
-
-    /**
-     * Gets the size of the data source.
-     *
-     * @return size of data source, or -1 if the length is unknown
-     */
-    public long getSize();
-}
diff --git a/media/java/android/media/MediaCodec.java b/media/java/android/media/MediaCodec.java
index 59fccda..d82afdf 100644
--- a/media/java/android/media/MediaCodec.java
+++ b/media/java/android/media/MediaCodec.java
@@ -669,10 +669,10 @@
      * Thrown when an internal codec error occurs.
      */
     public final static class CodecException extends IllegalStateException {
-        CodecException(int errorCode, int actionCode, String detailMessage) {
+        CodecException(int errorCode, int actionCode, String detailMessage, int reason) {
             super(detailMessage);
             mErrorCode = errorCode;
-            mReason = REASON_HARDWARE;
+            mReason = reason;
             mActionCode = actionCode;
 
             // TODO get this from codec
diff --git a/media/java/android/media/MediaDataSource.java b/media/java/android/media/MediaDataSource.java
new file mode 100644
index 0000000..246c0ef
--- /dev/null
+++ b/media/java/android/media/MediaDataSource.java
@@ -0,0 +1,62 @@
+/*
+ * 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 android.media;
+
+import java.io.Closeable;
+
+/**
+ * For supplying media data to the framework. Implement this if your app has
+ * special requirements for the way media data is obtained.
+ *
+ * <p class="note">Methods of this interface may be called on multiple different
+ * threads. There will be a thread synchronization point between each call to ensure that
+ * modifications to the state of your MediaDataSource are visible to future calls. This means
+ * you don't need to do your own synchronization unless you're modifying the
+ * MediaDataSource from another thread while it's being used by the framework.</p>
+ */
+public interface MediaDataSource extends Closeable {
+    /**
+     * Called to request data from the given position.
+     *
+     * Implementations should should write up to {@code size} bytes into
+     * {@code buffer}, and return the number of bytes written.
+     *
+     * Return {@code 0} to indicate that {@code position} is at, or beyond, the
+     * end of the source.
+     *
+     * Return {@code -1} to indicate that a fatal error occurred. The failed
+     * read will not be retried, so transient errors should be handled
+     * internally.
+     *
+     * Throwing an exception from this method will have the same effect as
+     * returning {@code -1}.
+     *
+     * @param position the position in the data source to read from.
+     * @param buffer the buffer to read the data into.
+     * @param size the number of bytes to read.
+     * @return the number of bytes read, or -1 if there was an error.
+     */
+    public int readAt(long position, byte[] buffer, int size);
+
+    /**
+     * Called to get the size of the data source.
+     *
+     * @return the size of data source in bytes, or -1 if the size is unknown.
+     */
+    public long getSize();
+}
diff --git a/media/java/android/media/MediaExtractor.java b/media/java/android/media/MediaExtractor.java
index b23b540..b4acbc0 100644
--- a/media/java/android/media/MediaExtractor.java
+++ b/media/java/android/media/MediaExtractor.java
@@ -66,10 +66,11 @@
     }
 
     /**
-     * Sets the DataSource object to be used as the data source for this extractor
-     * {@hide}
+     * Sets the data source (MediaDataSource) to use.
+     *
+     * @param dataSource the MediaDataSource for the media you want to extract from
      */
-    public native final void setDataSource(DataSource source) throws IOException;
+    public native final void setDataSource(MediaDataSource dataSource) throws IllegalArgumentException, IOException;
 
     /**
      * Sets the data source as a content Uri.
diff --git a/media/java/android/media/MediaMetadataRetriever.java b/media/java/android/media/MediaMetadataRetriever.java
index 9aa8003..a3ff080 100644
--- a/media/java/android/media/MediaMetadataRetriever.java
+++ b/media/java/android/media/MediaMetadataRetriever.java
@@ -203,7 +203,20 @@
     }
 
     /**
-     * Call this method after setDataSource(). This method retrieves the 
+     * Sets the data source (MediaDataSource) to use.
+     *
+     * @param dataSource the MediaDataSource for the media you want to play
+     */
+    public void setDataSource(MediaDataSource dataSource)
+            throws IllegalArgumentException {
+        _setDataSource(dataSource);
+    }
+
+    private native void _setDataSource(MediaDataSource dataSource)
+          throws IllegalArgumentException;
+
+    /**
+     * Call this method after setDataSource(). This method retrieves the
      * meta data value associated with the keyCode.
      * 
      * The keyCode currently supported is listed below as METADATA_XXX
diff --git a/media/java/android/media/MediaPlayer.java b/media/java/android/media/MediaPlayer.java
index 83954ae..cb80bc4 100644
--- a/media/java/android/media/MediaPlayer.java
+++ b/media/java/android/media/MediaPlayer.java
@@ -181,7 +181,8 @@
  *         {@link #setDataSource(FileDescriptor)}, or
  *         {@link #setDataSource(String)}, or
  *         {@link #setDataSource(Context, Uri)}, or
- *         {@link #setDataSource(FileDescriptor, long, long)} transfers a
+ *         {@link #setDataSource(FileDescriptor, long, long)}, or
+ *         {@link #setDataSource(MediaDataSource)} transfers a
  *         MediaPlayer object in the <em>Idle</em> state to the
  *         <em>Initialized</em> state.
  *         <ul>
@@ -1127,6 +1128,20 @@
             throws IOException, IllegalArgumentException, IllegalStateException;
 
     /**
+     * Sets the data source (MediaDataSource) to use.
+     *
+     * @param dataSource the MediaDataSource for the media you want to play
+     * @throws IllegalStateException if it is called in an invalid state
+     */
+    public void setDataSource(MediaDataSource dataSource)
+            throws IllegalArgumentException, IllegalStateException {
+        _setDataSource(dataSource);
+    }
+
+    private native void _setDataSource(MediaDataSource dataSource)
+          throws IllegalArgumentException, IllegalStateException;
+
+    /**
      * Prepares the player for playback, synchronously.
      *
      * After setting the datasource and the display surface, you need to either
diff --git a/media/java/android/media/MediaSync.java b/media/java/android/media/MediaSync.java
index 7350bd4..9446c0c 100644
--- a/media/java/android/media/MediaSync.java
+++ b/media/java/android/media/MediaSync.java
@@ -43,9 +43,9 @@
  * // MediaCodec videoDecoder = ...;
  * videoDecoder.configure(format, inputSurface, ...);
  * ...
- * sync.configureAudioTrack(audioTrack, nativeSampleRateInHz);
+ * sync.configureAudioTrack(audioTrack);
  * sync.setCallback(new MediaSync.Callback() {
- *     \@Override
+ *     {@literal @Override}
  *     public void onReturnAudioBuffer(MediaSync sync, ByteBuffer audioBuffer, int bufferIndex) {
  *         ...
  *     }
@@ -151,8 +151,6 @@
     private Handler mCallbackHandler = null;
     private MediaSync.Callback mCallback = null;
 
-    private int mNativeSampleRateInHz = 0;
-
     private Thread mAudioThread = null;
     // Created on mAudioThread when mAudioThread is started. When used on user thread, they should
     // be guarded by checking mAudioThread.
@@ -247,20 +245,17 @@
      * Configures the audio track for MediaSync.
      *
      * @param audioTrack Specify an AudioTrack through which to render the audio data.
-     * @throws IllegalArgumentException if the audioTrack has been released, or is invalid,
-     *     or nativeSampleRateInHz is invalid.
+     * @throws IllegalArgumentException if the audioTrack has been released, or is invalid.
      * @throws IllegalStateException if not in the Initialized state, or another audio track
      *     has already been configured.
      */
-    public void configureAudioTrack(AudioTrack audioTrack, int nativeSampleRateInHz) {
-        if (audioTrack != null && nativeSampleRateInHz <= 0) {
-            final String msg = "Native sample rate " + nativeSampleRateInHz + " is invalid";
-            throw new IllegalArgumentException(msg);
-        }
+    public void configureAudioTrack(AudioTrack audioTrack) {
+        // AudioTrack has sanity check for configured sample rate.
+        int nativeSampleRateInHz = (audioTrack == null ? 0 : audioTrack.getSampleRate());
+
         native_configureAudioTrack(audioTrack, nativeSampleRateInHz);
         mAudioTrack = audioTrack;
-        mNativeSampleRateInHz = nativeSampleRateInHz;
-        if (mAudioThread == null) {
+        if (audioTrack != null && mAudioThread == null) {
             createAudioThread();
         }
     }
@@ -349,8 +344,9 @@
 
         int status = AudioTrack.SUCCESS;
         if (mAudioTrack != null) {
-            int playbackSampleRate = (int)(rate * mNativeSampleRateInHz + 0.5);
-            rate = playbackSampleRate / (float)mNativeSampleRateInHz;
+            int nativeSampleRateInHz = mAudioTrack.getSampleRate();
+            int playbackSampleRate = (int)(rate * nativeSampleRateInHz + 0.5);
+            rate = playbackSampleRate / (float)nativeSampleRateInHz;
 
             try {
                 if (rate == 0.0) {
diff --git a/media/java/android/media/OnAudioDeviceConnectionListener.java b/media/java/android/media/OnAudioDeviceConnectionListener.java
index 4bdd4d0..71c135a 100644
--- a/media/java/android/media/OnAudioDeviceConnectionListener.java
+++ b/media/java/android/media/OnAudioDeviceConnectionListener.java
@@ -16,13 +16,16 @@
 
 package android.media;
 
-import java.util.ArrayList;
-
 /**
- * @hide
- * API candidate
+ * OnAudioDeviceConnectionListener defines the interface for notification listeners in the
+ * {@link AudioDevicesManager}
  */
-public abstract class OnAudioDeviceConnectionListener {
-    public void onConnect(ArrayList<AudioDevicesManager.AudioDeviceInfo> devices) {}
-    public void onDisconnect(ArrayList<AudioDevicesManager.AudioDeviceInfo> devices) {}
+public interface OnAudioDeviceConnectionListener {
+    /**
+     * Called by the {@link AudioDevicesManager} to indicate that an audio device has been
+     * connected or disconnected. A listener will probably call the
+     * {@link AudioDevicesManager#listDevices} method to retrieve the current list of audio
+     * devices.
+     */
+    public void onAudioDeviceConnection();
 }
diff --git a/media/java/android/media/audiofx/Virtualizer.java b/media/java/android/media/audiofx/Virtualizer.java
index be5adc8..49e56bc 100644
--- a/media/java/android/media/audiofx/Virtualizer.java
+++ b/media/java/android/media/audiofx/Virtualizer.java
@@ -17,7 +17,7 @@
 package android.media.audiofx;
 
 import android.annotation.IntDef;
-import android.media.AudioDevice;
+import android.media.AudioDeviceInfo;
 import android.media.AudioFormat;
 import android.media.audiofx.AudioEffect;
 import android.util.Log;
@@ -204,7 +204,7 @@
         // convert channel mask to internal native representation
         paramsConverter.putInt(AudioFormat.convertChannelOutMaskToNativeMask(channelMask));
         // convert Java device type to internal representation
-        paramsConverter.putInt(AudioDevice.convertDeviceTypeToInternalDevice(deviceType));
+        paramsConverter.putInt(AudioDeviceInfo.convertDeviceTypeToInternalDevice(deviceType));
         // allocate an array to store the results
         byte[] result = new byte[nbChannels * 4/*int to byte*/ * 3/*for mask, azimuth, elevation*/];
 
@@ -305,9 +305,9 @@
             throws IllegalArgumentException {
         switch (virtualizationMode) {
             case VIRTUALIZATION_MODE_BINAURAL:
-                return AudioDevice.TYPE_WIRED_HEADPHONES;
+                return AudioDeviceInfo.TYPE_WIRED_HEADPHONES;
             case VIRTUALIZATION_MODE_TRANSAURAL:
-                return AudioDevice.TYPE_BUILTIN_SPEAKER;
+                return AudioDeviceInfo.TYPE_BUILTIN_SPEAKER;
             default:
                 throw (new IllegalArgumentException(
                         "Virtualizer: illegal virtualization mode " + virtualizationMode));
@@ -317,7 +317,7 @@
     private static int getDeviceForModeForce(@ForceVirtualizationMode int virtualizationMode)
             throws IllegalArgumentException {
         if (virtualizationMode == VIRTUALIZATION_MODE_AUTO) {
-            return AudioDevice.TYPE_UNKNOWN;
+            return AudioDeviceInfo.TYPE_UNKNOWN;
         } else {
             return getDeviceForModeQuery(virtualizationMode);
         }
@@ -325,24 +325,24 @@
 
     private static int deviceToMode(int deviceType) {
         switch (deviceType) {
-            case AudioDevice.TYPE_WIRED_HEADSET:
-            case AudioDevice.TYPE_WIRED_HEADPHONES:
-            case AudioDevice.TYPE_BLUETOOTH_SCO:
-            case AudioDevice.TYPE_BUILTIN_EARPIECE:
+            case AudioDeviceInfo.TYPE_WIRED_HEADSET:
+            case AudioDeviceInfo.TYPE_WIRED_HEADPHONES:
+            case AudioDeviceInfo.TYPE_BLUETOOTH_SCO:
+            case AudioDeviceInfo.TYPE_BUILTIN_EARPIECE:
                 return VIRTUALIZATION_MODE_BINAURAL;
-            case AudioDevice.TYPE_BUILTIN_SPEAKER:
-            case AudioDevice.TYPE_LINE_ANALOG:
-            case AudioDevice.TYPE_LINE_DIGITAL:
-            case AudioDevice.TYPE_BLUETOOTH_A2DP:
-            case AudioDevice.TYPE_HDMI:
-            case AudioDevice.TYPE_HDMI_ARC:
-            case AudioDevice.TYPE_USB_DEVICE:
-            case AudioDevice.TYPE_USB_ACCESSORY:
-            case AudioDevice.TYPE_DOCK:
-            case AudioDevice.TYPE_FM:
-            case AudioDevice.TYPE_AUX_LINE:
+            case AudioDeviceInfo.TYPE_BUILTIN_SPEAKER:
+            case AudioDeviceInfo.TYPE_LINE_ANALOG:
+            case AudioDeviceInfo.TYPE_LINE_DIGITAL:
+            case AudioDeviceInfo.TYPE_BLUETOOTH_A2DP:
+            case AudioDeviceInfo.TYPE_HDMI:
+            case AudioDeviceInfo.TYPE_HDMI_ARC:
+            case AudioDeviceInfo.TYPE_USB_DEVICE:
+            case AudioDeviceInfo.TYPE_USB_ACCESSORY:
+            case AudioDeviceInfo.TYPE_DOCK:
+            case AudioDeviceInfo.TYPE_FM:
+            case AudioDeviceInfo.TYPE_AUX_LINE:
                 return VIRTUALIZATION_MODE_TRANSAURAL;
-            case AudioDevice.TYPE_UNKNOWN:
+            case AudioDeviceInfo.TYPE_UNKNOWN:
             default:
                 return VIRTUALIZATION_MODE_OFF;
         }
@@ -433,7 +433,7 @@
             throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException {
         // convert Java device type to internal representation
         int deviceType = getDeviceForModeForce(virtualizationMode);
-        int internalDevice = AudioDevice.convertDeviceTypeToInternalDevice(deviceType);
+        int internalDevice = AudioDeviceInfo.convertDeviceTypeToInternalDevice(deviceType);
 
         int status = setParameter(PARAM_FORCE_VIRTUALIZATION_MODE, internalDevice);
 
@@ -470,7 +470,7 @@
         int[] value = new int[1];
         int status = getParameter(PARAM_VIRTUALIZATION_MODE, value);
         if (status >= 0) {
-            return deviceToMode(AudioDevice.convertInternalDeviceToDeviceType(value[0]));
+            return deviceToMode(AudioDeviceInfo.convertInternalDeviceToDeviceType(value[0]));
         } else if (status == AudioEffect.ERROR_BAD_VALUE) {
             return VIRTUALIZATION_MODE_OFF;
         } else {
diff --git a/media/jni/Android.mk b/media/jni/Android.mk
index 66d055a..c8464c7 100644
--- a/media/jni/Android.mk
+++ b/media/jni/Android.mk
@@ -7,6 +7,7 @@
     android_media_MediaCrypto.cpp \
     android_media_MediaCodec.cpp \
     android_media_MediaCodecList.cpp \
+    android_media_MediaDataSource.cpp \
     android_media_MediaDrm.cpp \
     android_media_MediaExtractor.cpp \
     android_media_MediaHTTPConnection.cpp \
diff --git a/media/jni/android_media_MediaCodec.cpp b/media/jni/android_media_MediaCodec.cpp
index 16758d0..5f586a9 100644
--- a/media/jni/android_media_MediaCodec.cpp
+++ b/media/jni/android_media_MediaCodec.cpp
@@ -70,6 +70,11 @@
     jint codecActionRecoverable;
 } gCodecActionCodes;
 
+static struct ExceptionReason {
+    jint reasonHardware;
+    jint reasonReclaimed;
+} gExceptionReason;
+
 struct fields_t {
     jfieldID context;
     jmethodID postEventFromNativeID;
@@ -568,7 +573,7 @@
             env, env->FindClass("android/media/MediaCodec$CodecException"));
     CHECK(clazz.get() != NULL);
 
-    const jmethodID ctor = env->GetMethodID(clazz.get(), "<init>", "(IILjava/lang/String;)V");
+    const jmethodID ctor = env->GetMethodID(clazz.get(), "<init>", "(IILjava/lang/String;I)V");
     CHECK(ctor != NULL);
 
     ScopedLocalRef<jstring> msgObj(
@@ -587,7 +592,9 @@
         break;
     }
 
-    return (jthrowable)env->NewObject(clazz.get(), ctor, err, actionCode, msgObj.get());
+    // TODO: propagate reason from MediaCodec.
+    int reason = gExceptionReason.reasonHardware;
+    return (jthrowable)env->NewObject(clazz.get(), ctor, err, actionCode, msgObj.get(), reason);
 }
 
 void JMediaCodec::handleCallback(const sp<AMessage> &msg) {
@@ -1454,6 +1461,16 @@
     CHECK(field != NULL);
     gCodecActionCodes.codecActionRecoverable =
         env->GetStaticIntField(clazz.get(), field);
+
+    field = env->GetStaticFieldID(clazz.get(), "REASON_HARDWARE", "I");
+    CHECK(field != NULL);
+    gExceptionReason.reasonHardware =
+        env->GetStaticIntField(clazz.get(), field);
+
+    field = env->GetStaticFieldID(clazz.get(), "REASON_RECLAIMED", "I");
+    CHECK(field != NULL);
+    gExceptionReason.reasonReclaimed =
+        env->GetStaticIntField(clazz.get(), field);
 }
 
 static void android_media_MediaCodec_native_setup(
diff --git a/media/jni/android_media_MediaDataSource.cpp b/media/jni/android_media_MediaDataSource.cpp
new file mode 100644
index 0000000..1e6d2af
--- /dev/null
+++ b/media/jni/android_media_MediaDataSource.cpp
@@ -0,0 +1,148 @@
+/*
+ * 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.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "JMediaDataSource-JNI"
+#include <utils/Log.h>
+
+#include "android_media_MediaDataSource.h"
+
+#include "android_runtime/AndroidRuntime.h"
+#include "android_runtime/Log.h"
+#include "jni.h"
+#include "JNIHelp.h"
+
+#include <binder/MemoryDealer.h>
+#include <media/stagefright/foundation/ADebug.h>
+#include <nativehelper/ScopedLocalRef.h>
+
+namespace android {
+
+JMediaDataSource::JMediaDataSource(JNIEnv* env, jobject source)
+    : mJavaObjStatus(OK), mSizeIsCached(false), mCachedSize(0), mMemory(NULL) {
+    mMediaDataSourceObj = env->NewGlobalRef(source);
+    CHECK(mMediaDataSourceObj != NULL);
+
+    ScopedLocalRef<jclass> mediaDataSourceClass(env, env->GetObjectClass(mMediaDataSourceObj));
+    CHECK(mediaDataSourceClass.get() != NULL);
+
+    mReadMethod = env->GetMethodID(mediaDataSourceClass.get(), "readAt", "(J[BI)I");
+    CHECK(mReadMethod != NULL);
+    mGetSizeMethod = env->GetMethodID(mediaDataSourceClass.get(), "getSize", "()J");
+    CHECK(mGetSizeMethod != NULL);
+    mCloseMethod = env->GetMethodID(mediaDataSourceClass.get(), "close", "()V");
+    CHECK(mCloseMethod != NULL);
+
+    ScopedLocalRef<jbyteArray> tmp(env, env->NewByteArray(kBufferSize));
+    mByteArrayObj = (jbyteArray)env->NewGlobalRef(tmp.get());
+    CHECK(mByteArrayObj != NULL);
+
+    sp<MemoryDealer> memoryDealer = new MemoryDealer(kBufferSize, "JMediaDataSource");
+    mMemory = memoryDealer->allocate(kBufferSize);
+    if (mMemory == NULL) {
+        ALOGE("Failed to allocate memory!");
+    }
+}
+
+JMediaDataSource::~JMediaDataSource() {
+    JNIEnv* env = AndroidRuntime::getJNIEnv();
+    env->DeleteGlobalRef(mMediaDataSourceObj);
+    env->DeleteGlobalRef(mByteArrayObj);
+}
+
+sp<IMemory> JMediaDataSource::getIMemory() {
+    Mutex::Autolock lock(mLock);
+    return mMemory;
+}
+
+ssize_t JMediaDataSource::readAt(off64_t offset, size_t size) {
+    Mutex::Autolock lock(mLock);
+
+    if (mJavaObjStatus != OK || mMemory == NULL) {
+        return -1;
+    }
+    if (size > kBufferSize) {
+        size = kBufferSize;
+    }
+
+    JNIEnv* env = AndroidRuntime::getJNIEnv();
+    jint numread = env->CallIntMethod(mMediaDataSourceObj, mReadMethod,
+                                      (jlong)offset, mByteArrayObj, (jint)size);
+    if (env->ExceptionCheck()) {
+        ALOGW("An exception occurred in readAt()");
+        LOGW_EX(env);
+        env->ExceptionClear();
+        mJavaObjStatus = UNKNOWN_ERROR;
+        return -1;
+    }
+    if (numread < 0) {
+        ALOGW("An error occurred in readAt()");
+        mJavaObjStatus = UNKNOWN_ERROR;
+        return -1;
+    }
+    if ((size_t)numread > size) {
+        ALOGE("readAt read too many bytes.");
+        mJavaObjStatus = UNKNOWN_ERROR;
+        return -1;
+    }
+
+    ALOGV("readAt %lld / %zu => %d.", (long long)offset, size, numread);
+    env->GetByteArrayRegion(mByteArrayObj, 0, numread, (jbyte*)mMemory->pointer());
+    return numread;
+}
+
+status_t JMediaDataSource::getSize(off64_t* size) {
+    Mutex::Autolock lock(mLock);
+
+    if (mJavaObjStatus != OK) {
+        return UNKNOWN_ERROR;
+    }
+    if (mSizeIsCached) {
+        return mCachedSize;
+    }
+
+    JNIEnv* env = AndroidRuntime::getJNIEnv();
+    *size = env->CallLongMethod(mMediaDataSourceObj, mGetSizeMethod);
+    if (env->ExceptionCheck()) {
+        ALOGW("An exception occurred in getSize()");
+        LOGW_EX(env);
+        env->ExceptionClear();
+        // After returning an error, size shouldn't be used by callers.
+        *size = UNKNOWN_ERROR;
+        mJavaObjStatus = UNKNOWN_ERROR;
+        return UNKNOWN_ERROR;
+    }
+
+    // The minimum size should be -1, which indicates unknown size.
+    if (*size < 0) {
+        *size = -1;
+    }
+
+    mCachedSize = *size;
+    mSizeIsCached = true;
+    return OK;
+}
+
+void JMediaDataSource::close() {
+    Mutex::Autolock lock(mLock);
+
+    JNIEnv* env = AndroidRuntime::getJNIEnv();
+    env->CallVoidMethod(mMediaDataSourceObj, mCloseMethod);
+    // The closed state is effectively the same as an error state.
+    mJavaObjStatus = UNKNOWN_ERROR;
+}
+
+}  // namespace android
diff --git a/media/jni/android_media_MediaDataSource.h b/media/jni/android_media_MediaDataSource.h
new file mode 100644
index 0000000..2bc237e
--- /dev/null
+++ b/media/jni/android_media_MediaDataSource.h
@@ -0,0 +1,73 @@
+/*
+ * 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.
+ */
+
+#ifndef _ANDROID_MEDIA_MEDIADATASOURCE_H_
+#define _ANDROID_MEDIA_MEDIADATASOURCE_H_
+
+#include "jni.h"
+
+#include <media/IDataSource.h>
+#include <media/stagefright/foundation/ABase.h>
+#include <utils/Errors.h>
+#include <utils/Mutex.h>
+
+namespace android {
+
+// The native counterpart to a Java android.media.MediaDataSource. It inherits from
+// IDataSource so that it can be accessed remotely.
+//
+// If the java DataSource returns an error or throws an exception it
+// will be considered to be in a broken state, and the only further call this
+// will make is to close().
+class JMediaDataSource : public BnDataSource {
+public:
+    enum {
+        kBufferSize = 64 * 1024,
+    };
+
+    JMediaDataSource(JNIEnv *env, jobject source);
+    virtual ~JMediaDataSource();
+
+    virtual sp<IMemory> getIMemory();
+    virtual ssize_t readAt(off64_t offset, size_t size);
+    virtual status_t getSize(off64_t* size);
+    virtual void close();
+
+private:
+    // Protect all member variables with mLock because this object will be
+    // accessed on different binder worker threads.
+    Mutex mLock;
+
+    // The status of the java DataSource. Set to OK unless an error occurred or
+    // close() was called.
+    status_t mJavaObjStatus;
+    // Only call the java getSize() once so the app can't change the size on us.
+    bool mSizeIsCached;
+    off64_t mCachedSize;
+    sp<IMemory> mMemory;
+
+    jobject mMediaDataSourceObj;
+    jmethodID mReadMethod;
+    jmethodID mGetSizeMethod;
+    jmethodID mCloseMethod;
+    jbyteArray mByteArrayObj;
+
+    DISALLOW_EVIL_CONSTRUCTORS(JMediaDataSource);
+};
+
+}  // namespace android
+
+#endif  // _ANDROID_MEDIA_MEDIADATASOURCE_H_
diff --git a/media/jni/android_media_MediaExtractor.cpp b/media/jni/android_media_MediaExtractor.cpp
index c0795b6..b6b7a80 100644
--- a/media/jni/android_media_MediaExtractor.cpp
+++ b/media/jni/android_media_MediaExtractor.cpp
@@ -25,6 +25,7 @@
 #include "android_runtime/Log.h"
 #include "jni.h"
 #include "JNIHelp.h"
+#include "android_media_MediaDataSource.h"
 
 #include <media/IMediaHTTPService.h>
 #include <media/hardware/CryptoAPI.h>
@@ -50,74 +51,6 @@
 
 static fields_t gFields;
 
-class JavaDataSourceBridge : public DataSource {
-    jmethodID mReadMethod;
-    jmethodID mGetSizeMethod;
-    jmethodID mCloseMethod;
-    jobject   mDataSource;
- public:
-    JavaDataSourceBridge(JNIEnv *env, jobject source) {
-        mDataSource = env->NewGlobalRef(source);
-
-        jclass datasourceclass = env->GetObjectClass(mDataSource);
-        CHECK(datasourceclass != NULL);
-
-        mReadMethod = env->GetMethodID(datasourceclass, "readAt", "(J[BI)I");
-        CHECK(mReadMethod != NULL);
-
-        mGetSizeMethod = env->GetMethodID(datasourceclass, "getSize", "()J");
-        CHECK(mGetSizeMethod != NULL);
-
-        mCloseMethod = env->GetMethodID(datasourceclass, "close", "()V");
-        CHECK(mCloseMethod != NULL);
-    }
-
-    ~JavaDataSourceBridge() {
-        JNIEnv *env = AndroidRuntime::getJNIEnv();
-        env->CallVoidMethod(mDataSource, mCloseMethod);
-        env->DeleteGlobalRef(mDataSource);
-    }
-
-    virtual status_t initCheck() const {
-        return OK;
-    }
-
-    virtual ssize_t readAt(off64_t offset, void* buffer, size_t size) {
-        JNIEnv *env = AndroidRuntime::getJNIEnv();
-
-        // XXX could optimize this by reusing the same array
-        jbyteArray byteArrayObj = env->NewByteArray(size);
-        env->DeleteLocalRef(env->GetObjectClass(mDataSource));
-        env->DeleteLocalRef(env->GetObjectClass(byteArrayObj));
-        ssize_t numread = env->CallIntMethod(mDataSource, mReadMethod, offset, byteArrayObj, (jint)size);
-        env->GetByteArrayRegion(byteArrayObj, 0, size, (jbyte*) buffer);
-        env->DeleteLocalRef(byteArrayObj);
-        if (env->ExceptionCheck()) {
-            ALOGW("Exception occurred while reading %zu at %lld", size, (long long)offset);
-            LOGW_EX(env);
-            env->ExceptionClear();
-            return -1;
-        }
-        return numread;
-    }
-
-    virtual status_t getSize(off64_t *size) {
-        JNIEnv *env = AndroidRuntime::getJNIEnv();
-
-        CHECK(size != NULL);
-
-        int64_t len = env->CallLongMethod(mDataSource, mGetSizeMethod);
-        if (len < 0) {
-            *size = ERROR_UNSUPPORTED;
-        } else {
-            *size = len;
-        }
-        return OK;
-    }
-};
-
-////////////////////////////////////////////////////////////////////////////////
-
 JMediaExtractor::JMediaExtractor(JNIEnv *env, jobject thiz)
     : mClass(NULL),
       mObject(NULL) {
@@ -777,7 +710,8 @@
         return;
     }
 
-    sp<JavaDataSourceBridge> bridge = new JavaDataSourceBridge(env, callbackObj);
+    sp<DataSource> bridge =
+        DataSource::CreateFromIDataSource(new JMediaDataSource(env, callbackObj));
     status_t err = extractor->setDataSource(bridge);
 
     if (err != OK) {
@@ -881,7 +815,7 @@
     { "setDataSource", "(Ljava/io/FileDescriptor;JJ)V",
       (void *)android_media_MediaExtractor_setDataSourceFd },
 
-    { "setDataSource", "(Landroid/media/DataSource;)V",
+    { "setDataSource", "(Landroid/media/MediaDataSource;)V",
       (void *)android_media_MediaExtractor_setDataSourceCallback },
 
     { "getCachedDuration", "()J",
diff --git a/media/jni/android_media_MediaHTTPConnection.cpp b/media/jni/android_media_MediaHTTPConnection.cpp
index 7226ef5..393003d 100644
--- a/media/jni/android_media_MediaHTTPConnection.cpp
+++ b/media/jni/android_media_MediaHTTPConnection.cpp
@@ -134,7 +134,6 @@
 static jint android_media_MediaHTTPConnection_native_readAt(
         JNIEnv *env, jobject thiz, jlong offset, jint size) {
     sp<JMediaHTTPConnection> conn = getObject(env, thiz);
-
     if (size > JMediaHTTPConnection::kBufferSize) {
         size = JMediaHTTPConnection::kBufferSize;
     }
diff --git a/media/jni/android_media_MediaMetadataRetriever.cpp b/media/jni/android_media_MediaMetadataRetriever.cpp
index c6fa379..59fb6d6 100644
--- a/media/jni/android_media_MediaMetadataRetriever.cpp
+++ b/media/jni/android_media_MediaMetadataRetriever.cpp
@@ -30,6 +30,7 @@
 #include "jni.h"
 #include "JNIHelp.h"
 #include "android_runtime/AndroidRuntime.h"
+#include "android_media_MediaDataSource.h"
 #include "android_media_Utils.h"
 #include "android_util_Binder.h"
 
@@ -171,6 +172,23 @@
     process_media_retriever_call(env, retriever->setDataSource(fd, offset, length), "java/lang/RuntimeException", "setDataSource failed");
 }
 
+static void android_media_MediaMetadataRetriever_setDataSourceCallback(JNIEnv *env, jobject thiz, jobject dataSource)
+{
+    ALOGV("setDataSourceCallback");
+    MediaMetadataRetriever* retriever = getRetriever(env, thiz);
+    if (retriever == 0) {
+        jniThrowException(env, "java/lang/IllegalStateException", "No retriever available");
+        return;
+    }
+    if (dataSource == NULL) {
+        jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
+        return;
+    }
+
+    sp<IDataSource> callbackDataSource = new JMediaDataSource(env, dataSource);
+    process_media_retriever_call(env, retriever->setDataSource(callbackDataSource), "java/lang/RuntimeException", "setDataSourceCallback failed");
+}
+
 template<typename T>
 static void rotate0(T* dst, const T* src, size_t width, size_t height)
 {
@@ -457,6 +475,7 @@
         },
 
         {"setDataSource",   "(Ljava/io/FileDescriptor;JJ)V", (void *)android_media_MediaMetadataRetriever_setDataSourceFD},
+        {"_setDataSource",   "(Landroid/media/MediaDataSource;)V", (void *)android_media_MediaMetadataRetriever_setDataSourceCallback},
         {"_getFrameAtTime", "(JI)Landroid/graphics/Bitmap;", (void *)android_media_MediaMetadataRetriever_getFrameAtTime},
         {"extractMetadata", "(I)Ljava/lang/String;", (void *)android_media_MediaMetadataRetriever_extractMetadata},
         {"getEmbeddedPicture", "(I)[B", (void *)android_media_MediaMetadataRetriever_getEmbeddedPicture},
diff --git a/media/jni/android_media_MediaPlayer.cpp b/media/jni/android_media_MediaPlayer.cpp
index 3e41716..c247220 100644
--- a/media/jni/android_media_MediaPlayer.cpp
+++ b/media/jni/android_media_MediaPlayer.cpp
@@ -36,6 +36,7 @@
 #include "utils/Errors.h"  // for status_t
 #include "utils/KeyedVector.h"
 #include "utils/String8.h"
+#include "android_media_MediaDataSource.h"
 #include "android_media_Utils.h"
 
 #include "android_os_Parcel.h"
@@ -251,6 +252,23 @@
     process_media_player_call( env, thiz, mp->setDataSource(fd, offset, length), "java/io/IOException", "setDataSourceFD failed." );
 }
 
+static void
+android_media_MediaPlayer_setDataSourceCallback(JNIEnv *env, jobject thiz, jobject dataSource)
+{
+    sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
+    if (mp == NULL ) {
+        jniThrowException(env, "java/lang/IllegalStateException", NULL);
+        return;
+    }
+
+    if (dataSource == NULL) {
+        jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
+        return;
+    }
+    sp<IDataSource> callbackDataSource = new JMediaDataSource(env, dataSource);
+    process_media_player_call(env, thiz, mp->setDataSource(callbackDataSource), "java/lang/RuntimeException", "setDataSourceCallback failed." );
+}
+
 static sp<IGraphicBufferProducer>
 getVideoSurfaceTexture(JNIEnv* env, jobject thiz) {
     IGraphicBufferProducer * const p = (IGraphicBufferProducer*)env->GetLongField(thiz, fields.surface_texture);
@@ -871,7 +889,8 @@
         (void *)android_media_MediaPlayer_setDataSourceAndHeaders
     },
 
-    {"_setDataSource",       "(Ljava/io/FileDescriptor;JJ)V",    (void *)android_media_MediaPlayer_setDataSourceFD},
+    {"_setDataSource",      "(Ljava/io/FileDescriptor;JJ)V",    (void *)android_media_MediaPlayer_setDataSourceFD},
+    {"_setDataSource",      "(Landroid/media/MediaDataSource;)V",(void *)android_media_MediaPlayer_setDataSourceCallback },
     {"_setVideoSurface",    "(Landroid/view/Surface;)V",        (void *)android_media_MediaPlayer_setVideoSurface},
     {"_prepare",            "()V",                              (void *)android_media_MediaPlayer_prepare},
     {"prepareAsync",        "()V",                              (void *)android_media_MediaPlayer_prepareAsync},
diff --git a/media/jni/android_media_MediaSync.cpp b/media/jni/android_media_MediaSync.cpp
index b96c733..e167f83 100644
--- a/media/jni/android_media_MediaSync.cpp
+++ b/media/jni/android_media_MediaSync.cpp
@@ -71,8 +71,8 @@
     return mSync->createInputSurface(bufferProducer);
 }
 
-void JMediaSync::setPlaybackRate(float rate) {
-    mSync->setPlaybackRate(rate);
+status_t JMediaSync::setPlaybackRate(float rate) {
+    return mSync->setPlaybackRate(rate);
 }
 
 sp<const MediaClock> JMediaSync::getMediaClock() {
@@ -115,15 +115,23 @@
 static void throwExceptionAsNecessary(
         JNIEnv *env, status_t err, const char *msg = NULL) {
     switch (err) {
-        case INVALID_OPERATION:
-            jniThrowException(env, "java/lang/IllegalStateException", msg);
+        case NO_ERROR:
             break;
 
         case BAD_VALUE:
             jniThrowException(env, "java/lang/IllegalArgumentException", msg);
             break;
 
+        case NO_INIT:
+        case INVALID_OPERATION:
         default:
+            if (err > 0) {
+                break;
+            }
+            AString msgWithErrorCode(msg);
+            msgWithErrorCode.append(" error:");
+            msgWithErrorCode.append(err);
+            jniThrowException(env, "java/lang/IllegalStateException", msgWithErrorCode.c_str());
             break;
     }
 }
@@ -295,7 +303,11 @@
         return;
     }
 
-    sync->setPlaybackRate(rate);
+    status_t err = sync->setPlaybackRate(rate);
+    if (err != NO_ERROR) {
+        throwExceptionAsNecessary(env, err);
+        return;
+    }
 }
 
 static void android_media_MediaSync_native_finalize(JNIEnv *env, jobject thiz) {
diff --git a/media/jni/android_media_MediaSync.h b/media/jni/android_media_MediaSync.h
index 976a456..9e5de7e 100644
--- a/media/jni/android_media_MediaSync.h
+++ b/media/jni/android_media_MediaSync.h
@@ -39,7 +39,7 @@
 
     status_t updateQueuedAudioData(int sizeInBytes, int64_t presentationTimeUs);
 
-    void setPlaybackRate(float rate);
+    status_t setPlaybackRate(float rate);
 
     sp<const MediaClock> getMediaClock();
 
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraBinderTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraBinderTest.java
index 3bb5f01..14c2619 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraBinderTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraBinderTest.java
@@ -261,6 +261,16 @@
             // TODO Auto-generated method stub
 
         }
+
+        /*
+         * (non-Javadoc)
+         * @see android.hardware.camera2.ICameraDeviceCallbacks#onPrepared()
+         */
+        @Override
+        public void onPrepared(int streamId) throws RemoteException {
+            // TODO Auto-generated method stub
+
+        }
     }
 
     @SmallTest
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraDeviceBinderTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraDeviceBinderTest.java
index 0466540..6f33672 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraDeviceBinderTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraDeviceBinderTest.java
@@ -132,6 +132,16 @@
             // TODO Auto-generated method stub
 
         }
+
+        /*
+         * (non-Javadoc)
+         * @see android.hardware.camera2.ICameraDeviceCallbacks#onPrepared()
+         */
+        @Override
+        public void onPrepared(int streamId) throws RemoteException {
+            // TODO Auto-generated method stub
+
+        }
     }
 
     class IsMetadataNotEmpty extends ArgumentMatcher<CameraMetadataNative> {
diff --git a/obex/Android.mk b/obex/Android.mk
index fbfe9be..e7c1fd3 100644
--- a/obex/Android.mk
+++ b/obex/Android.mk
@@ -7,3 +7,14 @@
 LOCAL_MODULE:= javax.obex
 
 include $(BUILD_JAVA_LIBRARY)
+
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_SRC_FILES := $(call all-subdir-java-files)
+
+LOCAL_MODULE:= javax.obexstatic
+
+include $(BUILD_STATIC_JAVA_LIBRARY)
\ No newline at end of file
diff --git a/obex/javax/obex/ClientOperation.java b/obex/javax/obex/ClientOperation.java
index 75278b5..cc20d39 100644
--- a/obex/javax/obex/ClientOperation.java
+++ b/obex/javax/obex/ClientOperation.java
@@ -1,5 +1,6 @@
 /*
- * Copyright (c) 2014 The Android Open Source Project
+ * Copyright (c) 2015 The Android Open Source Project
+ * Copyright (C) 2015 Samsung LSI
  * Copyright (c) 2008-2009, Motorola, Inc.
  *
  * All rights reserved.
@@ -40,6 +41,8 @@
 import java.io.DataOutputStream;
 import java.io.ByteArrayOutputStream;
 
+import android.util.Log;
+
 /**
  * This class implements the <code>Operation</code> interface. It will read and
  * write data via puts and gets.
@@ -47,6 +50,10 @@
  */
 public final class ClientOperation implements Operation, BaseStream {
 
+    private static final String TAG = "ClientOperation";
+
+    private static final boolean V = ObexHelper.VDBG;
+
     private ClientSession mParent;
 
     private boolean mInputOpen;
@@ -75,6 +82,19 @@
 
     private boolean mEndOfBodySent;
 
+    private boolean mSendBodyHeader = true;
+    // A latch - when triggered, there is not way back ;-)
+    private boolean mSrmActive = false;
+
+    // Assume SRM disabled - until support is confirmed
+    // by the server
+    private boolean mSrmEnabled = false;
+    // keep waiting until final-bit is received in request
+    // to handle the case where the SRM enable header is in
+    // a different OBEX packet than the SRMP header.
+    private boolean mSrmWaitingForRemote = true;
+
+
     /**
      * Creates new OperationImpl to read and write data to a server
      * @param maxSize the maximum packet size
@@ -164,7 +184,7 @@
              * Since we are not sending any headers or returning any headers then
              * we just need to write and read the same bytes
              */
-            mParent.sendRequest(ObexHelper.OBEX_OPCODE_ABORT, null, mReplyHeader, null);
+            mParent.sendRequest(ObexHelper.OBEX_OPCODE_ABORT, null, mReplyHeader, null, false);
 
             if (mReplyHeader.responseCode != ResponseCodes.OBEX_HTTP_OK) {
                 throw new IOException("Invalid response code from server");
@@ -215,6 +235,7 @@
         try {
             return (String)mReplyHeader.getHeader(HeaderSet.TYPE);
         } catch (IOException e) {
+            if(V) Log.d(TAG, "Exception occured - returning null",e);
             return null;
         }
     }
@@ -236,6 +257,7 @@
                 return temp.longValue();
             }
         } catch (IOException e) {
+            if(V) Log.d(TAG,"Exception occured - returning -1",e);
             return -1;
         }
     }
@@ -408,7 +430,9 @@
     }
 
     /**
-     * Sends a request to the client of the specified type
+     * Sends a request to the client of the specified type.
+     * This function will enable SRM and set SRM active if the server
+     * response allows this.
      * @param opCode the request code to send to the client
      * @return <code>true</code> if there is more data to send;
      *         <code>false</code> if there is no more data to send
@@ -431,13 +455,16 @@
          * length, but it is a waste of resources if we can't send much of
          * the body.
          */
-        if ((ObexHelper.BASE_PACKET_LENGTH + headerArray.length) > mMaxPacketSize) {
+        final int MINIMUM_BODY_LENGTH = 3;
+        if ((ObexHelper.BASE_PACKET_LENGTH + headerArray.length + MINIMUM_BODY_LENGTH)
+                > mMaxPacketSize) {
             int end = 0;
             int start = 0;
             // split & send the headerArray in multiple packets.
 
             while (end != headerArray.length) {
                 //split the headerArray
+
                 end = ObexHelper.findHeaderEnd(headerArray, start, mMaxPacketSize
                         - ObexHelper.BASE_PACKET_LENGTH);
                 // can not split
@@ -459,7 +486,7 @@
 
                 byte[] sendHeader = new byte[end - start];
                 System.arraycopy(headerArray, start, sendHeader, 0, sendHeader.length);
-                if (!mParent.sendRequest(opCode, sendHeader, mReplyHeader, mPrivateInput)) {
+                if (!mParent.sendRequest(opCode, sendHeader, mReplyHeader, mPrivateInput, false)) {
                     return false;
                 }
 
@@ -470,12 +497,20 @@
                 start = end;
             }
 
+            // Enable SRM if it should be enabled
+            checkForSrm();
+
             if (bodyLength > 0) {
                 return true;
             } else {
                 return false;
             }
         } else {
+            /* All headers will fit into a single package */
+            if(mSendBodyHeader == false) {
+                /* As we are not to send any body data, set the FINAL_BIT */
+                opCode |= ObexHelper.OBEX_OPCODE_FINAL_BIT_MASK;
+            }
             out.write(headerArray);
         }
 
@@ -499,11 +534,11 @@
              * (End of Body) otherwise, we need to send 0x48 (Body)
              */
             if ((mPrivateOutput.isClosed()) && (!returnValue) && (!mEndOfBodySent)
-                    && ((opCode & 0x80) != 0)) {
-                out.write(0x49);
+                    && ((opCode & ObexHelper.OBEX_OPCODE_FINAL_BIT_MASK) != 0)) {
+                out.write(HeaderSet.END_OF_BODY);
                 mEndOfBodySent = true;
             } else {
-                out.write(0x48);
+                out.write(HeaderSet.BODY);
             }
 
             bodyLength += 3;
@@ -517,12 +552,11 @@
 
         if (mPrivateOutputOpen && bodyLength <= 0 && !mEndOfBodySent) {
             // only 0x82 or 0x83 can send 0x49
-            if ((opCode & 0x80) == 0) {
-                out.write(0x48);
+            if ((opCode & ObexHelper.OBEX_OPCODE_FINAL_BIT_MASK) == 0) {
+                out.write(HeaderSet.BODY);
             } else {
-                out.write(0x49);
+                out.write(HeaderSet.END_OF_BODY);
                 mEndOfBodySent = true;
-
             }
 
             bodyLength = 3;
@@ -531,15 +565,20 @@
         }
 
         if (out.size() == 0) {
-            if (!mParent.sendRequest(opCode, null, mReplyHeader, mPrivateInput)) {
+            if (!mParent.sendRequest(opCode, null, mReplyHeader, mPrivateInput, mSrmActive)) {
                 return false;
             }
+            // Enable SRM if it should be enabled
+            checkForSrm();
             return returnValue;
         }
         if ((out.size() > 0)
-                && (!mParent.sendRequest(opCode, out.toByteArray(), mReplyHeader, mPrivateInput))) {
+                && (!mParent.sendRequest(opCode, out.toByteArray(),
+                        mReplyHeader, mPrivateInput, mSrmActive))) {
             return false;
         }
+        // Enable SRM if it should be enabled
+        checkForSrm();
 
         // send all of the output data in 0x48,
         // send 0x49 with empty body
@@ -549,6 +588,35 @@
         return returnValue;
     }
 
+    private void checkForSrm() throws IOException {
+        Byte srmMode = (Byte)mReplyHeader.getHeader(HeaderSet.SINGLE_RESPONSE_MODE);
+        if(mParent.isSrmSupported() == true && srmMode != null
+                && srmMode == ObexHelper.OBEX_SRM_ENABLE) {
+            mSrmEnabled = true;
+        }
+        /**
+         * Call this only when a complete obex packet have been received.
+         * (This is not optimal, but the current design is not really suited to
+         * the way SRM is specified.)
+         * The BT usage of SRM is not really safe - it assumes that the SRMP will fit
+         * into every OBEX packet, hence if another header occupies the entire packet,
+         * the scheme will not work - unlikely though.
+         */
+        if(mSrmEnabled) {
+            mSrmWaitingForRemote = false;
+            Byte srmp = (Byte)mReplyHeader.getHeader(HeaderSet.SINGLE_RESPONSE_MODE_PARAMETER);
+            if(srmp != null && srmp == ObexHelper.OBEX_SRMP_WAIT) {
+                mSrmWaitingForRemote = true;
+                // Clear the wait header, as the absence of the header in the next packet
+                // indicates don't wait anymore.
+                mReplyHeader.setHeader(HeaderSet.SINGLE_RESPONSE_MODE_PARAMETER, null);
+            }
+        }
+        if((mSrmWaitingForRemote == false) && (mSrmEnabled == true)) {
+            mSrmActive = true;
+        }
+    }
+
     /**
      * This method starts the processing thread results. It will send the
      * initial request. If the response takes more then one packet, a thread
@@ -564,40 +632,35 @@
 
         if (mGetOperation) {
             if (!mOperationDone) {
-                if (!mGetFinalFlag) {
-                    mReplyHeader.responseCode = ResponseCodes.OBEX_HTTP_CONTINUE;
-                    while ((more) && (mReplyHeader.responseCode == ResponseCodes.OBEX_HTTP_CONTINUE)) {
-                        more = sendRequest(0x03);
-                    }
-
-                    if (mReplyHeader.responseCode == ResponseCodes.OBEX_HTTP_CONTINUE) {
-                        mParent.sendRequest(0x83, null, mReplyHeader, mPrivateInput);
-                    }
-                    if (mReplyHeader.responseCode != ResponseCodes.OBEX_HTTP_CONTINUE) {
-                        mOperationDone = true;
-                    }
-                } else {
-                    more = sendRequest(0x83);
-
-                    if (more) {
-                        throw new IOException("FINAL_GET forced but data did not fit into single packet!");
-                    }
-
+                mReplyHeader.responseCode = ResponseCodes.OBEX_HTTP_CONTINUE;
+                while ((more) && (mReplyHeader.responseCode == ResponseCodes.OBEX_HTTP_CONTINUE)) {
+                    more = sendRequest(ObexHelper.OBEX_OPCODE_GET);
+                }
+                // For GET we need to loop until all headers have been sent,
+                // And then we wait for the first continue package with the
+                // reply.
+                if (mReplyHeader.responseCode == ResponseCodes.OBEX_HTTP_CONTINUE) {
+                    mParent.sendRequest(ObexHelper.OBEX_OPCODE_GET_FINAL,
+                            null, mReplyHeader, mPrivateInput, mSrmActive);
+                }
+                if (mReplyHeader.responseCode != ResponseCodes.OBEX_HTTP_CONTINUE) {
                     mOperationDone = true;
+                } else {
+                    checkForSrm();
                 }
             }
         } else {
-
+            // PUT operation
             if (!mOperationDone) {
                 mReplyHeader.responseCode = ResponseCodes.OBEX_HTTP_CONTINUE;
                 while ((more) && (mReplyHeader.responseCode == ResponseCodes.OBEX_HTTP_CONTINUE)) {
-                    more = sendRequest(0x02);
-
+                    more = sendRequest(ObexHelper.OBEX_OPCODE_PUT);
                 }
             }
 
             if (mReplyHeader.responseCode == ResponseCodes.OBEX_HTTP_CONTINUE) {
-                mParent.sendRequest(0x82, null, mReplyHeader, mPrivateInput);
+                mParent.sendRequest(ObexHelper.OBEX_OPCODE_PUT_FINAL,
+                        null, mReplyHeader, mPrivateInput, mSrmActive);
             }
 
             if (mReplyHeader.responseCode != ResponseCodes.OBEX_HTTP_CONTINUE) {
@@ -617,15 +680,21 @@
     public synchronized boolean continueOperation(boolean sendEmpty, boolean inStream)
             throws IOException {
 
+        // One path to the first put operation - the other one does not need to
+        // handle SRM, as all will fit into one packet.
+
         if (mGetOperation) {
             if ((inStream) && (!mOperationDone)) {
                 // to deal with inputstream in get operation
-                mParent.sendRequest(0x83, null, mReplyHeader, mPrivateInput);
+                mParent.sendRequest(ObexHelper.OBEX_OPCODE_GET_FINAL,
+                        null, mReplyHeader, mPrivateInput, mSrmActive);
                 /*
                   * Determine if that was not the last packet in the operation
                   */
                 if (mReplyHeader.responseCode != ResponseCodes.OBEX_HTTP_CONTINUE) {
                     mOperationDone = true;
+                } else {
+                    checkForSrm();
                 }
 
                 return true;
@@ -636,16 +705,7 @@
                 if (mPrivateInput == null) {
                     mPrivateInput = new PrivateInputStream(this);
                 }
-
-                if (!mGetFinalFlag) {
-                    sendRequest(0x03);
-                } else {
-                    sendRequest(0x83);
-
-                    if (mReplyHeader.responseCode != ResponseCodes.OBEX_HTTP_CONTINUE) {
-                        mOperationDone = true;
-                    }
-                }
+                sendRequest(ObexHelper.OBEX_OPCODE_GET);
                 return true;
 
             } else if (mOperationDone) {
@@ -653,12 +713,13 @@
             }
 
         } else {
+            // PUT operation
             if ((!inStream) && (!mOperationDone)) {
                 // to deal with outputstream in put operation
                 if (mReplyHeader.responseCode == -1) {
                     mReplyHeader.responseCode = ResponseCodes.OBEX_HTTP_CONTINUE;
                 }
-                sendRequest(0x02);
+                sendRequest(ObexHelper.OBEX_OPCODE_PUT);
                 return true;
             } else if ((inStream) && (!mOperationDone)) {
                 // How to deal with inputstream  in put operation ?
@@ -696,7 +757,7 @@
                 }
 
                 while ((more) && (mReplyHeader.responseCode == ResponseCodes.OBEX_HTTP_CONTINUE)) {
-                    more = sendRequest(0x02);
+                    more = sendRequest(ObexHelper.OBEX_OPCODE_PUT);
                 }
 
                 /*
@@ -706,7 +767,7 @@
                  */
                 while (mReplyHeader.responseCode == ResponseCodes.OBEX_HTTP_CONTINUE) {
 
-                    sendRequest(0x82);
+                    sendRequest(ObexHelper.OBEX_OPCODE_PUT_FINAL);
                 }
                 mOperationDone = true;
             } else if ((inStream) && (mOperationDone)) {
@@ -724,12 +785,14 @@
                 }
 
                 while (mReplyHeader.responseCode == ResponseCodes.OBEX_HTTP_CONTINUE) {
-                    if (!sendRequest(0x83)) {
+                    if (!sendRequest(ObexHelper.OBEX_OPCODE_GET_FINAL)) {
                         break;
                     }
                 }
                 while (mReplyHeader.responseCode == ResponseCodes.OBEX_HTTP_CONTINUE) {
-                    mParent.sendRequest(0x83, null, mReplyHeader, mPrivateInput);
+                    mParent.sendRequest(ObexHelper.OBEX_OPCODE_GET_FINAL, null,
+                            mReplyHeader, mPrivateInput, false);
+                    // Regardless of the SRM state, wait for the response.
                 }
                 mOperationDone = true;
             } else if ((!inStream) && (!mOperationDone)) {
@@ -752,9 +815,9 @@
 
                 mReplyHeader.responseCode = ResponseCodes.OBEX_HTTP_CONTINUE;
                 while ((more) && (mReplyHeader.responseCode == ResponseCodes.OBEX_HTTP_CONTINUE)) {
-                    more = sendRequest(0x03);
+                    more = sendRequest(ObexHelper.OBEX_OPCODE_GET);
                 }
-                sendRequest(0x83);
+                sendRequest(ObexHelper.OBEX_OPCODE_GET_FINAL);
                 //                parent.sendRequest(0x83, null, replyHeaders, privateInput);
                 if (mReplyHeader.responseCode != ResponseCodes.OBEX_HTTP_CONTINUE) {
                     mOperationDone = true;
@@ -764,5 +827,6 @@
     }
 
     public void noBodyHeader(){
+        mSendBodyHeader = false;
     }
 }
diff --git a/obex/javax/obex/ClientSession.java b/obex/javax/obex/ClientSession.java
index 27d8976..272a920 100644
--- a/obex/javax/obex/ClientSession.java
+++ b/obex/javax/obex/ClientSession.java
@@ -1,4 +1,6 @@
 /*
+ * Copyright (c) 2015 The Android Open Source Project
+ * Copyright (C) 2015 Samsung LSI
  * Copyright (c) 2008-2009, Motorola, Inc.
  *
  * All rights reserved.
@@ -37,12 +39,16 @@
 import java.io.InputStream;
 import java.io.OutputStream;
 
+import android.util.Log;
+
 /**
  * This class in an implementation of the OBEX ClientSession.
  * @hide
  */
 public final class ClientSession extends ObexSession {
 
+    private static final String TAG = "ClientSession";
+
     private boolean mOpen;
 
     // Determines if an OBEX layer connection has been established
@@ -51,10 +57,10 @@
     private byte[] mConnectionId = null;
 
     /*
-     * The max Packet size must be at least 256 according to the OBEX
+     * The max Packet size must be at least 255 according to the OBEX
      * specification.
      */
-    private int maxPacketSize = 256;
+    private int mMaxTxPacketSize = ObexHelper.LOWER_LIMIT_MAX_PACKET_SIZE;
 
     private boolean mRequestActive;
 
@@ -62,11 +68,33 @@
 
     private final OutputStream mOutput;
 
+    private final boolean mLocalSrmSupported;
+
+    private final ObexTransport mTransport;
+
     public ClientSession(final ObexTransport trans) throws IOException {
         mInput = trans.openInputStream();
         mOutput = trans.openOutputStream();
         mOpen = true;
         mRequestActive = false;
+        mLocalSrmSupported = trans.isSrmSupported();
+        mTransport = trans;
+    }
+
+    /**
+     * Create a ClientSession
+     * @param trans The transport to use for OBEX transactions
+     * @param supportsSrm True if Single Response Mode should be used e.g. if the
+     *        supplied transport is a TCP or l2cap channel.
+     * @throws IOException if it occurs while opening the transport streams.
+     */
+    public ClientSession(final ObexTransport trans, final boolean supportsSrm) throws IOException {
+        mInput = trans.openInputStream();
+        mOutput = trans.openOutputStream();
+        mOpen = true;
+        mRequestActive = false;
+        mLocalSrmSupported = supportsSrm;
+        mTransport = trans;
     }
 
     public HeaderSet connect(final HeaderSet header) throws IOException {
@@ -98,23 +126,25 @@
         * Byte 7 to n: headers
         */
         byte[] requestPacket = new byte[totalLength];
+        int maxRxPacketSize = ObexHelper.getMaxRxPacketSize(mTransport);
         // We just need to start at  byte 3 since the sendRequest() method will
         // handle the length and 0x80.
         requestPacket[0] = (byte)0x10;
         requestPacket[1] = (byte)0x00;
-        requestPacket[2] = (byte)(ObexHelper.MAX_PACKET_SIZE_INT >> 8);
-        requestPacket[3] = (byte)(ObexHelper.MAX_PACKET_SIZE_INT & 0xFF);
+        requestPacket[2] = (byte)(maxRxPacketSize >> 8);
+        requestPacket[3] = (byte)(maxRxPacketSize & 0xFF);
         if (head != null) {
             System.arraycopy(head, 0, requestPacket, 4, head.length);
         }
 
-        // check with local max packet size
+        // Since we are not yet connected, the peer max packet size is unknown,
+        // hence we are only guaranteed the server will use the first 7 bytes.
         if ((requestPacket.length + 3) > ObexHelper.MAX_PACKET_SIZE_INT) {
-            throw new IOException("Packet size exceeds max packet size");
+            throw new IOException("Packet size exceeds max packet size for connect");
         }
 
         HeaderSet returnHeaderSet = new HeaderSet();
-        sendRequest(ObexHelper.OBEX_OPCODE_CONNECT, requestPacket, returnHeaderSet, null);
+        sendRequest(ObexHelper.OBEX_OPCODE_CONNECT, requestPacket, returnHeaderSet, null, false);
 
         /*
         * Read the response from the OBEX server.
@@ -158,7 +188,18 @@
             System.arraycopy(mConnectionId, 0, head.mConnectionID, 0, 4);
         }
 
-        return new ClientOperation(maxPacketSize, this, head, true);
+        if(mLocalSrmSupported) {
+            head.setHeader(HeaderSet.SINGLE_RESPONSE_MODE, ObexHelper.OBEX_SRM_ENABLE);
+            /* TODO: Consider creating an interface to get the wait state.
+             * On an android system, I cannot see when this is to be used.
+             * except perhaps if we are to wait for user accept on a push message.
+            if(getLocalWaitState()) {
+                head.setHeader(HeaderSet.SINGLE_RESPONSE_MODE_PARAMETER, ObexHelper.OBEX_SRMP_WAIT);
+            }
+            */
+        }
+
+        return new ClientOperation(mMaxTxPacketSize, this, head, true);
     }
 
     /**
@@ -202,7 +243,7 @@
             }
             head = ObexHelper.createHeader(header, false);
 
-            if ((head.length + 3) > maxPacketSize) {
+            if ((head.length + 3) > mMaxTxPacketSize) {
                 throw new IOException("Packet size exceeds max packet size");
             }
         } else {
@@ -215,7 +256,7 @@
         }
 
         HeaderSet returnHeaderSet = new HeaderSet();
-        sendRequest(ObexHelper.OBEX_OPCODE_DISCONNECT, head, returnHeaderSet, null);
+        sendRequest(ObexHelper.OBEX_OPCODE_DISCONNECT, head, returnHeaderSet, null, false);
 
         /*
          * An OBEX DISCONNECT reply from the server:
@@ -269,7 +310,16 @@
             System.arraycopy(mConnectionId, 0, head.mConnectionID, 0, 4);
         }
 
-        return new ClientOperation(maxPacketSize, this, head, false);
+        if(mLocalSrmSupported) {
+            head.setHeader(HeaderSet.SINGLE_RESPONSE_MODE, ObexHelper.OBEX_SRM_ENABLE);
+            /* TODO: Consider creating an interface to get the wait state.
+             * On an android system, I cannot see when this is to be used.
+            if(getLocalWaitState()) {
+                head.setHeader(HeaderSet.SINGLE_RESPONSE_MODE_PARAMETER, ObexHelper.OBEX_SRMP_WAIT);
+            }
+             */
+        }
+        return new ClientOperation(mMaxTxPacketSize, this, head, false);
     }
 
     public void setAuthenticator(Authenticator auth) throws IOException {
@@ -314,7 +364,7 @@
         head = ObexHelper.createHeader(headset, false);
         totalLength += head.length;
 
-        if (totalLength > maxPacketSize) {
+        if (totalLength > mMaxTxPacketSize) {
             throw new IOException("Packet size exceeds max packet size");
         }
 
@@ -348,7 +398,7 @@
         }
 
         HeaderSet returnHeaderSet = new HeaderSet();
-        sendRequest(ObexHelper.OBEX_OPCODE_SETPATH, packet, returnHeaderSet, null);
+        sendRequest(ObexHelper.OBEX_OPCODE_SETPATH, packet, returnHeaderSet, null, false);
 
         /*
          * An OBEX SETPATH reply from the server:
@@ -400,20 +450,40 @@
      * @param head the headers to send to the client
      * @param header the header object to update with the response
      * @param privateInput the input stream used by the Operation object; null
-     *        if this is called on a CONNECT, SETPATH or DISCONNECT return
+     *        if this is called on a CONNECT, SETPATH or DISCONNECT
+     * @return
      *        <code>true</code> if the operation completed successfully;
      *        <code>false</code> if an authentication response failed to pass
      * @throws IOException if an IO error occurs
      */
     public boolean sendRequest(int opCode, byte[] head, HeaderSet header,
-            PrivateInputStream privateInput) throws IOException {
+            PrivateInputStream privateInput, boolean srmActive) throws IOException {
         //check header length with local max size
         if (head != null) {
             if ((head.length + 3) > ObexHelper.MAX_PACKET_SIZE_INT) {
+                // TODO: This is an implementation limit - not a specification requirement.
                 throw new IOException("header too large ");
             }
         }
 
+        boolean skipSend = false;
+        boolean skipReceive = false;
+        if (srmActive == true) {
+            if (opCode == ObexHelper.OBEX_OPCODE_PUT) {
+                // we are in the middle of a SRM PUT operation, don't expect a continue.
+                skipReceive = true;
+            } else if (opCode == ObexHelper.OBEX_OPCODE_GET) {
+                // We are still sending the get request, send, but don't expect continue
+                // until the request is transfered (the final bit is set)
+                skipReceive = true;
+            } else if (opCode == ObexHelper.OBEX_OPCODE_GET_FINAL) {
+                // All done sending the request, expect data from the server, without
+                // sending continue.
+                skipSend = true;
+            }
+
+        }
+
         int bytesReceived;
         ByteArrayOutputStream out = new ByteArrayOutputStream();
         out.write((byte)opCode);
@@ -428,86 +498,105 @@
             out.write(head);
         }
 
-        // Write the request to the output stream and flush the stream
-        mOutput.write(out.toByteArray());
-        mOutput.flush();
-
-        header.responseCode = mInput.read();
-
-        int length = ((mInput.read() << 8) | (mInput.read()));
-
-        if (length > ObexHelper.MAX_PACKET_SIZE_INT) {
-            throw new IOException("Packet received exceeds packet size limit");
+        if (!skipSend) {
+            // Write the request to the output stream and flush the stream
+            mOutput.write(out.toByteArray());
+            // TODO: is this really needed? if this flush is implemented
+            //       correctly, we will get a gap between each obex packet.
+            //       which is kind of the idea behind SRM to avoid.
+            //  Consider offloading to another thread (async action)
+            mOutput.flush();
         }
-        if (length > ObexHelper.BASE_PACKET_LENGTH) {
-            byte[] data = null;
-            if (opCode == ObexHelper.OBEX_OPCODE_CONNECT) {
-                @SuppressWarnings("unused")
-                int version = mInput.read();
-                @SuppressWarnings("unused")
-                int flags = mInput.read();
-                maxPacketSize = (mInput.read() << 8) + mInput.read();
 
-                //check with local max size
-                if (maxPacketSize > ObexHelper.MAX_CLIENT_PACKET_SIZE) {
-                    maxPacketSize = ObexHelper.MAX_CLIENT_PACKET_SIZE;
-                }
+        if (!skipReceive) {
+            header.responseCode = mInput.read();
 
-                if (length > 7) {
-                    data = new byte[length - 7];
+            int length = ((mInput.read() << 8) | (mInput.read()));
 
-                    bytesReceived = mInput.read(data);
-                    while (bytesReceived != (length - 7)) {
-                        bytesReceived += mInput.read(data, bytesReceived, data.length
-                                - bytesReceived);
+            if (length > ObexHelper.getMaxRxPacketSize(mTransport)) {
+                throw new IOException("Packet received exceeds packet size limit");
+            }
+            if (length > ObexHelper.BASE_PACKET_LENGTH) {
+                byte[] data = null;
+                if (opCode == ObexHelper.OBEX_OPCODE_CONNECT) {
+                    @SuppressWarnings("unused")
+                    int version = mInput.read();
+                    @SuppressWarnings("unused")
+                    int flags = mInput.read();
+                    mMaxTxPacketSize = (mInput.read() << 8) + mInput.read();
+
+                    //check with local max size
+                    if (mMaxTxPacketSize > ObexHelper.MAX_CLIENT_PACKET_SIZE) {
+                        mMaxTxPacketSize = ObexHelper.MAX_CLIENT_PACKET_SIZE;
+                    }
+
+                    // check with transport maximum size
+                    if(mMaxTxPacketSize > ObexHelper.getMaxTxPacketSize(mTransport)) {
+                        // To increase this size, increase the buffer size in L2CAP layer
+                        // in Bluedroid.
+                        Log.w(TAG, "An OBEX packet size of " + mMaxTxPacketSize + "was"
+                                + " requested. Transport only allows: "
+                                + ObexHelper.getMaxTxPacketSize(mTransport)
+                                + " Lowering limit to this value.");
+                        mMaxTxPacketSize = ObexHelper.getMaxTxPacketSize(mTransport);
+                    }
+
+                    if (length > 7) {
+                        data = new byte[length - 7];
+
+                        bytesReceived = mInput.read(data);
+                        while (bytesReceived != (length - 7)) {
+                            bytesReceived += mInput.read(data, bytesReceived, data.length
+                                    - bytesReceived);
+                        }
+                    } else {
+                        return true;
                     }
                 } else {
-                    return true;
+                    data = new byte[length - 3];
+                    bytesReceived = mInput.read(data);
+
+                    while (bytesReceived != (length - 3)) {
+                        bytesReceived += mInput.read(data, bytesReceived, data.length - bytesReceived);
+                    }
+                    if (opCode == ObexHelper.OBEX_OPCODE_ABORT) {
+                        return true;
+                    }
                 }
-            } else {
-                data = new byte[length - 3];
-                bytesReceived = mInput.read(data);
 
-                while (bytesReceived != (length - 3)) {
-                    bytesReceived += mInput.read(data, bytesReceived, data.length - bytesReceived);
+                byte[] body = ObexHelper.updateHeaderSet(header, data);
+                if ((privateInput != null) && (body != null)) {
+                    privateInput.writeBytes(body, 1);
                 }
-                if (opCode == ObexHelper.OBEX_OPCODE_ABORT) {
-                    return true;
+
+                if (header.mConnectionID != null) {
+                    mConnectionId = new byte[4];
+                    System.arraycopy(header.mConnectionID, 0, mConnectionId, 0, 4);
                 }
-            }
 
-            byte[] body = ObexHelper.updateHeaderSet(header, data);
-            if ((privateInput != null) && (body != null)) {
-                privateInput.writeBytes(body, 1);
-            }
-
-            if (header.mConnectionID != null) {
-                mConnectionId = new byte[4];
-                System.arraycopy(header.mConnectionID, 0, mConnectionId, 0, 4);
-            }
-
-            if (header.mAuthResp != null) {
-                if (!handleAuthResp(header.mAuthResp)) {
-                    setRequestInactive();
-                    throw new IOException("Authentication Failed");
+                if (header.mAuthResp != null) {
+                    if (!handleAuthResp(header.mAuthResp)) {
+                        setRequestInactive();
+                        throw new IOException("Authentication Failed");
+                    }
                 }
-            }
 
-            if ((header.responseCode == ResponseCodes.OBEX_HTTP_UNAUTHORIZED)
-                    && (header.mAuthChall != null)) {
+                if ((header.responseCode == ResponseCodes.OBEX_HTTP_UNAUTHORIZED)
+                        && (header.mAuthChall != null)) {
 
-                if (handleAuthChall(header)) {
-                    out.write((byte)HeaderSet.AUTH_RESPONSE);
-                    out.write((byte)((header.mAuthResp.length + 3) >> 8));
-                    out.write((byte)(header.mAuthResp.length + 3));
-                    out.write(header.mAuthResp);
-                    header.mAuthChall = null;
-                    header.mAuthResp = null;
+                    if (handleAuthChall(header)) {
+                        out.write((byte)HeaderSet.AUTH_RESPONSE);
+                        out.write((byte)((header.mAuthResp.length + 3) >> 8));
+                        out.write((byte)(header.mAuthResp.length + 3));
+                        out.write(header.mAuthResp);
+                        header.mAuthChall = null;
+                        header.mAuthResp = null;
 
-                    byte[] sendHeaders = new byte[out.size() - 3];
-                    System.arraycopy(out.toByteArray(), 3, sendHeaders, 0, sendHeaders.length);
+                        byte[] sendHeaders = new byte[out.size() - 3];
+                        System.arraycopy(out.toByteArray(), 3, sendHeaders, 0, sendHeaders.length);
 
-                    return sendRequest(opCode, sendHeaders, header, privateInput);
+                        return sendRequest(opCode, sendHeaders, header, privateInput, false);
+                    }
                 }
             }
         }
@@ -520,4 +609,8 @@
         mInput.close();
         mOutput.close();
     }
+
+    public boolean isSrmSupported() {
+        return mLocalSrmSupported;
+    }
 }
diff --git a/obex/javax/obex/HeaderSet.java b/obex/javax/obex/HeaderSet.java
index 51b560a..35fe186 100644
--- a/obex/javax/obex/HeaderSet.java
+++ b/obex/javax/obex/HeaderSet.java
@@ -40,7 +40,7 @@
 
 /**
  * This class implements the javax.obex.HeaderSet interface for OBEX over
- * RFCOMM.
+ * RFCOMM or OBEX over l2cap.
  * @hide
  */
 public final class HeaderSet {
@@ -178,6 +178,22 @@
      */
     public static final int OBJECT_CLASS = 0x4F;
 
+    /**
+     * Represents the OBEX Single Response Mode (SRM). This header is used
+     * for Single response mode, introduced in OBEX 1.5.
+     * <P>
+     * The value of <code>SINGLE_RESPONSE_MODE</code> is 0x97 (151).
+     */
+    public static final int SINGLE_RESPONSE_MODE = 0x97;
+
+    /**
+     * Represents the OBEX Single Response Mode Parameters. This header is used
+     * for Single response mode, introduced in OBEX 1.5.
+     * <P>
+     * The value of <code>SINGLE_RESPONSE_MODE_PARAMETER</code> is 0x98 (152).
+     */
+    public static final int SINGLE_RESPONSE_MODE_PARAMETER = 0x98;
+
     private Long mCount; // 4 byte unsigned integer
 
     private String mName; // null terminated Unicode text string
@@ -204,7 +220,7 @@
 
     private byte[] mObjectClass; // byte sequence
 
-    private String[] mUnicodeUserDefined; //null terminated unicode string
+    private String[] mUnicodeUserDefined; // null terminated unicode string
 
     private byte[][] mSequenceUserDefined; // byte sequence user defined
 
@@ -212,7 +228,12 @@
 
     private Long[] mIntegerUserDefined; // 4 byte unsigned integer
 
-    private final SecureRandom mRandom;
+    private SecureRandom mRandom = null;
+
+    private Byte mSingleResponseMode; // byte to indicate enable/disable/support for SRM
+
+    private Byte mSrmParam; // byte representing the SRM parameters - only "wait"
+                            // is supported by Bluetooth
 
     /*package*/ byte[] nonce;
 
@@ -234,7 +255,6 @@
         mByteUserDefined = new Byte[16];
         mIntegerUserDefined = new Long[16];
         responseCode = -1;
-        mRandom = new SecureRandom();
     }
 
     /**
@@ -393,6 +413,30 @@
                     }
                 }
                 break;
+            case SINGLE_RESPONSE_MODE:
+                if (headerValue == null) {
+                    mSingleResponseMode = null;
+                } else {
+                    if (!(headerValue instanceof Byte)) {
+                        throw new IllegalArgumentException(
+                                "Single Response Mode must be a Byte");
+                    } else {
+                        mSingleResponseMode = (Byte)headerValue;
+                    }
+                }
+                break;
+            case SINGLE_RESPONSE_MODE_PARAMETER:
+                if (headerValue == null) {
+                    mSrmParam = null;
+                } else {
+                    if (!(headerValue instanceof Byte)) {
+                        throw new IllegalArgumentException(
+                                "Single Response Mode Parameter must be a Byte");
+                    } else {
+                        mSrmParam = (Byte)headerValue;
+                    }
+                }
+                break;
             default:
                 // Verify that it was not a Unicode String user Defined
                 if ((headerID >= 0x30) && (headerID <= 0x3F)) {
@@ -493,6 +537,10 @@
                 return mObjectClass;
             case APPLICATION_PARAMETER:
                 return mAppParam;
+            case SINGLE_RESPONSE_MODE:
+                return mSingleResponseMode;
+            case SINGLE_RESPONSE_MODE_PARAMETER:
+                return mSrmParam;
             default:
                 // Verify that it was not a Unicode String user Defined
                 if ((headerID >= 0x30) && (headerID <= 0x3F)) {
@@ -564,6 +612,12 @@
         if (mObjectClass != null) {
             out.write(OBJECT_CLASS);
         }
+        if(mSingleResponseMode != null) {
+            out.write(SINGLE_RESPONSE_MODE);
+        }
+        if(mSrmParam != null) {
+            out.write(SINGLE_RESPONSE_MODE_PARAMETER);
+        }
 
         for (int i = 0x30; i < 0x40; i++) {
             if (mUnicodeUserDefined[i - 0x30] != null) {
@@ -625,6 +679,9 @@
             throws IOException {
 
         nonce = new byte[16];
+        if(mRandom == null) {
+            mRandom = new SecureRandom();
+        }
         for (int i = 0; i < 16; i++) {
             nonce[i] = (byte)mRandom.nextInt();
         }
diff --git a/obex/javax/obex/ObexHelper.java b/obex/javax/obex/ObexHelper.java
index 0a06709..fa50943 100644
--- a/obex/javax/obex/ObexHelper.java
+++ b/obex/javax/obex/ObexHelper.java
@@ -1,5 +1,6 @@
 /*
- * Copyright (C) 2014 The Android Open Source Project
+ * Copyright (C) 2015 The Android Open Source Project
+ * Copyright (C) 2015 Samsung LSI
  * Copyright (c) 2008-2009, Motorola, Inc.
  *
  * All rights reserved.
@@ -42,12 +43,16 @@
 import java.util.Date;
 import java.util.TimeZone;
 
+import android.util.Log;
+
 /**
  * This class defines a set of helper methods for the implementation of Obex.
  * @hide
  */
 public final class ObexHelper {
 
+    private static final String TAG = "ObexHelper";
+    public static final boolean VDBG = false;
     /**
      * Defines the basic packet length used by OBEX. Every OBEX packet has the
      * same basic format:<BR>
@@ -65,18 +70,24 @@
      * should be the Max incoming MTU minus TODO: L2CAP package headers and
      * RFCOMM package headers. TODO: Retrieve the max incoming MTU from TODO:
      * LocalDevice.getProperty().
+     * NOTE: This value must be larger than or equal to the L2CAP SDU
      */
     /*
      * android note set as 0xFFFE to match remote MPS
      */
     public static final int MAX_PACKET_SIZE_INT = 0xFFFE;
 
+    // The minimum allowed max packet size is 255 according to the OBEX specification
+    public static final int LOWER_LIMIT_MAX_PACKET_SIZE = 255;
+
     /**
      * Temporary workaround to be able to push files to Windows 7.
      * TODO: Should be removed as soon as Microsoft updates their driver.
      */
     public static final int MAX_CLIENT_PACKET_SIZE = 0xFC00;
 
+    public static final int OBEX_OPCODE_FINAL_BIT_MASK = 0x80;
+
     public static final int OBEX_OPCODE_CONNECT = 0x80;
 
     public static final int OBEX_OPCODE_DISCONNECT = 0x81;
@@ -119,6 +130,12 @@
 
     public static final int OBEX_AUTH_REALM_CHARSET_UNICODE = 0xFF;
 
+    public static final byte OBEX_SRM_ENABLE         = 0x01; // For BT we only need enable/disable
+    public static final byte OBEX_SRM_DISABLE        = 0x00;
+    public static final byte OBEX_SRM_SUPPORT        = 0x02; // Unused for now
+
+    public static final byte OBEX_SRMP_WAIT          = 0x01; // Only SRMP value used by BT
+
     /**
      * Updates the HeaderSet with the headers received in the byte array
      * provided. Invalid headers are ignored.
@@ -314,7 +331,7 @@
                             }
                         } catch (Exception e) {
                             // Not a valid header so ignore
-                            throw new IOException("Header was not formatted properly");
+                            throw new IOException("Header was not formatted properly", e);
                         }
                         index += 4;
                         break;
@@ -322,7 +339,7 @@
 
             }
         } catch (IOException e) {
-            throw new IOException("Header was not formatted properly");
+            throw new IOException("Header was not formatted properly", e);
         }
 
         return body;
@@ -672,6 +689,33 @@
                 }
             }
 
+            // TODO:
+            // If the SRM and SRMP header is in use, they must be send in the same OBEX packet
+            // But the current structure of the obex code cannot handle this, and therefore
+            // it makes sense to put them in the tail of the headers, since we then reduce the
+            // chance of enabling SRM to soon. The down side is that SRM cannot be used while
+            // transferring non-body headers
+
+            // Add the SRM header
+            byteHeader = (Byte)headImpl.getHeader(HeaderSet.SINGLE_RESPONSE_MODE);
+            if (byteHeader != null) {
+                out.write((byte)HeaderSet.SINGLE_RESPONSE_MODE);
+                out.write(byteHeader.byteValue());
+                if (nullOut) {
+                    headImpl.setHeader(HeaderSet.SINGLE_RESPONSE_MODE, null);
+                }
+            }
+
+            // Add the SRM parameter header
+            byteHeader = (Byte)headImpl.getHeader(HeaderSet.SINGLE_RESPONSE_MODE_PARAMETER);
+            if (byteHeader != null) {
+                out.write((byte)HeaderSet.SINGLE_RESPONSE_MODE_PARAMETER);
+                out.write(byteHeader.byteValue());
+                if (nullOut) {
+                    headImpl.setHeader(HeaderSet.SINGLE_RESPONSE_MODE_PARAMETER, null);
+                }
+            }
+
         } catch (IOException e) {
         } finally {
             result = out.toByteArray();
@@ -702,6 +746,8 @@
         int index = start;
         int length = 0;
 
+        // TODO: Ensure SRM and SRMP headers are not split into two OBEX packets
+
         while ((fullLength < maxSize) && (index < headerArray.length)) {
             int headerID = (headerArray[index] < 0 ? headerArray[index] + 256 : headerArray[index]);
             lastLength = fullLength;
@@ -1008,4 +1054,39 @@
 
         return authChall;
     }
+
+    /**
+     * Return the maximum allowed OBEX packet to transmit.
+     * OBEX packets transmitted must be smaller than this value.
+     * @param transport Reference to the ObexTransport in use.
+     * @return the maximum allowed OBEX packet to transmit
+     */
+    public static int getMaxTxPacketSize(ObexTransport transport) {
+        int size = transport.getMaxTransmitPacketSize();
+        return validateMaxPacketSize(size);
+    }
+
+    /**
+     * Return the maximum allowed OBEX packet to receive - used in OBEX connect.
+     * @param transport
+     * @return he maximum allowed OBEX packet to receive
+     */
+    public static int getMaxRxPacketSize(ObexTransport transport) {
+        int size = transport.getMaxReceivePacketSize();
+        return validateMaxPacketSize(size);
+    }
+
+    private static int validateMaxPacketSize(int size) {
+        if(VDBG && (size > MAX_PACKET_SIZE_INT)) Log.w(TAG,
+                "The packet size supported for the connection (" + size + ") is larger"
+                + " than the configured OBEX packet size: " + MAX_PACKET_SIZE_INT);
+        if(size != -1) {
+            if(size < LOWER_LIMIT_MAX_PACKET_SIZE) {
+                throw new IllegalArgumentException(size + " is less that the lower limit: "
+                        + LOWER_LIMIT_MAX_PACKET_SIZE);
+            }
+            return size;
+        }
+        return MAX_PACKET_SIZE_INT;
+    }
 }
diff --git a/obex/javax/obex/ObexPacket.java b/obex/javax/obex/ObexPacket.java
new file mode 100644
index 0000000..bb6c96e
--- /dev/null
+++ b/obex/javax/obex/ObexPacket.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (c) 2015 The Android Open Source Project
+ * Copyright (c) 2015 Samsung LSI
+ *
+ * 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 javax.obex;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+public class ObexPacket {
+    public int mHeaderId;
+    public int mLength;
+    public byte[] mPayload = null;
+
+    private ObexPacket(int headerId, int length) {
+        mHeaderId = headerId;
+        mLength = length;
+    }
+
+    /**
+     * Create a complete OBEX packet by reading data from an InputStream.
+     * @param is the input stream to read from.
+     * @return the OBEX packet read.
+     * @throws IOException if an IO exception occurs during read.
+     */
+    public static ObexPacket read(InputStream is) throws IOException {
+        int headerId = is.read();
+        return read(headerId, is);
+    }
+
+    /**
+     * Read the remainder of an OBEX packet, with a specified headerId.
+     * @param headerId the headerId already read from the stream.
+     * @param is the stream to read from, assuming 1 byte have already been read.
+     * @return the OBEX packet read.
+     * @throws IOException
+     */
+    public static ObexPacket read(int headerId, InputStream is) throws IOException {
+        // Read the 2 byte length field from the stream
+        int length = is.read();
+        length = (length << 8) + is.read();
+
+        ObexPacket newPacket = new ObexPacket(headerId, length);
+
+        int bytesReceived;
+        byte[] temp = null;
+        if (length > 3) {
+            // First three bytes already read, compensating for this
+            temp = new byte[length - 3];
+            bytesReceived = is.read(temp);
+            while (bytesReceived != temp.length) {
+                bytesReceived += is.read(temp, bytesReceived, temp.length - bytesReceived);
+            }
+        }
+        newPacket.mPayload = temp;
+        return newPacket;
+    }
+}
diff --git a/obex/javax/obex/ObexSession.java b/obex/javax/obex/ObexSession.java
index a7daeb5..542b9c8 100644
--- a/obex/javax/obex/ObexSession.java
+++ b/obex/javax/obex/ObexSession.java
@@ -34,6 +34,8 @@
 
 import java.io.IOException;
 
+import android.util.Log;
+
 /**
  * The <code>ObexSession</code> interface characterizes the term
  * "OBEX Connection" as defined in the IrDA Object Exchange Protocol v1.2, which
@@ -47,6 +49,9 @@
  */
 public class ObexSession {
 
+    private static final String TAG = "ObexSession";
+    private static final boolean V = ObexHelper.VDBG;
+
     protected Authenticator mAuthenticator;
 
     protected byte[] mChallengeDigest;
@@ -125,6 +130,7 @@
             result = mAuthenticator
                     .onAuthenticationChallenge(realm, isUserIDRequired, isFullAccess);
         } catch (Exception e) {
+            if (V) Log.d(TAG, "Exception occured - returning false", e);
             return false;
         }
 
diff --git a/obex/javax/obex/ObexTransport.java b/obex/javax/obex/ObexTransport.java
index 445e267..a5a75f5 100644
--- a/obex/javax/obex/ObexTransport.java
+++ b/obex/javax/obex/ObexTransport.java
@@ -73,4 +73,39 @@
 
     DataOutputStream openDataOutputStream() throws IOException;
 
+    /**
+     * Must return the maximum allowed OBEX packet that can be sent over
+     * the transport. For L2CAP this will be the Max SDU reported by the
+     * peer device.
+     * The returned value will be used to set the outgoing OBEX packet
+     * size. Therefore this value shall not change.
+     * For RFCOMM or other transport types where the OBEX packets size
+     * is unrelated to the transport packet size, return -1;
+     * @return the maximum allowed OBEX packet that can be send over
+     *         the transport. Or -1 in case of don't care.
+     */
+    int getMaxTransmitPacketSize();
+
+    /**
+     * Must return the maximum allowed OBEX packet that can be received over
+     * the transport. For L2CAP this will be the Max SDU configured for the
+     * L2CAP channel.
+     * The returned value will be used to validate the incoming packet size
+     * values.
+     * For RFCOMM or other transport types where the OBEX packets size
+     * is unrelated to the transport packet size, return -1;
+     * @return the maximum allowed OBEX packet that can be send over
+     *         the transport. Or -1 in case of don't care.
+     */
+    int getMaxReceivePacketSize();
+
+    /**
+     * Shall return true if the transport in use supports SRM.
+     * @return
+     *        <code>true</code> if SRM operation is supported, and is to be enabled.
+     *        <code>false</code> if SRM operations are not supported, or should not be used.
+     */
+    boolean isSrmSupported();
+
+
 }
diff --git a/obex/javax/obex/ServerOperation.java b/obex/javax/obex/ServerOperation.java
index fc441e0..56a675a 100644
--- a/obex/javax/obex/ServerOperation.java
+++ b/obex/javax/obex/ServerOperation.java
@@ -1,4 +1,5 @@
-/*
+/* Copyright (c) 2015 The Android Open Source Project
+ * Copyright (C) 2015 Samsung LSI
  * Copyright (c) 2008-2009, Motorola, Inc.
  *
  * All rights reserved.
@@ -39,6 +40,8 @@
 import java.io.DataOutputStream;
 import java.io.ByteArrayOutputStream;
 
+import android.util.Log;
+
 /**
  * This class implements the Operation interface for server side connections.
  * <P>
@@ -54,6 +57,10 @@
  */
 public final class ServerOperation implements Operation, BaseStream {
 
+    private static final String TAG = "ServerOperation";
+
+    private static final boolean V = ObexHelper.VDBG; // Verbose debugging
+
     public boolean isAborted;
 
     public HeaderSet requestHeader;
@@ -78,6 +85,8 @@
 
     private PrivateOutputStream mPrivateOutput;
 
+    private ObexTransport mTransport;
+
     private boolean mPrivateOutputOpen;
 
     private String mExceptionString;
@@ -89,6 +98,19 @@
     private boolean mHasBody;
 
     private boolean mSendBodyHeader = true;
+    // Assume SRM disabled - needs to be explicit
+    // enabled by client
+    private boolean mSrmEnabled = false;
+    // A latch - when triggered, there is not way back ;-)
+    private boolean mSrmActive = false;
+    // Set to true when a SRM enable response have been send
+    private boolean mSrmResponseSent = false;
+    // keep waiting until final-bit is received in request
+    // to handle the case where the SRM enable header is in
+    // a different OBEX packet than the SRMP header.
+    private boolean mSrmWaitingForRemote = true;
+    // Why should we wait? - currently not exposed to apps.
+    private boolean mSrmLocalWait = false;
 
     /**
      * Creates new ServerOperation
@@ -116,12 +138,14 @@
         mRequestFinished = false;
         mPrivateOutputOpen = false;
         mHasBody = false;
-        int bytesReceived;
+        ObexPacket packet;
+        mTransport = p.getTransport();
 
         /*
          * Determine if this is a PUT request
          */
-        if ((request == 0x02) || (request == 0x82)) {
+        if ((request == ObexHelper.OBEX_OPCODE_PUT) ||
+                (request == ObexHelper.OBEX_OPCODE_PUT_FINAL)) {
             /*
              * It is a PUT request.
              */
@@ -130,13 +154,14 @@
             /*
              * Determine if the final bit is set
              */
-            if ((request & 0x80) == 0) {
+            if ((request & ObexHelper.OBEX_OPCODE_FINAL_BIT_MASK) == 0) {
                 finalBitSet = false;
             } else {
                 finalBitSet = true;
                 mRequestFinished = true;
             }
-        } else if ((request == 0x03) || (request == 0x83)) {
+        } else if ((request == ObexHelper.OBEX_OPCODE_GET) ||
+                (request == ObexHelper.OBEX_OPCODE_GET_FINAL)) {
             /*
              * It is a GET request.
              */
@@ -145,71 +170,32 @@
             // For Get request, final bit set is decided by server side logic
             finalBitSet = false;
 
-            if (request == 0x83) {
+            if (request == ObexHelper.OBEX_OPCODE_GET_FINAL) {
                 mRequestFinished = true;
             }
         } else {
             throw new IOException("ServerOperation can not handle such request");
         }
 
-        int length = in.read();
-        length = (length << 8) + in.read();
+        packet = ObexPacket.read(request, mInput);
 
         /*
          * Determine if the packet length is larger than this device can receive
          */
-        if (length > ObexHelper.MAX_PACKET_SIZE_INT) {
+        if (packet.mLength > ObexHelper.getMaxRxPacketSize(mTransport)) {
             mParent.sendResponse(ResponseCodes.OBEX_HTTP_REQ_TOO_LARGE, null);
-            throw new IOException("Packet received was too large");
+            throw new IOException("Packet received was too large. Length: "
+                    + packet.mLength + " maxLength: " + ObexHelper.getMaxRxPacketSize(mTransport));
         }
 
         /*
          * Determine if any headers were sent in the initial request
          */
-        if (length > 3) {
-            byte[] data = new byte[length - 3];
-            bytesReceived = in.read(data);
-
-            while (bytesReceived != data.length) {
-                bytesReceived += in.read(data, bytesReceived, data.length - bytesReceived);
+        if (packet.mLength > 3) {
+            if(!handleObexPacket(packet)) {
+                return;
             }
-
-            byte[] body = ObexHelper.updateHeaderSet(requestHeader, data);
-
-            if (body != null) {
-                mHasBody = true;
-            }
-
-            if (mListener.getConnectionId() != -1 && requestHeader.mConnectionID != null) {
-                mListener.setConnectionId(ObexHelper.convertToLong(requestHeader.mConnectionID));
-            } else {
-                mListener.setConnectionId(1);
-            }
-
-            if (requestHeader.mAuthResp != null) {
-                if (!mParent.handleAuthResp(requestHeader.mAuthResp)) {
-                    mExceptionString = "Authentication Failed";
-                    mParent.sendResponse(ResponseCodes.OBEX_HTTP_UNAUTHORIZED, null);
-                    mClosed = true;
-                    requestHeader.mAuthResp = null;
-                    return;
-                }
-            }
-
-            if (requestHeader.mAuthChall != null) {
-                mParent.handleAuthChall(requestHeader);
-                // send the  authResp to the client
-                replyHeader.mAuthResp = new byte[requestHeader.mAuthResp.length];
-                System.arraycopy(requestHeader.mAuthResp, 0, replyHeader.mAuthResp, 0,
-                        replyHeader.mAuthResp.length);
-                requestHeader.mAuthResp = null;
-                requestHeader.mAuthChall = null;
-
-            }
-
-            if (body != null) {
-                mPrivateInput.writeBytes(body, 1);
-            } else {
+            if (!mHasBody) {
                 while ((!mGetOperation) && (!finalBitSet)) {
                     sendReply(ResponseCodes.OBEX_HTTP_CONTINUE);
                     if (mPrivateInput.available() > 0) {
@@ -232,6 +218,100 @@
         }
     }
 
+    /**
+     * Parse headers and update member variables
+     * @param packet the received obex packet
+     * @return false for failing authentication - and a OBEX_HTTP_UNAUTHORIZED
+     * response have been send. Else true.
+     * @throws IOException
+     */
+    private boolean handleObexPacket(ObexPacket packet) throws IOException {
+        byte[] body = updateRequestHeaders(packet);
+
+        if (body != null) {
+            mHasBody = true;
+        }
+        if (mListener.getConnectionId() != -1 && requestHeader.mConnectionID != null) {
+            mListener.setConnectionId(ObexHelper
+                    .convertToLong(requestHeader.mConnectionID));
+        } else {
+            mListener.setConnectionId(1);
+        }
+
+        if (requestHeader.mAuthResp != null) {
+            if (!mParent.handleAuthResp(requestHeader.mAuthResp)) {
+                mExceptionString = "Authentication Failed";
+                mParent.sendResponse(ResponseCodes.OBEX_HTTP_UNAUTHORIZED, null);
+                mClosed = true;
+                requestHeader.mAuthResp = null;
+                return false;
+            }
+            requestHeader.mAuthResp = null;
+        }
+
+        if (requestHeader.mAuthChall != null) {
+            mParent.handleAuthChall(requestHeader);
+            // send the auhtResp to the client
+            replyHeader.mAuthResp = new byte[requestHeader.mAuthResp.length];
+            System.arraycopy(requestHeader.mAuthResp, 0, replyHeader.mAuthResp, 0,
+                    replyHeader.mAuthResp.length);
+            requestHeader.mAuthResp = null;
+            requestHeader.mAuthChall = null;
+        }
+
+        if (body != null) {
+            mPrivateInput.writeBytes(body, 1);
+        }
+        return true;
+    }
+
+    /**
+     * Update the request header set, and sniff on SRM headers to update local state.
+     * @param data the OBEX packet data
+     * @return any bytes in a body/end-of-body header returned by {@link ObexHelper.updateHeaderSet}
+     * @throws IOException
+     */
+    private byte[] updateRequestHeaders(ObexPacket packet) throws IOException {
+        byte[] body = null;
+        if (packet.mPayload != null) {
+            body = ObexHelper.updateHeaderSet(requestHeader, packet.mPayload);
+        }
+        Byte srmMode = (Byte)requestHeader.getHeader(HeaderSet.SINGLE_RESPONSE_MODE);
+        if(mTransport.isSrmSupported() && srmMode != null
+                && srmMode == ObexHelper.OBEX_SRM_ENABLE) {
+            mSrmEnabled = true;
+            if(V) Log.d(TAG,"SRM is now ENABLED (but not active) for this operation");
+        }
+        checkForSrmWait(packet.mHeaderId);
+        if((!mSrmWaitingForRemote) && (mSrmEnabled)) {
+            if(V) Log.d(TAG,"SRM is now ACTIVE for this operation");
+            mSrmActive = true;
+        }
+        return body;
+    }
+
+    /**
+     * Call this only when a complete request have been received.
+     * (This is not optimal, but the current design is not really suited to
+     * the way SRM is specified.)
+     */
+    private void checkForSrmWait(int headerId){
+        if (mSrmEnabled && (headerId == ObexHelper.OBEX_OPCODE_GET
+                || headerId == ObexHelper.OBEX_OPCODE_GET_FINAL
+                || headerId == ObexHelper.OBEX_OPCODE_PUT)) {
+            try {
+                mSrmWaitingForRemote = false;
+                Byte srmp = (Byte)requestHeader.getHeader(HeaderSet.SINGLE_RESPONSE_MODE_PARAMETER);
+                if(srmp != null && srmp == ObexHelper.OBEX_SRMP_WAIT) {
+                    mSrmWaitingForRemote = true;
+                    // Clear the wait header, as the absents of the header when the final bit is set
+                    // indicates don't wait.
+                    requestHeader.setHeader(HeaderSet.SINGLE_RESPONSE_MODE_PARAMETER, null);
+                }
+            } catch (IOException e) {if(V){Log.w(TAG,"Exception while extracting header",e);}}
+        }
+    }
+
     public boolean isValidBody() {
         return mHasBody;
     }
@@ -274,17 +354,19 @@
 
     /**
      * Sends a reply to the client. If the reply is a OBEX_HTTP_CONTINUE, it
-     * will wait for a response from the client before ending.
+     * will wait for a response from the client before ending unless SRM is active.
      * @param type the response code to send back to the client
      * @return <code>true</code> if the final bit was not set on the reply;
      *         <code>false</code> if no reply was received because the operation
-     *         ended, an abort was received, or the final bit was set in the
-     *         reply
+     *         ended, an abort was received, the final bit was set in the
+     *         reply or SRM is active.
      * @throws IOException if an IO error occurs
      */
     public synchronized boolean sendReply(int type) throws IOException {
         ByteArrayOutputStream out = new ByteArrayOutputStream();
-        int bytesReceived;
+        boolean skipSend = false;
+        boolean skipReceive = false;
+        boolean srmRespSendPending = false;
 
         long id = mListener.getConnectionId();
         if (id == -1) {
@@ -293,7 +375,19 @@
             replyHeader.mConnectionID = ObexHelper.convertToByteArray(id);
         }
 
-        byte[] headerArray = ObexHelper.createHeader(replyHeader, true);
+        if(mSrmEnabled && !mSrmResponseSent) {
+            // As we are not ensured that the SRM enable is in the first OBEX packet
+            // We must check for each reply.
+            if(V)Log.v(TAG, "mSrmEnabled==true, sending SRM enable response.");
+            replyHeader.setHeader(HeaderSet.SINGLE_RESPONSE_MODE, (byte)ObexHelper.OBEX_SRM_ENABLE);
+            srmRespSendPending = true;
+        }
+
+        if(mSrmEnabled && !mGetOperation && mSrmLocalWait) {
+            replyHeader.setHeader(HeaderSet.SINGLE_RESPONSE_MODE, (byte)ObexHelper.OBEX_SRMP_WAIT);
+        }
+
+        byte[] headerArray = ObexHelper.createHeader(replyHeader, true); // This clears the headers
         int bodyLength = -1;
         int orginalBodyLength = -1;
 
@@ -347,6 +441,28 @@
             finalBitSet = true;
         }
 
+        if(mSrmActive) {
+            if(!mGetOperation && type == ResponseCodes.OBEX_HTTP_CONTINUE &&
+                    mSrmResponseSent == true) {
+                // we are in the middle of a SRM PUT operation, don't send a continue.
+                skipSend = true;
+            } else if(mGetOperation && mRequestFinished == false && mSrmResponseSent == true) {
+                // We are still receiving the get request, receive, but don't send continue.
+                skipSend = true;
+            } else if(mGetOperation && mRequestFinished == true) {
+                // All done receiving the GET request, send data to the client, without
+                // expecting a continue.
+                skipReceive = true;
+            }
+            if(V)Log.v(TAG, "type==" + type + " skipSend==" + skipSend
+                    + " skipReceive==" + skipReceive);
+        }
+        if(srmRespSendPending) {
+            if(V)Log.v(TAG,
+                    "SRM Enabled (srmRespSendPending == true)- sending SRM Enable response");
+            mSrmResponseSent = true;
+        }
+
         if ((finalBitSet) || (headerArray.length < (mMaxPacketLength - 20))) {
             if (bodyLength > 0) {
                 /*
@@ -387,7 +503,7 @@
         }
 
         if ((finalBitSet) && (type == ResponseCodes.OBEX_HTTP_OK) && (orginalBodyLength <= 0)) {
-            if(mSendBodyHeader == true) {
+            if(mSendBodyHeader) {
                 out.write(0x49);
                 orginalBodyLength = 3;
                 out.write((byte)(orginalBodyLength >> 8));
@@ -395,107 +511,66 @@
             }
         }
 
-        mResponseSize = 3;
-        mParent.sendResponse(type, out.toByteArray());
+        if(skipSend == false) {
+            mResponseSize = 3;
+            mParent.sendResponse(type, out.toByteArray());
+        }
 
         if (type == ResponseCodes.OBEX_HTTP_CONTINUE) {
-            int headerID = mInput.read();
-            int length = mInput.read();
-            length = (length << 8) + mInput.read();
-            if ((headerID != ObexHelper.OBEX_OPCODE_PUT)
-                    && (headerID != ObexHelper.OBEX_OPCODE_PUT_FINAL)
-                    && (headerID != ObexHelper.OBEX_OPCODE_GET)
-                    && (headerID != ObexHelper.OBEX_OPCODE_GET_FINAL)) {
 
-                if (length > 3) {
-                    byte[] temp = new byte[length - 3];
-                    // First three bytes already read, compensating for this
-                    bytesReceived = mInput.read(temp);
-
-                    while (bytesReceived != temp.length) {
-                        bytesReceived += mInput.read(temp, bytesReceived,
-                                temp.length - bytesReceived);
-                    }
-                }
-
-                /*
-                 * Determine if an ABORT was sent as the reply
-                 */
-                if (headerID == ObexHelper.OBEX_OPCODE_ABORT) {
-                    mParent.sendResponse(ResponseCodes.OBEX_HTTP_OK, null);
-                    mClosed = true;
-                    isAborted = true;
-                    mExceptionString = "Abort Received";
-                    throw new IOException("Abort Received");
-                } else {
-                    mParent.sendResponse(ResponseCodes.OBEX_HTTP_BAD_REQUEST, null);
-                    mClosed = true;
-                    mExceptionString = "Bad Request Received";
-                    throw new IOException("Bad Request Received");
-                }
+            if(mGetOperation && skipReceive) {
+                // Here we need to check for and handle abort (throw an exception).
+                // Any other signal received should be discarded silently (only on server side)
+                checkSrmRemoteAbort();
             } else {
+                // Receive and handle data (only send reply if !skipSend)
+                // Read a complete OBEX Packet
+                ObexPacket packet = ObexPacket.read(mInput);
 
-                if ((headerID == ObexHelper.OBEX_OPCODE_PUT_FINAL)) {
-                    finalBitSet = true;
-                } else if (headerID == ObexHelper.OBEX_OPCODE_GET_FINAL) {
-                    mRequestFinished = true;
-                }
+                int headerId = packet.mHeaderId;
+                if ((headerId != ObexHelper.OBEX_OPCODE_PUT)
+                        && (headerId != ObexHelper.OBEX_OPCODE_PUT_FINAL)
+                        && (headerId != ObexHelper.OBEX_OPCODE_GET)
+                        && (headerId != ObexHelper.OBEX_OPCODE_GET_FINAL)) {
 
-                /*
-                 * Determine if the packet length is larger then this device can receive
-                 */
-                if (length > ObexHelper.MAX_PACKET_SIZE_INT) {
-                    mParent.sendResponse(ResponseCodes.OBEX_HTTP_REQ_TOO_LARGE, null);
-                    throw new IOException("Packet received was too large");
-                }
-
-                /*
-                 * Determine if any headers were sent in the initial request
-                 */
-                if (length > 3) {
-                    byte[] data = new byte[length - 3];
-                    bytesReceived = mInput.read(data);
-
-                    while (bytesReceived != data.length) {
-                        bytesReceived += mInput.read(data, bytesReceived, data.length
-                                - bytesReceived);
-                    }
-                    byte[] body = ObexHelper.updateHeaderSet(requestHeader, data);
-                    if (body != null) {
-                        mHasBody = true;
-                    }
-                    if (mListener.getConnectionId() != -1 && requestHeader.mConnectionID != null) {
-                        mListener.setConnectionId(ObexHelper
-                                .convertToLong(requestHeader.mConnectionID));
+                    /*
+                     * Determine if an ABORT was sent as the reply
+                     */
+                    if (headerId == ObexHelper.OBEX_OPCODE_ABORT) {
+                        handleRemoteAbort();
                     } else {
-                        mListener.setConnectionId(1);
+                        // TODO:shall we send this if it occurs during SRM? Errata on the subject
+                        mParent.sendResponse(ResponseCodes.OBEX_HTTP_BAD_REQUEST, null);
+                        mClosed = true;
+                        mExceptionString = "Bad Request Received";
+                        throw new IOException("Bad Request Received");
+                    }
+                } else {
+
+                    if ((headerId == ObexHelper.OBEX_OPCODE_PUT_FINAL)) {
+                        finalBitSet = true;
+                    } else if (headerId == ObexHelper.OBEX_OPCODE_GET_FINAL) {
+                        mRequestFinished = true;
                     }
 
-                    if (requestHeader.mAuthResp != null) {
-                        if (!mParent.handleAuthResp(requestHeader.mAuthResp)) {
-                            mExceptionString = "Authentication Failed";
-                            mParent.sendResponse(ResponseCodes.OBEX_HTTP_UNAUTHORIZED, null);
-                            mClosed = true;
-                            requestHeader.mAuthResp = null;
+                    /*
+                     * Determine if the packet length is larger than the negotiated packet size
+                     */
+                    if (packet.mLength > ObexHelper.getMaxRxPacketSize(mTransport)) {
+                        mParent.sendResponse(ResponseCodes.OBEX_HTTP_REQ_TOO_LARGE, null);
+                        throw new IOException("Packet received was too large");
+                    }
+
+                    /*
+                     * Determine if any headers were sent in the initial request
+                     */
+                    if (packet.mLength > 3 || (mSrmEnabled && packet.mLength == 3)) {
+                        if(handleObexPacket(packet) == false) {
                             return false;
                         }
-                        requestHeader.mAuthResp = null;
-                    }
-
-                    if (requestHeader.mAuthChall != null) {
-                        mParent.handleAuthChall(requestHeader);
-                        // send the auhtResp to the client
-                        replyHeader.mAuthResp = new byte[requestHeader.mAuthResp.length];
-                        System.arraycopy(requestHeader.mAuthResp, 0, replyHeader.mAuthResp, 0,
-                                replyHeader.mAuthResp.length);
-                        requestHeader.mAuthResp = null;
-                        requestHeader.mAuthChall = null;
-                    }
-
-                    if (body != null) {
-                        mPrivateInput.writeBytes(body, 1);
                     }
                 }
+
             }
             return true;
         } else {
@@ -504,6 +579,53 @@
     }
 
     /**
+     * This method will look for an abort from the peer during a SRM transfer.
+     * The function will not block if no data has been received from the remote device.
+     * If data have been received, the function will block while reading the incoming
+     * OBEX package.
+     * An Abort request will be handled, and cause an IOException("Abort Received").
+     * Other messages will be discarded silently as per GOEP specification.
+     * @throws IOException if an abort request have been received.
+     * TODO: I think this is an error in the specification. If we discard other messages,
+     *       the peer device will most likely stall, as it will not receive the expected
+     *       response for the message...
+     *       I'm not sure how to understand "Receipt of invalid or unexpected SRM or SRMP
+     *       header values shall be ignored by the receiving device."
+     *       If any signal is received during an active SRM transfer it is unexpected regardless
+     *       whether or not it contains SRM/SRMP headers...
+     */
+    private void checkSrmRemoteAbort() throws IOException {
+        if(mInput.available() > 0) {
+            ObexPacket packet = ObexPacket.read(mInput);
+            /*
+             * Determine if an ABORT was sent as the reply
+             */
+            if (packet.mHeaderId == ObexHelper.OBEX_OPCODE_ABORT) {
+                handleRemoteAbort();
+            } else {
+                // TODO: should we throw an exception here anyway? - don't see how to
+                //       ignore SRM/SRMP headers without ignoring the complete signal
+                //       (in this particular case).
+                Log.w(TAG, "Received unexpected request from client - discarding...\n"
+                        + "   headerId: " + packet.mHeaderId + " length: " + packet.mLength);
+            }
+        }
+    }
+
+    private void handleRemoteAbort() throws IOException {
+        /* TODO: To increase the speed of the abort operation in SRM, we need
+         *       to be able to flush the L2CAP queue for the PSM in use.
+         *       This could be implemented by introducing a control
+         *       message to be send over the socket, that in the abort case
+         *       could carry a flush command. */
+        mParent.sendResponse(ResponseCodes.OBEX_HTTP_OK, null);
+        mClosed = true;
+        isAborted = true;
+        mExceptionString = "Abort Received";
+        throw new IOException("Abort Received");
+    }
+
+    /**
      * Sends an ABORT message to the server. By calling this method, the
      * corresponding input and output streams will be closed along with this
      * object.
diff --git a/obex/javax/obex/ServerRequestHandler.java b/obex/javax/obex/ServerRequestHandler.java
index 0882572..09cbc2c 100644
--- a/obex/javax/obex/ServerRequestHandler.java
+++ b/obex/javax/obex/ServerRequestHandler.java
@@ -275,4 +275,13 @@
      */
     public void onClose() {
     }
+
+    /**
+     * Override to add Single Response Mode support - e.g. if the supplied
+     * transport is l2cap.
+     * @return True if SRM is supported, else False
+     */
+    public boolean isSrmSupported() {
+        return false;
+    }
 }
diff --git a/obex/javax/obex/ServerSession.java b/obex/javax/obex/ServerSession.java
index f1b9a0d..acee5dd 100644
--- a/obex/javax/obex/ServerSession.java
+++ b/obex/javax/obex/ServerSession.java
@@ -1,4 +1,6 @@
 /*
+ * Copyright (C) 2015 The Android Open Source Project
+ * Copyright (c) 2015 Samsung LSI
  * Copyright (c) 2008-2009, Motorola, Inc.
  *
  * All rights reserved.
@@ -45,6 +47,7 @@
 public final class ServerSession extends ObexSession implements Runnable {
 
     private static final String TAG = "Obex ServerSession";
+    private static final boolean V = ObexHelper.VDBG;
 
     private ObexTransport mTransport;
 
@@ -91,7 +94,9 @@
 
             boolean done = false;
             while (!done && !mClosed) {
+                if(V) Log.v(TAG, "Waiting for incoming request...");
                 int requestType = mInput.read();
+                if(V) Log.v(TAG, "Read request: " + requestType);
                 switch (requestType) {
                     case ObexHelper.OBEX_OPCODE_CONNECT:
                         handleConnectRequest();
@@ -140,9 +145,9 @@
             }
 
         } catch (NullPointerException e) {
-            Log.d(TAG, e.toString());
+            Log.d(TAG, "Exception occured - ignoring", e);
         } catch (Exception e) {
-            Log.d(TAG, e.toString());
+            Log.d(TAG, "Exception occured - ignoring", e);
         }
         close();
     }
@@ -163,7 +168,7 @@
 
         int length = mInput.read();
         length = (length << 8) + mInput.read();
-        if (length > ObexHelper.MAX_PACKET_SIZE_INT) {
+        if (length > ObexHelper.getMaxRxPacketSize(mTransport)) {
             code = ResponseCodes.OBEX_HTTP_REQ_TOO_LARGE;
         } else {
             for (int i = 3; i < length; i++) {
@@ -215,6 +220,7 @@
              *internal error should not be sent because server has already replied with
              *OK response in "sendReply")
              */
+            if(V) Log.d(TAG,"Exception occured - sending OBEX_HTTP_INTERNAL_ERROR reply",e);
             if (!op.isAborted) {
                 sendResponse(ResponseCodes.OBEX_HTTP_INTERNAL_ERROR, null);
             }
@@ -243,6 +249,7 @@
                 op.sendReply(response);
             }
         } catch (Exception e) {
+            if(V) Log.d(TAG,"Exception occured - sending OBEX_HTTP_INTERNAL_ERROR reply",e);
             sendResponse(ResponseCodes.OBEX_HTTP_INTERNAL_ERROR, null);
         }
     }
@@ -275,7 +282,7 @@
             data[2] = (byte)totalLength;
         }
         op.write(data);
-        op.flush();
+        op.flush(); // TODO: Do we need to flush?
     }
 
     /**
@@ -304,7 +311,7 @@
         flags = mInput.read();
         constants = mInput.read();
 
-        if (length > ObexHelper.MAX_PACKET_SIZE_INT) {
+        if (length > ObexHelper.getMaxRxPacketSize(mTransport)) {
             code = ResponseCodes.OBEX_HTTP_REQ_TOO_LARGE;
             totalLength = 3;
         } else {
@@ -358,6 +365,7 @@
                 try {
                     code = mListener.onSetPath(request, reply, backup, create);
                 } catch (Exception e) {
+                    if(V) Log.d(TAG,"Exception occured - sending OBEX_HTTP_INTERNAL_ERROR reply",e);
                     sendResponse(ResponseCodes.OBEX_HTTP_INTERNAL_ERROR, null);
                     return;
                 }
@@ -425,7 +433,7 @@
         length = mInput.read();
         length = (length << 8) + mInput.read();
 
-        if (length > ObexHelper.MAX_PACKET_SIZE_INT) {
+        if (length > ObexHelper.getMaxRxPacketSize(mTransport)) {
             code = ResponseCodes.OBEX_HTTP_REQ_TOO_LARGE;
             totalLength = 3;
         } else {
@@ -466,6 +474,7 @@
                 try {
                     mListener.onDisconnect(request, reply);
                 } catch (Exception e) {
+                    if(V) Log.d(TAG,"Exception occured - sending OBEX_HTTP_INTERNAL_ERROR reply",e);
                     sendResponse(ResponseCodes.OBEX_HTTP_INTERNAL_ERROR, null);
                     return;
                 }
@@ -531,23 +540,38 @@
         HeaderSet reply = new HeaderSet();
         int bytesReceived;
 
+        if(V) Log.v(TAG,"handleConnectRequest()");
+
         /*
          * Read in the length of the OBEX packet, OBEX version, flags, and max
          * packet length
          */
         packetLength = mInput.read();
         packetLength = (packetLength << 8) + mInput.read();
+        if(V) Log.v(TAG,"handleConnectRequest() - packetLength: " + packetLength);
+
         version = mInput.read();
         flags = mInput.read();
         mMaxPacketLength = mInput.read();
         mMaxPacketLength = (mMaxPacketLength << 8) + mInput.read();
 
+        if(V) Log.v(TAG,"handleConnectRequest() - version: " + version
+                + " MaxLength: " + mMaxPacketLength + " flags: " + flags);
+
         // should we check it?
         if (mMaxPacketLength > ObexHelper.MAX_PACKET_SIZE_INT) {
             mMaxPacketLength = ObexHelper.MAX_PACKET_SIZE_INT;
         }
 
-        if (packetLength > ObexHelper.MAX_PACKET_SIZE_INT) {
+        if(mMaxPacketLength > ObexHelper.getMaxTxPacketSize(mTransport)) {
+            Log.w(TAG, "Requested MaxObexPacketSize " + mMaxPacketLength
+                    + " is larger than the max size supported by the transport: "
+                    + ObexHelper.getMaxTxPacketSize(mTransport)
+                    + " Reducing to this size.");
+            mMaxPacketLength = ObexHelper.getMaxTxPacketSize(mTransport);
+        }
+
+        if (packetLength > ObexHelper.getMaxRxPacketSize(mTransport)) {
             code = ResponseCodes.OBEX_HTTP_REQ_TOO_LARGE;
             totalLength = 7;
         } else {
@@ -614,7 +638,7 @@
                         code = ResponseCodes.OBEX_HTTP_INTERNAL_ERROR;
                     }
                 } catch (Exception e) {
-                    e.printStackTrace();
+                    if(V) Log.d(TAG,"Exception occured - sending OBEX_HTTP_INTERNAL_ERROR reply",e);
                     totalLength = 7;
                     head = null;
                     code = ResponseCodes.OBEX_HTTP_INTERNAL_ERROR;
@@ -633,13 +657,14 @@
          * Packet Length (Defined in MAX_PACKET_SIZE) Byte 7 to n: headers
          */
         byte[] sendData = new byte[totalLength];
+        int maxRxLength = ObexHelper.getMaxRxPacketSize(mTransport);
         sendData[0] = (byte)code;
         sendData[1] = length[2];
         sendData[2] = length[3];
         sendData[3] = (byte)0x10;
         sendData[4] = (byte)0x00;
-        sendData[5] = (byte)(ObexHelper.MAX_PACKET_SIZE_INT >> 8);
-        sendData[6] = (byte)(ObexHelper.MAX_PACKET_SIZE_INT & 0xFF);
+        sendData[5] = (byte)(maxRxLength >> 8);
+        sendData[6] = (byte)(maxRxLength & 0xFF);
 
         if (head != null) {
             System.arraycopy(head, 0, sendData, 7, head.length);
@@ -659,11 +684,16 @@
             mListener.onClose();
         }
         try {
-            mInput.close();
-            mOutput.close();
-            mTransport.close();
+            /* Set state to closed before interrupting the thread by closing the streams */
             mClosed = true;
+            if(mInput != null)
+                mInput.close();
+            if(mOutput != null)
+                mOutput.close();
+            if(mTransport != null)
+                mTransport.close();
         } catch (Exception e) {
+            if(V) Log.d(TAG,"Exception occured during close() - ignore",e);
         }
         mTransport = null;
         mInput = null;
@@ -702,4 +732,7 @@
         return ResponseCodes.OBEX_HTTP_INTERNAL_ERROR;
     }
 
+    public ObexTransport getTransport() {
+        return mTransport;
+    }
 }
diff --git a/packages/DocumentsUI/res/layout/dialog_create_dir.xml b/packages/DocumentsUI/res/layout/dialog_create_dir.xml
index 54e26b4..5ed476f 100644
--- a/packages/DocumentsUI/res/layout/dialog_create_dir.xml
+++ b/packages/DocumentsUI/res/layout/dialog_create_dir.xml
@@ -22,6 +22,7 @@
     <EditText
         android:id="@android:id/text1"
         android:layout_width="match_parent"
-        android:layout_height="wrap_content" />
+        android:layout_height="wrap_content"
+        android:inputType="text" />
 
 </FrameLayout>
diff --git a/packages/DocumentsUI/src/com/android/documentsui/CopyService.java b/packages/DocumentsUI/src/com/android/documentsui/CopyService.java
index d2ef3d7..a9f03b6 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/CopyService.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/CopyService.java
@@ -191,7 +191,8 @@
         cancelIntent.putExtra(EXTRA_CANCEL, mJobId);
         mProgressBuilder.addAction(R.drawable.ic_cab_cancel,
                 getString(android.R.string.cancel), PendingIntent.getService(this, 0,
-                        cancelIntent, PendingIntent.FLAG_ONE_SHOT));
+                        cancelIntent,
+                        PendingIntent.FLAG_ONE_SHOT | PendingIntent.FLAG_CANCEL_CURRENT));
 
         // Send an initial progress notification.
         mProgressBuilder.setProgress(0, 0, true); // Indeterminate progress while setting up.
diff --git a/packages/DocumentsUI/src/com/android/documentsui/PickFragment.java b/packages/DocumentsUI/src/com/android/documentsui/PickFragment.java
index 5e565bf..7ea51b9 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/PickFragment.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/PickFragment.java
@@ -97,7 +97,6 @@
                               CharSequence displayName) {
         if (mContainer != null) {
             if (pickTarget != null) {
-                mContainer.setVisibility(View.VISIBLE);
                 final Locale locale = getResources().getConfiguration().locale;
                 switch (action) {
                     case BaseActivity.State.ACTION_OPEN_TREE:
@@ -112,7 +111,9 @@
                     default:
                         throw new IllegalArgumentException("Illegal action for PickFragment.");
                 }
-
+            }
+            if (pickTarget != null && pickTarget.isCreateSupported()) {
+                mContainer.setVisibility(View.VISIBLE);
             } else {
                 mContainer.setVisibility(View.GONE);
             }
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardHostView.java b/packages/Keyguard/src/com/android/keyguard/KeyguardHostView.java
index a88497c..be71b034 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardHostView.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardHostView.java
@@ -23,6 +23,7 @@
 import android.graphics.Canvas;
 import android.media.AudioManager;
 import android.os.SystemClock;
+import android.service.trust.TrustAgentService;
 import android.telephony.TelephonyManager;
 import android.util.AttributeSet;
 import android.util.Log;
@@ -69,14 +70,27 @@
         }
 
         @Override
-        public void onTrustInitiatedByUser(int userId) {
+        public void onTrustGrantedWithFlags(int flags, int userId) {
             if (userId != mLockPatternUtils.getCurrentUser()) return;
             if (!isAttachedToWindow()) return;
+            boolean bouncerVisible = isVisibleToUser();
+            boolean initiatedByUser =
+                    (flags & TrustAgentService.FLAG_GRANT_TRUST_INITIATED_BY_USER) != 0;
+            boolean dismissKeyguard =
+                    (flags & TrustAgentService.FLAG_GRANT_TRUST_DISMISS_KEYGUARD) != 0;
 
-            if (isVisibleToUser()) {
-                dismiss(false /* authenticated */);
-            } else {
-                mViewMediatorCallback.playTrustedSound();
+            if (initiatedByUser || dismissKeyguard) {
+                if (mViewMediatorCallback.isScreenOn() && (bouncerVisible || dismissKeyguard)) {
+                    if (!bouncerVisible) {
+                        // The trust agent dismissed the keyguard without the user proving
+                        // that they are present (by swiping up to show the bouncer). That's fine if
+                        // the user proved presence via some other way to the trust agent.
+                        Log.i(TAG, "TrustAgent dismissed Keyguard.");
+                    }
+                    dismiss(false /* authenticated */);
+                } else {
+                    mViewMediatorCallback.playTrustedSound();
+                }
             }
         }
     };
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitor.java
index a02fb4a..1eec5325 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -58,12 +58,12 @@
 import android.hardware.fingerprint.FingerprintManager.AuthenticationCallback;
 import android.hardware.fingerprint.FingerprintUtils;
 import android.hardware.fingerprint.FingerprintManager.AuthenticationResult;
+import android.service.trust.TrustAgentService;
 import android.telephony.SubscriptionInfo;
 import android.telephony.SubscriptionManager;
 import android.telephony.SubscriptionManager.OnSubscriptionsChangedListener;
 import android.telephony.TelephonyManager;
 import android.util.Log;
-import android.util.Slog;
 import android.util.SparseBooleanArray;
 
 import com.google.android.collect.Lists;
@@ -245,15 +245,14 @@
     private SparseBooleanArray mUserFaceUnlockRunning = new SparseBooleanArray();
 
     @Override
-    public void onTrustChanged(boolean enabled, int userId, boolean initiatedByUser) {
+    public void onTrustChanged(boolean enabled, int userId, int flags) {
         mUserHasTrust.put(userId, enabled);
-
         for (int i = 0; i < mCallbacks.size(); i++) {
             KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
             if (cb != null) {
                 cb.onTrustChanged(userId);
-                if (enabled && initiatedByUser) {
-                    cb.onTrustInitiatedByUser(userId);
+                if (enabled && flags != 0) {
+                    cb.onTrustGrantedWithFlags(flags, userId);
                 }
             }
         }
@@ -783,21 +782,14 @@
 
     private void startListeningForFingerprint() {
         if (DEBUG) Log.v(TAG, "startListeningForFingerprint()");
-        final int userId;
-        try {
-            userId = ActivityManagerNative.getDefault().getCurrentUser().id;
-        } catch (RemoteException e) {
-            Log.e(TAG, "Failed to get current user id: ", e);
-            return;
-        }
+        int userId = ActivityManager.getCurrentUser();
         if (mFpm != null && mFpm.isHardwareDetected() && !isFingerprintDisabled(userId)
-                && mFpm.getEnrolledFingerprints().size() > 0) {
+                && mFpm.getEnrolledFingerprints(userId).size() > 0) {
             if (mFingerprintCancelSignal != null) {
                 mFingerprintCancelSignal.cancel();
             }
             mFingerprintCancelSignal = new CancellationSignal();
-            mFpm.authenticate(null, mFingerprintCancelSignal, mAuthenticationCallback, 0,
-                    ActivityManager.getCurrentUser());
+            mFpm.authenticate(null, mFingerprintCancelSignal, mAuthenticationCallback, 0, userId);
             setFingerprintRunningDetectionRunning(true);
         }
     }
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java b/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java
index 756a7a4..26e6973 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java
@@ -171,9 +171,9 @@
     public void onTrustManagedChanged(int userId) { }
 
     /**
-     * Called when the user has proved to a trust agent that they want to use the device.
+     * Called after trust was granted with non-zero flags.
      */
-    public void onTrustInitiatedByUser(int userId) { }
+    public void onTrustGrantedWithFlags(int flags, int userId) { }
 
     /**
      * Called when a fingerprint is recognized.
diff --git a/packages/Keyguard/src/com/android/keyguard/ViewMediatorCallback.java b/packages/Keyguard/src/com/android/keyguard/ViewMediatorCallback.java
index 5bbcc8c..f5c809a 100644
--- a/packages/Keyguard/src/com/android/keyguard/ViewMediatorCallback.java
+++ b/packages/Keyguard/src/com/android/keyguard/ViewMediatorCallback.java
@@ -76,4 +76,9 @@
      *         (legacy API)
      */
     boolean isInputRestricted();
+
+    /**
+     * @return true if the screen is on
+     */
+    boolean isScreenOn();
 }
diff --git a/packages/Keyguard/test/SampleTrustAgent/src/com/android/trustagent/test/SampleTrustAgent.java b/packages/Keyguard/test/SampleTrustAgent/src/com/android/trustagent/test/SampleTrustAgent.java
index e6a0dd7..b8f16e7 100644
--- a/packages/Keyguard/test/SampleTrustAgent/src/com/android/trustagent/test/SampleTrustAgent.java
+++ b/packages/Keyguard/test/SampleTrustAgent/src/com/android/trustagent/test/SampleTrustAgent.java
@@ -38,7 +38,7 @@
      * <pre>
      * $ adb shell am broadcast -a action.sample_trust_agent.grant_trust\
      *  -e extra.message SampleTrust\
-     *  --el extra.duration 1000 --ez extra.init_by_user false
+     *  --el extra.duration 1000 --ez extra.init_by_user false --ez extra.dismiss_keyguard false
      * </pre>
      */
     private static final boolean ALLOW_EXTERNAL_BROADCASTS = false;
@@ -51,6 +51,7 @@
     private static final String EXTRA_MESSAGE = "extra.message";
     private static final String EXTRA_DURATION = "extra.duration";
     private static final String EXTRA_INITIATED_BY_USER = "extra.init_by_user";
+    private static final String EXTRA_DISMISS_KEYGUARD = "extra.dismiss_keyguard";
 
     private static final String PREFERENCE_REPORT_UNLOCK_ATTEMPTS
             = "preference.report_unlock_attempts";
@@ -141,10 +142,17 @@
         public void onReceive(Context context, Intent intent) {
             String action = intent.getAction();
             if (ACTION_GRANT_TRUST.equals(action)) {
+                int flags = 0;
+                if (intent.getBooleanExtra(EXTRA_INITIATED_BY_USER, false)) {
+                    flags |= TrustAgentService.FLAG_GRANT_TRUST_INITIATED_BY_USER;
+                }
+                if (intent.getBooleanExtra(EXTRA_DISMISS_KEYGUARD, false)) {
+                    flags |= TrustAgentService.FLAG_GRANT_TRUST_DISMISS_KEYGUARD;
+                }
+
                 try {
                     grantTrust(intent.getStringExtra(EXTRA_MESSAGE),
-                            intent.getLongExtra(EXTRA_DURATION, 0),
-                            intent.getBooleanExtra(EXTRA_INITIATED_BY_USER, false));
+                            intent.getLongExtra(EXTRA_DURATION, 0), flags);
                 } catch (IllegalStateException e) {
                     logAndShowToast("IllegalStateException: " + e.getMessage());
                 }
diff --git a/packages/SettingsLib/src/com/android/settingslib/applications/PermissionsInfo.java b/packages/SettingsLib/src/com/android/settingslib/applications/PermissionsInfo.java
new file mode 100644
index 0000000..60b5ba5
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/applications/PermissionsInfo.java
@@ -0,0 +1,188 @@
+/*
+ * 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.applications;
+
+import android.content.Context;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.pm.PermissionGroupInfo;
+import android.content.pm.PermissionInfo;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.ShapeDrawable;
+import android.os.AsyncTask;
+import android.os.Build;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.util.ArrayMap;
+import android.util.Log;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+
+public class PermissionsInfo {
+
+    private static final String TAG = "PermissionsInfo";
+
+    private final PackageManager mPm;
+    private final ArrayList<PermissionGroup> mGroups = new ArrayList<>();
+    private final Map<String, PermissionGroup> mGroupLookup = new ArrayMap<>();
+    private final Callback mCallback;
+    private final Context mContext;
+    // Count of apps that request runtime permissions.
+    private int mRuntimePermAppsCt;
+    // Count of apps that are granted runtime permissions.
+    private int mRuntimePermAppsGrantedCt;
+
+    public PermissionsInfo(Context context, Callback callback) {
+        mContext = context;
+        mPm = context.getPackageManager();
+        mCallback = callback;
+        new PermissionsLoader().execute();
+    }
+
+    public List<PermissionGroup> getGroups() {
+        synchronized (mGroups) {
+            return new ArrayList<>(mGroups);
+        }
+    }
+
+    public int getRuntimePermAppsCount() {
+        return mRuntimePermAppsCt;
+    }
+
+    public int getRuntimePermAppsGrantedCount() {
+        return mRuntimePermAppsGrantedCt;
+    }
+
+    private PermissionGroup getOrCreateGroup(String permission) {
+        PermissionGroup group = mGroupLookup.get(permission);
+        if (group == null) {
+            // Some permissions don't have a group, in that case treat them like a group
+            // and create their own PermissionGroup (only if they are runtime).
+            try {
+                PermissionInfo info = mPm.getPermissionInfo(permission, 0);
+                if (info.protectionLevel == PermissionInfo.PROTECTION_DANGEROUS) {
+                    group = new PermissionGroup();
+                    // TODO: Add default permission icon.
+                    group.icon = info.icon != 0 ? info.loadIcon(mPm) : new ShapeDrawable();
+                    group.name = info.name;
+                    group.label = info.loadLabel(mPm).toString();
+                    mGroups.add(group);
+                    mGroupLookup.put(permission, group);
+                }
+            } catch (NameNotFoundException e) {
+                Log.w(TAG, "Unknown permission " + permission, e);
+            }
+        }
+        return group;
+    }
+
+    private class PermissionsLoader extends AsyncTask<Void, Void, Void> {
+
+        @Override
+        protected Void doInBackground(Void... params) {
+            List<PermissionGroupInfo> groups =
+                    mPm.getAllPermissionGroups(PackageManager.GET_META_DATA);
+            // Get the groups.
+            for (PermissionGroupInfo groupInfo : groups) {
+                PermissionGroup group = new PermissionGroup();
+                // TODO: Add default permission icon.
+                group.icon = groupInfo.icon != 0 ? groupInfo.loadIcon(mPm) : new ShapeDrawable();
+                group.name = groupInfo.name;
+                group.label = groupInfo.loadLabel(mPm).toString();
+                synchronized (mGroups) {
+                    mGroups.add(group);
+                }
+            }
+            // Load permissions and which are runtime.
+            for (PermissionGroup group : mGroups) {
+                try {
+                    List<PermissionInfo> permissions =
+                            mPm.queryPermissionsByGroup(group.name, 0);
+                    for (PermissionInfo info : permissions) {
+                        if (info.protectionLevel != PermissionInfo.PROTECTION_DANGEROUS) continue;
+                        mGroupLookup.put(info.name, group);
+                    }
+                } catch (NameNotFoundException e) {
+                    Log.w(TAG, "Problem getting permissions", e);
+                }
+            }
+            // Load granted info.
+            for (UserHandle user : UserManager.get(mContext).getUserProfiles()) {
+                List<PackageInfo> allApps = mPm.getInstalledPackages(
+                        PackageManager.GET_PERMISSIONS, user.getIdentifier());
+                for (PackageInfo info : allApps) {
+                    if (info.applicationInfo.targetSdkVersion <= Build.VERSION_CODES.LOLLIPOP_MR1
+                            || info.requestedPermissions == null)  {
+                        continue;
+                    }
+                    final int N = info.requestedPermissionsFlags.length;
+                    boolean appHasRuntimePerms = false;
+                    boolean appGrantedRuntimePerms = false;
+                    for (int i = 0; i < N; i++) {
+                        boolean granted = (info.requestedPermissionsFlags[i]
+                                & PackageInfo.REQUESTED_PERMISSION_GRANTED) != 0;
+                        PermissionGroup group = getOrCreateGroup(info.requestedPermissions[i]);
+                        String key = Integer.toString(info.applicationInfo.uid);
+                        if (group != null && !group.possibleApps.contains(key)) {
+                            appHasRuntimePerms = true;
+                            group.possibleApps.add(key);
+                            if (granted) {
+                                appGrantedRuntimePerms = true;
+                                group.grantedApps.add(key);
+                            }
+                        }
+                    }
+                    if (appHasRuntimePerms) {
+                        mRuntimePermAppsCt++;
+                        if (appGrantedRuntimePerms) {
+                            mRuntimePermAppsGrantedCt++;
+                        }
+                    }
+                }
+            }
+            Collections.sort(mGroups);
+
+            return null;
+        }
+
+        @Override
+        protected void onPostExecute(Void result) {
+            mCallback.onPermissionLoadComplete();
+        }
+    }
+
+    public static class PermissionGroup implements Comparable<PermissionGroup> {
+        public final List<String> possibleApps = new ArrayList<>();
+        public final List<String> grantedApps = new ArrayList<>();
+        public String name;
+        public String label;
+        public Drawable icon;
+
+        @Override
+        public int compareTo(PermissionGroup another) {
+            return label.compareTo(another.label);
+        }
+    }
+
+    public interface Callback {
+        void onPermissionLoadComplete();
+    }
+
+}
diff --git a/packages/SystemUI/res/anim/heads_up_enter.xml b/packages/SystemUI/res/anim/heads_up_enter.xml
deleted file mode 100644
index 59eef42..0000000
--- a/packages/SystemUI/res/anim/heads_up_enter.xml
+++ /dev/null
@@ -1,12 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<set xmlns:android="http://schemas.android.com/apk/res/android"
-        >
-    <translate
-        android:interpolator="@android:interpolator/overshoot"
-        android:fromYDelta="-50%" android:toYDelta="0"
-        android:duration="@android:integer/config_shortAnimTime" />
-    <alpha 
-        android:interpolator="@android:interpolator/decelerate_quad"
-        android:fromAlpha="0.0" android:toAlpha="1.0"
-        android:duration="@android:integer/config_shortAnimTime" />
-</set>
diff --git a/packages/SystemUI/res/anim/heads_up_exit.xml b/packages/SystemUI/res/anim/heads_up_exit.xml
deleted file mode 100644
index 2cad8f6..0000000
--- a/packages/SystemUI/res/anim/heads_up_exit.xml
+++ /dev/null
@@ -1,12 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<set xmlns:android="http://schemas.android.com/apk/res/android"
-        >
-    <translate
-            android:interpolator="@android:interpolator/overshoot"
-            android:fromYDelta="0" android:toYDelta="-50%"
-            android:duration="@android:integer/config_shortAnimTime" />
-    <alpha
-        android:interpolator="@android:interpolator/accelerate_quad"
-        android:fromAlpha="1.0" android:toAlpha="0.0"
-        android:duration="@android:integer/config_shortAnimTime" />
-</set>
diff --git a/packages/SystemUI/res/anim/ic_qs_signal_blink_1.xml b/packages/SystemUI/res/anim/ic_qs_signal_blink_1.xml
new file mode 100644
index 0000000..57b61da
--- /dev/null
+++ b/packages/SystemUI/res/anim/ic_qs_signal_blink_1.xml
@@ -0,0 +1,38 @@
+<!--
+    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.
+-->
+<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
+    android:interpolator="@android:anim/linear_interpolator"
+    android:duration="@integer/carrier_network_change_anim_time"
+    android:repeatCount="-1">
+
+    <propertyValuesHolder
+        android:propertyName="fillColor"
+        android:valueType="colorType">
+        <keyframe
+            android:fraction="0.0"
+            android:value="#FFFFFFFF"/>
+        <keyframe
+            android:fraction="0.32"
+            android:value="#FFFFFFFF"/>
+        <keyframe
+            android:fraction="0.33"
+            android:value="#4DFFFFFF"/>
+        <keyframe
+            android:fraction="1.0"
+            android:value="#4DFFFFFF"/>
+    </propertyValuesHolder>
+
+</objectAnimator>
diff --git a/packages/SystemUI/res/anim/ic_qs_signal_blink_2.xml b/packages/SystemUI/res/anim/ic_qs_signal_blink_2.xml
new file mode 100644
index 0000000..09694c3
--- /dev/null
+++ b/packages/SystemUI/res/anim/ic_qs_signal_blink_2.xml
@@ -0,0 +1,44 @@
+<!--
+    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.
+-->
+<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
+    android:interpolator="@android:anim/linear_interpolator"
+    android:duration="@integer/carrier_network_change_anim_time"
+    android:repeatCount="-1">
+
+    <propertyValuesHolder
+        android:propertyName="fillColor"
+        android:valueType="colorType">
+        <keyframe
+            android:fraction="0.0"
+            android:value="#4DFFFFFF"/>
+        <keyframe
+            android:fraction="0.32"
+            android:value="#4DFFFFFF"/>
+        <keyframe
+            android:fraction="0.33"
+            android:value="#FFFFFFFF"/>
+        <keyframe
+            android:fraction="0.66"
+            android:value="#FFFFFFFF"/>
+        <keyframe
+            android:fraction="0.67"
+            android:value="#4DFFFFFF"/>
+        <keyframe
+            android:fraction="1.0"
+            android:value="#4DFFFFFF"/>
+    </propertyValuesHolder>
+
+</objectAnimator>
diff --git a/packages/SystemUI/res/anim/ic_qs_signal_blink_3.xml b/packages/SystemUI/res/anim/ic_qs_signal_blink_3.xml
new file mode 100644
index 0000000..2270e3f
--- /dev/null
+++ b/packages/SystemUI/res/anim/ic_qs_signal_blink_3.xml
@@ -0,0 +1,38 @@
+<!--
+    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.
+-->
+<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
+    android:interpolator="@android:anim/linear_interpolator"
+    android:duration="@integer/carrier_network_change_anim_time"
+    android:repeatCount="-1">
+
+    <propertyValuesHolder
+        android:propertyName="fillColor"
+        android:valueType="colorType">
+        <keyframe
+            android:fraction="0.0"
+            android:value="#4DFFFFFF"/>
+        <keyframe
+            android:fraction="0.66"
+            android:value="#4DFFFFFF"/>
+        <keyframe
+            android:fraction="0.67"
+            android:value="#FFFFFFFF"/>
+        <keyframe
+            android:fraction="1.0"
+            android:value="#FFFFFFFF"/>
+    </propertyValuesHolder>
+
+</objectAnimator>
diff --git a/packages/SystemUI/res/anim/ic_signal_blink_1.xml b/packages/SystemUI/res/anim/ic_signal_blink_1.xml
new file mode 100644
index 0000000..ab1905a
--- /dev/null
+++ b/packages/SystemUI/res/anim/ic_signal_blink_1.xml
@@ -0,0 +1,38 @@
+<!--
+    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.
+-->
+<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
+    android:interpolator="@android:anim/linear_interpolator"
+    android:duration="@integer/carrier_network_change_anim_time"
+    android:repeatCount="-1">
+
+    <propertyValuesHolder
+        android:propertyName="fillColor"
+        android:valueType="colorType">
+        <keyframe
+            android:fraction="0.0"
+            android:value="@color/light_mode_icon_color_dual_tone_fill"/>
+        <keyframe
+            android:fraction="0.32"
+            android:value="@color/light_mode_icon_color_dual_tone_fill"/>
+        <keyframe
+            android:fraction="0.33"
+            android:value="@color/light_mode_icon_color_dual_tone_background"/>
+        <keyframe
+            android:fraction="1.0"
+            android:value="@color/light_mode_icon_color_dual_tone_background"/>
+    </propertyValuesHolder>
+
+</objectAnimator>
diff --git a/packages/SystemUI/res/anim/ic_signal_blink_2.xml b/packages/SystemUI/res/anim/ic_signal_blink_2.xml
new file mode 100644
index 0000000..1b7ace2
--- /dev/null
+++ b/packages/SystemUI/res/anim/ic_signal_blink_2.xml
@@ -0,0 +1,44 @@
+<!--
+    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.
+-->
+<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
+    android:interpolator="@android:anim/linear_interpolator"
+    android:duration="@integer/carrier_network_change_anim_time"
+    android:repeatCount="-1">
+
+    <propertyValuesHolder
+        android:propertyName="fillColor"
+        android:valueType="colorType">
+        <keyframe
+            android:fraction="0.0"
+            android:value="@color/light_mode_icon_color_dual_tone_background"/>
+        <keyframe
+            android:fraction="0.32"
+            android:value="@color/light_mode_icon_color_dual_tone_background"/>
+        <keyframe
+            android:fraction="0.33"
+            android:value="@color/light_mode_icon_color_dual_tone_fill"/>
+        <keyframe
+            android:fraction="0.66"
+            android:value="@color/light_mode_icon_color_dual_tone_fill"/>
+        <keyframe
+            android:fraction="0.67"
+            android:value="@color/light_mode_icon_color_dual_tone_background"/>
+        <keyframe
+            android:fraction="1.0"
+            android:value="@color/light_mode_icon_color_dual_tone_background"/>
+    </propertyValuesHolder>
+
+</objectAnimator>
diff --git a/packages/SystemUI/res/anim/ic_signal_blink_3.xml b/packages/SystemUI/res/anim/ic_signal_blink_3.xml
new file mode 100644
index 0000000..cee831c
--- /dev/null
+++ b/packages/SystemUI/res/anim/ic_signal_blink_3.xml
@@ -0,0 +1,38 @@
+<!--
+    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.
+-->
+<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
+    android:interpolator="@android:anim/linear_interpolator"
+    android:duration="@integer/carrier_network_change_anim_time"
+    android:repeatCount="-1">
+
+    <propertyValuesHolder
+        android:propertyName="fillColor"
+        android:valueType="colorType">
+        <keyframe
+            android:fraction="0.0"
+            android:value="@color/light_mode_icon_color_dual_tone_background"/>
+        <keyframe
+            android:fraction="0.66"
+            android:value="@color/light_mode_icon_color_dual_tone_background"/>
+        <keyframe
+            android:fraction="0.67"
+            android:value="@color/light_mode_icon_color_dual_tone_fill"/>
+        <keyframe
+            android:fraction="1.0"
+            android:value="@color/light_mode_icon_color_dual_tone_fill"/>
+    </propertyValuesHolder>
+
+</objectAnimator>
diff --git a/packages/SystemUI/res/anim/ic_signal_dark_blink_1.xml b/packages/SystemUI/res/anim/ic_signal_dark_blink_1.xml
new file mode 100644
index 0000000..9d398fa
--- /dev/null
+++ b/packages/SystemUI/res/anim/ic_signal_dark_blink_1.xml
@@ -0,0 +1,38 @@
+<!--
+    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.
+-->
+<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
+    android:interpolator="@android:anim/linear_interpolator"
+    android:duration="@integer/carrier_network_change_anim_time"
+    android:repeatCount="-1">
+
+    <propertyValuesHolder
+        android:propertyName="fillColor"
+        android:valueType="colorType">
+        <keyframe
+            android:fraction="0.0"
+            android:value="@color/dark_mode_icon_color_dual_tone_fill"/>
+        <keyframe
+            android:fraction="0.32"
+            android:value="@color/dark_mode_icon_color_dual_tone_fill"/>
+        <keyframe
+            android:fraction="0.33"
+            android:value="@color/dark_mode_icon_color_dual_tone_background"/>
+        <keyframe
+            android:fraction="1.0"
+            android:value="@color/dark_mode_icon_color_dual_tone_background"/>
+    </propertyValuesHolder>
+
+</objectAnimator>
diff --git a/packages/SystemUI/res/anim/ic_signal_dark_blink_2.xml b/packages/SystemUI/res/anim/ic_signal_dark_blink_2.xml
new file mode 100644
index 0000000..c6e213d
--- /dev/null
+++ b/packages/SystemUI/res/anim/ic_signal_dark_blink_2.xml
@@ -0,0 +1,44 @@
+<!--
+    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.
+-->
+<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
+    android:interpolator="@android:anim/linear_interpolator"
+    android:duration="@integer/carrier_network_change_anim_time"
+    android:repeatCount="-1">
+
+    <propertyValuesHolder
+        android:propertyName="fillColor"
+        android:valueType="colorType">
+        <keyframe
+            android:fraction="0.0"
+            android:value="@color/dark_mode_icon_color_dual_tone_background"/>
+        <keyframe
+            android:fraction="0.32"
+            android:value="@color/dark_mode_icon_color_dual_tone_background"/>
+        <keyframe
+            android:fraction="0.33"
+            android:value="@color/dark_mode_icon_color_dual_tone_fill"/>
+        <keyframe
+            android:fraction="0.66"
+            android:value="@color/dark_mode_icon_color_dual_tone_fill"/>
+        <keyframe
+            android:fraction="0.67"
+            android:value="@color/dark_mode_icon_color_dual_tone_background"/>
+        <keyframe
+            android:fraction="1.0"
+            android:value="@color/dark_mode_icon_color_dual_tone_background"/>
+    </propertyValuesHolder>
+
+</objectAnimator>
diff --git a/packages/SystemUI/res/anim/ic_signal_dark_blink_3.xml b/packages/SystemUI/res/anim/ic_signal_dark_blink_3.xml
new file mode 100644
index 0000000..dce148c
--- /dev/null
+++ b/packages/SystemUI/res/anim/ic_signal_dark_blink_3.xml
@@ -0,0 +1,38 @@
+<!--
+    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.
+-->
+<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
+    android:interpolator="@android:anim/linear_interpolator"
+    android:duration="@integer/carrier_network_change_anim_time"
+    android:repeatCount="-1">
+
+    <propertyValuesHolder
+        android:propertyName="fillColor"
+        android:valueType="colorType">
+        <keyframe
+            android:fraction="0.0"
+            android:value="@color/dark_mode_icon_color_dual_tone_background"/>
+        <keyframe
+            android:fraction="0.66"
+            android:value="@color/dark_mode_icon_color_dual_tone_background"/>
+        <keyframe
+            android:fraction="0.67"
+            android:value="@color/dark_mode_icon_color_dual_tone_fill"/>
+        <keyframe
+            android:fraction="1.0"
+            android:value="@color/dark_mode_icon_color_dual_tone_fill"/>
+    </propertyValuesHolder>
+
+</objectAnimator>
diff --git a/packages/SystemUI/res/drawable/ic_audio_alarm.xml b/packages/SystemUI/res/drawable/ic_audio_alarm.xml
deleted file mode 100644
index 91010a3..0000000
--- a/packages/SystemUI/res/drawable/ic_audio_alarm.xml
+++ /dev/null
@@ -1,24 +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.
--->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
-        android:width="28.0dp"
-        android:height="28.0dp"
-        android:viewportWidth="48.0"
-        android:viewportHeight="48.0">
-    <path
-        android:fillColor="#ffffffff"
-        android:pathData="M44.0,11.44l-9.19,-7.71 -2.57,3.06 9.19,7.71 2.57,-3.06zm-28.24,-4.66l-2.57,-3.06 -9.19,7.71 2.57,3.06 9.19,-7.71zm9.24,9.22l-3.0,0.0l0.0,12.0l9.49,5.71 1.51,-2.47 -8.0,-4.74l0.0,-10.5zm-1.01,-8.0c-9.95,0.0 -17.99,8.06 -17.99,18.0s8.04,18.0 17.99,18.0 18.01,-8.06 18.01,-18.0 -8.06,-18.0 -18.01,-18.0zm0.01,32.0c-7.73,0.0 -14.0,-6.27 -14.0,-14.0s6.27,-14.0 14.0,-14.0 14.0,6.27 14.0,14.0 -6.26,14.0 -14.0,14.0z"/>
-</vector>
diff --git a/packages/SystemUI/res/drawable/ic_audio_alarm_mute.xml b/packages/SystemUI/res/drawable/ic_audio_alarm_mute.xml
deleted file mode 100644
index dd124d7..0000000
--- a/packages/SystemUI/res/drawable/ic_audio_alarm_mute.xml
+++ /dev/null
@@ -1,24 +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.
--->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
-        android:width="28.0dp"
-        android:height="28.0dp"
-        android:viewportWidth="48.0"
-        android:viewportHeight="48.0">
-    <path
-        android:fillColor="#ffffffff"
-        android:pathData="M24.0,12.0c7.73,0.0 14.0,6.27 14.0,14.0 0.0,1.69 -0.31,3.3 -0.86,4.8l3.04,3.04c1.16,-2.37 1.82,-5.03 1.82,-7.84 0.0,-9.94 -8.06,-18.0 -18.01,-18.0 -2.81,0.0 -5.46,0.66 -7.84,1.81l3.05,3.05c1.5,-0.55 3.11,-0.86 4.8,-0.86zm20.0,-0.56l-9.19,-7.71 -2.57,3.06 9.19,7.71 2.57,-3.06zm-38.16,-6.85l-2.55,2.54 2.66,2.66 -2.22,1.86 2.84,2.84 2.22,-1.86 1.6,1.6c-2.73,3.16 -4.39,7.27 -4.39,11.77 0.0,9.94 8.04,18.0 17.99,18.0 4.51,0.0 8.62,-1.67 11.77,-4.4l4.4,4.4 2.54,-2.55 -34.91,-34.91 -1.95,-1.95zm27.1,32.19c-2.43,2.01 -5.54,3.22 -8.94,3.22 -7.73,0.0 -14.0,-6.27 -14.0,-14.0 0.0,-3.4 1.21,-6.51 3.22,-8.94l19.72,19.72zm-16.91,-30.23l-2.84,-2.84 -1.7,1.43 2.84,2.84 1.7,-1.43z"/>
-</vector>
diff --git a/packages/SystemUI/res/drawable/ic_audio_bt.xml b/packages/SystemUI/res/drawable/ic_audio_bt.xml
deleted file mode 100644
index c0da519..0000000
--- a/packages/SystemUI/res/drawable/ic_audio_bt.xml
+++ /dev/null
@@ -1,25 +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.
--->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
-        android:width="28dp"
-        android:height="28dp"
-        android:viewportWidth="48.0"
-        android:viewportHeight="48.0">
-
-    <path
-        android:fillColor="#FFFFFFFF"
-        android:pathData="M35.4,15.4L24.0,4.0l-2.0,0.0l0.0,15.2L12.8,10.0L10.0,12.8L21.2,24.0L10.0,35.2l2.8,2.8l9.2,-9.2L22.0,44.0l2.0,0.0l11.4,-11.4L26.8,24.0L35.4,15.4zM26.0,11.7l3.8,3.8L26.0,19.2L26.0,11.7zM29.8,32.6L26.0,36.3l0.0,-7.5L29.8,32.6z"/>
-</vector>
diff --git a/packages/SystemUI/res/drawable/ic_audio_bt_mute.xml b/packages/SystemUI/res/drawable/ic_audio_bt_mute.xml
deleted file mode 100644
index 718eee5..0000000
--- a/packages/SystemUI/res/drawable/ic_audio_bt_mute.xml
+++ /dev/null
@@ -1,25 +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.
--->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
-        android:width="28dp"
-        android:height="28dp"
-        android:viewportWidth="48.0"
-        android:viewportHeight="48.0">
-
-    <path
-        android:fillColor="#FFFFFFFF"
-        android:pathData="M26.0,11.8l3.8,3.8l-3.2,3.2l2.8,2.8l6.0,-6.0L24.0,4.2l-2.0,0.0l0.0,10.1l4.0,4.0L26.0,11.8zM10.8,8.2L8.0,11.0l13.2,13.2L10.0,35.3l2.8,2.8L22.0,29.0l0.0,15.2l2.0,0.0l8.6,-8.6l4.6,4.6l2.8,-2.8L10.8,8.2zM26.0,36.5L26.0,29.0l3.8,3.8L26.0,36.5z"/>
-</vector>
diff --git a/packages/SystemUI/res/drawable/ic_audio_remote.xml b/packages/SystemUI/res/drawable/ic_audio_remote.xml
deleted file mode 100644
index 762878b..0000000
--- a/packages/SystemUI/res/drawable/ic_audio_remote.xml
+++ /dev/null
@@ -1,25 +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.
--->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
-        android:width="28dp"
-        android:height="28dp"
-        android:viewportWidth="48.0"
-        android:viewportHeight="48.0">
-
-    <path
-        android:fillColor="#FFFFFFFF"
-        android:pathData="M2.0,36.0l0.0,6.0l6.0,0.0C8.0,38.7 5.3,36.0 2.0,36.0zM2.0,28.0l0.0,4.0c5.5,0.0 10.0,4.5 10.0,10.0l4.0,0.0C16.0,34.3 9.7,28.0 2.0,28.0zM38.0,14.0L10.0,14.0l0.0,3.3c7.9,2.6 14.2,8.8 16.7,16.7L38.0,34.0L38.0,14.0zM2.0,20.0l0.0,4.0c9.9,0.0 18.0,8.1 18.0,18.0l4.0,0.0C24.0,29.8 14.1,20.0 2.0,20.0zM42.0,6.0L6.0,6.0c-2.2,0.0 -4.0,1.8 -4.0,4.0l0.0,6.0l4.0,0.0l0.0,-6.0l36.0,0.0l0.0,28.0L28.0,38.0l0.0,4.0l14.0,0.0c2.2,0.0 4.0,-1.8 4.0,-4.0L46.0,10.0C46.0,7.8 44.2,6.0 42.0,6.0z"/>
-</vector>
diff --git a/packages/SystemUI/res/drawable/ic_audio_vol.xml b/packages/SystemUI/res/drawable/ic_audio_vol.xml
deleted file mode 100644
index 587ea89..0000000
--- a/packages/SystemUI/res/drawable/ic_audio_vol.xml
+++ /dev/null
@@ -1,24 +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.
--->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
-        android:width="28.0dp"
-        android:height="28.0dp"
-        android:viewportWidth="48.0"
-        android:viewportHeight="48.0">
-    <path
-        android:fillColor="#ffffffff"
-        android:pathData="M6.0,18.0l0.0,12.0l8.0,0.0l10.0,10.0L24.0,8.0L14.0,18.0L6.0,18.0zm27.0,6.0c0.0,-3.53 -2.04,-6.58 -5.0,-8.05l0.0,16.11c2.96,-1.48 5.0,-4.53 5.0,-8.06zM28.0,6.46l0.0,4.13c5.78,1.72 10.0,7.07 10.0,13.41s-4.22,11.69 -10.0,13.41l0.0,4.13c8.01,-1.82 14.0,-8.97 14.0,-17.54S36.01,8.28 28.0,6.46z"/>
-</vector>
diff --git a/packages/SystemUI/res/drawable/ic_audio_vol_mute.xml b/packages/SystemUI/res/drawable/ic_audio_vol_mute.xml
deleted file mode 100644
index 8a7c7ec..0000000
--- a/packages/SystemUI/res/drawable/ic_audio_vol_mute.xml
+++ /dev/null
@@ -1,24 +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.
--->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
-        android:width="28.0dp"
-        android:height="28.0dp"
-        android:viewportWidth="48.0"
-        android:viewportHeight="48.0">
-    <path
-        android:fillColor="#ffffffff"
-        android:pathData="M33.0,24.0c0.0,-3.53 -2.04,-6.58 -5.0,-8.05l0.0,4.42l4.91,4.91c0.06,-0.42 0.09,-0.85 0.09,-1.28zm5.0,0.0c0.0,1.88 -0.41,3.65 -1.08,5.28l3.03,3.03C41.25,29.82 42.0,27.0 42.0,24.0c0.0,-8.56 -5.99,-15.72 -14.0,-17.54l0.0,4.13c5.78,1.72 10.0,7.07 10.0,13.41zM8.55,6.0L6.0,8.55 15.45,18.0L6.0,18.0l0.0,12.0l8.0,0.0l10.0,10.0L24.0,26.55l8.51,8.51c-1.34,1.03 -2.85,1.86 -4.51,2.36l0.0,4.13c2.75,-0.63 5.26,-1.89 7.37,-3.62L39.45,42.0 42.0,39.45l-18.0,-18.0L8.55,6.0zM24.0,8.0l-4.18,4.18L24.0,16.36L24.0,8.0z"/>
-</vector>
diff --git a/packages/SystemUI/res/drawable/ic_qs_signal_carrier_network_change.xml b/packages/SystemUI/res/drawable/ic_qs_signal_carrier_network_change.xml
new file mode 100644
index 0000000..96e2fd4
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_qs_signal_carrier_network_change.xml
@@ -0,0 +1,36 @@
+<!--
+    Copyright (C) 2015 The Android Open Source Project
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="32dp"
+        android:height="32dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:name="dot1"
+        android:fillColor="#FFFFFFFF"
+        android:pathData="M9.0,19.0l3.0,0.0l0.0,3.0l-3.0,0.0z"/>
+    <path
+        android:name="dot2"
+        android:fillColor="#4DFFFFFF"
+        android:pathData="M14.0,19.0l3.0,0.0l0.0,3.0l-3.0,0.0z"/>
+    <path
+        android:name="dot3"
+        android:fillColor="#4DFFFFFF"
+        android:pathData="M19.0,19.0l3.0,0.0l0.0,3.0l-3.0,0.0z"/>
+    <path
+        android:fillColor="#4DFFFFFF"
+        android:pathData="M2.0,22.0l6.0,0.0 0.0,-4.0 14.0,0.0 0.0,-16.0z"/>
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_qs_signal_carrier_network_change_animation.xml b/packages/SystemUI/res/drawable/ic_qs_signal_carrier_network_change_animation.xml
new file mode 100644
index 0000000..2186aa8
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_qs_signal_carrier_network_change_animation.xml
@@ -0,0 +1,27 @@
+<!--
+    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.
+-->
+<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:drawable="@drawable/ic_qs_signal_carrier_network_change" >
+    <target
+        android:name="dot1"
+        android:animation="@anim/ic_qs_signal_blink_1"/>
+    <target
+        android:name="dot2"
+        android:animation="@anim/ic_qs_signal_blink_2"/>
+    <target
+        android:name="dot3"
+        android:animation="@anim/ic_qs_signal_blink_3"/>
+</animated-vector>
diff --git a/packages/SystemUI/res/drawable/ic_ringer_audible.xml b/packages/SystemUI/res/drawable/ic_ringer_audible.xml
deleted file mode 100644
index fd50617..0000000
--- a/packages/SystemUI/res/drawable/ic_ringer_audible.xml
+++ /dev/null
@@ -1,25 +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.
--->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
-        android:width="28dp"
-        android:height="28dp"
-        android:viewportWidth="24.0"
-        android:viewportHeight="24.0">
-
-    <path
-        android:fillColor="#FFFFFFFF"
-        android:pathData="M11.5,22.0c1.1,0.0 2.0,-0.9 2.0,-2.0l-4.0,0.0C9.5,21.1 10.4,22.0 11.5,22.0zM18.0,16.0l0.0,-5.5c0.0,-3.1 -2.1,-5.6 -5.0,-6.3L13.0,3.5C13.0,2.7 12.3,2.0 11.5,2.0C10.7,2.0 10.0,2.7 10.0,3.5l0.0,0.7c-2.9,0.7 -5.0,3.2 -5.0,6.3L5.0,16.0l-2.0,2.0l0.0,1.0l17.0,0.0l0.0,-1.0L18.0,16.0z"/>
-</vector>
diff --git a/packages/SystemUI/res/drawable/ic_ringer_mute.xml b/packages/SystemUI/res/drawable/ic_ringer_mute.xml
deleted file mode 100644
index b29a139..0000000
--- a/packages/SystemUI/res/drawable/ic_ringer_mute.xml
+++ /dev/null
@@ -1,24 +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.
--->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
-        android:width="28dp"
-        android:height="28dp"
-        android:viewportWidth="48.0"
-        android:viewportHeight="48.0">
-    <path
-        android:fillColor="#FFFFFFFF"
-        android:pathData="M23.000000,44.000000c2.200000,0.000000 4.000000,-1.800000 4.000000,-4.000000l-8.000000,0.000000C19.000000,42.200001 20.799999,44.000000 23.000000,44.000000zM36.000000,21.000000c0.000000,-6.100000 -4.300000,-11.300000 -10.000000,-12.600000L26.000000,7.000000c0.000000,-1.700000 -1.300000,-3.000000 -3.000000,-3.000000c-1.700000,0.000000 -3.000000,1.300000 -3.000000,3.000000l0.000000,1.400000c-1.000000,0.200000 -2.000000,0.600000 -2.900000,1.100000L36.000000,28.400000L36.000000,21.000000zM35.500000,38.000000l4.000000,4.000000l2.500000,-2.500000L8.500000,6.000000L6.000000,8.500000l5.800000,5.800000C10.700000,16.299999 10.000000,18.600000 10.000000,21.000000l0.000000,11.000000l-4.000000,4.000000l0.000000,2.000000L35.500000,38.000000z"/>
-</vector>
diff --git a/packages/SystemUI/res/drawable/ic_ringer_vibrate.xml b/packages/SystemUI/res/drawable/ic_ringer_vibrate.xml
deleted file mode 100644
index 4bff96d..0000000
--- a/packages/SystemUI/res/drawable/ic_ringer_vibrate.xml
+++ /dev/null
@@ -1,25 +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.
--->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
-        android:width="28dp"
-        android:height="28dp"
-        android:viewportWidth="24.0"
-        android:viewportHeight="24.0">
-
-    <path
-        android:fillColor="#FFFFFFFF"
-        android:pathData="M0.0,15.0l2.0,0.0L2.0,9.0L0.0,9.0L0.0,15.0zM3.0,17.0l2.0,0.0L5.0,7.0L3.0,7.0L3.0,17.0zM22.0,9.0l0.0,6.0l2.0,0.0L24.0,9.0L22.0,9.0zM19.0,17.0l2.0,0.0L21.0,7.0l-2.0,0.0L19.0,17.0zM16.5,3.0l-9.0,0.0C6.7,3.0 6.0,3.7 6.0,4.5l0.0,15.0C6.0,20.3 6.7,21.0 7.5,21.0l9.0,0.0c0.8,0.0 1.5,-0.7 1.5,-1.5l0.0,-15.0C18.0,3.7 17.3,3.0 16.5,3.0zM16.0,19.0L8.0,19.0L8.0,5.0l8.0,0.0L16.0,19.0z"/>
-</vector>
diff --git a/packages/SystemUI/res/drawable/stat_sys_signal_carrier_network_change.xml b/packages/SystemUI/res/drawable/stat_sys_signal_carrier_network_change.xml
new file mode 100644
index 0000000..f69ffe4
--- /dev/null
+++ b/packages/SystemUI/res/drawable/stat_sys_signal_carrier_network_change.xml
@@ -0,0 +1,36 @@
+<!--
+    Copyright (C) 2015 The Android Open Source Project
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="17dp"
+        android:height="17dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:name="dot1"
+        android:fillColor="?attr/fillColor"
+        android:pathData="M9.0,19.0l3.0,0.0l0.0,3.0l-3.0,0.0z"/>
+    <path
+        android:name="dot2"
+        android:fillColor="?attr/backgroundColor"
+        android:pathData="M14.0,19.0l3.0,0.0l0.0,3.0l-3.0,0.0z"/>
+    <path
+        android:name="dot3"
+        android:fillColor="?attr/backgroundColor"
+        android:pathData="M19.0,19.0l3.0,0.0l0.0,3.0l-3.0,0.0z"/>
+    <path
+        android:fillColor="?attr/backgroundColor"
+        android:pathData="M2.0,22.0l6.0,0.0 0.0,-4.0 14.0,0.0 0.0,-16.0z"/>
+</vector>
diff --git a/packages/SystemUI/res/drawable/stat_sys_signal_carrier_network_change_animation.xml b/packages/SystemUI/res/drawable/stat_sys_signal_carrier_network_change_animation.xml
new file mode 100644
index 0000000..275f037
--- /dev/null
+++ b/packages/SystemUI/res/drawable/stat_sys_signal_carrier_network_change_animation.xml
@@ -0,0 +1,27 @@
+<!--
+    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.
+-->
+<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:drawable="@drawable/stat_sys_signal_carrier_network_change" >
+    <target
+        android:name="dot1"
+        android:animation="@anim/ic_signal_blink_1"/>
+    <target
+        android:name="dot2"
+        android:animation="@anim/ic_signal_blink_2"/>
+    <target
+        android:name="dot3"
+        android:animation="@anim/ic_signal_blink_3"/>
+</animated-vector>
diff --git a/packages/SystemUI/res/drawable/stat_sys_signal_dark_carrier_network_change_animation.xml b/packages/SystemUI/res/drawable/stat_sys_signal_dark_carrier_network_change_animation.xml
new file mode 100644
index 0000000..ff49d4c
--- /dev/null
+++ b/packages/SystemUI/res/drawable/stat_sys_signal_dark_carrier_network_change_animation.xml
@@ -0,0 +1,27 @@
+<!--
+    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.
+-->
+<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:drawable="@drawable/stat_sys_signal_carrier_network_change" >
+    <target
+        android:name="dot1"
+        android:animation="@anim/ic_signal_dark_blink_1"/>
+    <target
+        android:name="dot2"
+        android:animation="@anim/ic_signal_dark_blink_2"/>
+    <target
+        android:name="dot3"
+        android:animation="@anim/ic_signal_dark_blink_3"/>
+</animated-vector>
diff --git a/packages/SystemUI/res/layout/heads_up.xml b/packages/SystemUI/res/layout/heads_up.xml
deleted file mode 100644
index 650ee5d..0000000
--- a/packages/SystemUI/res/layout/heads_up.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.
--->
-<!-- extends FrameLayout -->
-<com.android.systemui.statusbar.policy.HeadsUpNotificationView
-        xmlns:android="http://schemas.android.com/apk/res/android"
-        android:layout_height="match_parent"
-        android:layout_width="match_parent"
-        android:background="@drawable/heads_up_scrim">
-
-        <FrameLayout
-                android:layout_width="@dimen/notification_panel_width"
-                android:layout_height="wrap_content"
-                android:layout_gravity="@integer/notification_panel_layout_gravity"
-                android:paddingStart="@dimen/notification_side_padding"
-                android:paddingEnd="@dimen/notification_side_padding"
-                android:elevation="8dp"
-                android:id="@+id/content_holder" />
-
-</com.android.systemui.statusbar.policy.HeadsUpNotificationView>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/super_status_bar.xml b/packages/SystemUI/res/layout/super_status_bar.xml
index 532e1b7..539aabf 100644
--- a/packages/SystemUI/res/layout/super_status_bar.xml
+++ b/packages/SystemUI/res/layout/super_status_bar.xml
@@ -46,6 +46,13 @@
         android:layout_height="match_parent"
         android:importantForAccessibility="no" />
 
+    <com.android.systemui.statusbar.AlphaOptimizedView
+        android:id="@+id/heads_up_scrim"
+        android:layout_width="match_parent"
+        android:layout_height="@dimen/heads_up_scrim_height"
+        android:background="@drawable/heads_up_scrim"
+        android:importantForAccessibility="no"/>
+
     <include layout="@layout/status_bar"
         android:layout_width="match_parent"
         android:layout_height="@dimen/status_bar_height" />
diff --git a/packages/SystemUI/res/layout/volume_panel.xml b/packages/SystemUI/res/layout/volume_panel.xml
deleted file mode 100644
index 4d8aaa7..0000000
--- a/packages/SystemUI/res/layout/volume_panel.xml
+++ /dev/null
@@ -1,38 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-     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.
--->
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
-    android:id="@+id/visible_panel"
-    android:layout_width="match_parent"
-    android:layout_height="wrap_content"
-    android:orientation="vertical" >
-
-    <FrameLayout
-        android:id="@+id/slider_panel"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:background="@color/system_secondary_color"
-        android:paddingTop="8dp"
-        android:paddingLeft="8dp"
-        android:paddingRight="8dp"
-        android:clipChildren="false" />
-
-     <include
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        layout="@layout/zen_mode_panel" />
-
-</LinearLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/volume_panel_dialog.xml b/packages/SystemUI/res/layout/volume_panel_dialog.xml
deleted file mode 100644
index 700102f..0000000
--- a/packages/SystemUI/res/layout/volume_panel_dialog.xml
+++ /dev/null
@@ -1,27 +0,0 @@
-<!--
-     Copyright (C) 2015 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
-    android:layout_width="match_parent"
-    android:layout_height="wrap_content"
-    android:layout_marginBottom="@dimen/volume_panel_z"
-    android:layout_marginLeft="@dimen/notification_side_padding"
-    android:layout_marginRight="@dimen/notification_side_padding"
-    android:background="@drawable/qs_background_primary"
-    android:translationZ="@dimen/volume_panel_z" >
-
-    <include layout="@layout/volume_panel" />
-
-</FrameLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/volume_panel_item.xml b/packages/SystemUI/res/layout/volume_panel_item.xml
deleted file mode 100644
index dad68c3..0000000
--- a/packages/SystemUI/res/layout/volume_panel_item.xml
+++ /dev/null
@@ -1,74 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-     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.
--->
-
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
-    android:layout_width="match_parent"
-    android:clipChildren="false"
-    android:gravity="start|center_vertical"
-    android:orientation="horizontal" >
-
-    <ImageView
-        android:id="@+id/stream_icon"
-        android:layout_width="48dp"
-        android:layout_height="48dp"
-        android:scaleType="center"
-        android:background="@drawable/btn_borderless_rect"
-        android:contentDescription="@null" />
-
-    <FrameLayout
-        android:layout_width="0dp"
-        android:layout_height="wrap_content"
-        android:layout_weight="1" >
-
-        <TextView
-            android:id="@+id/suppressor"
-            android:visibility="gone"
-            android:textAppearance="@style/TextAppearance.QS.VolumeSuppressor"
-            android:paddingStart="8dp"
-            android:paddingEnd="8dp"
-            android:singleLine="true"
-            android:ellipsize="end"
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content" />
-
-        <SeekBar
-            android:id="@+id/seekbar"
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            android:paddingBottom="0dp"
-            android:paddingEnd="16dp"
-            android:paddingStart="8dp"
-            android:paddingTop="0dp" />
-
-    </FrameLayout>
-
-    <View
-        android:id="@+id/divider"
-        android:layout_width="1dp"
-        android:layout_height="32dp"
-        android:layout_marginLeft="8dp"
-        android:layout_marginRight="8dp"
-        android:background="@color/volume_panel_divider" />
-
-    <ImageView
-        android:id="@+id/secondary_icon"
-        android:layout_width="48dp"
-        android:layout_height="48dp"
-        android:scaleType="center"
-        android:background="@drawable/btn_borderless_rect"
-        android:contentDescription="@null" />
-</LinearLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/zen_mode_panel.xml b/packages/SystemUI/res/layout/zen_mode_panel.xml
index d829f0e..b676bce 100644
--- a/packages/SystemUI/res/layout/zen_mode_panel.xml
+++ b/packages/SystemUI/res/layout/zen_mode_panel.xml
@@ -126,7 +126,7 @@
             android:lineSpacingMultiplier="1.20029"
             android:layout_toStartOf="@id/zen_introduction_confirm"
             android:text="@string/zen_priority_introduction"
-            android:textAppearance="@style/TextAppearance.QS.VolumeSuppressor" />
+            android:textAppearance="@style/TextAppearance.QS.Introduction" />
 
         <TextView
             android:id="@+id/zen_introduction_customize"
diff --git a/packages/SystemUI/res/values-af/strings.xml b/packages/SystemUI/res/values-af/strings.xml
index bba8a6d..1f13404 100644
--- a/packages/SystemUI/res/values-af/strings.xml
+++ b/packages/SystemUI/res/values-af/strings.xml
@@ -399,7 +399,6 @@
     <string name="volumeui_prompt_deny" msgid="5720663643411696731">"Weier"</string>
     <string name="volumeui_notification_title" msgid="4906770126345910955">"<xliff:g id="APP_NAME">%1$s</xliff:g> is die volumedialoog"</string>
     <string name="volumeui_notification_text" msgid="1826889705095768656">"Raak om die oorspronklike terug te stel."</string>
-    <string name="volume_zen_switch_text" msgid="8149183012610587643">"@*android:string/zen-kenmerk"</string>
     <!-- no translation found for managed_profile_foreground_toast (3199278359979281097) -->
     <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-am/strings.xml b/packages/SystemUI/res/values-am/strings.xml
index 06e9fb2..a631b90 100644
--- a/packages/SystemUI/res/values-am/strings.xml
+++ b/packages/SystemUI/res/values-am/strings.xml
@@ -399,7 +399,6 @@
     <string name="volumeui_prompt_deny" msgid="5720663643411696731">"ከልክል"</string>
     <string name="volumeui_notification_title" msgid="4906770126345910955">"<xliff:g id="APP_NAME">%1$s</xliff:g> የድምጽ መጠን መገናኛው ነው"</string>
     <string name="volumeui_notification_text" msgid="1826889705095768656">"የመጀመሪያውን ወደነበረበት ለመመለስ ይንኩ።"</string>
-    <string name="volume_zen_switch_text" msgid="8149183012610587643">"@*android:string/zen_mode_feature_name"</string>
     <!-- no translation found for managed_profile_foreground_toast (3199278359979281097) -->
     <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-ar/strings.xml b/packages/SystemUI/res/values-ar/strings.xml
index ac86dbd..dfaf408 100644
--- a/packages/SystemUI/res/values-ar/strings.xml
+++ b/packages/SystemUI/res/values-ar/strings.xml
@@ -403,7 +403,6 @@
     <string name="volumeui_prompt_deny" msgid="5720663643411696731">"رفض"</string>
     <string name="volumeui_notification_title" msgid="4906770126345910955">"<xliff:g id="APP_NAME">%1$s</xliff:g> هو مربع حوار مستوى الصوت"</string>
     <string name="volumeui_notification_text" msgid="1826889705095768656">"المس لاستعادة الإعداد الأصلي."</string>
-    <string name="volume_zen_switch_text" msgid="8149183012610587643">"@*android:string/zen_mode_feature_name"</string>
     <!-- no translation found for managed_profile_foreground_toast (3199278359979281097) -->
     <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-bg/strings.xml b/packages/SystemUI/res/values-bg/strings.xml
index 64e5afa..6bd9969 100644
--- a/packages/SystemUI/res/values-bg/strings.xml
+++ b/packages/SystemUI/res/values-bg/strings.xml
@@ -399,7 +399,6 @@
     <string name="volumeui_prompt_deny" msgid="5720663643411696731">"Отказване"</string>
     <string name="volumeui_notification_title" msgid="4906770126345910955">"<xliff:g id="APP_NAME">%1$s</xliff:g> изпълнява ролята на диалоговия прозорец за силата на звука"</string>
     <string name="volumeui_notification_text" msgid="1826889705095768656">"Докоснете, за да възстановите оригинала."</string>
-    <string name="volume_zen_switch_text" msgid="8149183012610587643">"@*android:string/zen_mode_feature_name"</string>
     <!-- no translation found for managed_profile_foreground_toast (3199278359979281097) -->
     <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-bn-rBD/strings.xml b/packages/SystemUI/res/values-bn-rBD/strings.xml
index dd83d59..8f057e4 100644
--- a/packages/SystemUI/res/values-bn-rBD/strings.xml
+++ b/packages/SystemUI/res/values-bn-rBD/strings.xml
@@ -399,7 +399,6 @@
     <string name="volumeui_prompt_deny" msgid="5720663643411696731">"প্রত্যাখ্যান করুন"</string>
     <string name="volumeui_notification_title" msgid="4906770126345910955">"<xliff:g id="APP_NAME">%1$s</xliff:g> হল ভলিউম ডায়লগ"</string>
     <string name="volumeui_notification_text" msgid="1826889705095768656">"আসলটি পুনঃস্থাপন করতে স্পর্শ করুন৷"</string>
-    <string name="volume_zen_switch_text" msgid="8149183012610587643">"@*android:string/zen_mode_feature_name"</string>
     <!-- no translation found for managed_profile_foreground_toast (3199278359979281097) -->
     <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-ca/strings.xml b/packages/SystemUI/res/values-ca/strings.xml
index eda024d..706086a 100644
--- a/packages/SystemUI/res/values-ca/strings.xml
+++ b/packages/SystemUI/res/values-ca/strings.xml
@@ -401,7 +401,6 @@
     <string name="volumeui_prompt_deny" msgid="5720663643411696731">"Denega"</string>
     <string name="volumeui_notification_title" msgid="4906770126345910955">"<xliff:g id="APP_NAME">%1$s</xliff:g> és el diàleg de volum"</string>
     <string name="volumeui_notification_text" msgid="1826889705095768656">"Toca per restaurar l\'original."</string>
-    <string name="volume_zen_switch_text" msgid="8149183012610587643">"@*android:string/zen_mode_feature_name"</string>
     <!-- no translation found for managed_profile_foreground_toast (3199278359979281097) -->
     <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-cs/strings.xml b/packages/SystemUI/res/values-cs/strings.xml
index 7abcc11..de7c62a 100644
--- a/packages/SystemUI/res/values-cs/strings.xml
+++ b/packages/SystemUI/res/values-cs/strings.xml
@@ -403,7 +403,6 @@
     <string name="volumeui_prompt_deny" msgid="5720663643411696731">"Odmítnout"</string>
     <string name="volumeui_notification_title" msgid="4906770126345910955">"<xliff:g id="APP_NAME">%1$s</xliff:g> je dialog hlasitosti"</string>
     <string name="volumeui_notification_text" msgid="1826889705095768656">"Klepnutím obnovíte originál."</string>
-    <string name="volume_zen_switch_text" msgid="8149183012610587643">"@*android:string/zen_mode_feature_name"</string>
     <!-- no translation found for managed_profile_foreground_toast (3199278359979281097) -->
     <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-da/strings.xml b/packages/SystemUI/res/values-da/strings.xml
index 3d0a0b0..99fc264 100644
--- a/packages/SystemUI/res/values-da/strings.xml
+++ b/packages/SystemUI/res/values-da/strings.xml
@@ -399,7 +399,6 @@
     <string name="volumeui_prompt_deny" msgid="5720663643411696731">"Afvis"</string>
     <string name="volumeui_notification_title" msgid="4906770126345910955">"<xliff:g id="APP_NAME">%1$s</xliff:g> er dialogboksen for lydstyrke"</string>
     <string name="volumeui_notification_text" msgid="1826889705095768656">"Tryk for at gendanne originalen."</string>
-    <string name="volume_zen_switch_text" msgid="8149183012610587643">"@*android:string/zen_mode_feature_name"</string>
     <!-- no translation found for managed_profile_foreground_toast (3199278359979281097) -->
     <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-de/strings.xml b/packages/SystemUI/res/values-de/strings.xml
index f77da12..eb0ea2c 100644
--- a/packages/SystemUI/res/values-de/strings.xml
+++ b/packages/SystemUI/res/values-de/strings.xml
@@ -401,7 +401,6 @@
     <string name="volumeui_prompt_deny" msgid="5720663643411696731">"Ablehnen"</string>
     <string name="volumeui_notification_title" msgid="4906770126345910955">"<xliff:g id="APP_NAME">%1$s</xliff:g> regelt die Lautstärke."</string>
     <string name="volumeui_notification_text" msgid="1826889705095768656">"Zum Wiederherstellen des Originals hier tippen"</string>
-    <string name="volume_zen_switch_text" msgid="8149183012610587643">"@*android:string/zen_mode_feature_name"</string>
     <!-- no translation found for managed_profile_foreground_toast (3199278359979281097) -->
     <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-el/strings.xml b/packages/SystemUI/res/values-el/strings.xml
index e1e8ba3..e4c85c1 100644
--- a/packages/SystemUI/res/values-el/strings.xml
+++ b/packages/SystemUI/res/values-el/strings.xml
@@ -401,7 +401,6 @@
     <string name="volumeui_prompt_deny" msgid="5720663643411696731">"Απόρριψη"</string>
     <string name="volumeui_notification_title" msgid="4906770126345910955">"Η εφαρμογή <xliff:g id="APP_NAME">%1$s</xliff:g> αποτελεί το παράθυρο διαλόγου ελέγχου έντασης"</string>
     <string name="volumeui_notification_text" msgid="1826889705095768656">"Αγγίξτε για επαναφορά αρχικού."</string>
-    <string name="volume_zen_switch_text" msgid="8149183012610587643">"@*android:string/zen_mode_feature_name"</string>
     <!-- no translation found for managed_profile_foreground_toast (3199278359979281097) -->
     <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-en-rAU/strings.xml b/packages/SystemUI/res/values-en-rAU/strings.xml
index 9e76616..aadd011 100644
--- a/packages/SystemUI/res/values-en-rAU/strings.xml
+++ b/packages/SystemUI/res/values-en-rAU/strings.xml
@@ -167,8 +167,7 @@
     <string name="accessibility_desc_lock_screen" msgid="5625143713611759164">"Lock screen."</string>
     <string name="accessibility_desc_settings" msgid="3417884241751434521">"Settings"</string>
     <string name="accessibility_desc_recent_apps" msgid="4876900986661819788">"Overview."</string>
-    <!-- no translation found for accessibility_desc_confirm (3446792278337969766) -->
-    <skip />
+    <string name="accessibility_desc_confirm" msgid="3446792278337969766">"Confirm"</string>
     <string name="accessibility_quick_settings_user" msgid="1104846699869476855">"User <xliff:g id="USER">%s</xliff:g>."</string>
     <string name="accessibility_quick_settings_wifi" msgid="5518210213118181692">"<xliff:g id="SIGNAL">%1$s</xliff:g>."</string>
     <string name="accessibility_quick_settings_wifi_changed_off" msgid="8716484460897819400">"Wi-Fi turned off."</string>
@@ -303,10 +302,8 @@
     <string name="description_direction_up" msgid="7169032478259485180">"Slide up for <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
     <string name="description_direction_left" msgid="7207478719805562165">"Slide left for <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
     <string name="zen_no_interruptions_with_warning" msgid="4396898053735625287">"No interruptions. Not even alarms."</string>
-    <!-- no translation found for zen_priority_introduction (7253045784560169993) -->
-    <skip />
-    <!-- no translation found for zen_priority_customize_button (7948043278226955063) -->
-    <skip />
+    <string name="zen_priority_introduction" msgid="7253045784560169993">"You won\'t be disturbed by sounds and vibrations, except from alarms, reminders, events and callers that you specify."</string>
+    <string name="zen_priority_customize_button" msgid="7948043278226955063">"Customise"</string>
     <string name="zen_no_interruptions" msgid="7970973750143632592">"No interruptions"</string>
     <string name="zen_important_interruptions" msgid="3477041776609757628">"Priority interruptions only"</string>
     <string name="zen_alarms" msgid="5055668280767657759">"Alarms only"</string>
@@ -342,12 +339,9 @@
     <string name="guest_wipe_session_message" msgid="8476238178270112811">"Do you want to continue your session?"</string>
     <string name="guest_wipe_session_wipe" msgid="5065558566939858884">"Start again"</string>
     <string name="guest_wipe_session_dontwipe" msgid="1401113462524894716">"Yes, continue"</string>
-    <!-- no translation found for guest_notification_title (1585278533840603063) -->
-    <skip />
-    <!-- no translation found for guest_notification_text (7513706222848825467) -->
-    <skip />
-    <!-- no translation found for guest_notification_remove_action (8820670703892101990) -->
-    <skip />
+    <string name="guest_notification_title" msgid="1585278533840603063">"Guest user"</string>
+    <string name="guest_notification_text" msgid="7513706222848825467">"Remove guest to delete apps and data"</string>
+    <string name="guest_notification_remove_action" msgid="8820670703892101990">"REMOVE GUEST"</string>
     <string name="user_add_user_title" msgid="4553596395824132638">"Add new user?"</string>
     <string name="user_add_user_message_short" msgid="2161624834066214559">"When you add a new user, that person needs to set up their space.\n\nAny user can update apps for all other users."</string>
     <string name="battery_saver_notification_title" msgid="237918726750955859">"Battery saver is on"</string>
@@ -399,7 +393,5 @@
     <string name="volumeui_prompt_deny" msgid="5720663643411696731">"Deny"</string>
     <string name="volumeui_notification_title" msgid="4906770126345910955">"<xliff:g id="APP_NAME">%1$s</xliff:g> is the volume dialogue"</string>
     <string name="volumeui_notification_text" msgid="1826889705095768656">"Touch to restore the original."</string>
-    <string name="volume_zen_switch_text" msgid="8149183012610587643">"@*android:string/zen_mode_feature_name"</string>
-    <!-- no translation found for managed_profile_foreground_toast (3199278359979281097) -->
-    <skip />
+    <string name="managed_profile_foreground_toast" msgid="3199278359979281097">"You are in the Work profile"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-en-rGB/strings.xml b/packages/SystemUI/res/values-en-rGB/strings.xml
index 9e76616..aadd011 100644
--- a/packages/SystemUI/res/values-en-rGB/strings.xml
+++ b/packages/SystemUI/res/values-en-rGB/strings.xml
@@ -167,8 +167,7 @@
     <string name="accessibility_desc_lock_screen" msgid="5625143713611759164">"Lock screen."</string>
     <string name="accessibility_desc_settings" msgid="3417884241751434521">"Settings"</string>
     <string name="accessibility_desc_recent_apps" msgid="4876900986661819788">"Overview."</string>
-    <!-- no translation found for accessibility_desc_confirm (3446792278337969766) -->
-    <skip />
+    <string name="accessibility_desc_confirm" msgid="3446792278337969766">"Confirm"</string>
     <string name="accessibility_quick_settings_user" msgid="1104846699869476855">"User <xliff:g id="USER">%s</xliff:g>."</string>
     <string name="accessibility_quick_settings_wifi" msgid="5518210213118181692">"<xliff:g id="SIGNAL">%1$s</xliff:g>."</string>
     <string name="accessibility_quick_settings_wifi_changed_off" msgid="8716484460897819400">"Wi-Fi turned off."</string>
@@ -303,10 +302,8 @@
     <string name="description_direction_up" msgid="7169032478259485180">"Slide up for <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
     <string name="description_direction_left" msgid="7207478719805562165">"Slide left for <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
     <string name="zen_no_interruptions_with_warning" msgid="4396898053735625287">"No interruptions. Not even alarms."</string>
-    <!-- no translation found for zen_priority_introduction (7253045784560169993) -->
-    <skip />
-    <!-- no translation found for zen_priority_customize_button (7948043278226955063) -->
-    <skip />
+    <string name="zen_priority_introduction" msgid="7253045784560169993">"You won\'t be disturbed by sounds and vibrations, except from alarms, reminders, events and callers that you specify."</string>
+    <string name="zen_priority_customize_button" msgid="7948043278226955063">"Customise"</string>
     <string name="zen_no_interruptions" msgid="7970973750143632592">"No interruptions"</string>
     <string name="zen_important_interruptions" msgid="3477041776609757628">"Priority interruptions only"</string>
     <string name="zen_alarms" msgid="5055668280767657759">"Alarms only"</string>
@@ -342,12 +339,9 @@
     <string name="guest_wipe_session_message" msgid="8476238178270112811">"Do you want to continue your session?"</string>
     <string name="guest_wipe_session_wipe" msgid="5065558566939858884">"Start again"</string>
     <string name="guest_wipe_session_dontwipe" msgid="1401113462524894716">"Yes, continue"</string>
-    <!-- no translation found for guest_notification_title (1585278533840603063) -->
-    <skip />
-    <!-- no translation found for guest_notification_text (7513706222848825467) -->
-    <skip />
-    <!-- no translation found for guest_notification_remove_action (8820670703892101990) -->
-    <skip />
+    <string name="guest_notification_title" msgid="1585278533840603063">"Guest user"</string>
+    <string name="guest_notification_text" msgid="7513706222848825467">"Remove guest to delete apps and data"</string>
+    <string name="guest_notification_remove_action" msgid="8820670703892101990">"REMOVE GUEST"</string>
     <string name="user_add_user_title" msgid="4553596395824132638">"Add new user?"</string>
     <string name="user_add_user_message_short" msgid="2161624834066214559">"When you add a new user, that person needs to set up their space.\n\nAny user can update apps for all other users."</string>
     <string name="battery_saver_notification_title" msgid="237918726750955859">"Battery saver is on"</string>
@@ -399,7 +393,5 @@
     <string name="volumeui_prompt_deny" msgid="5720663643411696731">"Deny"</string>
     <string name="volumeui_notification_title" msgid="4906770126345910955">"<xliff:g id="APP_NAME">%1$s</xliff:g> is the volume dialogue"</string>
     <string name="volumeui_notification_text" msgid="1826889705095768656">"Touch to restore the original."</string>
-    <string name="volume_zen_switch_text" msgid="8149183012610587643">"@*android:string/zen_mode_feature_name"</string>
-    <!-- no translation found for managed_profile_foreground_toast (3199278359979281097) -->
-    <skip />
+    <string name="managed_profile_foreground_toast" msgid="3199278359979281097">"You are in the Work profile"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-en-rIN/strings.xml b/packages/SystemUI/res/values-en-rIN/strings.xml
index 9e76616..aadd011 100644
--- a/packages/SystemUI/res/values-en-rIN/strings.xml
+++ b/packages/SystemUI/res/values-en-rIN/strings.xml
@@ -167,8 +167,7 @@
     <string name="accessibility_desc_lock_screen" msgid="5625143713611759164">"Lock screen."</string>
     <string name="accessibility_desc_settings" msgid="3417884241751434521">"Settings"</string>
     <string name="accessibility_desc_recent_apps" msgid="4876900986661819788">"Overview."</string>
-    <!-- no translation found for accessibility_desc_confirm (3446792278337969766) -->
-    <skip />
+    <string name="accessibility_desc_confirm" msgid="3446792278337969766">"Confirm"</string>
     <string name="accessibility_quick_settings_user" msgid="1104846699869476855">"User <xliff:g id="USER">%s</xliff:g>."</string>
     <string name="accessibility_quick_settings_wifi" msgid="5518210213118181692">"<xliff:g id="SIGNAL">%1$s</xliff:g>."</string>
     <string name="accessibility_quick_settings_wifi_changed_off" msgid="8716484460897819400">"Wi-Fi turned off."</string>
@@ -303,10 +302,8 @@
     <string name="description_direction_up" msgid="7169032478259485180">"Slide up for <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
     <string name="description_direction_left" msgid="7207478719805562165">"Slide left for <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
     <string name="zen_no_interruptions_with_warning" msgid="4396898053735625287">"No interruptions. Not even alarms."</string>
-    <!-- no translation found for zen_priority_introduction (7253045784560169993) -->
-    <skip />
-    <!-- no translation found for zen_priority_customize_button (7948043278226955063) -->
-    <skip />
+    <string name="zen_priority_introduction" msgid="7253045784560169993">"You won\'t be disturbed by sounds and vibrations, except from alarms, reminders, events and callers that you specify."</string>
+    <string name="zen_priority_customize_button" msgid="7948043278226955063">"Customise"</string>
     <string name="zen_no_interruptions" msgid="7970973750143632592">"No interruptions"</string>
     <string name="zen_important_interruptions" msgid="3477041776609757628">"Priority interruptions only"</string>
     <string name="zen_alarms" msgid="5055668280767657759">"Alarms only"</string>
@@ -342,12 +339,9 @@
     <string name="guest_wipe_session_message" msgid="8476238178270112811">"Do you want to continue your session?"</string>
     <string name="guest_wipe_session_wipe" msgid="5065558566939858884">"Start again"</string>
     <string name="guest_wipe_session_dontwipe" msgid="1401113462524894716">"Yes, continue"</string>
-    <!-- no translation found for guest_notification_title (1585278533840603063) -->
-    <skip />
-    <!-- no translation found for guest_notification_text (7513706222848825467) -->
-    <skip />
-    <!-- no translation found for guest_notification_remove_action (8820670703892101990) -->
-    <skip />
+    <string name="guest_notification_title" msgid="1585278533840603063">"Guest user"</string>
+    <string name="guest_notification_text" msgid="7513706222848825467">"Remove guest to delete apps and data"</string>
+    <string name="guest_notification_remove_action" msgid="8820670703892101990">"REMOVE GUEST"</string>
     <string name="user_add_user_title" msgid="4553596395824132638">"Add new user?"</string>
     <string name="user_add_user_message_short" msgid="2161624834066214559">"When you add a new user, that person needs to set up their space.\n\nAny user can update apps for all other users."</string>
     <string name="battery_saver_notification_title" msgid="237918726750955859">"Battery saver is on"</string>
@@ -399,7 +393,5 @@
     <string name="volumeui_prompt_deny" msgid="5720663643411696731">"Deny"</string>
     <string name="volumeui_notification_title" msgid="4906770126345910955">"<xliff:g id="APP_NAME">%1$s</xliff:g> is the volume dialogue"</string>
     <string name="volumeui_notification_text" msgid="1826889705095768656">"Touch to restore the original."</string>
-    <string name="volume_zen_switch_text" msgid="8149183012610587643">"@*android:string/zen_mode_feature_name"</string>
-    <!-- no translation found for managed_profile_foreground_toast (3199278359979281097) -->
-    <skip />
+    <string name="managed_profile_foreground_toast" msgid="3199278359979281097">"You are in the Work profile"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-es-rUS/strings.xml b/packages/SystemUI/res/values-es-rUS/strings.xml
index 40400f3..e754925 100644
--- a/packages/SystemUI/res/values-es-rUS/strings.xml
+++ b/packages/SystemUI/res/values-es-rUS/strings.xml
@@ -401,7 +401,6 @@
     <string name="volumeui_prompt_deny" msgid="5720663643411696731">"Rechazar"</string>
     <string name="volumeui_notification_title" msgid="4906770126345910955">"<xliff:g id="APP_NAME">%1$s</xliff:g> es el cuadro de diálogo de volumen."</string>
     <string name="volumeui_notification_text" msgid="1826889705095768656">"Toca para restaurar el original."</string>
-    <string name="volume_zen_switch_text" msgid="8149183012610587643">"@*android:string/zen_mode_feature_name"</string>
     <!-- no translation found for managed_profile_foreground_toast (3199278359979281097) -->
     <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-es/strings.xml b/packages/SystemUI/res/values-es/strings.xml
index cb061a5..c131176 100644
--- a/packages/SystemUI/res/values-es/strings.xml
+++ b/packages/SystemUI/res/values-es/strings.xml
@@ -399,7 +399,6 @@
     <string name="volumeui_prompt_deny" msgid="5720663643411696731">"Rechazar"</string>
     <string name="volumeui_notification_title" msgid="4906770126345910955">"<xliff:g id="APP_NAME">%1$s</xliff:g> es el cuadro de diálogo de volumen"</string>
     <string name="volumeui_notification_text" msgid="1826889705095768656">"Toca para restaurar la versión original."</string>
-    <string name="volume_zen_switch_text" msgid="8149183012610587643">"@*android:string/zen_mode_feature_name"</string>
     <!-- no translation found for managed_profile_foreground_toast (3199278359979281097) -->
     <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-et-rEE/strings.xml b/packages/SystemUI/res/values-et-rEE/strings.xml
index 7fffec7..387fe82 100644
--- a/packages/SystemUI/res/values-et-rEE/strings.xml
+++ b/packages/SystemUI/res/values-et-rEE/strings.xml
@@ -399,7 +399,6 @@
     <string name="volumeui_prompt_deny" msgid="5720663643411696731">"Keela"</string>
     <string name="volumeui_notification_title" msgid="4906770126345910955">"<xliff:g id="APP_NAME">%1$s</xliff:g> on helitugevuse dialoog"</string>
     <string name="volumeui_notification_text" msgid="1826889705095768656">"Originaali taastamiseks puudutage."</string>
-    <string name="volume_zen_switch_text" msgid="8149183012610587643">"@*android:string/zen_mode_feature_name"</string>
     <!-- no translation found for managed_profile_foreground_toast (3199278359979281097) -->
     <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-eu-rES/strings.xml b/packages/SystemUI/res/values-eu-rES/strings.xml
index 94dd6b1..d8c1789 100644
--- a/packages/SystemUI/res/values-eu-rES/strings.xml
+++ b/packages/SystemUI/res/values-eu-rES/strings.xml
@@ -399,7 +399,6 @@
     <string name="volumeui_prompt_deny" msgid="5720663643411696731">"Ukatu"</string>
     <string name="volumeui_notification_title" msgid="4906770126345910955">"<xliff:g id="APP_NAME">%1$s</xliff:g> da bolumenaren leihoa"</string>
     <string name="volumeui_notification_text" msgid="1826889705095768656">"Ukitu jatorrizkora leheneratzeko"</string>
-    <string name="volume_zen_switch_text" msgid="8149183012610587643">"@*android:string/zen_mode_feature_name"</string>
     <!-- no translation found for managed_profile_foreground_toast (3199278359979281097) -->
     <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-fa/strings.xml b/packages/SystemUI/res/values-fa/strings.xml
index 0260d3a..7594ad9 100644
--- a/packages/SystemUI/res/values-fa/strings.xml
+++ b/packages/SystemUI/res/values-fa/strings.xml
@@ -399,7 +399,6 @@
     <string name="volumeui_prompt_deny" msgid="5720663643411696731">"رد کردن"</string>
     <string name="volumeui_notification_title" msgid="4906770126345910955">"<xliff:g id="APP_NAME">%1$s</xliff:g> کنترل‌کننده صدا است"</string>
     <string name="volumeui_notification_text" msgid="1826889705095768656">"برای بازیابی کنترل‌کننده اصلی، لمس کنید."</string>
-    <string name="volume_zen_switch_text" msgid="8149183012610587643">"‎@*android:string/zen_mode_feature_name"</string>
     <!-- no translation found for managed_profile_foreground_toast (3199278359979281097) -->
     <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-fi/strings.xml b/packages/SystemUI/res/values-fi/strings.xml
index 156bbb7..a131701 100644
--- a/packages/SystemUI/res/values-fi/strings.xml
+++ b/packages/SystemUI/res/values-fi/strings.xml
@@ -399,7 +399,6 @@
     <string name="volumeui_prompt_deny" msgid="5720663643411696731">"Estä"</string>
     <string name="volumeui_notification_title" msgid="4906770126345910955">"<xliff:g id="APP_NAME">%1$s</xliff:g> on äänenvoimakkuusvalinta."</string>
     <string name="volumeui_notification_text" msgid="1826889705095768656">"Palauta alkuperäinen koskettamalla."</string>
-    <string name="volume_zen_switch_text" msgid="8149183012610587643">"@*android:string/zen_mode_feature_name"</string>
     <!-- no translation found for managed_profile_foreground_toast (3199278359979281097) -->
     <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-fr-rCA/strings.xml b/packages/SystemUI/res/values-fr-rCA/strings.xml
index de5b7aa..9d10e48 100644
--- a/packages/SystemUI/res/values-fr-rCA/strings.xml
+++ b/packages/SystemUI/res/values-fr-rCA/strings.xml
@@ -401,7 +401,6 @@
     <string name="volumeui_prompt_deny" msgid="5720663643411696731">"Refuser"</string>
     <string name="volumeui_notification_title" msgid="4906770126345910955">"<xliff:g id="APP_NAME">%1$s</xliff:g> correspond à la boîte de dialogue du volume"</string>
     <string name="volumeui_notification_text" msgid="1826889705095768656">"Touchez pour restaurer l\'original."</string>
-    <string name="volume_zen_switch_text" msgid="8149183012610587643">"@*android:string/zen_mode_feature_name"</string>
     <!-- no translation found for managed_profile_foreground_toast (3199278359979281097) -->
     <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-fr/strings.xml b/packages/SystemUI/res/values-fr/strings.xml
index ed17e4af..2788a87 100644
--- a/packages/SystemUI/res/values-fr/strings.xml
+++ b/packages/SystemUI/res/values-fr/strings.xml
@@ -401,7 +401,6 @@
     <string name="volumeui_prompt_deny" msgid="5720663643411696731">"Refuser"</string>
     <string name="volumeui_notification_title" msgid="4906770126345910955">"<xliff:g id="APP_NAME">%1$s</xliff:g> correspond à la boîte de dialogue du volume"</string>
     <string name="volumeui_notification_text" msgid="1826889705095768656">"Appuyez pour restaurer l\'interface d\'origine."</string>
-    <string name="volume_zen_switch_text" msgid="8149183012610587643">"@*android:string/zen_mode_feature_name"</string>
     <!-- no translation found for managed_profile_foreground_toast (3199278359979281097) -->
     <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-gl-rES/strings.xml b/packages/SystemUI/res/values-gl-rES/strings.xml
index 89d5fc3..a4786df 100644
--- a/packages/SystemUI/res/values-gl-rES/strings.xml
+++ b/packages/SystemUI/res/values-gl-rES/strings.xml
@@ -401,7 +401,6 @@
     <string name="volumeui_prompt_deny" msgid="5720663643411696731">"Denegar"</string>
     <string name="volumeui_notification_title" msgid="4906770126345910955">"<xliff:g id="APP_NAME">%1$s</xliff:g> é o cadro de diálogo de volume"</string>
     <string name="volumeui_notification_text" msgid="1826889705095768656">"Toca para restaurar o orixinal."</string>
-    <string name="volume_zen_switch_text" msgid="8149183012610587643">"@*android:string/zen_mode_feature_name"</string>
     <!-- no translation found for managed_profile_foreground_toast (3199278359979281097) -->
     <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-hi/strings.xml b/packages/SystemUI/res/values-hi/strings.xml
index 08fbe69..7d6f51b 100644
--- a/packages/SystemUI/res/values-hi/strings.xml
+++ b/packages/SystemUI/res/values-hi/strings.xml
@@ -399,7 +399,6 @@
     <string name="volumeui_prompt_deny" msgid="5720663643411696731">"अस्वीकार करें"</string>
     <string name="volumeui_notification_title" msgid="4906770126345910955">"<xliff:g id="APP_NAME">%1$s</xliff:g> वॉल्यूम संवाद है"</string>
     <string name="volumeui_notification_text" msgid="1826889705095768656">"मूल वॉल्यूम को फिर से लाने के लिए स्पर्श करें."</string>
-    <string name="volume_zen_switch_text" msgid="8149183012610587643">"@*android:string/zen_mode_feature_name"</string>
     <!-- no translation found for managed_profile_foreground_toast (3199278359979281097) -->
     <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-hr/strings.xml b/packages/SystemUI/res/values-hr/strings.xml
index 85bb634..7ea2336 100644
--- a/packages/SystemUI/res/values-hr/strings.xml
+++ b/packages/SystemUI/res/values-hr/strings.xml
@@ -400,7 +400,6 @@
     <string name="volumeui_prompt_deny" msgid="5720663643411696731">"Odbij"</string>
     <string name="volumeui_notification_title" msgid="4906770126345910955">"<xliff:g id="APP_NAME">%1$s</xliff:g> predstavlja dijaloški okvir za upravljanje glasnoćom"</string>
     <string name="volumeui_notification_text" msgid="1826889705095768656">"Dodirnite da biste vratili izvorno."</string>
-    <string name="volume_zen_switch_text" msgid="8149183012610587643">"@*android:string/zen_mode_feature_name"</string>
     <!-- no translation found for managed_profile_foreground_toast (3199278359979281097) -->
     <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-hu/strings.xml b/packages/SystemUI/res/values-hu/strings.xml
index 0773849..f674327 100644
--- a/packages/SystemUI/res/values-hu/strings.xml
+++ b/packages/SystemUI/res/values-hu/strings.xml
@@ -399,7 +399,6 @@
     <string name="volumeui_prompt_deny" msgid="5720663643411696731">"Elutasítás"</string>
     <string name="volumeui_notification_title" msgid="4906770126345910955">"A(z) <xliff:g id="APP_NAME">%1$s</xliff:g> alkalmazás kezeli a hangerőt"</string>
     <string name="volumeui_notification_text" msgid="1826889705095768656">"Érintse meg az eredeti érték visszaállításához."</string>
-    <string name="volume_zen_switch_text" msgid="8149183012610587643">"@*android:string/zen_mode_feature_name"</string>
     <!-- no translation found for managed_profile_foreground_toast (3199278359979281097) -->
     <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-hy-rAM/strings.xml b/packages/SystemUI/res/values-hy-rAM/strings.xml
index 3ffc030..c3d0f6d 100644
--- a/packages/SystemUI/res/values-hy-rAM/strings.xml
+++ b/packages/SystemUI/res/values-hy-rAM/strings.xml
@@ -399,7 +399,6 @@
     <string name="volumeui_prompt_deny" msgid="5720663643411696731">"Մերժել"</string>
     <string name="volumeui_notification_title" msgid="4906770126345910955">"<xliff:g id="APP_NAME">%1$s</xliff:g>-ը ձայնի ուժգնության երկխոսության հավելված է"</string>
     <string name="volumeui_notification_text" msgid="1826889705095768656">"Դիպչեք՝ սկզբնօրինակը վերականգնելու համար:"</string>
-    <string name="volume_zen_switch_text" msgid="8149183012610587643">"@*android:string/zen_mode_feature_name"</string>
     <!-- no translation found for managed_profile_foreground_toast (3199278359979281097) -->
     <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-in/strings.xml b/packages/SystemUI/res/values-in/strings.xml
index edee7c6..f53cecc 100644
--- a/packages/SystemUI/res/values-in/strings.xml
+++ b/packages/SystemUI/res/values-in/strings.xml
@@ -399,7 +399,6 @@
     <string name="volumeui_prompt_deny" msgid="5720663643411696731">"Tolak"</string>
     <string name="volumeui_notification_title" msgid="4906770126345910955">"<xliff:g id="APP_NAME">%1$s</xliff:g> adalah dialog volume"</string>
     <string name="volumeui_notification_text" msgid="1826889705095768656">"Sentuh untuk memulihkan aslinya."</string>
-    <string name="volume_zen_switch_text" msgid="8149183012610587643">"@*android:string/zen_mode_feature_name"</string>
     <!-- no translation found for managed_profile_foreground_toast (3199278359979281097) -->
     <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-is-rIS/strings.xml b/packages/SystemUI/res/values-is-rIS/strings.xml
index 660834e..8000344d 100644
--- a/packages/SystemUI/res/values-is-rIS/strings.xml
+++ b/packages/SystemUI/res/values-is-rIS/strings.xml
@@ -399,7 +399,6 @@
     <string name="volumeui_prompt_deny" msgid="5720663643411696731">"Hafna"</string>
     <string name="volumeui_notification_title" msgid="4906770126345910955">"<xliff:g id="APP_NAME">%1$s</xliff:g> er hljóðstyrksvalmyndin"</string>
     <string name="volumeui_notification_text" msgid="1826889705095768656">"Snertu til að færa í upprunalegt horf."</string>
-    <string name="volume_zen_switch_text" msgid="8149183012610587643">"@*android:string/zen_mode_feature_name"</string>
     <!-- no translation found for managed_profile_foreground_toast (3199278359979281097) -->
     <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-it/strings.xml b/packages/SystemUI/res/values-it/strings.xml
index a7ac816..a0ceb62 100644
--- a/packages/SystemUI/res/values-it/strings.xml
+++ b/packages/SystemUI/res/values-it/strings.xml
@@ -401,7 +401,6 @@
     <string name="volumeui_prompt_deny" msgid="5720663643411696731">"Nega"</string>
     <string name="volumeui_notification_title" msgid="4906770126345910955">"<xliff:g id="APP_NAME">%1$s</xliff:g> rappresenta la finestra di dialogo relativa al volume"</string>
     <string name="volumeui_notification_text" msgid="1826889705095768656">"Tocca per ripristinare l\'originale."</string>
-    <string name="volume_zen_switch_text" msgid="8149183012610587643">"@*android:string/zen_mode_feature_name"</string>
     <!-- no translation found for managed_profile_foreground_toast (3199278359979281097) -->
     <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-iw/strings.xml b/packages/SystemUI/res/values-iw/strings.xml
index 837bba7..b02ff0a 100644
--- a/packages/SystemUI/res/values-iw/strings.xml
+++ b/packages/SystemUI/res/values-iw/strings.xml
@@ -401,7 +401,6 @@
     <string name="volumeui_prompt_deny" msgid="5720663643411696731">"דחה"</string>
     <string name="volumeui_notification_title" msgid="4906770126345910955">"<xliff:g id="APP_NAME">%1$s</xliff:g> הוא תיבת הדו-שיח של עוצמת הקול"</string>
     <string name="volumeui_notification_text" msgid="1826889705095768656">"גע כדי לשחזר את עוצמת הקול המקורית."</string>
-    <string name="volume_zen_switch_text" msgid="8149183012610587643">"@*android:string/zen_mode_feature_name"</string>
     <!-- no translation found for managed_profile_foreground_toast (3199278359979281097) -->
     <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-ja/strings.xml b/packages/SystemUI/res/values-ja/strings.xml
index e01fe53..f8f767b 100644
--- a/packages/SystemUI/res/values-ja/strings.xml
+++ b/packages/SystemUI/res/values-ja/strings.xml
@@ -401,7 +401,6 @@
     <string name="volumeui_prompt_deny" msgid="5720663643411696731">"許可しない"</string>
     <string name="volumeui_notification_title" msgid="4906770126345910955">"<xliff:g id="APP_NAME">%1$s</xliff:g>を音量ダイアログとして使用"</string>
     <string name="volumeui_notification_text" msgid="1826889705095768656">"タップすると元の音量ダイアログが復元されます。"</string>
-    <string name="volume_zen_switch_text" msgid="8149183012610587643">"@*android:string/zen_mode_feature_name"</string>
     <!-- no translation found for managed_profile_foreground_toast (3199278359979281097) -->
     <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-ka-rGE/strings.xml b/packages/SystemUI/res/values-ka-rGE/strings.xml
index a41c9f5..4377663 100644
--- a/packages/SystemUI/res/values-ka-rGE/strings.xml
+++ b/packages/SystemUI/res/values-ka-rGE/strings.xml
@@ -399,7 +399,6 @@
     <string name="volumeui_prompt_deny" msgid="5720663643411696731">"უარყოფა"</string>
     <string name="volumeui_notification_title" msgid="4906770126345910955">"<xliff:g id="APP_NAME">%1$s</xliff:g> ხმოვან დიალოგშია"</string>
     <string name="volumeui_notification_text" msgid="1826889705095768656">"ორიგინალის აღდგენისათვის, შეეხეთ."</string>
-    <string name="volume_zen_switch_text" msgid="8149183012610587643">"@*android:string/zen_mode_feature_name"</string>
     <!-- no translation found for managed_profile_foreground_toast (3199278359979281097) -->
     <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-kk-rKZ/strings.xml b/packages/SystemUI/res/values-kk-rKZ/strings.xml
index 0beea99..c4141e1 100644
--- a/packages/SystemUI/res/values-kk-rKZ/strings.xml
+++ b/packages/SystemUI/res/values-kk-rKZ/strings.xml
@@ -399,7 +399,6 @@
     <string name="volumeui_prompt_deny" msgid="5720663643411696731">"Өшіру"</string>
     <string name="volumeui_notification_title" msgid="4906770126345910955">"<xliff:g id="APP_NAME">%1$s</xliff:g> — көлем диалогтық терезесі"</string>
     <string name="volumeui_notification_text" msgid="1826889705095768656">"Түпнұсқаны қалпына келтіру үшін түртіңіз."</string>
-    <string name="volume_zen_switch_text" msgid="8149183012610587643">"@*android:string/zen_mode_feature_name"</string>
     <!-- no translation found for managed_profile_foreground_toast (3199278359979281097) -->
     <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-km-rKH/strings.xml b/packages/SystemUI/res/values-km-rKH/strings.xml
index ba84a54..8087373 100644
--- a/packages/SystemUI/res/values-km-rKH/strings.xml
+++ b/packages/SystemUI/res/values-km-rKH/strings.xml
@@ -399,7 +399,6 @@
     <string name="volumeui_prompt_deny" msgid="5720663643411696731">"បដិសេធ"</string>
     <string name="volumeui_notification_title" msgid="4906770126345910955">"<xliff:g id="APP_NAME">%1$s</xliff:g> គឺជាប្រអប់សម្លេង"</string>
     <string name="volumeui_notification_text" msgid="1826889705095768656">"ប៉ះដើម្បីស្តារច្បាប់ដើម។"</string>
-    <string name="volume_zen_switch_text" msgid="8149183012610587643">"@*android:string/zen_mode_feature_name"</string>
     <!-- no translation found for managed_profile_foreground_toast (3199278359979281097) -->
     <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-kn-rIN/strings.xml b/packages/SystemUI/res/values-kn-rIN/strings.xml
index 76b5707..fe138ce 100644
--- a/packages/SystemUI/res/values-kn-rIN/strings.xml
+++ b/packages/SystemUI/res/values-kn-rIN/strings.xml
@@ -399,7 +399,6 @@
     <string name="volumeui_prompt_deny" msgid="5720663643411696731">"ನಿರಾಕರಿಸು"</string>
     <string name="volumeui_notification_title" msgid="4906770126345910955">"<xliff:g id="APP_NAME">%1$s</xliff:g> ವಾಲ್ಯೂಮ್ ಸಂವಾದವಾಗಿದೆ"</string>
     <string name="volumeui_notification_text" msgid="1826889705095768656">"ಮೂಲ ಮರುಸ್ಥಾಪಿಸಲು ಸ್ಪರ್ಶಿಸಿ."</string>
-    <string name="volume_zen_switch_text" msgid="8149183012610587643">"@*android:string/zen_mode_feature_name"</string>
     <!-- no translation found for managed_profile_foreground_toast (3199278359979281097) -->
     <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-ko/strings.xml b/packages/SystemUI/res/values-ko/strings.xml
index 32f751f..c525095 100644
--- a/packages/SystemUI/res/values-ko/strings.xml
+++ b/packages/SystemUI/res/values-ko/strings.xml
@@ -399,7 +399,6 @@
     <string name="volumeui_prompt_deny" msgid="5720663643411696731">"거부"</string>
     <string name="volumeui_notification_title" msgid="4906770126345910955">"<xliff:g id="APP_NAME">%1$s</xliff:g>은(는) 볼륨 대화입니다."</string>
     <string name="volumeui_notification_text" msgid="1826889705095768656">"원본을 복원하려면 터치하세요."</string>
-    <string name="volume_zen_switch_text" msgid="8149183012610587643">"@*android:string/zen_mode_feature_name"</string>
     <!-- no translation found for managed_profile_foreground_toast (3199278359979281097) -->
     <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-ky-rKG/strings.xml b/packages/SystemUI/res/values-ky-rKG/strings.xml
index 8e2defd..eca5b2c 100644
--- a/packages/SystemUI/res/values-ky-rKG/strings.xml
+++ b/packages/SystemUI/res/values-ky-rKG/strings.xml
@@ -424,7 +424,6 @@
     <string name="volumeui_prompt_deny" msgid="5720663643411696731">"Жок"</string>
     <string name="volumeui_notification_title" msgid="4906770126345910955">"<xliff:g id="APP_NAME">%1$s</xliff:g> үндү катуулатуу диалогу"</string>
     <string name="volumeui_notification_text" msgid="1826889705095768656">"Түпнусканы калыбына келтирүү үчүн тийип коюңуз."</string>
-    <string name="volume_zen_switch_text" msgid="8149183012610587643">"@*android:string/zen_mode_feature_name"</string>
     <!-- no translation found for managed_profile_foreground_toast (3199278359979281097) -->
     <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-lo-rLA/strings.xml b/packages/SystemUI/res/values-lo-rLA/strings.xml
index d7bb83b..8ce9eda 100644
--- a/packages/SystemUI/res/values-lo-rLA/strings.xml
+++ b/packages/SystemUI/res/values-lo-rLA/strings.xml
@@ -399,7 +399,6 @@
     <string name="volumeui_prompt_deny" msgid="5720663643411696731">"ປະຕິເສດ"</string>
     <string name="volumeui_notification_title" msgid="4906770126345910955">"<xliff:g id="APP_NAME">%1$s</xliff:g> ແມ່ນ​ໜ້າ​ຕ່າງ​ລະ​ດັບ​ສຽງ"</string>
     <string name="volumeui_notification_text" msgid="1826889705095768656">"ສໍາ​ຜັດ​ເພື່ອກູ້​ຄືນ​ຕົ້ນ​ສະ​ບັບ​."</string>
-    <string name="volume_zen_switch_text" msgid="8149183012610587643">"@*android:string/zen_mode_feature_name"</string>
     <!-- no translation found for managed_profile_foreground_toast (3199278359979281097) -->
     <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-lt/strings.xml b/packages/SystemUI/res/values-lt/strings.xml
index 2ac4636..7e211cd 100644
--- a/packages/SystemUI/res/values-lt/strings.xml
+++ b/packages/SystemUI/res/values-lt/strings.xml
@@ -401,7 +401,6 @@
     <string name="volumeui_prompt_deny" msgid="5720663643411696731">"Atmesti"</string>
     <string name="volumeui_notification_title" msgid="4906770126345910955">"„<xliff:g id="APP_NAME">%1$s</xliff:g>“ yra garsumo valdymo dialogo langas"</string>
     <string name="volumeui_notification_text" msgid="1826889705095768656">"Palieskite, kad atkurtumėte originalą."</string>
-    <string name="volume_zen_switch_text" msgid="8149183012610587643">"@*android:string/zen_mode_feature_name"</string>
     <!-- no translation found for managed_profile_foreground_toast (3199278359979281097) -->
     <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-lv/strings.xml b/packages/SystemUI/res/values-lv/strings.xml
index a184664..c692791 100644
--- a/packages/SystemUI/res/values-lv/strings.xml
+++ b/packages/SystemUI/res/values-lv/strings.xml
@@ -400,7 +400,6 @@
     <string name="volumeui_prompt_deny" msgid="5720663643411696731">"Neatļaut"</string>
     <string name="volumeui_notification_title" msgid="4906770126345910955">"<xliff:g id="APP_NAME">%1$s</xliff:g> ir skaļuma dialoglodziņš"</string>
     <string name="volumeui_notification_text" msgid="1826889705095768656">"Pieskarieties, lai atjaunotu sākotnējo."</string>
-    <string name="volume_zen_switch_text" msgid="8149183012610587643">"@*android:string/zen_mode_feature_name"</string>
     <!-- no translation found for managed_profile_foreground_toast (3199278359979281097) -->
     <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-mk-rMK/strings.xml b/packages/SystemUI/res/values-mk-rMK/strings.xml
index e288451..b13a58a 100644
--- a/packages/SystemUI/res/values-mk-rMK/strings.xml
+++ b/packages/SystemUI/res/values-mk-rMK/strings.xml
@@ -401,7 +401,6 @@
     <string name="volumeui_prompt_deny" msgid="5720663643411696731">"Одбиј"</string>
     <string name="volumeui_notification_title" msgid="4906770126345910955">"<xliff:g id="APP_NAME">%1$s</xliff:g> е дијалог за јачина на звук"</string>
     <string name="volumeui_notification_text" msgid="1826889705095768656">"Допрете за да го вратите оригиналот."</string>
-    <string name="volume_zen_switch_text" msgid="8149183012610587643">"@*android:string/zen_mode_feature_name"</string>
     <!-- no translation found for managed_profile_foreground_toast (3199278359979281097) -->
     <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-ml-rIN/strings.xml b/packages/SystemUI/res/values-ml-rIN/strings.xml
index 3bc27a1..e0d3eaf 100644
--- a/packages/SystemUI/res/values-ml-rIN/strings.xml
+++ b/packages/SystemUI/res/values-ml-rIN/strings.xml
@@ -399,7 +399,6 @@
     <string name="volumeui_prompt_deny" msgid="5720663643411696731">"നിരസിക്കുക"</string>
     <string name="volumeui_notification_title" msgid="4906770126345910955">"<xliff:g id="APP_NAME">%1$s</xliff:g>, വോളിയം ഡയലോഗാണ്"</string>
     <string name="volumeui_notification_text" msgid="1826889705095768656">"ആദ്യത്തേത് പുനഃസ്ഥാപിക്കാൻ സ്‌പർശിക്കുക."</string>
-    <string name="volume_zen_switch_text" msgid="8149183012610587643">"@*android:string/zen_mode_feature_name"</string>
     <!-- no translation found for managed_profile_foreground_toast (3199278359979281097) -->
     <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-mn-rMN/strings.xml b/packages/SystemUI/res/values-mn-rMN/strings.xml
index 367de5d..b68af61 100644
--- a/packages/SystemUI/res/values-mn-rMN/strings.xml
+++ b/packages/SystemUI/res/values-mn-rMN/strings.xml
@@ -397,7 +397,6 @@
     <string name="volumeui_prompt_deny" msgid="5720663643411696731">"Татгалзах"</string>
     <string name="volumeui_notification_title" msgid="4906770126345910955">"<xliff:g id="APP_NAME">%1$s</xliff:g> нь дууны диалог юм."</string>
     <string name="volumeui_notification_text" msgid="1826889705095768656">"Анхны хувилбарыг эргүүлэн хадгалахыг хүсвэл хүрнэ үү."</string>
-    <string name="volume_zen_switch_text" msgid="8149183012610587643">"@*android:string/zen_mode_feature_name"</string>
     <!-- no translation found for managed_profile_foreground_toast (3199278359979281097) -->
     <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-mr-rIN/strings.xml b/packages/SystemUI/res/values-mr-rIN/strings.xml
index eb8c6ad..c9dd37e 100644
--- a/packages/SystemUI/res/values-mr-rIN/strings.xml
+++ b/packages/SystemUI/res/values-mr-rIN/strings.xml
@@ -399,7 +399,6 @@
     <string name="volumeui_prompt_deny" msgid="5720663643411696731">"नकार द्या"</string>
     <string name="volumeui_notification_title" msgid="4906770126345910955">"<xliff:g id="APP_NAME">%1$s</xliff:g> हा व्हॉल्यूम संवाद आहे"</string>
     <string name="volumeui_notification_text" msgid="1826889705095768656">"मूळ पुनर्संचयित करण्यासाठी स्पर्श करा."</string>
-    <string name="volume_zen_switch_text" msgid="8149183012610587643">"@*android:string/zen_mode_feature_name"</string>
     <!-- no translation found for managed_profile_foreground_toast (3199278359979281097) -->
     <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-ms-rMY/strings.xml b/packages/SystemUI/res/values-ms-rMY/strings.xml
index 1b1fded..f5624a0 100644
--- a/packages/SystemUI/res/values-ms-rMY/strings.xml
+++ b/packages/SystemUI/res/values-ms-rMY/strings.xml
@@ -399,7 +399,6 @@
     <string name="volumeui_prompt_deny" msgid="5720663643411696731">"Tolak"</string>
     <string name="volumeui_notification_title" msgid="4906770126345910955">"<xliff:g id="APP_NAME">%1$s</xliff:g> ialah dialog kelantangan"</string>
     <string name="volumeui_notification_text" msgid="1826889705095768656">"Sentuh untuk memulihkan yang asal."</string>
-    <string name="volume_zen_switch_text" msgid="8149183012610587643">"@*android:rentetan/nama_ciri_mod_zen"</string>
     <!-- no translation found for managed_profile_foreground_toast (3199278359979281097) -->
     <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-my-rMM/strings.xml b/packages/SystemUI/res/values-my-rMM/strings.xml
index 52c4298..f0aa3b1 100644
--- a/packages/SystemUI/res/values-my-rMM/strings.xml
+++ b/packages/SystemUI/res/values-my-rMM/strings.xml
@@ -399,7 +399,6 @@
     <string name="volumeui_prompt_deny" msgid="5720663643411696731">"ငြင်းပယ်သည်"</string>
     <string name="volumeui_notification_title" msgid="4906770126345910955">"<xliff:g id="APP_NAME">%1$s</xliff:g> သည် အသံဒိုင်ယာလော့ခ်ဖြစ်သည်"</string>
     <string name="volumeui_notification_text" msgid="1826889705095768656">"မူရင်းအားပြန်လည်သိမ်းဆည်းရန် ထိပါ။"</string>
-    <string name="volume_zen_switch_text" msgid="8149183012610587643">"@*android:string/zen_mode_feature_name"</string>
     <!-- no translation found for managed_profile_foreground_toast (3199278359979281097) -->
     <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-nb/strings.xml b/packages/SystemUI/res/values-nb/strings.xml
index 2168f14..ef64990 100644
--- a/packages/SystemUI/res/values-nb/strings.xml
+++ b/packages/SystemUI/res/values-nb/strings.xml
@@ -399,7 +399,6 @@
     <string name="volumeui_prompt_deny" msgid="5720663643411696731">"Ikke tillat"</string>
     <string name="volumeui_notification_title" msgid="4906770126345910955">"<xliff:g id="APP_NAME">%1$s</xliff:g> er volumdialogen"</string>
     <string name="volumeui_notification_text" msgid="1826889705095768656">"Trykk for å gå tilbake til den opprinnelige volumdialogen."</string>
-    <string name="volume_zen_switch_text" msgid="8149183012610587643">"@*android:string/zen_mode_feature_name"</string>
     <!-- no translation found for managed_profile_foreground_toast (3199278359979281097) -->
     <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-ne-rNP/strings.xml b/packages/SystemUI/res/values-ne-rNP/strings.xml
index 11e19e7..1561177 100644
--- a/packages/SystemUI/res/values-ne-rNP/strings.xml
+++ b/packages/SystemUI/res/values-ne-rNP/strings.xml
@@ -399,7 +399,6 @@
     <string name="volumeui_prompt_deny" msgid="5720663643411696731">"अस्वीकार गर्नुहोस्"</string>
     <string name="volumeui_notification_title" msgid="4906770126345910955">"<xliff:g id="APP_NAME">%1$s</xliff:g> भोल्यूम संवाद हो"</string>
     <string name="volumeui_notification_text" msgid="1826889705095768656">"मूल पुनर्स्थापना गर्न छुनुहोस्।"</string>
-    <string name="volume_zen_switch_text" msgid="8149183012610587643">"@*Android: स्ट्रिङ/zen_mode_feature_name"</string>
     <!-- no translation found for managed_profile_foreground_toast (3199278359979281097) -->
     <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-nl/strings.xml b/packages/SystemUI/res/values-nl/strings.xml
index 06ed986..24ace95 100644
--- a/packages/SystemUI/res/values-nl/strings.xml
+++ b/packages/SystemUI/res/values-nl/strings.xml
@@ -399,7 +399,6 @@
     <string name="volumeui_prompt_deny" msgid="5720663643411696731">"Afwijzen"</string>
     <string name="volumeui_notification_title" msgid="4906770126345910955">"<xliff:g id="APP_NAME">%1$s</xliff:g> is het volumedialoogvenster"</string>
     <string name="volumeui_notification_text" msgid="1826889705095768656">"Tik hierop om het origineel te herstellen."</string>
-    <string name="volume_zen_switch_text" msgid="8149183012610587643">"@*android:string/zen_mode_feature_name"</string>
     <!-- no translation found for managed_profile_foreground_toast (3199278359979281097) -->
     <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-pl/strings.xml b/packages/SystemUI/res/values-pl/strings.xml
index ffeff1e..02c1d74 100644
--- a/packages/SystemUI/res/values-pl/strings.xml
+++ b/packages/SystemUI/res/values-pl/strings.xml
@@ -401,7 +401,6 @@
     <string name="volumeui_prompt_deny" msgid="5720663643411696731">"Odmów"</string>
     <string name="volumeui_notification_title" msgid="4906770126345910955">"<xliff:g id="APP_NAME">%1$s</xliff:g> steruje głośnością"</string>
     <string name="volumeui_notification_text" msgid="1826889705095768656">"Dotknij, by przywrócić pierwotną."</string>
-    <string name="volume_zen_switch_text" msgid="8149183012610587643">"@*android:string/zen_mode_feature_name"</string>
     <!-- no translation found for managed_profile_foreground_toast (3199278359979281097) -->
     <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-pt-rPT/strings.xml b/packages/SystemUI/res/values-pt-rPT/strings.xml
index 23413be..2b802c0 100644
--- a/packages/SystemUI/res/values-pt-rPT/strings.xml
+++ b/packages/SystemUI/res/values-pt-rPT/strings.xml
@@ -399,7 +399,6 @@
     <string name="volumeui_prompt_deny" msgid="5720663643411696731">"Recusar"</string>
     <string name="volumeui_notification_title" msgid="4906770126345910955">"<xliff:g id="APP_NAME">%1$s</xliff:g> é a caixa de diálogo do volume"</string>
     <string name="volumeui_notification_text" msgid="1826889705095768656">"Toque para restaurar o original."</string>
-    <string name="volume_zen_switch_text" msgid="8149183012610587643">"@*android:string/zen_mode_feature_name"</string>
     <!-- no translation found for managed_profile_foreground_toast (3199278359979281097) -->
     <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-pt/strings.xml b/packages/SystemUI/res/values-pt/strings.xml
index caf85cc..8d82942 100644
--- a/packages/SystemUI/res/values-pt/strings.xml
+++ b/packages/SystemUI/res/values-pt/strings.xml
@@ -401,7 +401,6 @@
     <string name="volumeui_prompt_deny" msgid="5720663643411696731">"Negar"</string>
     <string name="volumeui_notification_title" msgid="4906770126345910955">"<xliff:g id="APP_NAME">%1$s</xliff:g> é a caixa de diálogo referente ao volume"</string>
     <string name="volumeui_notification_text" msgid="1826889705095768656">"Toque para restaurar o original."</string>
-    <string name="volume_zen_switch_text" msgid="8149183012610587643">"@*android:string/zen_mode_feature_name"</string>
     <!-- no translation found for managed_profile_foreground_toast (3199278359979281097) -->
     <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-ro/strings.xml b/packages/SystemUI/res/values-ro/strings.xml
index 509ae92..06d9ac2 100644
--- a/packages/SystemUI/res/values-ro/strings.xml
+++ b/packages/SystemUI/res/values-ro/strings.xml
@@ -400,7 +400,6 @@
     <string name="volumeui_prompt_deny" msgid="5720663643411696731">"Refuzați"</string>
     <string name="volumeui_notification_title" msgid="4906770126345910955">"<xliff:g id="APP_NAME">%1$s</xliff:g> afișează caseta de dialog pentru volum"</string>
     <string name="volumeui_notification_text" msgid="1826889705095768656">"Atingeți pentru a reveni la setarea inițială."</string>
-    <string name="volume_zen_switch_text" msgid="8149183012610587643">"@*android:string/zen_mode_feature_name"</string>
     <!-- no translation found for managed_profile_foreground_toast (3199278359979281097) -->
     <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-ru/strings.xml b/packages/SystemUI/res/values-ru/strings.xml
index 1c584fe..b6c506f 100644
--- a/packages/SystemUI/res/values-ru/strings.xml
+++ b/packages/SystemUI/res/values-ru/strings.xml
@@ -403,7 +403,6 @@
     <string name="volumeui_prompt_deny" msgid="5720663643411696731">"Нет"</string>
     <string name="volumeui_notification_title" msgid="4906770126345910955">"Приложение <xliff:g id="APP_NAME">%1$s</xliff:g> назначено регулятором громкости"</string>
     <string name="volumeui_notification_text" msgid="1826889705095768656">"Нажмите, чтобы восстановить приложение по умолчанию."</string>
-    <string name="volume_zen_switch_text" msgid="8149183012610587643">"@*android:string/zen_mode_feature_name"</string>
     <!-- no translation found for managed_profile_foreground_toast (3199278359979281097) -->
     <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-si-rLK/strings.xml b/packages/SystemUI/res/values-si-rLK/strings.xml
index 9643102..1e1d981 100644
--- a/packages/SystemUI/res/values-si-rLK/strings.xml
+++ b/packages/SystemUI/res/values-si-rLK/strings.xml
@@ -167,8 +167,7 @@
     <string name="accessibility_desc_lock_screen" msgid="5625143713611759164">"අගුළු තිරය."</string>
     <string name="accessibility_desc_settings" msgid="3417884241751434521">"සැකසීම්"</string>
     <string name="accessibility_desc_recent_apps" msgid="4876900986661819788">"දළ විශ්ලේෂණය."</string>
-    <!-- no translation found for accessibility_desc_confirm (3446792278337969766) -->
-    <skip />
+    <string name="accessibility_desc_confirm" msgid="3446792278337969766">"තහවුරු කරන්න"</string>
     <string name="accessibility_quick_settings_user" msgid="1104846699869476855">"පරිශීලකයා <xliff:g id="USER">%s</xliff:g>."</string>
     <string name="accessibility_quick_settings_wifi" msgid="5518210213118181692">"<xliff:g id="SIGNAL">%1$s</xliff:g>."</string>
     <string name="accessibility_quick_settings_wifi_changed_off" msgid="8716484460897819400">"Wifi අක්‍රියයි."</string>
@@ -303,10 +302,8 @@
     <string name="description_direction_up" msgid="7169032478259485180">"<xliff:g id="TARGET_DESCRIPTION">%s</xliff:g> සඳහා උඩට සර්පණය කරන්න."</string>
     <string name="description_direction_left" msgid="7207478719805562165">"<xliff:g id="TARGET_DESCRIPTION">%s</xliff:g> සඳහා වමට සර්පණය කරන්න."</string>
     <string name="zen_no_interruptions_with_warning" msgid="4396898053735625287">"අතුරු බිඳීම් නැත. අඩුම තරමේ අනතුරු ඇඟවීමක්වත් නැත."</string>
-    <!-- no translation found for zen_priority_introduction (7253045784560169993) -->
-    <skip />
-    <!-- no translation found for zen_priority_customize_button (7948043278226955063) -->
-    <skip />
+    <string name="zen_priority_introduction" msgid="7253045784560169993">"සීනු, සිහි කැඳවීම්, සිදුවීම් සහ ඔබ සඳහන් කරන අමතන්නන් හැර වෙනත් ශබ්ද සහ කම්පන වලින් ඔබව බාධා නොකරයි."</string>
+    <string name="zen_priority_customize_button" msgid="7948043278226955063">"අභිරුචිකරණය"</string>
     <string name="zen_no_interruptions" msgid="7970973750143632592">"අතුරු බිදුම් නැත"</string>
     <string name="zen_important_interruptions" msgid="3477041776609757628">"ප්‍රමුඛ අතුරු බිඳීම් පමණයි"</string>
     <string name="zen_alarms" msgid="5055668280767657759">"ඇඟවීම් පමණි"</string>
@@ -342,12 +339,9 @@
     <string name="guest_wipe_session_message" msgid="8476238178270112811">"ඔබගේ සැසිය දිගටම කරගෙන යෑමට ඔබට අවශ්‍යද?"</string>
     <string name="guest_wipe_session_wipe" msgid="5065558566939858884">"යළි මුල සිට අරඹන්න"</string>
     <string name="guest_wipe_session_dontwipe" msgid="1401113462524894716">"ඔව්, දිගටම කරගෙන යන්න"</string>
-    <!-- no translation found for guest_notification_title (1585278533840603063) -->
-    <skip />
-    <!-- no translation found for guest_notification_text (7513706222848825467) -->
-    <skip />
-    <!-- no translation found for guest_notification_remove_action (8820670703892101990) -->
-    <skip />
+    <string name="guest_notification_title" msgid="1585278533840603063">"ආගන්තුක පරිශිලකයා"</string>
+    <string name="guest_notification_text" msgid="7513706222848825467">"යෙදුම් සහ දත්ත ඉවත් කිරීමට ආගන්තුකයා ඉවත් කරන්න"</string>
+    <string name="guest_notification_remove_action" msgid="8820670703892101990">"ආගන්තුකයා ඉවත් කරන්නද?"</string>
     <string name="user_add_user_title" msgid="4553596395824132638">"අලුත් පරිශීලකයෙක් එකතු කරන්නද?"</string>
     <string name="user_add_user_message_short" msgid="2161624834066214559">"ඔබ අලුත් පරිශීලකයෙක් එකතු කරන විට, එම පුද්ගලයා ඔහුගේ වැඩ කරන ඉඩ සකසා ගත යුතුය.\n\nසියළුම අනෙක් පරිශීලකයින් සඳහා ඕනෑම පරිශීලකයෙකුට යාවත්කාලීන කළ හැක."</string>
     <string name="battery_saver_notification_title" msgid="237918726750955859">"බැටරිය සුරකින්නා සක්‍රීයයි"</string>
@@ -399,7 +393,5 @@
     <string name="volumeui_prompt_deny" msgid="5720663643411696731">"ප්‍රතික්ෂේප කරන්න"</string>
     <string name="volumeui_notification_title" msgid="4906770126345910955">"<xliff:g id="APP_NAME">%1$s</xliff:g> ධාරිතා සංවාදයයි"</string>
     <string name="volumeui_notification_text" msgid="1826889705095768656">"මුල් තත්ත්වය නැවත ප්‍රතිසාධනය කිරීමට ස්පර්ශ කරන්න."</string>
-    <string name="volume_zen_switch_text" msgid="8149183012610587643">"@*android:string/zen_mode_feature_name"</string>
-    <!-- no translation found for managed_profile_foreground_toast (3199278359979281097) -->
-    <skip />
+    <string name="managed_profile_foreground_toast" msgid="3199278359979281097">"ඔබ කාර්යාල පැතිකඩේ සිටියි"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-sk/strings.xml b/packages/SystemUI/res/values-sk/strings.xml
index d826901..72e30b5 100644
--- a/packages/SystemUI/res/values-sk/strings.xml
+++ b/packages/SystemUI/res/values-sk/strings.xml
@@ -403,7 +403,6 @@
     <string name="volumeui_prompt_deny" msgid="5720663643411696731">"Odmietnuť"</string>
     <string name="volumeui_notification_title" msgid="4906770126345910955">"<xliff:g id="APP_NAME">%1$s</xliff:g> je dialóg hlasitosti"</string>
     <string name="volumeui_notification_text" msgid="1826889705095768656">"Klepnutím obnovíte originál."</string>
-    <string name="volume_zen_switch_text" msgid="8149183012610587643">"@*android:string/zen_mode_feature_name"</string>
     <!-- no translation found for managed_profile_foreground_toast (3199278359979281097) -->
     <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-sl/strings.xml b/packages/SystemUI/res/values-sl/strings.xml
index a7eca4d..2c8e221 100644
--- a/packages/SystemUI/res/values-sl/strings.xml
+++ b/packages/SystemUI/res/values-sl/strings.xml
@@ -401,7 +401,6 @@
     <string name="volumeui_prompt_deny" msgid="5720663643411696731">"Zavrni"</string>
     <string name="volumeui_notification_title" msgid="4906770126345910955">"<xliff:g id="APP_NAME">%1$s</xliff:g> je pogovorno okno glede prostornine"</string>
     <string name="volumeui_notification_text" msgid="1826889705095768656">"Dotaknite se, če želite obnoviti izvirnik."</string>
-    <string name="volume_zen_switch_text" msgid="8149183012610587643">"@*android:string/zen_mode_feature_name"</string>
     <!-- no translation found for managed_profile_foreground_toast (3199278359979281097) -->
     <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-sr/strings.xml b/packages/SystemUI/res/values-sr/strings.xml
index edb1166..a51f8ad 100644
--- a/packages/SystemUI/res/values-sr/strings.xml
+++ b/packages/SystemUI/res/values-sr/strings.xml
@@ -400,7 +400,6 @@
     <string name="volumeui_prompt_deny" msgid="5720663643411696731">"Одбиј"</string>
     <string name="volumeui_notification_title" msgid="4906770126345910955">"<xliff:g id="APP_NAME">%1$s</xliff:g> је дијалог за јачину звука"</string>
     <string name="volumeui_notification_text" msgid="1826889705095768656">"Додирните да бисте вратили оригинал."</string>
-    <string name="volume_zen_switch_text" msgid="8149183012610587643">"@*android:string/zen_mode_feature_name"</string>
     <!-- no translation found for managed_profile_foreground_toast (3199278359979281097) -->
     <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-sv/strings.xml b/packages/SystemUI/res/values-sv/strings.xml
index ee03be4..ccf94eb 100644
--- a/packages/SystemUI/res/values-sv/strings.xml
+++ b/packages/SystemUI/res/values-sv/strings.xml
@@ -399,7 +399,6 @@
     <string name="volumeui_prompt_deny" msgid="5720663643411696731">"Neka"</string>
     <string name="volumeui_notification_title" msgid="4906770126345910955">"<xliff:g id="APP_NAME">%1$s</xliff:g> används som volymkontroll"</string>
     <string name="volumeui_notification_text" msgid="1826889705095768656">"Tryck här om du vill återställa den ursprungliga appen."</string>
-    <string name="volume_zen_switch_text" msgid="8149183012610587643">"@*android:string/zen_mode_feature_name"</string>
     <!-- no translation found for managed_profile_foreground_toast (3199278359979281097) -->
     <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-sw/strings.xml b/packages/SystemUI/res/values-sw/strings.xml
index 2ac3926..c01d0ac 100644
--- a/packages/SystemUI/res/values-sw/strings.xml
+++ b/packages/SystemUI/res/values-sw/strings.xml
@@ -399,7 +399,6 @@
     <string name="volumeui_prompt_deny" msgid="5720663643411696731">"Kataa"</string>
     <string name="volumeui_notification_title" msgid="4906770126345910955">"<xliff:g id="APP_NAME">%1$s</xliff:g> ni mazungumzo ya sauti"</string>
     <string name="volumeui_notification_text" msgid="1826889705095768656">"Gusa ili urejeshe ya awali."</string>
-    <string name="volume_zen_switch_text" msgid="8149183012610587643">"@*android:string/zen_mode_feature_name"</string>
     <!-- no translation found for managed_profile_foreground_toast (3199278359979281097) -->
     <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-ta-rIN/strings.xml b/packages/SystemUI/res/values-ta-rIN/strings.xml
index a8177de..2632a81 100644
--- a/packages/SystemUI/res/values-ta-rIN/strings.xml
+++ b/packages/SystemUI/res/values-ta-rIN/strings.xml
@@ -399,7 +399,6 @@
     <string name="volumeui_prompt_deny" msgid="5720663643411696731">"நிராகரி"</string>
     <string name="volumeui_notification_title" msgid="4906770126345910955">"ஒலியளவு செய்தி: <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
     <string name="volumeui_notification_text" msgid="1826889705095768656">"அசலை மீட்டமைக்கத் தொடவும்."</string>
-    <string name="volume_zen_switch_text" msgid="8149183012610587643">"@*android:string/zen_mode_feature_name"</string>
     <!-- no translation found for managed_profile_foreground_toast (3199278359979281097) -->
     <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-te-rIN/strings.xml b/packages/SystemUI/res/values-te-rIN/strings.xml
index 009eddb..8fe687e 100644
--- a/packages/SystemUI/res/values-te-rIN/strings.xml
+++ b/packages/SystemUI/res/values-te-rIN/strings.xml
@@ -399,7 +399,6 @@
     <string name="volumeui_prompt_deny" msgid="5720663643411696731">"తిరస్కరించు"</string>
     <string name="volumeui_notification_title" msgid="4906770126345910955">"<xliff:g id="APP_NAME">%1$s</xliff:g> అనేది వాల్యూమ్ డైలాగ్"</string>
     <string name="volumeui_notification_text" msgid="1826889705095768656">"అసలుదాన్ని పునరుద్ధరించడానికి తాకండి."</string>
-    <string name="volume_zen_switch_text" msgid="8149183012610587643">"@*android:string/zen_mode_feature_name"</string>
     <!-- no translation found for managed_profile_foreground_toast (3199278359979281097) -->
     <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-th/strings.xml b/packages/SystemUI/res/values-th/strings.xml
index bd0f58e..48b0a38 100644
--- a/packages/SystemUI/res/values-th/strings.xml
+++ b/packages/SystemUI/res/values-th/strings.xml
@@ -399,7 +399,6 @@
     <string name="volumeui_prompt_deny" msgid="5720663643411696731">"ปฏิเสธ"</string>
     <string name="volumeui_notification_title" msgid="4906770126345910955">"<xliff:g id="APP_NAME">%1$s</xliff:g> เป็นช่องโต้ตอบระดับเสียง"</string>
     <string name="volumeui_notification_text" msgid="1826889705095768656">"แตะเพื่อคืนค่าดั้งเดิม"</string>
-    <string name="volume_zen_switch_text" msgid="8149183012610587643">"@*android:string/zen_mode_feature_name"</string>
     <!-- no translation found for managed_profile_foreground_toast (3199278359979281097) -->
     <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-tl/strings.xml b/packages/SystemUI/res/values-tl/strings.xml
index b8858af..9c5daa8 100644
--- a/packages/SystemUI/res/values-tl/strings.xml
+++ b/packages/SystemUI/res/values-tl/strings.xml
@@ -399,7 +399,6 @@
     <string name="volumeui_prompt_deny" msgid="5720663643411696731">"Tanggihan"</string>
     <string name="volumeui_notification_title" msgid="4906770126345910955">"Ang <xliff:g id="APP_NAME">%1$s</xliff:g> ang volume dialog"</string>
     <string name="volumeui_notification_text" msgid="1826889705095768656">"Pindutin upang ibalik ang orihinal."</string>
-    <string name="volume_zen_switch_text" msgid="8149183012610587643">"@*android:string/zen_mode_feature_name"</string>
     <!-- no translation found for managed_profile_foreground_toast (3199278359979281097) -->
     <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-tr/strings.xml b/packages/SystemUI/res/values-tr/strings.xml
index 1b0d11f..7c44bf1 100644
--- a/packages/SystemUI/res/values-tr/strings.xml
+++ b/packages/SystemUI/res/values-tr/strings.xml
@@ -399,7 +399,6 @@
     <string name="volumeui_prompt_deny" msgid="5720663643411696731">"Reddet"</string>
     <string name="volumeui_notification_title" msgid="4906770126345910955">"<xliff:g id="APP_NAME">%1$s</xliff:g> ses denetimi iletişim kutusu olarak ayarlandı"</string>
     <string name="volumeui_notification_text" msgid="1826889705095768656">"Orijinali geri yüklemek için dokunun."</string>
-    <string name="volume_zen_switch_text" msgid="8149183012610587643">"@*android:string/zen_mode_feature_name"</string>
     <!-- no translation found for managed_profile_foreground_toast (3199278359979281097) -->
     <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-uk/strings.xml b/packages/SystemUI/res/values-uk/strings.xml
index c5ebd3a..76ed716 100644
--- a/packages/SystemUI/res/values-uk/strings.xml
+++ b/packages/SystemUI/res/values-uk/strings.xml
@@ -401,7 +401,6 @@
     <string name="volumeui_prompt_deny" msgid="5720663643411696731">"Відхилити"</string>
     <string name="volumeui_notification_title" msgid="4906770126345910955">"<xliff:g id="APP_NAME">%1$s</xliff:g> призначено регулятором гучності"</string>
     <string name="volumeui_notification_text" msgid="1826889705095768656">"Торкніться, щоб відновити оригінал."</string>
-    <string name="volume_zen_switch_text" msgid="8149183012610587643">"@*android:string/zen_mode_feature_name"</string>
     <!-- no translation found for managed_profile_foreground_toast (3199278359979281097) -->
     <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-ur-rPK/strings.xml b/packages/SystemUI/res/values-ur-rPK/strings.xml
index c01a197..608c6d3 100644
--- a/packages/SystemUI/res/values-ur-rPK/strings.xml
+++ b/packages/SystemUI/res/values-ur-rPK/strings.xml
@@ -399,7 +399,6 @@
     <string name="volumeui_prompt_deny" msgid="5720663643411696731">"مسترد کریں"</string>
     <string name="volumeui_notification_title" msgid="4906770126345910955">"<xliff:g id="APP_NAME">%1$s</xliff:g> والیوم ڈائلاگ ہے"</string>
     <string name="volumeui_notification_text" msgid="1826889705095768656">"اصل کو بحال کرنے کیلئے ٹچ کریں۔"</string>
-    <string name="volume_zen_switch_text" msgid="8149183012610587643">"‎@*android:string/zen_mode_feature_name"</string>
     <!-- no translation found for managed_profile_foreground_toast (3199278359979281097) -->
     <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-uz-rUZ/strings.xml b/packages/SystemUI/res/values-uz-rUZ/strings.xml
index 37e9542..63af257 100644
--- a/packages/SystemUI/res/values-uz-rUZ/strings.xml
+++ b/packages/SystemUI/res/values-uz-rUZ/strings.xml
@@ -399,7 +399,6 @@
     <string name="volumeui_prompt_deny" msgid="5720663643411696731">"Rad etish"</string>
     <string name="volumeui_notification_title" msgid="4906770126345910955">"<xliff:g id="APP_NAME">%1$s</xliff:g> ovoz balandligini boshqaradi"</string>
     <string name="volumeui_notification_text" msgid="1826889705095768656">"Aslini tiklash uchun bosing."</string>
-    <string name="volume_zen_switch_text" msgid="8149183012610587643">"@*android:string/zen_mode_feature_name"</string>
     <!-- no translation found for managed_profile_foreground_toast (3199278359979281097) -->
     <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-vi/strings.xml b/packages/SystemUI/res/values-vi/strings.xml
index 3c2d0ef..a434168 100644
--- a/packages/SystemUI/res/values-vi/strings.xml
+++ b/packages/SystemUI/res/values-vi/strings.xml
@@ -399,7 +399,6 @@
     <string name="volumeui_prompt_deny" msgid="5720663643411696731">"Từ chối"</string>
     <string name="volumeui_notification_title" msgid="4906770126345910955">"<xliff:g id="APP_NAME">%1$s</xliff:g> là hộp thoại khối lượng"</string>
     <string name="volumeui_notification_text" msgid="1826889705095768656">"Chạm để khôi phục bản gốc."</string>
-    <string name="volume_zen_switch_text" msgid="8149183012610587643">"@*android:string/zen_mode_feature_name"</string>
     <!-- no translation found for managed_profile_foreground_toast (3199278359979281097) -->
     <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-zh-rCN/strings.xml b/packages/SystemUI/res/values-zh-rCN/strings.xml
index 32e7735..4e3c265 100644
--- a/packages/SystemUI/res/values-zh-rCN/strings.xml
+++ b/packages/SystemUI/res/values-zh-rCN/strings.xml
@@ -401,7 +401,6 @@
     <string name="volumeui_prompt_deny" msgid="5720663643411696731">"拒绝"</string>
     <string name="volumeui_notification_title" msgid="4906770126345910955">"“<xliff:g id="APP_NAME">%1$s</xliff:g>”已用作音量控制对话框"</string>
     <string name="volumeui_notification_text" msgid="1826889705095768656">"触摸即可恢复原始设置。"</string>
-    <string name="volume_zen_switch_text" msgid="8149183012610587643">"@*android:string/zen_mode_feature_name"</string>
     <!-- no translation found for managed_profile_foreground_toast (3199278359979281097) -->
     <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-zh-rHK/strings.xml b/packages/SystemUI/res/values-zh-rHK/strings.xml
index bf8dfb2..d6be63f 100644
--- a/packages/SystemUI/res/values-zh-rHK/strings.xml
+++ b/packages/SystemUI/res/values-zh-rHK/strings.xml
@@ -401,7 +401,6 @@
     <string name="volumeui_prompt_deny" msgid="5720663643411696731">"拒絕"</string>
     <string name="volumeui_notification_title" msgid="4906770126345910955">"「<xliff:g id="APP_NAME">%1$s</xliff:g>」為音量對話框"</string>
     <string name="volumeui_notification_text" msgid="1826889705095768656">"輕觸即可復原。"</string>
-    <string name="volume_zen_switch_text" msgid="8149183012610587643">"@*android:string/zen_mode_feature_name"</string>
     <!-- no translation found for managed_profile_foreground_toast (3199278359979281097) -->
     <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-zh-rTW/strings.xml b/packages/SystemUI/res/values-zh-rTW/strings.xml
index f9b33ca..ddab028 100644
--- a/packages/SystemUI/res/values-zh-rTW/strings.xml
+++ b/packages/SystemUI/res/values-zh-rTW/strings.xml
@@ -401,7 +401,6 @@
     <string name="volumeui_prompt_deny" msgid="5720663643411696731">"拒絕"</string>
     <string name="volumeui_notification_title" msgid="4906770126345910955">"「<xliff:g id="APP_NAME">%1$s</xliff:g>」現在是預設的音量控制對話方塊。"</string>
     <string name="volumeui_notification_text" msgid="1826889705095768656">"輕觸這裡即可恢復原始設定。"</string>
-    <string name="volume_zen_switch_text" msgid="8149183012610587643">"@*android:string/zen_mode_feature_name"</string>
     <!-- no translation found for managed_profile_foreground_toast (3199278359979281097) -->
     <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-zu/strings.xml b/packages/SystemUI/res/values-zu/strings.xml
index 9a32c4b..e91577d 100644
--- a/packages/SystemUI/res/values-zu/strings.xml
+++ b/packages/SystemUI/res/values-zu/strings.xml
@@ -399,7 +399,6 @@
     <string name="volumeui_prompt_deny" msgid="5720663643411696731">"Phika"</string>
     <string name="volumeui_notification_title" msgid="4906770126345910955">"I-<xliff:g id="APP_NAME">%1$s</xliff:g> yingxoxo yevolumu"</string>
     <string name="volumeui_notification_text" msgid="1826889705095768656">"Thinta ukuze ubuyisele kokwangempela."</string>
-    <string name="volume_zen_switch_text" msgid="8149183012610587643">"@*android:string/zen_mode_feature_name"</string>
     <!-- no translation found for managed_profile_foreground_toast (3199278359979281097) -->
     <skip />
 </resources>
diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml
index 88d1769..1f1455a 100644
--- a/packages/SystemUI/res/values/colors.xml
+++ b/packages/SystemUI/res/values/colors.xml
@@ -129,7 +129,6 @@
 
     <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="dark_mode_icon_color_single_tone">#99000000</color>
     <color name="dark_mode_icon_color_dual_tone_background">#3d000000</color>
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index 2e9e9f7..2e44547 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -147,7 +147,7 @@
     <integer name="heads_up_default_snooze_length_ms">60000</integer>
 
     <!-- Minimum display time for a heads up notification, in milliseconds. -->
-    <integer name="heads_up_notification_minimum_time">3000</integer>
+    <integer name="heads_up_notification_minimum_time">2000</integer>
 
     <!-- milliseconds before the heads up notification accepts touches. -->
     <integer name="heads_up_sensitivity_delay">700</integer>
@@ -276,9 +276,6 @@
     <!-- Doze: alpha to apply to small icons when dozing -->
     <integer name="doze_small_icon_alpha">222</integer><!-- 87% of 0xff -->
 
-    <!-- Volume: time to delay dismissing the volume panel after a click is performed -->
-    <integer name="volume_panel_dismiss_delay">200</integer>
-
     <!-- Hotspot tile: number of days to show after feature is used. -->
     <integer name="days_to_show_hotspot_tile">30</integer>
 
@@ -293,5 +290,8 @@
 
     <!-- Enable the default volume dialog -->
     <bool name="enable_volume_ui">true</bool>
+
+    <!-- Duration of the full carrier network change icon animation. -->
+    <integer name="carrier_network_change_anim_time">3000</integer>
 </resources>
 
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index b6ff1ce..7e50454 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -330,7 +330,7 @@
          keyguard_clock_height_fraction_* for the difference between min and max.-->
     <dimen name="keyguard_clock_notifications_margin_min">24dp</dimen>
     <dimen name="keyguard_clock_notifications_margin_max">36dp</dimen>
-    <dimen name="heads_up_window_height">250dp</dimen>
+    <dimen name="heads_up_scrim_height">250dp</dimen>
 
     <!-- The minimum amount the user needs to swipe to go to the camera / phone. -->
     <dimen name="keyguard_min_swipe_amount">110dp</dimen>
@@ -344,15 +344,6 @@
     <!-- The chevron padding to the circle when hinting -->
     <dimen name="hint_chevron_circle_padding">16dp</dimen>
 
-    <!-- Volume panel dialog y offset -->
-    <dimen name="volume_panel_top">0dp</dimen>
-
-    <!-- Volume panel dialog width -->
-    <dimen name="volume_panel_width">344dp</dimen>
-
-    <!-- Volume panel z depth -->
-    <dimen name="volume_panel_z">3dp</dimen>
-
     <!-- Distance between notifications and header when they are considered to be colliding. -->
     <dimen name="header_notifications_collide_distance">48dp</dimen>
 
diff --git a/packages/SystemUI/res/values/ids.xml b/packages/SystemUI/res/values/ids.xml
index 3fc75d2..6d84727 100644
--- a/packages/SystemUI/res/values/ids.xml
+++ b/packages/SystemUI/res/values/ids.xml
@@ -37,6 +37,8 @@
     <item type="id" name="doze_saved_filter_tag"/>
     <item type="id" name="qs_icon_tag"/>
     <item type="id" name="scrim"/>
+    <item type="id" name="hun_scrim_alpha_start"/>
+    <item type="id" name="hun_scrim_alpha_end"/>
     <item type="id" name="notification_power"/>
     <item type="id" name="notification_screenshot"/>
     <item type="id" name="notification_hidden"/>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index ae134c6..67a0bc6 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -359,6 +359,9 @@
     <!-- Content description of the airplane mode icon for accessibility (not shown on the screen). [CHAR LIMIT=NONE] -->
     <string name="accessibility_airplane_mode">Airplane mode.</string>
 
+    <!-- Content description of the carrier network changing icon for accessibility (not shown on the screen). [CHAR LIMIT=NONE] -->
+    <string name="accessibility_carrier_network_change_mode">Carrier network changing.</string>
+
     <!-- Content description of the battery level icon for accessibility (not shown on the screen). [CHAR LIMIT=NONE] -->
     <string name="accessibility_battery_level">Battery <xliff:g id="number">%d</xliff:g> percent.</string>
 
@@ -998,7 +1001,7 @@
     <string name="volumeui_notification_text">Touch to restore the original.</string>
 
     <!-- Volume dialog zen toggle switch title -->
-    <string name="volume_zen_switch_text">@*android:string/zen_mode_feature_name</string>
+    <string name="volume_zen_switch_text" translatable="false">@*android:string/zen_mode_feature_name</string>
 
     <!-- Toast shown when user unlocks screen and managed profile activity is in the foreground -->
     <string name="managed_profile_foreground_toast">You are in the Work profile</string>
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index 974cc48..ef2e6f3 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -138,9 +138,8 @@
         <item name="android:textColor">@color/system_accent_color</item>
     </style>
 
-    <style name="TextAppearance.QS.VolumeSuppressor">
+    <style name="TextAppearance.QS.Introduction">
         <item name="android:textSize">14sp</item>
-        <item name="android:textColor">@color/qs_tile_text</item>
     </style>
 
     <style name="TextAppearance.QS.DetailButton">
@@ -205,11 +204,6 @@
     <style name="Animation.StatusBar">
     </style>
 
-    <style name="Animation.StatusBar.HeadsUp">
-        <item name="android:windowEnterAnimation">@anim/heads_up_enter</item>
-        <item name="android:windowExitAnimation">@anim/heads_up_exit</item>
-    </style>
-
     <style name="systemui_theme" parent="@android:style/Theme.DeviceDefault">
         <item name="android:colorPrimary">@color/system_primary_color</item>
         <item name="android:colorControlActivated">@color/system_accent_color</item>
@@ -233,12 +227,6 @@
         <item name="android:gravity">center</item>
     </style>
 
-    <!-- Window animations used for volume panel. -->
-    <style name="VolumePanelAnimation">
-        <item name="android:windowEnterAnimation">@*android:anim/popup_enter_material</item>
-        <item name="android:windowExitAnimation">@*android:anim/popup_exit_material</item>
-    </style>
-
     <style name="TextAppearance.Material.Notification.Parenthetical"
            parent="@*android:style/TextAppearance.Material.Notification">
         <item name="android:textStyle">italic</item>
diff --git a/packages/SystemUI/src/com/android/systemui/ExpandHelper.java b/packages/SystemUI/src/com/android/systemui/ExpandHelper.java
index bc7f7452..fece07f 100644
--- a/packages/SystemUI/src/com/android/systemui/ExpandHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/ExpandHelper.java
@@ -322,6 +322,10 @@
                         isInside(mScrollAdapter.getHostView(), x, y)
                         && mScrollAdapter.isScrolledToTop();
                 mResizedView = findView(x, y);
+                if (mResizedView != null && !mCallback.canChildBeExpanded(mResizedView)) {
+                    mResizedView = null;
+                    mWatchingForPull = false;
+                }
                 mInitialTouchY = ev.getY();
                 break;
 
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index 065d62eb..b828e78 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -171,6 +171,11 @@
      */
     private static final String KEYGUARD_ANALYTICS_SETTING = "keyguard_analytics";
 
+    /**
+     * How much faster we collapse the lockscreen when authenticating with fingerprint.
+     */
+    private static final float FINGERPRINT_COLLAPSE_SPEEDUP_FACTOR = 1.3f;
+
     /** The stream type that the lock sounds are tied to. */
     private int mUiSoundsStreamType;
 
@@ -441,7 +446,8 @@
             if (mStatusBarKeyguardViewManager.isBouncerShowing()) {
                 mViewMediatorCallback.keyguardDone(true);
             } else {
-                mStatusBarKeyguardViewManager.animateCollapsePanels();
+                mStatusBarKeyguardViewManager.animateCollapsePanels(
+                        FINGERPRINT_COLLAPSE_SPEEDUP_FACTOR);
             }
         };
 
@@ -505,6 +511,11 @@
         public boolean isInputRestricted() {
             return KeyguardViewMediator.this.isInputRestricted();
         }
+
+        @Override
+        public boolean isScreenOn() {
+            return mScreenOn;
+        }
     };
 
     public void userActivity() {
@@ -867,6 +878,12 @@
      */
     private void handleSetOccluded(boolean isOccluded) {
         synchronized (KeyguardViewMediator.this) {
+            if (mHiding && isOccluded) {
+                // We're in the process of going away but WindowManager wants to show a
+                // SHOW_WHEN_LOCKED activity instead.
+                startKeyguardExitAnimation(0, 0);
+            }
+
             if (mOccluded != isOccluded) {
                 mOccluded = isOccluded;
                 mStatusBarKeyguardViewManager.setOccluded(isOccluded);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSTile.java b/packages/SystemUI/src/com/android/systemui/qs/QSTile.java
index 1790a4e..5a84db5 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSTile.java
@@ -18,6 +18,7 @@
 
 import android.content.Context;
 import android.content.Intent;
+import android.graphics.drawable.Animatable;
 import android.graphics.drawable.AnimatedVectorDrawable;
 import android.graphics.drawable.Drawable;
 import android.os.Handler;
@@ -325,7 +326,7 @@
     public static class ResourceIcon extends Icon {
         private static final SparseArray<Icon> ICONS = new SparseArray<Icon>();
 
-        private final int mResId;
+        protected final int mResId;
 
         private ResourceIcon(int resId) {
             mResId = resId;
@@ -342,7 +343,11 @@
 
         @Override
         public Drawable getDrawable(Context context) {
-            return context.getDrawable(mResId);
+            Drawable d = context.getDrawable(mResId);
+            if (d instanceof Animatable) {
+                ((Animatable) d).start();
+            }
+            return d;
         }
 
         @Override
@@ -370,7 +375,7 @@
         @Override
         public Drawable getDrawable(Context context) {
             // workaround: get a clean state for every new AVD
-            final AnimatedVectorDrawable d = (AnimatedVectorDrawable) super.getDrawable(context)
+            final AnimatedVectorDrawable d = (AnimatedVectorDrawable) context.getDrawable(mResId)
                     .getConstantState().newDrawable();
             d.start();
             if (mAllowAnimation) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSTileView.java b/packages/SystemUI/src/com/android/systemui/qs/QSTileView.java
index ec83ca7..af9d3a5 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSTileView.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSTileView.java
@@ -38,6 +38,7 @@
 
 import com.android.systemui.FontSizeUtils;
 import com.android.systemui.R;
+import com.android.systemui.qs.QSTile.AnimationIcon;
 import com.android.systemui.qs.QSTile.State;
 
 import java.util.Objects;
@@ -315,8 +316,9 @@
             iv.setImageDrawable(d);
             iv.setTag(R.id.qs_icon_tag, state.icon);
             if (d instanceof Animatable) {
-                if (!iv.isShown()) {
-                    ((Animatable) d).stop(); // skip directly to end state
+                Animatable a = (Animatable) d;
+                if (state.icon instanceof AnimationIcon && !iv.isShown()) {
+                    a.stop(); // skip directly to end state
                 }
             }
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
index ee607a7..de4874f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
@@ -96,7 +96,7 @@
 import com.android.systemui.statusbar.phone.NavigationBarView;
 import com.android.systemui.statusbar.phone.NotificationGroupManager;
 import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
-import com.android.systemui.statusbar.policy.HeadsUpNotificationView;
+import com.android.systemui.statusbar.policy.HeadsUpManager;
 import com.android.systemui.statusbar.policy.PreviewInflater;
 import com.android.systemui.statusbar.policy.RemoteInputView;
 import com.android.systemui.statusbar.stack.NotificationStackScrollLayout;
@@ -130,9 +130,6 @@
     protected static final int MSG_CANCEL_PRELOAD_RECENT_APPS = 1023;
     protected static final int MSG_SHOW_NEXT_AFFILIATED_TASK = 1024;
     protected static final int MSG_SHOW_PREV_AFFILIATED_TASK = 1025;
-    protected static final int MSG_SHOW_HEADS_UP = 1028;
-    protected static final int MSG_HIDE_HEADS_UP = 1029;
-    protected static final int MSG_ESCALATE_HEADS_UP = 1030;
 
     protected static final boolean ENABLE_HEADS_UP = true;
     // scores above this threshold should be displayed in heads up mode.
@@ -158,8 +155,7 @@
     protected NotificationGroupManager mGroupManager = new NotificationGroupManager();
 
     // for heads up notifications
-    protected HeadsUpNotificationView mHeadsUpNotificationView;
-    protected int mHeadsUpNotificationDecay;
+    protected HeadsUpManager mHeadsUpManager;
 
     protected int mCurrentUserId = 0;
     final protected SparseArray<UserInfo> mCurrentProfiles = new SparseArray<UserInfo>();
@@ -446,9 +442,8 @@
                     @Override
                     public void run() {
                         processForRemoteInput(sbn.getNotification());
-                        Notification n = sbn.getNotification();
-                        boolean isUpdate = mNotificationData.get(sbn.getKey()) != null
-                                || isHeadsUp(sbn.getKey());
+                        String key = sbn.getKey();
+                        boolean isUpdate = mNotificationData.get(key) != null;
 
                         // In case we don't allow child notifications, we ignore children of
                         // notifications that have a summary, since we're not going to show them
@@ -462,7 +457,7 @@
 
                             // Remove existing notification to avoid stale data.
                             if (isUpdate) {
-                                removeNotification(sbn.getKey(), rankingMap);
+                                removeNotification(key, rankingMap);
                             } else {
                                 mNotificationData.updateRanking(rankingMap);
                             }
@@ -692,15 +687,7 @@
         setHeadsUpUser(newUserId);
     }
 
-    private void setHeadsUpUser(int newUserId) {
-        if (mHeadsUpNotificationView != null) {
-            mHeadsUpNotificationView.setUser(newUserId);
-        }
-    }
-
-    public boolean isHeadsUp(String key) {
-      return mHeadsUpNotificationView != null && mHeadsUpNotificationView.isShowing(key);
-    }
+    protected abstract void setHeadsUpUser(int newUserId);
 
     @Override  // NotificationData.Environment
     public boolean isNotificationForCurrentProfiles(StatusBarNotification n) {
@@ -758,8 +745,7 @@
 
     protected View updateNotificationVetoButton(View row, StatusBarNotification n) {
         View vetoButton = row.findViewById(R.id.veto);
-        if (n.isClearable() || (mHeadsUpNotificationView.getEntry() != null
-                && mHeadsUpNotificationView.getEntry().row == row)) {
+        if (n.isClearable()) {
             final String _pkg = n.getPackageName();
             final String _tag = n.getTag();
             final int _id = n.getId();
@@ -1002,9 +988,6 @@
         }
     }
 
-    public void onHeadsUpDismissed() {
-    }
-
     @Override
     public void showRecentApps(boolean triggeredFromAltTab) {
         int msg = MSG_SHOW_RECENT_APPS;
@@ -1141,13 +1124,10 @@
         // Do nothing
     }
 
-    public abstract void scheduleHeadsUpDecay(long delay);
-
-    public abstract void scheduleHeadsUpOpen();
-
-    public abstract void scheduleHeadsUpClose();
-
-    public abstract void scheduleHeadsUpEscalation();
+    /**
+     * if the interrupting notification had a fullscreen intent, fire it now.
+     */
+    public abstract void escalateHeadsUp();
 
     /**
      * Save the current "public" (locked and secure) state of the lockscreen.
@@ -1238,15 +1218,7 @@
     protected void workAroundBadLayerDrawableOpacity(View v) {
     }
 
-    protected boolean inflateViews(NotificationData.Entry entry, ViewGroup parent) {
-            return inflateViews(entry, parent, false);
-    }
-
-    protected boolean inflateViewsForHeadsUp(NotificationData.Entry entry, ViewGroup parent) {
-        return inflateViews(entry, parent, true);
-    }
-
-    private boolean inflateViews(NotificationData.Entry entry, ViewGroup parent, boolean isHeadsUp) {
+    protected boolean inflateViews(Entry entry, ViewGroup parent) {
         PackageManager pmUser = getPackageManagerForUser(
                 entry.notification.getUser().getIdentifier());
 
@@ -1254,12 +1226,7 @@
         final StatusBarNotification sbn = entry.notification;
         RemoteViews contentView = sbn.getNotification().contentView;
         RemoteViews bigContentView = sbn.getNotification().bigContentView;
-
-        if (isHeadsUp) {
-            maxHeight =
-                    mContext.getResources().getDimensionPixelSize(R.dimen.notification_mid_height);
-            bigContentView = sbn.getNotification().headsUpContentView;
-        }
+        RemoteViews headsUpContentView = sbn.getNotification().headsUpContentView;
 
         if (contentView == null) {
             return false;
@@ -1284,7 +1251,6 @@
             hasUserChangedExpansion = row.hasUserChangedExpansion();
             userExpanded = row.isUserExpanded();
             userLocked = row.isUserLocked();
-            entry.row.setHeadsUp(isHeadsUp);
             entry.reset();
             if (hasUserChangedExpansion) {
                 row.setUserExpanded(userExpanded);
@@ -1307,10 +1273,8 @@
         // NB: the large icon is now handled entirely by the template
 
         // bind the click event to the content area
-        NotificationContentView expanded =
-                (NotificationContentView) row.findViewById(R.id.expanded);
-        NotificationContentView expandedPublic =
-                (NotificationContentView) row.findViewById(R.id.expandedPublic);
+        NotificationContentView contentContainer = row.getPrivateLayout();
+        NotificationContentView contentContainerPublic = row.getPublicLayout();
 
         row.setDescendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS);
         if (ENABLE_REMOTE_INPUT) {
@@ -1328,11 +1292,16 @@
         // set up the adaptive layout
         View contentViewLocal = null;
         View bigContentViewLocal = null;
+        View headsUpContentViewLocal = null;
         try {
-            contentViewLocal = contentView.apply(mContext, expanded,
+            contentViewLocal = contentView.apply(mContext, contentContainer,
                     mOnClickHandler);
             if (bigContentView != null) {
-                bigContentViewLocal = bigContentView.apply(mContext, expanded,
+                bigContentViewLocal = bigContentView.apply(mContext, contentContainer,
+                        mOnClickHandler);
+            }
+            if (headsUpContentView != null) {
+                headsUpContentViewLocal = headsUpContentView.apply(mContext, contentContainer,
                         mOnClickHandler);
             }
         }
@@ -1344,23 +1313,27 @@
 
         if (contentViewLocal != null) {
             contentViewLocal.setIsRootNamespace(true);
-            expanded.setContractedChild(contentViewLocal);
+            contentContainer.setContractedChild(contentViewLocal);
         }
         if (bigContentViewLocal != null) {
             bigContentViewLocal.setIsRootNamespace(true);
-            expanded.setExpandedChild(bigContentViewLocal);
+            contentContainer.setExpandedChild(bigContentViewLocal);
+        }
+        if (headsUpContentViewLocal != null) {
+            headsUpContentViewLocal.setIsRootNamespace(true);
+            contentContainer.setHeadsUpChild(headsUpContentViewLocal);
         }
 
         // now the public version
         View publicViewLocal = null;
         if (publicNotification != null) {
             try {
-                publicViewLocal = publicNotification.contentView.apply(mContext, expandedPublic,
+                publicViewLocal = publicNotification.contentView.apply(mContext, contentContainerPublic,
                         mOnClickHandler);
 
                 if (publicViewLocal != null) {
                     publicViewLocal.setIsRootNamespace(true);
-                    expandedPublic.setContractedChild(publicViewLocal);
+                    contentContainerPublic.setContractedChild(publicViewLocal);
                 }
             }
             catch (RuntimeException e) {
@@ -1382,9 +1355,9 @@
             // Add a basic notification template
             publicViewLocal = LayoutInflater.from(mContext).inflate(
                     R.layout.notification_public_default,
-                    expandedPublic, false);
+                    contentContainerPublic, false);
             publicViewLocal.setIsRootNamespace(true);
-            expandedPublic.setContractedChild(publicViewLocal);
+            contentContainerPublic.setContractedChild(publicViewLocal);
 
             final TextView title = (TextView) publicViewLocal.findViewById(R.id.title);
             try {
@@ -1520,13 +1493,8 @@
                 stripped.contentView = null;
                 stripped.extras.putBoolean("android.rebuild.bigView", true);
                 stripped.bigContentView = null;
-
-                // Don't create the HUN input view for now because input doesn't work there yet.
-                // TODO: Enable once HUNs can take remote input correctly.
-                if (false) {
-                    stripped.extras.putBoolean("android.rebuild.hudView", true);
-                    stripped.headsUpContentView = null;
-                }
+                stripped.extras.putBoolean("android.rebuild.hudView", true);
+                stripped.headsUpContentView = null;
 
                 Notification rebuilt = Notification.Builder.rebuild(mContext, stripped);
 
@@ -1558,16 +1526,23 @@
         }
 
         // See if we have somewhere to put that remote input
-        ViewGroup actionContainer = null;
-        if (remoteInput != null && entry.expandedBig != null) {
-            View actionContainerCandidate = entry.expandedBig
-                    .findViewById(com.android.internal.R.id.actions);
-            if (actionContainerCandidate instanceof ViewGroup) {
-                actionContainer = (ViewGroup) actionContainerCandidate;
+        if (remoteInput != null) {
+            if (entry.expandedBig != null) {
+                inflateRemoteInput(entry.expandedBig, remoteInput, actions);
+            }
+            View headsUpChild = entry.row.getPrivateLayout().getHeadsUpChild();
+            if (headsUpChild != null) {
+                inflateRemoteInput(headsUpChild, remoteInput, actions);
             }
         }
 
-        if (actionContainer != null) {
+    }
+
+    private void inflateRemoteInput(View view, RemoteInput remoteInput,
+            Notification.Action[] actions) {
+        View actionContainerCandidate = view.findViewById(com.android.internal.R.id.actions);
+        if (actionContainerCandidate instanceof ViewGroup) {
+            ViewGroup actionContainer = (ViewGroup) actionContainerCandidate;
             actionContainer.removeAllViews();
             actionContainer.addView(
                     RemoteInputView.inflate(mContext, actionContainer, actions[0], remoteInput));
@@ -1597,12 +1572,12 @@
                             mCurrentUserId);
             dismissKeyguardThenExecute(new OnDismissAction() {
                 public boolean onDismiss() {
-                    if (mNotificationKey.equals(mHeadsUpNotificationView.getKey())) {
+                    if (mHeadsUpManager != null && mHeadsUpManager.isHeadsUp(mNotificationKey)) {
                         // Release the HUN notification to the shade.
                         //
                         // In most cases, when FLAG_AUTO_CANCEL is set, the notification will
                         // become canceled shortly by NoMan, but we can't assume that.
-                        mHeadsUpNotificationView.releaseImmediately();
+                        mHeadsUpManager.releaseImmediately(mNotificationKey);
                     }
                     new Thread() {
                         @Override
@@ -1889,26 +1864,108 @@
         if (DEBUG) Log.d(TAG, "updateNotification(" + notification + ")");
 
         final String key = notification.getKey();
-        boolean wasHeadsUp = isHeadsUp(key);
-        Entry oldEntry;
-        if (wasHeadsUp) {
-            oldEntry = mHeadsUpNotificationView.getEntry();
-        } else {
-            oldEntry = mNotificationData.get(key);
-        }
-        if (oldEntry == null) {
+        Entry entry = mNotificationData.get(key);
+        if (entry == null) {
             return;
         }
 
-        final StatusBarNotification oldNotification = oldEntry.notification;
+        Notification n = notification.getNotification();
+        if (DEBUG) {
+            logUpdate(entry, n);
+        }
+        boolean applyInPlace = shouldApplyInPlace(entry, n);
+        final boolean shouldInterrupt = shouldInterrupt(notification);
+        final boolean alertAgain = alertAgain(entry, n);
 
+        entry.notification = notification;
+        mGroupManager.onEntryUpdated(entry, entry.notification);
+
+        boolean updateSuccessful = false;
+        if (applyInPlace) {
+            // We can just reapply the notifications in place
+            if (DEBUG) Log.d(TAG, "reusing notification for key: " + key);
+            try {
+                if (entry.icon != null) {
+                    // Update the icon
+                    final StatusBarIcon ic = new StatusBarIcon(notification.getPackageName(),
+                            notification.getUser(),
+                            n.icon,
+                            n.iconLevel,
+                            n.number,
+                            n.tickerText);
+                    entry.icon.setNotification(n);
+                    if (!entry.icon.set(ic)) {
+                        handleNotificationError(notification, "Couldn't update icon: " + ic);
+                        return;
+                    }
+                }
+                updateNotificationViews(entry, notification);
+                updateSuccessful = true;
+            }
+            catch (RuntimeException e) {
+                // It failed to add cleanly.  Log, and remove the view from the panel.
+                Log.w(TAG, "Couldn't reapply views for package " + n.contentView.getPackage(), e);
+            }
+        }
+        if (!updateSuccessful) {
+            if (DEBUG) Log.d(TAG, "not reusing notification for key: " + key);
+            final StatusBarIcon ic = new StatusBarIcon(notification.getPackageName(),
+                    notification.getUser(),
+                    n.icon,
+                    n.iconLevel,
+                    n.number,
+                    n.tickerText);
+            entry.icon.setNotification(n);
+            entry.icon.set(ic);
+            inflateViews(entry, mStackScroller);
+        }
+        updateHeadsUp(key, entry, shouldInterrupt, alertAgain);
+        mNotificationData.updateRanking(ranking);
+        updateNotifications();
+
+        // Update the veto button accordingly (and as a result, whether this row is
+        // swipe-dismissable)
+        updateNotificationVetoButton(entry.row, notification);
+
+        // Is this for you?
+        boolean isForCurrentUser = isNotificationForCurrentProfiles(notification);
+        if (DEBUG) Log.d(TAG, "notification is " + (isForCurrentUser ? "" : "not ") + "for you");
+
+        // Recalculate the position of the sliding windows and the titles.
+        setAreThereNotifications();
+    }
+
+    protected abstract void updateHeadsUp(String key, Entry entry, boolean shouldInterrupt,
+            boolean alertAgain);
+
+    private void logUpdate(Entry oldEntry, Notification n) {
+        StatusBarNotification oldNotification = oldEntry.notification;
+        Log.d(TAG, "old notification: when=" + oldNotification.getNotification().when
+                + " ongoing=" + oldNotification.isOngoing()
+                + " expanded=" + oldEntry.expanded
+                + " contentView=" + oldNotification.getNotification().contentView
+                + " bigContentView=" + oldNotification.getNotification().bigContentView
+                + " publicView=" + oldNotification.getNotification().publicVersion
+                + " rowParent=" + oldEntry.row.getParent());
+        Log.d(TAG, "new notification: when=" + n.when
+                + " ongoing=" + oldNotification.isOngoing()
+                + " contentView=" + n.contentView
+                + " bigContentView=" + n.bigContentView
+                + " publicView=" + n.publicVersion);
+    }
+
+    /**
+     * @return whether we can just reapply the RemoteViews in place when it is updated
+     */
+    private boolean shouldApplyInPlace(Entry entry, Notification n) {
+        StatusBarNotification oldNotification = entry.notification;
         // XXX: modify when we do something more intelligent with the two content views
         final RemoteViews oldContentView = oldNotification.getNotification().contentView;
-        Notification n = notification.getNotification();
         final RemoteViews contentView = n.contentView;
         final RemoteViews oldBigContentView = oldNotification.getNotification().bigContentView;
         final RemoteViews bigContentView = n.bigContentView;
-        final RemoteViews oldHeadsUpContentView = oldNotification.getNotification().headsUpContentView;
+        final RemoteViews oldHeadsUpContentView
+                = oldNotification.getNotification().headsUpContentView;
         final RemoteViews headsUpContentView = n.headsUpContentView;
         final Notification oldPublicNotification = oldNotification.getNotification().publicVersion;
         final RemoteViews oldPublicContentView = oldPublicNotification != null
@@ -1916,34 +1973,15 @@
         final Notification publicNotification = n.publicVersion;
         final RemoteViews publicContentView = publicNotification != null
                 ? publicNotification.contentView : null;
-
-        if (DEBUG) {
-            Log.d(TAG, "old notification: when=" + oldNotification.getNotification().when
-                    + " ongoing=" + oldNotification.isOngoing()
-                    + " expanded=" + oldEntry.expanded
-                    + " contentView=" + oldContentView
-                    + " bigContentView=" + oldBigContentView
-                    + " publicView=" + oldPublicContentView
-                    + " rowParent=" + oldEntry.row.getParent());
-            Log.d(TAG, "new notification: when=" + n.when
-                    + " ongoing=" + oldNotification.isOngoing()
-                    + " contentView=" + contentView
-                    + " bigContentView=" + bigContentView
-                    + " publicView=" + publicContentView);
-        }
-
-        // Can we just reapply the RemoteViews in place?
-
-        // 1U is never null
-        boolean contentsUnchanged = oldEntry.expanded != null
+        boolean contentsUnchanged = entry.expanded != null
                 && contentView.getPackage() != null
                 && oldContentView.getPackage() != null
                 && oldContentView.getPackage().equals(contentView.getPackage())
                 && oldContentView.getLayoutId() == contentView.getLayoutId();
         // large view may be null
         boolean bigContentsUnchanged =
-                (oldEntry.getBigContentView() == null && bigContentView == null)
-                || ((oldEntry.getBigContentView() != null && bigContentView != null)
+                (entry.getBigContentView() == null && bigContentView == null)
+                || ((entry.getBigContentView() != null && bigContentView != null)
                     && bigContentView.getPackage() != null
                     && oldBigContentView.getPackage() != null
                     && oldBigContentView.getPackage().equals(bigContentView.getPackage())
@@ -1962,131 +2000,14 @@
                         && oldPublicContentView.getPackage() != null
                         && oldPublicContentView.getPackage().equals(publicContentView.getPackage())
                         && oldPublicContentView.getLayoutId() == publicContentView.getLayoutId());
-
-        final boolean shouldInterrupt = shouldInterrupt(notification);
-        final boolean alertAgain = shouldInterrupt && alertAgain(oldEntry, n);
-        boolean updateSuccessful = false;
-        if (contentsUnchanged && bigContentsUnchanged && headsUpContentsUnchanged
-                && publicUnchanged) {
-            if (DEBUG) Log.d(TAG, "reusing notification for key: " + key);
-            oldEntry.notification = notification;
-            mGroupManager.onEntryUpdated(oldEntry, oldNotification);
-            try {
-                if (oldEntry.icon != null) {
-                    // Update the icon
-                    final StatusBarIcon ic = new StatusBarIcon(notification.getPackageName(),
-                            notification.getUser(),
-                            n.icon,
-                            n.iconLevel,
-                            n.number,
-                            n.tickerText);
-                    oldEntry.icon.setNotification(n);
-                    if (!oldEntry.icon.set(ic)) {
-                        handleNotificationError(notification, "Couldn't update icon: " + ic);
-                        return;
-                    }
-                }
-
-                if (wasHeadsUp) {
-                    // Release may hang on to the views for a bit, so we should always update them.
-                    updateHeadsUpViews(oldEntry, notification);
-                    mHeadsUpNotificationView.updateNotification(oldEntry, alertAgain);
-                    if (!shouldInterrupt) {
-                        // we updated the notification above, so release to build a new shade entry
-                        mHeadsUpNotificationView.release();
-                        return;
-                    }
-                } else {
-                    if (shouldInterrupt && alertAgain) {
-                        mStackScroller.setRemoveAnimationEnabled(false);
-                        removeNotificationViews(key, ranking);
-                        mStackScroller.setRemoveAnimationEnabled(true);
-                        addNotification(notification, ranking, oldEntry);  //this will pop the headsup
-                    } else {
-                        updateNotificationViews(oldEntry, notification);
-                    }
-                }
-                mNotificationData.updateRanking(ranking);
-                updateNotifications();
-                updateSuccessful = true;
-            }
-            catch (RuntimeException e) {
-                // It failed to add cleanly.  Log, and remove the view from the panel.
-                Log.w(TAG, "Couldn't reapply views for package " + contentView.getPackage(), e);
-            }
-        }
-        if (!updateSuccessful) {
-            if (DEBUG) Log.d(TAG, "not reusing notification for key: " + key);
-            if (wasHeadsUp) {
-                if (DEBUG) Log.d(TAG, "rebuilding heads up for key: " + key);
-                ViewGroup holder = mHeadsUpNotificationView.getHolder();
-                if (inflateViewsForHeadsUp(oldEntry, holder)) {
-                    mHeadsUpNotificationView.updateNotification(oldEntry, alertAgain);
-                } else {
-                    Log.w(TAG, "Couldn't create new updated headsup for package "
-                            + contentView.getPackage());
-                }
-                if (!shouldInterrupt) {
-                    if (DEBUG) Log.d(TAG, "releasing heads up for key: " + key);
-                    oldEntry.notification = notification;
-                    mGroupManager.onEntryUpdated(oldEntry, oldNotification);
-                    mHeadsUpNotificationView.release();
-                    return;
-                }
-            } else {
-                if (shouldInterrupt && alertAgain) {
-                    if (DEBUG) Log.d(TAG, "reposting to invoke heads up for key: " + key);
-                    mStackScroller.setRemoveAnimationEnabled(false);
-                    removeNotificationViews(key, ranking);
-                    mStackScroller.setRemoveAnimationEnabled(true);
-                    addNotification(notification, ranking, oldEntry);  //this will pop the headsup
-                } else {
-                    if (DEBUG) Log.d(TAG, "rebuilding update in place for key: " + key);
-                    oldEntry.notification = notification;
-                    mGroupManager.onEntryUpdated(oldEntry, oldNotification);
-                    final StatusBarIcon ic = new StatusBarIcon(notification.getPackageName(),
-                            notification.getUser(),
-                            n.icon,
-                            n.iconLevel,
-                            n.number,
-                            n.tickerText);
-                    oldEntry.icon.setNotification(n);
-                    oldEntry.icon.set(ic);
-                    inflateViews(oldEntry, mStackScroller, wasHeadsUp);
-                    mNotificationData.updateRanking(ranking);
-                    updateNotifications();
-                }
-            }
-        }
-
-        // Update the veto button accordingly (and as a result, whether this row is
-        // swipe-dismissable)
-        updateNotificationVetoButton(oldEntry.row, notification);
-
-        // Is this for you?
-        boolean isForCurrentUser = isNotificationForCurrentProfiles(notification);
-        if (DEBUG) Log.d(TAG, "notification is " + (isForCurrentUser ? "" : "not ") + "for you");
-
-        // Recalculate the position of the sliding windows and the titles.
-        setAreThereNotifications();
+        return contentsUnchanged && bigContentsUnchanged && headsUpContentsUnchanged
+                && publicUnchanged;
     }
 
-    private void updateNotificationViews(NotificationData.Entry entry,
-            StatusBarNotification notification) {
-        updateNotificationViews(entry, notification, false);
-    }
-
-    private void updateHeadsUpViews(NotificationData.Entry entry,
-            StatusBarNotification notification) {
-        updateNotificationViews(entry, notification, true);
-    }
-
-    private void updateNotificationViews(NotificationData.Entry entry,
-            StatusBarNotification notification, boolean isHeadsUp) {
+    private void updateNotificationViews(Entry entry, StatusBarNotification notification) {
         final RemoteViews contentView = notification.getNotification().contentView;
-        final RemoteViews bigContentView = isHeadsUp
-                ? notification.getNotification().headsUpContentView
-                : notification.getNotification().bigContentView;
+        final RemoteViews bigContentView = notification.getNotification().bigContentView;
+        final RemoteViews headsUpContentView = notification.getNotification().headsUpContentView;
         final Notification publicVersion = notification.getNotification().publicVersion;
         final RemoteViews publicContentView = publicVersion != null ? publicVersion.contentView
                 : null;
@@ -2097,6 +2018,10 @@
             bigContentView.reapply(mContext, entry.getBigContentView(),
                     mOnClickHandler);
         }
+        View headsUpChild = entry.row.getPrivateLayout().getHeadsUpChild();
+        if (headsUpContentView != null && headsUpChild != null) {
+            headsUpContentView.reapply(mContext, headsUpChild, mOnClickHandler);
+        }
         if (publicContentView != null && entry.getPublicContentView() != null) {
             publicContentView.reapply(mContext, entry.getPublicContentView(), mOnClickHandler);
         }
@@ -2115,10 +2040,8 @@
         applyRemoteInput(entry);
     }
 
-    protected void notifyHeadsUpScreenOn(boolean screenOn) {
-        if (!screenOn) {
-            scheduleHeadsUpEscalation();
-        }
+    protected void notifyHeadsUpScreenOff() {
+        escalateHeadsUp();
     }
 
     private boolean alertAgain(Entry oldEntry, Notification newNotification) {
@@ -2134,7 +2057,7 @@
             return false;
         }
 
-        if (mHeadsUpNotificationView.isSnoozed(sbn.getPackageName())) {
+        if (isSnoozedPackage(sbn)) {
             return false;
         }
 
@@ -2168,6 +2091,8 @@
         return interrupt;
     }
 
+    protected abstract boolean isSnoozedPackage(StatusBarNotification sbn);
+
     public void setInteracting(int barWindow, boolean interacting) {
         // hook for subclasses
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
index 06a174e..cb8217e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
@@ -62,7 +62,7 @@
     private boolean mShowingPublic;
     private boolean mSensitive;
     private boolean mShowingPublicInitialized;
-    private boolean mShowingPublicForIntrinsicHeight;
+    private boolean mHideSensitiveForIntrinsicHeight;
 
     /**
      * Is this notification expanded by the system. The expansion state can be overridden by the
@@ -78,13 +78,14 @@
     private NotificationContentView mPublicLayout;
     private NotificationContentView mPrivateLayout;
     private int mMaxExpandHeight;
+    private int mHeadsUpHeight;
     private View mVetoButton;
     private boolean mClearable;
     private ExpansionLogger mLogger;
     private String mLoggingKey;
     private boolean mWasReset;
-    private NotificationGuts mGuts;
 
+    private NotificationGuts mGuts;
     private StatusBarNotification mStatusBarNotification;
     private boolean mIsHeadsUp;
     private View mExpandButton;
@@ -108,6 +109,15 @@
                     !mChildrenExpanded);
         }
     };
+    private boolean mInShade;
+
+    public NotificationContentView getPrivateLayout() {
+        return mPrivateLayout;
+    }
+
+    public NotificationContentView getPublicLayout() {
+        return mPublicLayout;
+    }
 
     public void setIconAnimationRunning(boolean running) {
         setIconAnimationRunning(running, mPublicLayout);
@@ -118,8 +128,10 @@
         if (layout != null) {
             View contractedChild = layout.getContractedChild();
             View expandedChild = layout.getExpandedChild();
+            View headsUpChild = layout.getHeadsUpChild();
             setIconAnimationRunningForChild(running, contractedChild);
             setIconAnimationRunningForChild(running, expandedChild);
+            setIconAnimationRunningForChild(running, headsUpChild);
         }
     }
 
@@ -164,8 +176,17 @@
         return mStatusBarNotification;
     }
 
+    public boolean isHeadsUp() {
+        return mIsHeadsUp;
+    }
+
     public void setHeadsUp(boolean isHeadsUp) {
+        int intrinsicBefore = getIntrinsicHeight();
         mIsHeadsUp = isHeadsUp;
+        mPrivateLayout.setHeadsUp(isHeadsUp);
+        if (intrinsicBefore != getIntrinsicHeight()) {
+            notifyHeightChanged(false  /* needsAnimation */);
+        }
     }
 
     public void setGroupManager(NotificationGroupManager groupManager) {
@@ -263,6 +284,18 @@
         return realActualHeight;
     }
 
+    public void setInShade(boolean inShade) {
+        mInShade = inShade;
+    }
+
+    public boolean isInShade() {
+        return mInShade;
+    }
+
+    public int getHeadsUpHeight() {
+        return mHeadsUpHeight;
+    }
+
     public interface ExpansionLogger {
         public void logNotificationExpansion(String key, boolean userAction, boolean expanded);
     }
@@ -299,6 +332,7 @@
             resetActualHeight();
         }
         mMaxExpandHeight = 0;
+        mHeadsUpHeight = 0;
         mWasReset = true;
         onHeightReset();
         requestLayout();
@@ -536,7 +570,15 @@
         }
         boolean inExpansionState = isExpanded();
         int maxContentHeight;
-        if ((!inExpansionState && !mChildrenExpanded) || mShowingPublicForIntrinsicHeight) {
+        if (mSensitive && mHideSensitiveForIntrinsicHeight) {
+            return mRowMinHeight;
+        } else if (mIsHeadsUp) {
+            if (inExpansionState) {
+                maxContentHeight = Math.max(mMaxExpandHeight, mHeadsUpHeight);
+            } else {
+                maxContentHeight = Math.max(mRowMinHeight, mHeadsUpHeight);
+            }
+        } else if ((!inExpansionState && !mChildrenExpanded)) {
             maxContentHeight = mRowMinHeight;
         } else if (mChildrenExpanded) {
             maxContentHeight = mChildrenContainer.getIntrinsicHeight();
@@ -583,7 +625,7 @@
     protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
         super.onLayout(changed, left, top, right, bottom);
         boolean updateExpandHeight = mMaxExpandHeight == 0 && !mWasReset;
-        updateMaxExpandHeight();
+        updateMaxHeights();
         if (updateExpandHeight) {
             applyExpansionToLayout();
         }
@@ -599,9 +641,18 @@
         return super.isChildInvisible(child) || isInvisibleChildContainer;
     }
 
-    private void updateMaxExpandHeight() {
+    private void updateMaxHeights() {
         int intrinsicBefore = getIntrinsicHeight();
-        mMaxExpandHeight = mPrivateLayout.getMaxHeight();
+        View expandedChild = mPrivateLayout.getExpandedChild();
+        if (expandedChild == null) {
+            expandedChild = mPrivateLayout.getContractedChild();
+        }
+        mMaxExpandHeight = expandedChild.getHeight();
+        View headsUpChild = mPrivateLayout.getHeadsUpChild();
+        if (headsUpChild == null) {
+            headsUpChild = mPrivateLayout.getContractedChild();
+        }
+        mHeadsUpHeight = headsUpChild.getHeight();
         if (intrinsicBefore != getIntrinsicHeight()) {
             notifyHeightChanged(false  /* needsAnimation */);
         }
@@ -612,7 +663,7 @@
     }
 
     public void setHideSensitiveForIntrinsicHeight(boolean hideSensitive) {
-        mShowingPublicForIntrinsicHeight = mSensitive && hideSensitive;
+        mHideSensitiveForIntrinsicHeight = hideSensitive;
     }
 
     public void setHideSensitive(boolean hideSensitive, boolean animated, long delay,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java
index 7ae0d6d..e632cc8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java
@@ -118,6 +118,7 @@
                 setContentHeight(initialHeight);
             }
         }
+        updateClipping();
     }
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java
index 745e75d..964d75f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java
@@ -23,10 +23,12 @@
 import android.graphics.Rect;
 import android.util.AttributeSet;
 import android.view.View;
+import android.view.ViewGroup;
 import android.view.ViewTreeObserver;
 import android.view.animation.Interpolator;
 import android.view.animation.LinearInterpolator;
 import android.widget.FrameLayout;
+
 import com.android.systemui.R;
 
 /**
@@ -37,23 +39,28 @@
 public class NotificationContentView extends FrameLayout {
 
     private static final long ANIMATION_DURATION_LENGTH = 170;
+    private static final int CONTRACTED = 1;
+    private static final int EXPANDED = 2;
+    private static final int HEADSUP = 3;
 
     private final Rect mClipBounds = new Rect();
 
     private View mContractedChild;
     private View mExpandedChild;
+    private View mHeadsUpChild;
 
     private NotificationViewWrapper mContractedWrapper;
 
-    private int mSmallHeight;
+    private final int mSmallHeight;
+    private final int mHeadsUpHeight;
     private int mClipTopAmount;
+
     private int mContentHeight;
 
     private final Interpolator mLinearInterpolator = new LinearInterpolator();
+    private int mVisibleView = CONTRACTED;
 
-    private boolean mContractedVisible = true;
     private boolean mDark;
-
     private final Paint mFadePaint = new Paint();
     private boolean mAnimate;
     private ViewTreeObserver.OnPreDrawListener mEnableAnimationPredrawListener
@@ -65,14 +72,62 @@
             return true;
         }
     };
+    private boolean mIsHeadsUp;
 
     public NotificationContentView(Context context, AttributeSet attrs) {
         super(context, attrs);
         mFadePaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.ADD));
+        mSmallHeight = getResources().getDimensionPixelSize(R.dimen.notification_min_height);
+        mHeadsUpHeight = getResources().getDimensionPixelSize(R.dimen.notification_mid_height);
         reset(true);
     }
 
     @Override
+    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+        int heightMode = MeasureSpec.getMode(heightMeasureSpec);
+        boolean hasFixedHeight = heightMode == MeasureSpec.EXACTLY;
+        boolean isHeightLimited = heightMode == MeasureSpec.AT_MOST;
+        int maxSize = Integer.MAX_VALUE;
+        if (hasFixedHeight || isHeightLimited) {
+            maxSize = MeasureSpec.getSize(heightMeasureSpec);
+        }
+        int maxChildHeight = 0;
+        if (mContractedChild != null) {
+            int size = Math.min(maxSize, mSmallHeight);
+            mContractedChild.measure(widthMeasureSpec,
+                    MeasureSpec.makeMeasureSpec(size, MeasureSpec.AT_MOST));
+            maxChildHeight = Math.max(maxChildHeight, mContractedChild.getMeasuredHeight());
+        }
+        if (mExpandedChild != null) {
+            int size = maxSize;
+            ViewGroup.LayoutParams layoutParams = mExpandedChild.getLayoutParams();
+            if (layoutParams.height >= 0) {
+                // An actual height is set
+                size = Math.min(maxSize, layoutParams.height);
+            }
+            int spec = size == Integer.MAX_VALUE ?
+                    MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED) :
+                    MeasureSpec.makeMeasureSpec(size, MeasureSpec.AT_MOST);
+            mExpandedChild.measure(widthMeasureSpec, spec);
+            maxChildHeight = Math.max(maxChildHeight, mExpandedChild.getMeasuredHeight());
+        }
+        if (mHeadsUpChild != null) {
+            int size = Math.min(maxSize, mHeadsUpHeight);
+            ViewGroup.LayoutParams layoutParams = mHeadsUpChild.getLayoutParams();
+            if (layoutParams.height >= 0) {
+                // An actual height is set
+                size = Math.min(maxSize, layoutParams.height);
+            }
+            mHeadsUpChild.measure(widthMeasureSpec,
+                    MeasureSpec.makeMeasureSpec(size, MeasureSpec.AT_MOST));
+            maxChildHeight = Math.max(maxChildHeight, mHeadsUpChild.getMeasuredHeight());
+        }
+        int ownHeight = Math.min(maxChildHeight, maxSize);
+        int width = MeasureSpec.getSize(widthMeasureSpec);
+        setMeasuredDimension(width, ownHeight);
+    }
+
+    @Override
     protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
         super.onLayout(changed, left, top, right, bottom);
         updateClipping();
@@ -91,11 +146,14 @@
         if (mExpandedChild != null) {
             mExpandedChild.animate().cancel();
         }
+        if (mHeadsUpChild != null) {
+            mHeadsUpChild.animate().cancel();
+        }
         removeAllViews();
         mContractedChild = null;
         mExpandedChild = null;
-        mSmallHeight = getResources().getDimensionPixelSize(R.dimen.notification_min_height);
-        mContractedVisible = true;
+        mHeadsUpChild = null;
+        mVisibleView = CONTRACTED;
         if (resetActualHeight) {
             mContentHeight = mSmallHeight;
         }
@@ -109,12 +167,15 @@
         return mExpandedChild;
     }
 
+    public View getHeadsUpChild() {
+        return mHeadsUpChild;
+    }
+
     public void setContractedChild(View child) {
         if (mContractedChild != null) {
             mContractedChild.animate().cancel();
             removeView(mContractedChild);
         }
-        sanitizeContractedLayoutParams(child);
         addView(child);
         mContractedChild = child;
         mContractedWrapper = NotificationViewWrapper.wrap(getContext(), child);
@@ -132,6 +193,16 @@
         selectLayout(false /* animate */, true /* force */);
     }
 
+    public void setHeadsUpChild(View child) {
+        if (mHeadsUpChild != null) {
+            mHeadsUpChild.animate().cancel();
+            removeView(mHeadsUpChild);
+        }
+        addView(child);
+        mHeadsUpChild = child;
+        selectLayout(false /* animate */, true /* force */);
+    }
+
     @Override
     protected void onVisibilityChanged(View changedView, int visibility) {
         super.onVisibilityChanged(changedView, visibility);
@@ -166,9 +237,12 @@
     }
 
     public int getMaxHeight() {
-
-        // The maximum height is just the laid out height.
-        return getHeight();
+        if (mIsHeadsUp && mHeadsUpChild != null) {
+            return mHeadsUpChild.getHeight();
+        } else if (mExpandedChild != null) {
+            return mExpandedChild.getHeight();
+        }
+        return mSmallHeight;
     }
 
     public int getMinHeight() {
@@ -185,62 +259,91 @@
         setClipBounds(mClipBounds);
     }
 
-    private void sanitizeContractedLayoutParams(View contractedChild) {
-        LayoutParams lp = (LayoutParams) contractedChild.getLayoutParams();
-        lp.height = mSmallHeight;
-        contractedChild.setLayoutParams(lp);
-    }
-
     private void selectLayout(boolean animate, boolean force) {
         if (mContractedChild == null) {
             return;
         }
-        boolean showContractedChild = showContractedChild();
-        if (showContractedChild != mContractedVisible || force) {
+        int visibleView = calculateVisibleView();
+        if (visibleView != mVisibleView || force) {
             if (animate && mExpandedChild != null) {
-                runSwitchAnimation(showContractedChild);
-            } else if (mExpandedChild != null) {
-                mContractedChild.setVisibility(showContractedChild ? View.VISIBLE : View.INVISIBLE);
-                mContractedChild.setAlpha(showContractedChild ? 1f : 0f);
-                mExpandedChild.setVisibility(showContractedChild ? View.INVISIBLE : View.VISIBLE);
-                mExpandedChild.setAlpha(showContractedChild ? 0f : 1f);
+                runSwitchAnimation(visibleView);
+            } else {
+                updateViewVisibilities(visibleView);
             }
+            mVisibleView = visibleView;
         }
-        mContractedVisible = showContractedChild;
     }
 
-    private void runSwitchAnimation(final boolean showContractedChild) {
-        mContractedChild.setVisibility(View.VISIBLE);
-        mExpandedChild.setVisibility(View.VISIBLE);
-        mContractedChild.setLayerType(LAYER_TYPE_HARDWARE, mFadePaint);
-        mExpandedChild.setLayerType(LAYER_TYPE_HARDWARE, mFadePaint);
+    private void updateViewVisibilities(int visibleView) {
+        boolean contractedVisible = visibleView == CONTRACTED;
+        mContractedChild.setVisibility(contractedVisible ? View.VISIBLE : View.INVISIBLE);
+        mContractedChild.setAlpha(contractedVisible ? 1f : 0f);
+        mContractedChild.setLayerType(LAYER_TYPE_NONE, null);
+        if (mExpandedChild != null) {
+            boolean expandedVisible = visibleView == EXPANDED;
+            mExpandedChild.setVisibility(expandedVisible ? View.VISIBLE : View.INVISIBLE);
+            mExpandedChild.setAlpha(expandedVisible ? 1f : 0f);
+            mExpandedChild.setLayerType(LAYER_TYPE_NONE, null);
+        }
+        if (mHeadsUpChild != null) {
+            boolean headsUpVisible = visibleView == HEADSUP;
+            mHeadsUpChild.setVisibility(headsUpVisible ? View.VISIBLE : View.INVISIBLE);
+            mHeadsUpChild.setAlpha(headsUpVisible ? 1f : 0f);
+            mHeadsUpChild.setLayerType(LAYER_TYPE_NONE, null);
+        }
+        setLayerType(LAYER_TYPE_NONE, null);
+    }
+
+    private void runSwitchAnimation(int visibleView) {
+        View shownView = getViewFromFlag(visibleView);
+        View hiddenView = getViewFromFlag(mVisibleView);
+        shownView.setVisibility(View.VISIBLE);
+        hiddenView.setVisibility(View.VISIBLE);
+        shownView.setLayerType(LAYER_TYPE_HARDWARE, mFadePaint);
+        hiddenView.setLayerType(LAYER_TYPE_HARDWARE, mFadePaint);
         setLayerType(LAYER_TYPE_HARDWARE, null);
-        mContractedChild.animate()
-                .alpha(showContractedChild ? 1f : 0f)
+        hiddenView.animate()
+                .alpha(0f)
                 .setDuration(ANIMATION_DURATION_LENGTH)
-                .setInterpolator(mLinearInterpolator);
-        mExpandedChild.animate()
-                .alpha(showContractedChild ? 0f : 1f)
+                .setInterpolator(mLinearInterpolator)
+                .withEndAction(null); // In case we have multiple changes in one frame.
+        shownView.animate()
+                .alpha(1f)
                 .setDuration(ANIMATION_DURATION_LENGTH)
                 .setInterpolator(mLinearInterpolator)
                 .withEndAction(new Runnable() {
                     @Override
                     public void run() {
-                        mContractedChild.setLayerType(LAYER_TYPE_NONE, null);
-                        mExpandedChild.setLayerType(LAYER_TYPE_NONE, null);
-                        setLayerType(LAYER_TYPE_NONE, null);
-                        mContractedChild.setVisibility(showContractedChild
-                                ? View.VISIBLE
-                                : View.INVISIBLE);
-                        mExpandedChild.setVisibility(showContractedChild
-                                ? View.INVISIBLE
-                                : View.VISIBLE);
+                        updateViewVisibilities(mVisibleView);
                     }
                 });
     }
 
-    private boolean showContractedChild() {
-        return mContentHeight <= mSmallHeight || mExpandedChild == null;
+    private View getViewFromFlag(int visibleView) {
+        switch (visibleView) {
+            case EXPANDED:
+                return mExpandedChild;
+            case HEADSUP:
+                return mHeadsUpChild;
+        }
+        return mContractedChild;
+    }
+
+    private int calculateVisibleView() {
+        boolean noExpandedChild = mExpandedChild == null;
+        if (mIsHeadsUp && mHeadsUpChild != null) {
+            if (mContentHeight <= mHeadsUpChild.getHeight() || noExpandedChild) {
+                return HEADSUP;
+            } else {
+                return EXPANDED;
+            }
+        } else {
+            if (mContentHeight <= mSmallHeight || noExpandedChild) {
+                return CONTRACTED;
+            } else {
+                return EXPANDED;
+            }
+        }
     }
 
     public void notifyContentUpdated() {
@@ -261,6 +364,11 @@
         mContractedWrapper.setDark(dark, fade, delay);
     }
 
+    public void setHeadsUp(boolean headsUp) {
+        mIsHeadsUp = headsUp;
+        selectLayout(false /* animate */, true /* force */);
+    }
+
     @Override
     public boolean hasOverlappingRendering() {
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java
index 912f414..429889d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java
@@ -25,6 +25,7 @@
 import android.view.View;
 
 import com.android.systemui.statusbar.phone.NotificationGroupManager;
+import com.android.systemui.statusbar.policy.HeadsUpManager;
 
 import java.io.PrintWriter;
 import java.util.ArrayList;
@@ -37,6 +38,7 @@
 public class NotificationData {
 
     private final Environment mEnvironment;
+    private HeadsUpManager mHeadsUpManager;
 
     public static final class Entry {
         public String key;
@@ -98,43 +100,47 @@
     private RankingMap mRankingMap;
     private final Ranking mTmpRanking = new Ranking();
 
+    public void setHeadsUpManager(HeadsUpManager headsUpManager) {
+        mHeadsUpManager = headsUpManager;
+    }
+
     private final Comparator<Entry> mRankingComparator = new Comparator<Entry>() {
         private final Ranking mRankingA = new Ranking();
         private final Ranking mRankingB = new Ranking();
 
         @Override
         public int compare(Entry a, Entry b) {
-            // Upsort current media notification.
             String mediaNotification = mEnvironment.getCurrentMediaNotificationKey();
             boolean aMedia = a.key.equals(mediaNotification);
             boolean bMedia = b.key.equals(mediaNotification);
-            if (aMedia != bMedia) {
-                return aMedia ? -1 : 1;
-            }
 
             final StatusBarNotification na = a.notification;
             final StatusBarNotification nb = b.notification;
 
-            // Upsort PRIORITY_MAX system notifications
             boolean aSystemMax = na.getNotification().priority >= Notification.PRIORITY_MAX &&
                     isSystemNotification(na);
             boolean bSystemMax = nb.getNotification().priority >= Notification.PRIORITY_MAX &&
                     isSystemNotification(nb);
-            if (aSystemMax != bSystemMax) {
-                return aSystemMax ? -1 : 1;
-            }
+            int d = nb.getScore() - na.getScore();
 
-            // RankingMap as received from NoMan.
-            if (mRankingMap != null) {
+            boolean isHeadsUp = a.row.isHeadsUp();
+            if (isHeadsUp != b.row.isHeadsUp()) {
+                return isHeadsUp ? -1 : 1;
+            } else if (isHeadsUp) {
+                // Provide consistent ranking with headsUpManager
+                return mHeadsUpManager.compare(a, b);
+            } else if (aMedia != bMedia) {
+                // Upsort current media notification.
+                return aMedia ? -1 : 1;
+            } else if (aSystemMax != bSystemMax) {
+                // Upsort PRIORITY_MAX system notifications
+                return aSystemMax ? -1 : 1;
+            } else if (mRankingMap != null) {
+                // RankingMap as received from NoMan
                 mRankingMap.getRanking(a.key, mRankingA);
                 mRankingMap.getRanking(b.key, mRankingB);
                 return mRankingA.getRank() - mRankingB.getRank();
-            }
-
-            int d = nb.getScore() - na.getScore();
-            if (a.interruption != b.interruption) {
-                return a.interruption ? -1 : 1;
-            } else if (d != 0) {
+            } if (d != 0) {
                 return d;
             } else {
                 return (int) (nb.getNotification().when - na.getNotification().when);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/SignalClusterView.java b/packages/SystemUI/src/com/android/systemui/statusbar/SignalClusterView.java
index a82afcf..b2bb021 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/SignalClusterView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/SignalClusterView.java
@@ -20,6 +20,8 @@
 import android.content.res.ColorStateList;
 import android.graphics.Color;
 import android.graphics.PorterDuff;
+import android.graphics.drawable.Animatable;
+import android.graphics.drawable.Drawable;
 import android.telephony.SubscriptionInfo;
 import android.util.AttributeSet;
 import android.util.Log;
@@ -165,12 +167,13 @@
     }
 
     @Override
-    public void setMobileDataIndicators(boolean visible, int strengthIcon, int typeIcon,
-            String contentDescription, String typeContentDescription, boolean isTypeIconWide,
-            int subId) {
+    public void setMobileDataIndicators(boolean visible, int strengthIcon, int darkStrengthIcon,
+            int typeIcon, String contentDescription, String typeContentDescription,
+            boolean isTypeIconWide, int subId) {
         PhoneState state = getOrInflateState(subId);
         state.mMobileVisible = visible;
         state.mMobileStrengthId = strengthIcon;
+        state.mMobileDarkStrengthId = darkStrengthIcon;
         state.mMobileTypeId = typeIcon;
         state.mMobileDescription = contentDescription;
         state.mMobileTypeDescription = typeContentDescription;
@@ -360,7 +363,7 @@
     private class PhoneState {
         private final int mSubId;
         private boolean mMobileVisible = false;
-        private int mMobileStrengthId = 0, mMobileTypeId = 0;
+        private int mMobileStrengthId = 0, mMobileDarkStrengthId = 0, mMobileTypeId = 0;
         private boolean mIsMobileTypeIconWide;
         private String mMobileDescription, mMobileTypeDescription;
 
@@ -384,7 +387,23 @@
         public boolean apply(boolean isSecondaryIcon) {
             if (mMobileVisible && !mIsAirplaneMode) {
                 mMobile.setImageResource(mMobileStrengthId);
+                Drawable mobileDrawable = mMobile.getDrawable();
+                if (mobileDrawable instanceof Animatable) {
+                    Animatable ad = (Animatable) mobileDrawable;
+                    if (!ad.isRunning()) {
+                        ad.start();
+                    }
+                }
+
                 mMobileDark.setImageResource(mMobileStrengthId);
+                Drawable mobileDarkDrawable = mMobileDark.getDrawable();
+                if (mobileDarkDrawable instanceof Animatable) {
+                    Animatable ad = (Animatable) mobileDarkDrawable;
+                    if (!ad.isRunning()) {
+                        ad.start();
+                    }
+                }
+
                 mMobileType.setImageResource(mMobileTypeId);
                 mMobileGroup.setContentDescription(mMobileTypeDescription
                         + " " + mMobileDescription);
@@ -401,8 +420,9 @@
             mMobileDark.setPaddingRelative(mIsMobileTypeIconWide ? mWideTypeIconStartPadding : 0,
                     0, 0, 0);
 
-            if (DEBUG) Log.d(TAG, String.format("mobile: %s sig=%d typ=%d",
-                        (mMobileVisible ? "VISIBLE" : "GONE"), mMobileStrengthId, mMobileTypeId));
+            if (DEBUG) Log.d(TAG, String.format("mobile: %s sig=%d dark=%d typ=%d",
+                        (mMobileVisible ? "VISIBLE" : "GONE"), mMobileStrengthId,
+                        mMobileDarkStrengthId, mMobileTypeId));
 
             mMobileType.setVisibility(mMobileTypeId != 0 ? View.VISIBLE : View.GONE);
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpTouchHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpTouchHelper.java
new file mode 100644
index 0000000..3997807
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpTouchHelper.java
@@ -0,0 +1,160 @@
+/*
+ * 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.view.MotionEvent;
+import android.view.ViewConfiguration;
+
+import com.android.systemui.Gefingerpoken;
+import com.android.systemui.statusbar.ExpandableNotificationRow;
+import com.android.systemui.statusbar.ExpandableView;
+import com.android.systemui.statusbar.policy.HeadsUpManager;
+import com.android.systemui.statusbar.stack.NotificationStackScrollLayout;
+
+/**
+ * A Helper class to handle touches on the heads-up views
+ */
+public class HeadsUpTouchHelper implements Gefingerpoken {
+
+    private HeadsUpManager mHeadsUpManager;
+    private NotificationStackScrollLayout mStackScroller;
+    private int mTrackingPointer;
+    private float mTouchSlop;
+    private float mInitialTouchX;
+    private float mInitialTouchY;
+    private boolean mMotionOnHeadsUpView;
+    private boolean mTrackingHeadsUp;
+    private boolean mCollapseSnoozes;
+    private NotificationPanelView mPanel;
+    private ExpandableNotificationRow mPickedChild;
+
+    public boolean isTrackingHeadsUp() {
+        return mTrackingHeadsUp;
+    }
+
+    @Override
+    public boolean onInterceptTouchEvent(MotionEvent event) {
+        if (!mMotionOnHeadsUpView && event.getActionMasked() != MotionEvent.ACTION_DOWN) {
+            return false;
+        }
+        int pointerIndex = event.findPointerIndex(mTrackingPointer);
+        if (pointerIndex < 0) {
+            pointerIndex = 0;
+            mTrackingPointer = event.getPointerId(pointerIndex);
+        }
+        final float x = event.getX(pointerIndex);
+        final float y = event.getY(pointerIndex);
+        switch (event.getActionMasked()) {
+            case MotionEvent.ACTION_DOWN:
+                mInitialTouchY = y;
+                mInitialTouchX = x;
+                setTrackingHeadsUp(false);
+                ExpandableView child = mStackScroller.getChildAtPosition(x, y);
+                mMotionOnHeadsUpView = false;
+                if (child instanceof ExpandableNotificationRow) {
+                    mPickedChild = (ExpandableNotificationRow) child;
+                    mMotionOnHeadsUpView = mPickedChild.isHeadsUp() && !mPickedChild.isInShade();
+                }
+                break;
+            case MotionEvent.ACTION_POINTER_UP:
+                final int upPointer = event.getPointerId(event.getActionIndex());
+                if (mTrackingPointer == upPointer) {
+                    // gesture is ongoing, find a new pointer to track
+                    final int newIndex = event.getPointerId(0) != upPointer ? 0 : 1;
+                    mTrackingPointer = event.getPointerId(newIndex);
+                    mInitialTouchX = event.getX(newIndex);
+                    mInitialTouchY = event.getY(newIndex);
+                }
+                break;
+
+            case MotionEvent.ACTION_MOVE:
+                final float h = y - mInitialTouchY;
+                if (Math.abs(h) > mTouchSlop && Math.abs(h) > Math.abs(x - mInitialTouchX)) {
+                    setTrackingHeadsUp(true);
+                    mCollapseSnoozes = h < 0;
+                    mInitialTouchX = x;
+                    mInitialTouchY = y;
+                    int expandedHeight = mPickedChild.getActualHeight();
+                    mPanel.startExpandMotion(x, y, true /* startTracking */, expandedHeight);
+                    return true;
+                }
+                break;
+
+            case MotionEvent.ACTION_CANCEL:
+            case MotionEvent.ACTION_UP:
+                if (mPickedChild != null && mMotionOnHeadsUpView) {
+                    if (mHeadsUpManager.shouldSwallowClick(
+                            mPickedChild.getStatusBarNotification().getKey())) {
+                        endMotion();
+                        return true;
+                    }
+                }
+                endMotion();
+                break;
+        }
+        return false;
+    }
+
+    private void setTrackingHeadsUp(boolean tracking) {
+        mTrackingHeadsUp = tracking;
+        mHeadsUpManager.setTrackingHeadsUp(tracking);
+        mPanel.setTrackingHeadsUp(tracking);
+    }
+
+    public void notifyFling(boolean collapse) {
+        if (collapse && mCollapseSnoozes) {
+            mHeadsUpManager.snooze();
+        }
+        mCollapseSnoozes = false;
+    }
+
+    @Override
+    public boolean onTouchEvent(MotionEvent event) {
+        if (!mTrackingHeadsUp) {
+            return false;
+        }
+        switch (event.getActionMasked()) {
+            case MotionEvent.ACTION_UP:
+            case MotionEvent.ACTION_CANCEL:
+                endMotion();
+                setTrackingHeadsUp(false);
+                break;
+        }
+        return true;
+    }
+
+    private void endMotion() {
+        mTrackingPointer = -1;
+        mPickedChild = null;
+        mMotionOnHeadsUpView = false;
+    }
+
+    public ExpandableView getPickedChild() {
+        return mPickedChild;
+    }
+
+    public void bind(HeadsUpManager headsUpManager, NotificationStackScrollLayout stackScroller,
+            NotificationPanelView notificationPanelView) {
+        mHeadsUpManager = headsUpManager;
+        mStackScroller = stackScroller;
+        mPanel = notificationPanelView;
+        Context context = stackScroller.getContext();
+        final ViewConfiguration configuration = ViewConfiguration.get(context);
+        mTouchSlop = configuration.getScaledTouchSlop();
+    }
+}
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 02b6c19..96e95439 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
@@ -32,6 +32,7 @@
 import android.view.VelocityTracker;
 import android.view.View;
 import android.view.ViewTreeObserver;
+import android.view.WindowInsets;
 import android.view.accessibility.AccessibilityEvent;
 import android.view.animation.AnimationUtils;
 import android.view.animation.Interpolator;
@@ -39,16 +40,19 @@
 import android.widget.TextView;
 
 import com.android.keyguard.KeyguardStatusView;
-import com.android.systemui.EventLogTags;
 import com.android.systemui.EventLogConstants;
+import com.android.systemui.EventLogTags;
 import com.android.systemui.R;
 import com.android.systemui.qs.QSContainer;
 import com.android.systemui.qs.QSPanel;
+import com.android.systemui.statusbar.ExpandableNotificationRow;
 import com.android.systemui.statusbar.ExpandableView;
 import com.android.systemui.statusbar.FlingAnimationUtils;
 import com.android.systemui.statusbar.GestureRecorder;
 import com.android.systemui.statusbar.KeyguardAffordanceView;
+import com.android.systemui.statusbar.NotificationData;
 import com.android.systemui.statusbar.StatusBarState;
+import com.android.systemui.statusbar.policy.HeadsUpManager;
 import com.android.systemui.statusbar.policy.KeyguardUserSwitcher;
 import com.android.systemui.statusbar.stack.NotificationStackScrollLayout;
 import com.android.systemui.statusbar.stack.StackStateAnimator;
@@ -56,7 +60,8 @@
 public class NotificationPanelView extends PanelView implements
         ExpandableView.OnHeightChangedListener, ObservableScrollView.Listener,
         View.OnClickListener, NotificationStackScrollLayout.OnOverscrollTopChangedListener,
-        KeyguardAffordanceHelper.Callback, NotificationStackScrollLayout.OnEmptySpaceClickListener {
+        KeyguardAffordanceHelper.Callback, NotificationStackScrollLayout.OnEmptySpaceClickListener,
+        HeadsUpManager.OnHeadsUpChangedListener {
 
     private static final boolean DEBUG = false;
 
@@ -81,7 +86,7 @@
     private TextView mClockView;
     private View mReserveNotificationSpace;
     private View mQsNavbarScrim;
-    private View mNotificationContainerParent;
+    private NotificationsQuickSettingsContainer mNotificationContainerParent;
     private NotificationStackScrollLayout mNotificationStackScroller;
     private int mNotificationTopPadding;
     private boolean mAnimateNextTopPaddingChange;
@@ -177,6 +182,17 @@
 
     private float mKeyguardStatusBarAnimateAlpha = 1f;
     private int mOldLayoutDirection;
+    private HeadsUpTouchHelper mHeadsUpTouchHelper = new HeadsUpTouchHelper();
+    private boolean mPinnedHeadsUpExist;
+    private boolean mExpansionIsFromHeadsUp;
+    private int mBottomBarHeight;
+    private boolean mExpandingFromHeadsUp;
+    private Runnable mHeadsUpExistenceChangedRunnable = new Runnable() {
+        @Override
+        public void run() {
+            notifyBarPanelExpansionChanged();
+        }
+    };
 
     public NotificationPanelView(Context context, AttributeSet attrs) {
         super(context, attrs);
@@ -201,7 +217,8 @@
         mScrollView.setListener(this);
         mScrollView.setFocusable(false);
         mReserveNotificationSpace = findViewById(R.id.reserve_notification_space);
-        mNotificationContainerParent = findViewById(R.id.notification_container_parent);
+        mNotificationContainerParent = (NotificationsQuickSettingsContainer)
+                findViewById(R.id.notification_container_parent);
         mNotificationStackScroller = (NotificationStackScrollLayout)
                 findViewById(R.id.notification_stack_scroller);
         mNotificationStackScroller.setOnHeightChangedListener(this);
@@ -317,6 +334,7 @@
         if (mQsSizeChangeAnimator == null) {
             mQsContainer.setHeightOverride(mQsContainer.getDesiredHeight());
         }
+        updateMaxHeadsUpTranslation();
     }
 
     @Override
@@ -493,22 +511,40 @@
     }
 
     @Override
+    protected void flingToHeight(float vel, boolean expand, float target,
+            float collapseSpeedUpFactor) {
+        mHeadsUpTouchHelper.notifyFling(!expand);
+        super.flingToHeight(vel, expand, target, collapseSpeedUpFactor);
+    }
+
+    @Override
     public boolean dispatchPopulateAccessibilityEventInternal(AccessibilityEvent event) {
         if (event.getEventType() == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED) {
             event.getText().add(getKeyguardOrLockScreenString());
             mLastAnnouncementWasQuickSettings = false;
             return true;
         }
-
         return super.dispatchPopulateAccessibilityEventInternal(event);
     }
 
     @Override
-    public boolean onInterceptTouchEvent(MotionEvent event) {
+    public boolean
+    onInterceptTouchEvent(MotionEvent event) {
         if (mBlockTouches) {
             return false;
         }
         initDownStates(event);
+        if (mHeadsUpTouchHelper.onInterceptTouchEvent(event)) {
+            mExpansionIsFromHeadsUp = true;
+            return true;
+        }
+        if (!isShadeCollapsed() && onQsIntercept(event)) {
+            return true;
+        }
+        return super.onInterceptTouchEvent(event);
+    }
+
+    private boolean onQsIntercept(MotionEvent event) {
         int pointerIndex = event.findPointerIndex(mTrackingPointer);
         if (pointerIndex < 0) {
             pointerIndex = 0;
@@ -583,7 +619,7 @@
                 mIntercepting = false;
                 break;
         }
-        return super.onInterceptTouchEvent(event);
+        return false;
     }
 
     @Override
@@ -652,6 +688,15 @@
         if (mOnlyAffordanceInThisMotion) {
             return true;
         }
+        mHeadsUpTouchHelper.onTouchEvent(event);
+        if (!mHeadsUpTouchHelper.isTrackingHeadsUp() && handleQSTouch(event)) {
+            return true;
+        }
+        super.onTouchEvent(event);
+        return true;
+    }
+
+    private boolean handleQSTouch(MotionEvent event) {
         if (event.getActionMasked() == MotionEvent.ACTION_DOWN && getExpandedFraction() == 1f
                 && mStatusBar.getBarState() != StatusBarState.KEYGUARD && !mQsExpanded
                 && mQsExpansionEnabled) {
@@ -664,7 +709,7 @@
             mInitialTouchY = event.getX();
             mInitialTouchX = event.getY();
         }
-        if (mExpandedHeight != 0) {
+        if (!isShadeCollapsed()) {
             handleQsDown(event);
         }
         if (!mQsExpandImmediate && mQsTracking) {
@@ -677,7 +722,7 @@
                 || event.getActionMasked() == MotionEvent.ACTION_UP) {
             mConflictingQsExpansionGesture = false;
         }
-        if (event.getActionMasked() == MotionEvent.ACTION_DOWN && mExpandedHeight == 0
+        if (event.getActionMasked() == MotionEvent.ACTION_DOWN && isShadeCollapsed()
                 && mQsExpansionEnabled) {
             mTwoFingerQsExpandPossible = true;
         }
@@ -691,8 +736,7 @@
             // earlier so the state is already up to date when dragging down.
             setListening(true);
         }
-        super.onTouchEvent(event);
-        return true;
+        return false;
     }
 
     private boolean isInQsArea(float x, float y) {
@@ -834,13 +878,13 @@
         setQsExpansion(mQsExpansionHeight);
         flingSettings(!mQsExpansionEnabled && open ? 0f : velocity, open && mQsExpansionEnabled,
                 new Runnable() {
-            @Override
-            public void run() {
-                mStackScrollerOverscrolling = false;
-                mQsExpansionFromOverscroll = false;
-                updateQsState();
-            }
-        });
+                    @Override
+                    public void run() {
+                        mStackScrollerOverscrolling = false;
+                        mQsExpansionFromOverscroll = false;
+                        updateQsState();
+                    }
+                });
     }
 
     private void onQsExpansionStarted() {
@@ -869,6 +913,7 @@
             mNotificationStackScroller.setInterceptDelegateEnabled(expanded);
             mStatusBar.setQsExpanded(expanded);
             mQsPanel.setExpanded(expanded);
+            mNotificationContainerParent.setQsExpanded(expanded);
         }
     }
 
@@ -1056,7 +1101,7 @@
             mKeyguardBottomArea.animate()
                     .alpha(0f)
                     .setStartDelay(mStatusBar.getKeyguardFadingAwayDelay())
-                    .setDuration(mStatusBar.getKeyguardFadingAwayDuration()/2)
+                    .setDuration(mStatusBar.getKeyguardFadingAwayDuration() / 2)
                     .setInterpolator(PhoneStatusBar.ALPHA_OUT)
                     .withEndAction(mAnimateKeyguardBottomAreaInvisibleEndRunnable)
                     .start();
@@ -1132,8 +1177,8 @@
         updateEmptyShadeView();
         mQsNavbarScrim.setVisibility(mStatusBarState == StatusBarState.SHADE && mQsExpanded
                 && !mStackScrollerOverscrolling && mQsScrimEnabled
-                        ? View.VISIBLE
-                        : View.INVISIBLE);
+                ? View.VISIBLE
+                : View.INVISIBLE);
         if (mKeyguardUserSwitcher != null && mQsExpanded && !mStackScrollerOverscrolling) {
             mKeyguardUserSwitcher.hideIfNotSimple(true /* animate */);
         }
@@ -1402,6 +1447,8 @@
         updateHeader();
         updateUnlockIcon();
         updateNotificationTranslucency();
+        mHeadsUpManager.setIsExpanded(!isShadeCollapsed());
+        mNotificationStackScroller.setShadeExpanded(!isShadeCollapsed());
         if (DEBUG) {
             invalidate();
         }
@@ -1471,16 +1518,24 @@
         }
     }
     private void updateNotificationTranslucency() {
-        float alpha = (getNotificationsTopY() + mNotificationStackScroller.getItemHeight())
-                / (mQsMinExpansionHeight + mNotificationStackScroller.getBottomStackPeekSize()
-                        - mNotificationStackScroller.getCollapseSecondCardPadding());
-        alpha = Math.max(0, Math.min(alpha, 1));
-        alpha = (float) Math.pow(alpha, 0.75);
-        if (alpha != 1f && mNotificationStackScroller.getLayerType() != LAYER_TYPE_HARDWARE) {
-            mNotificationStackScroller.setLayerType(LAYER_TYPE_HARDWARE, null);
-        } else if (alpha == 1f
-                && mNotificationStackScroller.getLayerType() == LAYER_TYPE_HARDWARE) {
-            mNotificationStackScroller.setLayerType(LAYER_TYPE_NONE, null);
+        float alpha;
+        if (mExpandingFromHeadsUp || mHeadsUpManager.hasPinnedHeadsUp()) {
+            alpha = 1f;
+            if (mNotificationStackScroller.getLayerType() == LAYER_TYPE_HARDWARE) {
+                mNotificationStackScroller.setLayerType(LAYER_TYPE_NONE, null);
+            }
+        } else {
+            alpha = (getNotificationsTopY() + mNotificationStackScroller.getItemHeight())
+                    / (mQsMinExpansionHeight + mNotificationStackScroller.getBottomStackPeekSize()
+                    - mNotificationStackScroller.getCollapseSecondCardPadding());
+            alpha = Math.max(0, Math.min(alpha, 1));
+            alpha = (float) Math.pow(alpha, 0.75);
+            if (alpha != 1f && mNotificationStackScroller.getLayerType() != LAYER_TYPE_HARDWARE) {
+                mNotificationStackScroller.setLayerType(LAYER_TYPE_HARDWARE, null);
+            } else if (alpha == 1f
+                    && mNotificationStackScroller.getLayerType() == LAYER_TYPE_HARDWARE) {
+                mNotificationStackScroller.setLayerType(LAYER_TYPE_NONE, null);
+            }
         }
         mNotificationStackScroller.setAlpha(alpha);
     }
@@ -1544,7 +1599,13 @@
                 return mExpandedHeight / HEADER_RUBBERBAND_FACTOR - mQsMinExpansionHeight;
             }
         }
-        return Math.min(0, mNotificationStackScroller.getTranslationY()) / HEADER_RUBBERBAND_FACTOR;
+        float stackTranslation = mNotificationStackScroller.getStackTranslation();
+        float translation = stackTranslation / HEADER_RUBBERBAND_FACTOR;
+        if (mHeadsUpManager.hasPinnedHeadsUp() || mExpansionIsFromHeadsUp) {
+            translation = mNotificationStackScroller.getTopPadding() + stackTranslation
+                    - mNotificationTopPadding - mQsMinExpansionHeight;
+        }
+        return Math.min(0, translation);
     }
 
     /**
@@ -1605,15 +1666,19 @@
     protected void onExpandingFinished() {
         super.onExpandingFinished();
         mNotificationStackScroller.onExpansionStopped();
+        mHeadsUpManager.onExpandingFinished();
         mIsExpanding = false;
         mScrollYOverride = -1;
-        if (mExpandedHeight == 0f) {
+        if (isShadeCollapsed()) {
             setListening(false);
         } else {
             setListening(true);
         }
         mQsExpandImmediate = false;
         mTwoFingerQsExpandPossible = false;
+        mExpansionIsFromHeadsUp = false;
+        mNotificationStackScroller.setTrackingHeadsUp(mHeadsUpTouchHelper.isTrackingHeadsUp());
+        mExpandingFromHeadsUp = mHeadsUpTouchHelper.isTrackingHeadsUp();
     }
 
     private void setListening(boolean listening) {
@@ -1709,6 +1774,17 @@
     }
 
     @Override
+    public WindowInsets onApplyWindowInsets(WindowInsets insets) {
+        mBottomBarHeight = insets.getSystemWindowInsetBottom();
+        updateMaxHeadsUpTranslation();
+        return insets;
+    }
+
+    private void updateMaxHeadsUpTranslation() {
+        mNotificationStackScroller.setHeadsUpBoundaries(getHeight(), mBottomBarHeight);
+    }
+
+    @Override
     public void onRtlPropertiesChanged(int layoutDirection) {
         if (layoutDirection != mOldLayoutDirection) {
             mAfforanceHelper.onRtlPropertiesChanged();
@@ -2066,4 +2142,49 @@
                     mNotificationStackScroller.getTopPadding(), p);
         }
     }
+
+    @Override
+    public void OnPinnedHeadsUpExistChanged(final boolean exist, boolean changeImmediatly) {
+        if (exist != mPinnedHeadsUpExist) {
+            mPinnedHeadsUpExist = exist;
+            if (exist) {
+                mHeadsUpExistenceChangedRunnable.run();
+                updateNotificationTranslucency();
+            } else {
+                mNotificationStackScroller.performOnAnimationFinished(
+                        mHeadsUpExistenceChangedRunnable);
+            }
+        }
+    }
+
+    @Override
+    public void OnHeadsUpPinnedChanged(ExpandableNotificationRow headsUp, boolean isHeadsUp) {
+        if (isHeadsUp) {
+            mNotificationStackScroller.generateHeadsUpAnimation(headsUp, true);
+        }
+    }
+
+    @Override
+    public void OnHeadsUpStateChanged(NotificationData.Entry entry, boolean isHeadsUp) {
+        mNotificationStackScroller.generateHeadsUpAnimation(entry.row, isHeadsUp);
+    }
+
+    @Override
+    protected boolean isShadeCollapsed() {
+        return mExpandedHeight == 0;
+    }
+
+    @Override
+    public void setHeadsUpManager(HeadsUpManager headsUpManager) {
+        super.setHeadsUpManager(headsUpManager);
+        mHeadsUpTouchHelper.bind(headsUpManager, mNotificationStackScroller, this);
+    }
+
+    public void setTrackingHeadsUp(boolean tracking) {
+        if (tracking) {
+            // otherwise we update the state when the expansion is finished
+            mNotificationStackScroller.setTrackingHeadsUp(true);
+            mExpandingFromHeadsUp = true;
+        }
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationsQuickSettingsContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationsQuickSettingsContainer.java
index a03c297..cbb71c5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationsQuickSettingsContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationsQuickSettingsContainer.java
@@ -37,6 +37,7 @@
     private View mStackScroller;
     private View mKeyguardStatusBar;
     private boolean mInflated;
+    private boolean mQsExpanded;
 
     public NotificationsQuickSettingsContainer(Context context, AttributeSet attrs) {
         super(context, attrs);
@@ -64,26 +65,29 @@
         boolean userSwitcherVisible = mInflated && mUserSwitcher.getVisibility() == View.VISIBLE;
         boolean statusBarVisible = mKeyguardStatusBar.getVisibility() == View.VISIBLE;
 
+        View stackQsTop = mQsExpanded ? mStackScroller : mScrollView;
+        View stackQsBottom = !mQsExpanded ? mStackScroller : mScrollView;
         // Invert the order of the scroll view and user switcher such that the notifications receive
         // touches first but the panel gets drawn above.
         if (child == mScrollView) {
-            return super.drawChild(canvas, mStackScroller, drawingTime);
-        } else if (child == mStackScroller) {
-            return super.drawChild(canvas,
-                    userSwitcherVisible && statusBarVisible ? mUserSwitcher
+            return super.drawChild(canvas, userSwitcherVisible && statusBarVisible ? mUserSwitcher
                     : statusBarVisible ? mKeyguardStatusBar
                     : userSwitcherVisible ? mUserSwitcher
-                    : mScrollView,
+                    : stackQsBottom, drawingTime);
+        } else if (child == mStackScroller) {
+            return super.drawChild(canvas,
+                    userSwitcherVisible && statusBarVisible ? mKeyguardStatusBar
+                    : statusBarVisible || userSwitcherVisible ? stackQsBottom
+                    : stackQsTop,
                     drawingTime);
         } else if (child == mUserSwitcher) {
             return super.drawChild(canvas,
-                    userSwitcherVisible && statusBarVisible ? mKeyguardStatusBar
-                    : mScrollView,
+                    userSwitcherVisible && statusBarVisible ? stackQsBottom
+                    : stackQsTop,
                     drawingTime);
         } else if (child == mKeyguardStatusBar) {
             return super.drawChild(canvas,
-                    userSwitcherVisible && statusBarVisible ? mScrollView
-                    : mScrollView,
+                    stackQsTop,
                     drawingTime);
         }else {
             return super.drawChild(canvas, child, drawingTime);
@@ -97,4 +101,11 @@
             mInflated = true;
         }
     }
+
+    public void setQsExpanded(boolean expanded) {
+        if (mQsExpanded != expanded) {
+            mQsExpanded = expanded;
+            invalidate();
+        }
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelBar.java
index c6e1be9..f3d4c7f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelBar.java
@@ -140,7 +140,7 @@
         mPanelHolder.setSelectedPanel(mTouchingPanel);
         for (PanelView pv : mPanels) {
             if (pv != panel) {
-                pv.collapse(false /* delayed */);
+                pv.collapse(false /* delayed */, 1.0f /* speedUpFactor */);
             }
         }
     }
@@ -157,8 +157,7 @@
         if (DEBUG) LOG("panelExpansionChanged: start state=%d panel=%s", mState, panel.getName());
         mPanelExpandedFractionSum = 0f;
         for (PanelView pv : mPanels) {
-            boolean visible = pv.getExpandedHeight() > 0;
-            pv.setVisibility(visible ? View.VISIBLE : View.GONE);
+            pv.setVisibility(expanded ? View.VISIBLE : View.INVISIBLE);
             // adjust any other panels that may be partially visible
             if (expanded) {
                 if (mState == STATE_CLOSED) {
@@ -167,7 +166,7 @@
                 }
                 fullyClosed = false;
                 final float thisFrac = pv.getExpandedFraction();
-                mPanelExpandedFractionSum += (visible ? thisFrac : 0);
+                mPanelExpandedFractionSum += thisFrac;
                 if (DEBUG) LOG("panelExpansionChanged:  -> %s: f=%.1f", pv.getName(), thisFrac);
                 if (panel == pv) {
                     if (thisFrac == 1f) fullyOpenedPanel = panel;
@@ -187,16 +186,15 @@
                 (fullyOpenedPanel!=null)?" fullyOpened":"", fullyClosed?" fullyClosed":"");
     }
 
-    public void collapseAllPanels(boolean animate, boolean delayed) {
+    public void collapseAllPanels(boolean animate, boolean delayed, float speedUpFactor) {
         boolean waiting = false;
         for (PanelView pv : mPanels) {
             if (animate && !pv.isFullyCollapsed()) {
-                pv.collapse(delayed);
+                pv.collapse(delayed, speedUpFactor);
                 waiting = true;
             } else {
                 pv.resetViews();
                 pv.setExpandedFraction(0); // just in case
-                pv.setVisibility(View.GONE);
                 pv.cancelPeek();
             }
         }
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 4bbf690..3a30429 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
@@ -38,6 +38,7 @@
 import com.android.systemui.doze.DozeLog;
 import com.android.systemui.statusbar.FlingAnimationUtils;
 import com.android.systemui.statusbar.StatusBarState;
+import com.android.systemui.statusbar.policy.HeadsUpManager;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
@@ -45,6 +46,7 @@
 public abstract class PanelView extends FrameLayout {
     public static final boolean DEBUG = PanelBar.DEBUG;
     public static final String TAG = PanelView.class.getSimpleName();
+    protected HeadsUpManager mHeadsUpManager;
 
     private final void logf(String fmt, Object... args) {
         Log.v(TAG, (mViewName != null ? (mViewName + ": ") : "") + String.format(fmt, args));
@@ -99,6 +101,12 @@
 
     private boolean mPeekPending;
     private boolean mCollapseAfterPeek;
+
+    /**
+     * Speed-up factor to be used when {@link #mFlingCollapseRunnable} runs the next time.
+     */
+    private float mNextCollapseSpeedUpFactor = 1.0f;
+
     private boolean mExpanding;
     private boolean mGestureWaitForTouchSlop;
     private Runnable mPeekRunnable = new Runnable() {
@@ -162,7 +170,7 @@
                     postOnAnimation(new Runnable() {
                         @Override
                         public void run() {
-                            collapse(false /* delayed */);
+                            collapse(false /* delayed */, 1.0f /* speedUpFactor */);
                         }
                     });
                 }
@@ -232,18 +240,15 @@
         final float y = event.getY(pointerIndex);
 
         if (event.getActionMasked() == MotionEvent.ACTION_DOWN) {
-            mGestureWaitForTouchSlop = mExpandedHeight == 0f;
+            mGestureWaitForTouchSlop = isShadeCollapsed();
         }
         boolean waitForTouchSlop = hasConflictingGestures() || mGestureWaitForTouchSlop;
 
         switch (event.getActionMasked()) {
             case MotionEvent.ACTION_DOWN:
-                mInitialTouchY = y;
-                mInitialTouchX = x;
-                mInitialOffsetOnTouch = mExpandedHeight;
-                mTouchSlopExceeded = false;
+                startExpandMotion(x, y, false /* startTracking */, mExpandedHeight);
                 mJustPeeked = false;
-                mPanelClosedOnDown = mExpandedHeight == 0.0f;
+                mPanelClosedOnDown = isShadeCollapsed();
                 mHasLayoutedSinceDown = false;
                 mUpdateFlingOnLayout = false;
                 mMotionAborted = false;
@@ -261,7 +266,7 @@
                             || mPeekPending || mPeekAnimator != null;
                     onTrackingStarted();
                 }
-                if (mExpandedHeight == 0) {
+                if (isShadeCollapsed()) {
                     schedulePeek();
                 }
                 break;
@@ -274,9 +279,7 @@
                     final float newY = event.getY(newIndex);
                     final float newX = event.getX(newIndex);
                     mTrackingPointer = event.getPointerId(newIndex);
-                    mInitialOffsetOnTouch = mExpandedHeight;
-                    mInitialTouchY = newY;
-                    mInitialTouchX = newX;
+                    startExpandMotion(newX, newY, true /* startTracking */, mExpandedHeight);
                 }
                 break;
             case MotionEvent.ACTION_POINTER_DOWN:
@@ -297,9 +300,7 @@
                     mTouchSlopExceeded = true;
                     if (waitForTouchSlop && !mTracking) {
                         if (!mJustPeeked && mInitialOffsetOnTouch != 0f) {
-                            mInitialOffsetOnTouch = mExpandedHeight;
-                            mInitialTouchX = x;
-                            mInitialTouchY = y;
+                            startExpandMotion(x, y, false /* startTracking */, mExpandedHeight);
                             h = 0;
                         }
                         cancelHeightAnimator();
@@ -334,6 +335,17 @@
         return !waitForTouchSlop || mTracking;
     }
 
+    protected void startExpandMotion(float newX, float newY, boolean startTracking,
+            float expandedHeight) {
+        mInitialOffsetOnTouch = expandedHeight;
+        mInitialTouchY = newY;
+        mInitialTouchX = newX;
+        if (startTracking) {
+            mTouchSlopExceeded = true;
+            onTrackingStarted();
+        }
+    }
+
     private void endMotionEvent(MotionEvent event, float x, float y, boolean forceCancel) {
         mTrackingPointer = -1;
         if ((mTracking && mTouchSlopExceeded)
@@ -442,7 +454,7 @@
                 mTouchSlopExceeded = false;
                 mJustPeeked = false;
                 mMotionAborted = false;
-                mPanelClosedOnDown = mExpandedHeight == 0.0f;
+                mPanelClosedOnDown = isShadeCollapsed();
                 mHasLayoutedSinceDown = false;
                 mUpdateFlingOnLayout = false;
                 mTouchAboveFalsingThreshold = false;
@@ -474,12 +486,7 @@
                 if (scrolledToBottom || mTouchStartedInEmptyArea) {
                     if (h < -mTouchSlop && h < -Math.abs(x - mInitialTouchX)) {
                         cancelHeightAnimator();
-                        mInitialOffsetOnTouch = mExpandedHeight;
-                        mInitialTouchY = y;
-                        mInitialTouchX = x;
-                        mTracking = true;
-                        mTouchSlopExceeded = true;
-                        onTrackingStarted();
+                        startExpandMotion(x, y, true /* startTracking */, mExpandedHeight);
                         return true;
                     }
                 }
@@ -562,9 +569,17 @@
     }
 
     protected void fling(float vel, boolean expand) {
+        fling(vel, expand, 1.0f /* collapseSpeedUpFactor */);
+    }
+
+    protected void fling(float vel, boolean expand, float collapseSpeedUpFactor) {
         cancelPeek();
         float target = expand ? getMaxPanelHeight() : 0.0f;
+        flingToHeight(vel, expand, target, collapseSpeedUpFactor);
+    }
 
+    protected void flingToHeight(float vel, boolean expand, float target,
+            float collapseSpeedUpFactor) {
         // Hack to make the expand transition look nice when clear all button is visible - we make
         // the animation only to the last notification, and then jump to the maximum panel height so
         // clear all just fades in and the decelerating motion is towards the last notification.
@@ -596,7 +611,8 @@
             // Make it shorter if we run a canned animation
             if (vel == 0) {
                 animator.setDuration((long)
-                        (animator.getDuration() * getCannedFlingDurationFactor()));
+                        (animator.getDuration() * getCannedFlingDurationFactor()
+                                / collapseSpeedUpFactor));
             }
         }
         animator.addListener(new AnimatorListenerAdapter() {
@@ -644,7 +660,7 @@
         mHasLayoutedSinceDown = true;
         if (mUpdateFlingOnLayout) {
             abortAnimations();
-            fling(mUpdateFlingVelocity, true);
+            fling(mUpdateFlingVelocity, true /* expands */);
             mUpdateFlingOnLayout = false;
         }
     }
@@ -655,7 +671,7 @@
         // If the user isn't actively poking us, let's update the height
         if ((!mTracking || isTrackingBlocked())
                 && mHeightAnimator == null
-                && mExpandedHeight > 0
+                && !isShadeCollapsed()
                 && currentMaxPanelHeight != mExpandedHeight
                 && !mPeekPending
                 && mPeekAnimator == null
@@ -741,7 +757,7 @@
         mBar = panelBar;
     }
 
-    public void collapse(boolean delayed) {
+    public void collapse(boolean delayed, float speedUpFactor) {
         if (DEBUG) logf("collapse: " + this);
         if (mPeekPending || mPeekAnimator != null) {
             mCollapseAfterPeek = true;
@@ -757,9 +773,10 @@
             mClosing = true;
             notifyExpandingStarted();
             if (delayed) {
+                mNextCollapseSpeedUpFactor = speedUpFactor;
                 postDelayed(mFlingCollapseRunnable, 120);
             } else {
-                fling(0, false /* expand */);
+                fling(0, false /* expand */, speedUpFactor);
             }
         }
     }
@@ -767,7 +784,7 @@
     private final Runnable mFlingCollapseRunnable = new Runnable() {
         @Override
         public void run() {
-            fling(0, false /* expand */);
+            fling(0, false /* expand */, mNextCollapseSpeedUpFactor);
         }
     };
 
@@ -805,7 +822,7 @@
         if (mExpanding) {
             notifyExpandingFinished();
         }
-        setVisibility(VISIBLE);
+        notifyBarPanelExpansionChanged();
 
         // Wait for window manager to pickup the change, so we know the maximum height of the panel
         // then.
@@ -941,9 +958,9 @@
         return animator;
     }
 
-    private void notifyBarPanelExpansionChanged() {
+    protected void notifyBarPanelExpansionChanged() {
         mBar.panelExpansionChanged(this, mExpandedFraction, mExpandedFraction > 0f || mPeekPending
-                || mPeekAnimator != null);
+                || mPeekAnimator != null || mInstantExpanding || mHeadsUpManager.hasPinnedHeadsUp());
     }
 
     /**
@@ -971,7 +988,7 @@
     protected final Runnable mPostCollapseRunnable = new Runnable() {
         @Override
         public void run() {
-            collapse(false /* delayed */);
+            collapse(false /* delayed */, 1.0f /* speedUpFactor */);
         }
     };
 
@@ -1014,4 +1031,10 @@
      * @return the height of the clear all button, in pixels
      */
     protected abstract int getClearAllHeight();
+
+    protected abstract boolean isShadeCollapsed();
+
+    public void setHeadsUpManager(HeadsUpManager headsUpManager) {
+        mHeadsUpManager = headsUpManager;
+    }
 }
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 b0d6178..b6dbfce 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -17,19 +17,6 @@
 package com.android.systemui.statusbar.phone;
 
 
-import static android.app.StatusBarManager.NAVIGATION_HINT_BACK_ALT;
-import static android.app.StatusBarManager.NAVIGATION_HINT_IME_SHOWN;
-import static android.app.StatusBarManager.WINDOW_STATE_HIDDEN;
-import static android.app.StatusBarManager.WINDOW_STATE_SHOWING;
-import static android.app.StatusBarManager.windowStateToString;
-import static com.android.systemui.statusbar.phone.BarTransitions.MODE_LIGHTS_OUT;
-import static com.android.systemui.statusbar.phone.BarTransitions.MODE_LIGHTS_OUT_TRANSPARENT;
-import static com.android.systemui.statusbar.phone.BarTransitions.MODE_OPAQUE;
-import static com.android.systemui.statusbar.phone.BarTransitions.MODE_SEMI_TRANSPARENT;
-import static com.android.systemui.statusbar.phone.BarTransitions.MODE_TRANSLUCENT;
-import static com.android.systemui.statusbar.phone.BarTransitions.MODE_TRANSPARENT;
-import static com.android.systemui.statusbar.phone.BarTransitions.MODE_WARNING;
-
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
 import android.annotation.NonNull;
@@ -86,13 +73,11 @@
 import android.util.EventLog;
 import android.util.Log;
 import android.view.Display;
-import android.view.Gravity;
 import android.view.KeyEvent;
 import android.view.LayoutInflater;
 import android.view.MotionEvent;
 import android.view.VelocityTracker;
 import android.view.View;
-import android.view.ViewGroup;
 import android.view.ViewGroup.LayoutParams;
 import android.view.ViewStub;
 import android.view.WindowManager;
@@ -115,13 +100,13 @@
 import com.android.systemui.EventLogTags;
 import com.android.systemui.Prefs;
 import com.android.systemui.R;
+import com.android.systemui.assist.AssistGestureManager;
 import com.android.systemui.doze.DozeHost;
 import com.android.systemui.doze.DozeLog;
 import com.android.systemui.keyguard.KeyguardViewMediator;
 import com.android.systemui.qs.QSPanel;
 import com.android.systemui.recents.ScreenPinningRequest;
 import com.android.systemui.statusbar.ActivatableNotificationView;
-import com.android.systemui.assist.AssistGestureManager;
 import com.android.systemui.statusbar.BackDropView;
 import com.android.systemui.statusbar.BaseStatusBar;
 import com.android.systemui.statusbar.CommandQueue;
@@ -137,7 +122,6 @@
 import com.android.systemui.statusbar.ScrimView;
 import com.android.systemui.statusbar.SignalClusterView;
 import com.android.systemui.statusbar.SpeedBumpView;
-import com.android.systemui.statusbar.StatusBarIconView;
 import com.android.systemui.statusbar.StatusBarState;
 import com.android.systemui.statusbar.phone.UnlockMethodCache.OnUnlockMethodChangedListener;
 import com.android.systemui.statusbar.policy.AccessibilityController;
@@ -147,7 +131,7 @@
 import com.android.systemui.statusbar.policy.BrightnessMirrorController;
 import com.android.systemui.statusbar.policy.CastControllerImpl;
 import com.android.systemui.statusbar.policy.FlashlightController;
-import com.android.systemui.statusbar.policy.HeadsUpNotificationView;
+import com.android.systemui.statusbar.policy.HeadsUpManager;
 import com.android.systemui.statusbar.policy.HotspotControllerImpl;
 import com.android.systemui.statusbar.policy.KeyButtonView;
 import com.android.systemui.statusbar.policy.KeyguardMonitor;
@@ -172,11 +156,27 @@
 import java.util.Collection;
 import java.util.Collections;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
+import java.util.TreeSet;
+
+import static android.app.StatusBarManager.NAVIGATION_HINT_BACK_ALT;
+import static android.app.StatusBarManager.NAVIGATION_HINT_IME_SHOWN;
+import static android.app.StatusBarManager.WINDOW_STATE_HIDDEN;
+import static android.app.StatusBarManager.WINDOW_STATE_SHOWING;
+import static android.app.StatusBarManager.windowStateToString;
+import static com.android.systemui.statusbar.phone.BarTransitions.MODE_LIGHTS_OUT;
+import static com.android.systemui.statusbar.phone.BarTransitions.MODE_LIGHTS_OUT_TRANSPARENT;
+import static com.android.systemui.statusbar.phone.BarTransitions.MODE_OPAQUE;
+import static com.android.systemui.statusbar.phone.BarTransitions.MODE_SEMI_TRANSPARENT;
+import static com.android.systemui.statusbar.phone.BarTransitions.MODE_TRANSLUCENT;
+import static com.android.systemui.statusbar.phone.BarTransitions.MODE_TRANSPARENT;
+import static com.android.systemui.statusbar.phone.BarTransitions.MODE_WARNING;
 
 public class PhoneStatusBar extends BaseStatusBar implements DemoMode,
-        DragDownHelper.DragDownCallback, ActivityStarter, OnUnlockMethodChangedListener {
+        DragDownHelper.DragDownCallback, ActivityStarter, OnUnlockMethodChangedListener,
+        HeadsUpManager.OnHeadsUpChangedListener {
     static final String TAG = "PhoneStatusBar";
     public static final boolean DEBUG = BaseStatusBar.DEBUG;
     public static final boolean SPEW = false;
@@ -360,11 +360,7 @@
             if (wasUsing != mUseHeadsUp) {
                 if (!mUseHeadsUp) {
                     Log.d(TAG, "dismissing any existing heads up notification on disable event");
-                    setHeadsUpVisibility(false);
-                    mHeadsUpNotificationView.releaseImmediately();
-                    removeHeadsUpView();
-                } else {
-                    addHeadsUpView();
+                    mHeadsUpManager.releaseAllImmediately();
                 }
             }
         }
@@ -528,6 +524,8 @@
     };
     private HashMap<ExpandableNotificationRow, List<ExpandableNotificationRow>> mTmpChildOrderMap
             = new HashMap<>();
+    private HashSet<Entry> mHeadsUpEntriesToRemoveOnSwitch = new HashSet<>();
+    private RankingMap mLatestRankingMap;
 
     @Override
     public void start() {
@@ -598,7 +596,8 @@
                     }
                 }
                 return mStatusBarWindow.onTouchEvent(event);
-            }});
+            }
+        });
 
         mStatusBarView = (PhoneStatusBarView) mStatusBarWindow.findViewById(R.id.status_bar);
         mStatusBarView.setBar(this);
@@ -615,12 +614,14 @@
             mNotificationPanel.setBackground(new FastColorDrawable(context.getColor(
                     R.color.notification_panel_solid_background)));
         }
-        if (ENABLE_HEADS_UP) {
-            mHeadsUpNotificationView =
-                    (HeadsUpNotificationView) View.inflate(context, R.layout.heads_up, null);
-            mHeadsUpNotificationView.setVisibility(View.GONE);
-            mHeadsUpNotificationView.setBar(this);
-        }
+
+        mHeadsUpManager = new HeadsUpManager(context, mNotificationPanel.getViewTreeObserver());
+        mHeadsUpManager.setBar(this);
+        mHeadsUpManager.addListener(this);
+        mHeadsUpManager.addListener(mNotificationPanel);
+        mNotificationPanel.setHeadsUpManager(mHeadsUpManager);
+        mNotificationData.setHeadsUpManager(mHeadsUpManager);
+
         if (MULTIUSER_DEBUG) {
             mNotificationPanelDebugText = (TextView) mNotificationPanel.findViewById(
                     R.id.header_debug_info);
@@ -667,6 +668,7 @@
         mStackScroller.setLongPressListener(getNotificationLongClicker());
         mStackScroller.setPhoneStatusBar(this);
         mStackScroller.setGroupManager(mGroupManager);
+        mStackScroller.setHeadsUpManager(mHeadsUpManager);
         mGroupManager.setOnGroupChangeListener(mStackScroller);
 
         mKeyguardIconOverflowContainer =
@@ -699,7 +701,11 @@
 
         ScrimView scrimBehind = (ScrimView) mStatusBarWindow.findViewById(R.id.scrim_behind);
         ScrimView scrimInFront = (ScrimView) mStatusBarWindow.findViewById(R.id.scrim_in_front);
-        mScrimController = new ScrimController(scrimBehind, scrimInFront, mScrimSrcModeEnabled);
+        View headsUpScrim = mStatusBarWindow.findViewById(R.id.heads_up_scrim);
+        mScrimController = new ScrimController(scrimBehind, scrimInFront, headsUpScrim,
+                mScrimSrcModeEnabled);
+        mHeadsUpManager.addListener(mScrimController);
+        mStackScroller.setScrimController(mScrimController);
         mScrimController.setBackDropView(mBackdrop);
         mStatusBarView.setScrimController(mScrimController);
         mDozeScrimController = new DozeScrimController(mScrimController, context);
@@ -1084,32 +1090,6 @@
         return lp;
     }
 
-    private void addHeadsUpView() {
-        int headsUpHeight = mContext.getResources()
-                .getDimensionPixelSize(R.dimen.heads_up_window_height);
-        WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
-                LayoutParams.MATCH_PARENT, headsUpHeight,
-                WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL, // above the status bar!
-                WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
-                    | WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS
-                    | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
-                    | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
-                    | WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM
-                    | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH,
-                PixelFormat.TRANSLUCENT);
-        lp.flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;
-        lp.gravity = Gravity.TOP;
-        lp.setTitle("Heads Up");
-        lp.packageName = mContext.getPackageName();
-        lp.windowAnimations = R.style.Animation_StatusBar_HeadsUp;
-
-        mWindowManager.addView(mHeadsUpNotificationView, lp);
-    }
-
-    private void removeHeadsUpView() {
-        mWindowManager.removeView(mHeadsUpNotificationView);
-    }
-
     public void addIcon(String slot, int index, int viewIndex, StatusBarIcon icon) {
         mIconController.addSystemIcon(slot, index, viewIndex, icon);
     }
@@ -1131,32 +1111,17 @@
     public void addNotification(StatusBarNotification notification, RankingMap ranking,
             Entry oldEntry) {
         if (DEBUG) Log.d(TAG, "addNotification key=" + notification.getKey());
-        if (mUseHeadsUp && shouldInterrupt(notification)) {
-            if (DEBUG) Log.d(TAG, "launching notification in heads up mode");
-            Entry interruptionCandidate = oldEntry;
-            if (interruptionCandidate == null) {
-                final StatusBarIconView iconView = createIcon(notification);
-                if (iconView == null) {
-                    return;
-                }
-                interruptionCandidate = new Entry(notification, iconView);
-            }
-            ViewGroup holder = mHeadsUpNotificationView.getHolder();
-            if (inflateViewsForHeadsUp(interruptionCandidate, holder)) {
-                // 1. Populate mHeadsUpNotificationView
-                mHeadsUpNotificationView.showNotification(interruptionCandidate);
-
-                // do not show the notification in the shade, yet.
-                return;
-            }
-        }
 
         Entry shadeEntry = createNotificationViews(notification);
         if (shadeEntry == null) {
             return;
         }
+        boolean isHeadsUped = mUseHeadsUp && shouldInterrupt(notification);
+        if (isHeadsUped) {
+            mHeadsUpManager.showNotification(shadeEntry);
+        }
 
-        if (notification.getNotification().fullScreenIntent != null) {
+        if (!isHeadsUped && notification.getNotification().fullScreenIntent != null) {
             // Stop screensaver if the notification has a full-screen intent.
             // (like an incoming phone call)
             awakenDreams();
@@ -1175,18 +1140,6 @@
         setAreThereNotifications();
     }
 
-    public void displayNotificationFromHeadsUp(Entry shadeEntry) {
-
-        // The notification comes from the headsup, let's inflate the normal layout again
-        inflateViews(shadeEntry, mStackScroller);
-        shadeEntry.setInterruption();
-        shadeEntry.row.setHeadsUp(false);
-
-        addNotificationViews(shadeEntry, null);
-        // Recalculate the position of the sliding windows and the titles.
-        setAreThereNotifications();
-    }
-
     @Override
     protected void updateNotificationRanking(RankingMap ranking) {
         mNotificationData.updateRanking(ranking);
@@ -1195,10 +1148,15 @@
 
     @Override
     public void removeNotification(String key, RankingMap ranking) {
-        if (ENABLE_HEADS_UP) {
-            mHeadsUpNotificationView.removeNotification(key);
+        boolean defferRemoval = false;
+        if (mHeadsUpManager.isHeadsUp(key)) {
+            defferRemoval = !mHeadsUpManager.removeNotification(key);
         }
-
+        if (defferRemoval) {
+            mLatestRankingMap = ranking;
+            mHeadsUpEntriesToRemoveOnSwitch.add(mHeadsUpManager.getEntry(key));
+            return;
+        }
         StatusBarNotification old = removeNotificationViews(key, ranking);
         if (SPEW) Log.d(TAG, "removeNotification key=" + key + " old=" + old);
 
@@ -1870,6 +1828,74 @@
         logStateToEventlog();
     }
 
+    @Override
+    public void OnPinnedHeadsUpExistChanged(boolean exist, boolean changeImmediatly) {
+        if (exist) {
+            mStatusBarWindowManager.setHeadsUpShowing(true);
+        } else {
+            Runnable endRunnable = new Runnable() {
+                @Override
+                public void run() {
+                    if (!mHeadsUpManager.hasPinnedHeadsUp()) {
+                        mStatusBarWindowManager.setHeadsUpShowing(false);
+                    }
+                }
+            };
+            if (changeImmediatly) {
+                endRunnable.run();
+            } else {
+                mStackScroller.performOnAnimationFinished(endRunnable);
+            }
+        }
+    }
+
+    @Override
+    public void OnHeadsUpPinnedChanged(ExpandableNotificationRow headsUp, boolean isHeadsUp) {
+    }
+
+    @Override
+    public void OnHeadsUpStateChanged(Entry entry, boolean isHeadsUp) {
+        if (!isHeadsUp && mHeadsUpEntriesToRemoveOnSwitch.contains(entry)) {
+            removeNotification(entry.key, mLatestRankingMap);
+            mHeadsUpEntriesToRemoveOnSwitch.remove(entry);
+            if (mHeadsUpEntriesToRemoveOnSwitch.isEmpty()) {
+                mLatestRankingMap = null;
+            }
+        } else {
+            updateNotificationRanking(null);
+        }
+
+    }
+
+    protected void updateHeadsUp(String key, Entry entry, boolean shouldInterrupt,
+            boolean alertAgain) {
+        final boolean wasHeadsUp = isHeadsUp(key);
+        if (wasHeadsUp) {
+            mHeadsUpManager.updateNotification(entry, alertAgain);
+            if (!shouldInterrupt) {
+                // We don't want this to be interrupting anymore, lets remove it
+                mHeadsUpManager.removeNotification(key);
+            }
+        } else if (shouldInterrupt && alertAgain) {
+            // This notification was updated to be a heads-up, show it!
+            mHeadsUpManager.showNotification(entry);
+        }
+    }
+
+    protected void setHeadsUpUser(int newUserId) {
+        if (mHeadsUpManager != null) {
+            mHeadsUpManager.setUser(newUserId);
+        }
+    }
+
+    public boolean isHeadsUp(String key) {
+        return mHeadsUpManager.isHeadsUp(key);
+    }
+
+    protected boolean isSnoozedPackage(StatusBarNotification sbn) {
+        return mHeadsUpManager.isSnoozed(sbn.getPackageName());
+    }
+
     /**
      * All changes to the status bar and notifications funnel through here and are batched.
      */
@@ -1886,15 +1912,6 @@
                 case MSG_CLOSE_PANELS:
                     animateCollapsePanels();
                     break;
-                case MSG_SHOW_HEADS_UP:
-                    setHeadsUpVisibility(true);
-                    break;
-                case MSG_ESCALATE_HEADS_UP:
-                    escalateHeadsUp();
-                case MSG_HIDE_HEADS_UP:
-                    mHeadsUpNotificationView.releaseImmediately();
-                    setHeadsUpVisibility(false);
-                    break;
                 case MSG_LAUNCH_TRANSITION_TIMEOUT:
                     onLaunchTransitionTimeout();
                     break;
@@ -1903,44 +1920,15 @@
     }
 
     @Override
-    public void scheduleHeadsUpDecay(long delay) {
-        mHandler.removeMessages(MSG_HIDE_HEADS_UP);
-        if (mHeadsUpNotificationView.isClearable()) {
-            mHandler.sendEmptyMessageDelayed(MSG_HIDE_HEADS_UP, delay);
-        }
-    }
-
-    @Override
-    public void scheduleHeadsUpOpen() {
-        mHandler.removeMessages(MSG_HIDE_HEADS_UP);
-        mHandler.removeMessages(MSG_SHOW_HEADS_UP);
-        mHandler.sendEmptyMessage(MSG_SHOW_HEADS_UP);
-    }
-
-    @Override
-    public void scheduleHeadsUpClose() {
-        mHandler.removeMessages(MSG_HIDE_HEADS_UP);
-        if (mHeadsUpNotificationView.getVisibility() != View.GONE) {
-            mHandler.sendEmptyMessage(MSG_HIDE_HEADS_UP);
-        }
-    }
-
-    @Override
-    public void scheduleHeadsUpEscalation() {
-        mHandler.removeMessages(MSG_HIDE_HEADS_UP);
-        mHandler.removeMessages(MSG_ESCALATE_HEADS_UP);
-        mHandler.sendEmptyMessage(MSG_ESCALATE_HEADS_UP);
-    }
-
-    /**  if the interrupting notification had a fullscreen intent, fire it now.  */
-    private void escalateHeadsUp() {
-        if (mHeadsUpNotificationView.getEntry() != null) {
-            final StatusBarNotification sbn = mHeadsUpNotificationView.getEntry().notification;
-            mHeadsUpNotificationView.releaseImmediately();
+    public void escalateHeadsUp() {
+        TreeSet<HeadsUpManager.HeadsUpEntry> entries = mHeadsUpManager.getSortedEntries();
+        for (HeadsUpManager.HeadsUpEntry entry : entries) {
+            final StatusBarNotification sbn = entry.entry.notification;
             final Notification notification = sbn.getNotification();
             if (notification.fullScreenIntent != null) {
-                if (DEBUG)
+                if (DEBUG) {
                     Log.d(TAG, "converting a heads up to fullScreen");
+                }
                 try {
                     EventLog.writeEvent(EventLogTags.SYSUI_HEADS_UP_ESCALATION,
                             sbn.getKey());
@@ -1949,6 +1937,7 @@
                 }
             }
         }
+        mHeadsUpManager.releaseAllImmediately();
     }
 
     boolean panelsEnabled() {
@@ -1992,14 +1981,20 @@
     }
 
     public void animateCollapsePanels(int flags) {
-        animateCollapsePanels(flags, false /* force */, false /* delayed */);
+        animateCollapsePanels(flags, false /* force */, false /* delayed */,
+                1.0f /* speedUpFactor */);
     }
 
     public void animateCollapsePanels(int flags, boolean force) {
-        animateCollapsePanels(flags, force, false /* delayed*/);
+        animateCollapsePanels(flags, force, false /* delayed */, 1.0f /* speedUpFactor */);
     }
 
     public void animateCollapsePanels(int flags, boolean force, boolean delayed) {
+        animateCollapsePanels(flags, force, delayed, 1.0f /* speedUpFactor */);
+    }
+
+    public void animateCollapsePanels(int flags, boolean force, boolean delayed,
+            float speedUpFactor) {
         if (!force &&
                 (mState == StatusBarState.KEYGUARD || mState == StatusBarState.SHADE_LOCKED)) {
             runPostCollapseRunnables();
@@ -2023,7 +2018,7 @@
             mStatusBarWindowManager.setStatusBarFocusable(false);
 
             mStatusBarWindow.cancelExpandHelper();
-            mStatusBarView.collapseAllPanels(true /* animate */, delayed);
+            mStatusBarView.collapseAllPanels(true /* animate */, delayed, speedUpFactor);
         }
     }
 
@@ -2066,7 +2061,7 @@
 
     public void animateCollapseQuickSettings() {
         if (mState == StatusBarState.SHADE) {
-            mStatusBarView.collapseAllPanels(true, false /* delayed */);
+            mStatusBarView.collapseAllPanels(true, false /* delayed */, 1.0f /* speedUpFactor */);
         }
     }
 
@@ -2079,11 +2074,8 @@
         }
 
         // Ensure the panel is fully collapsed (just in case; bug 6765842, 7260868)
-        mStatusBarView.collapseAllPanels(/*animate=*/ false, false /* delayed*/);
-
-        // reset things to their proper state
-        mStackScroller.setVisibility(View.VISIBLE);
-        mNotificationPanel.setVisibility(View.GONE);
+        mStatusBarView.collapseAllPanels(/*animate=*/ false, false /* delayed*/,
+                1.0f /* speedUpFactor */);
 
         mNotificationPanel.closeQs();
 
@@ -2173,7 +2165,8 @@
             mStatusBarWindowState = state;
             if (DEBUG_WINDOW_STATE) Log.d(TAG, "Status bar " + windowStateToString(state));
             if (!showing && mState == StatusBarState.SHADE) {
-                mStatusBarView.collapseAllPanels(false /* animate */, false /* delayed */);
+                mStatusBarView.collapseAllPanels(false /* animate */, false /* delayed */,
+                        1.0f /* speedUpFactor */);
             }
         }
         if (mNavigationBarView != null
@@ -2478,8 +2471,6 @@
         pw.println(Settings.Global.zenModeToString(mZenMode));
         pw.print("  mUseHeadsUp=");
         pw.println(mUseHeadsUp);
-        pw.print("  interrupting package: ");
-        pw.println(hunStateToString(mHeadsUpNotificationView.getEntry()));
         dumpBarTransitions(pw, "mStatusBarView", mStatusBarView.getBarTransitions());
         if (mNavigationBarView != null) {
             pw.print("  mNavigationBarWindowState=");
@@ -2571,10 +2562,10 @@
         if (mSecurityController != null) {
             mSecurityController.dump(fd, pw, args);
         }
-        if (mHeadsUpNotificationView != null) {
-            mHeadsUpNotificationView.dump(fd, pw, args);
+        if (mHeadsUpManager != null) {
+            mHeadsUpManager.dump(fd, pw, args);
         } else {
-            pw.println("  mHeadsUpNotificationView: null");
+            pw.println("  mHeadsUpManager: null");
         }
 
         pw.println("SharedPreferences:");
@@ -2672,7 +2663,7 @@
             else if (Intent.ACTION_SCREEN_OFF.equals(action)) {
                 mScreenOn = false;
                 notifyNavigationBarScreenOn(false);
-                notifyHeadsUpScreenOn(false);
+                notifyHeadsUpScreenOff();
                 finishBarAnimations();
                 resetUserExpandedStates();
             }
@@ -2764,15 +2755,6 @@
                 mUserSetupObserver, mCurrentUserId);
     }
 
-    private void setHeadsUpVisibility(boolean vis) {
-        if (!ENABLE_HEADS_UP) return;
-        if (DEBUG) Log.v(TAG, (vis ? "showing" : "hiding") + " heads up window");
-        EventLog.writeEvent(EventLogTags.SYSUI_HEADS_UP_STATUS,
-                vis ? mHeadsUpNotificationView.getKey() : "",
-                vis ? 1 : 0);
-        mHeadsUpNotificationView.setVisibility(vis ? View.VISIBLE : View.GONE);
-    }
-
     /**
      * Reload some of our resources when the configuration changes.
      *
@@ -2791,9 +2773,6 @@
         if (mNotificationPanel != null) {
             mNotificationPanel.updateResources();
         }
-        if (mHeadsUpNotificationView != null) {
-            mHeadsUpNotificationView.updateResources();
-        }
         if (mBrightnessMirrorController != null) {
             mBrightnessMirrorController.updateResources();
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
index 0e8a794..e701783 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
@@ -18,6 +18,7 @@
 
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
+import android.animation.PropertyValuesHolder;
 import android.animation.ValueAnimator;
 import android.content.Context;
 import android.graphics.Color;
@@ -29,13 +30,18 @@
 
 import com.android.systemui.R;
 import com.android.systemui.statusbar.BackDropView;
+import com.android.systemui.statusbar.ExpandableNotificationRow;
+import com.android.systemui.statusbar.NotificationData;
 import com.android.systemui.statusbar.ScrimView;
+import com.android.systemui.statusbar.policy.HeadsUpManager;
+import com.android.systemui.statusbar.stack.StackStateAnimator;
 
 /**
  * Controls both the scrim behind the notifications and in front of the notifications (when a
  * security method gets shown).
  */
-public class ScrimController implements ViewTreeObserver.OnPreDrawListener {
+public class ScrimController implements ViewTreeObserver.OnPreDrawListener,
+        HeadsUpManager.OnHeadsUpChangedListener {
     public static final long ANIMATION_DURATION = 220;
 
     private static final float SCRIM_BEHIND_ALPHA = 0.62f;
@@ -43,10 +49,13 @@
     private static final float SCRIM_BEHIND_ALPHA_UNLOCKING = 0.2f;
     private static final float SCRIM_IN_FRONT_ALPHA = 0.75f;
     private static final int TAG_KEY_ANIM = R.id.scrim;
+    private static final int TAG_HUN_START_ALPHA = R.id.hun_scrim_alpha_start;
+    private static final int TAG_HUN_END_ALPHA = R.id.hun_scrim_alpha_end;
 
     private final ScrimView mScrimBehind;
     private final ScrimView mScrimInFront;
     private final UnlockMethodCache mUnlockMethodCache;
+    private final View mHeadsUpScrim;
 
     private boolean mKeyguardShowing;
     private float mFraction;
@@ -70,15 +79,22 @@
     private float mDozeBehindAlpha;
     private float mCurrentInFrontAlpha;
     private float mCurrentBehindAlpha;
+    private float mCurrentHeadsUpAlpha = 1;
+    private int mAmountOfPinnedHeadsUps;
+    private float mTopHeadsUpDragAmount;
+    private View mDraggedHeadsUpView;
 
-    public ScrimController(ScrimView scrimBehind, ScrimView scrimInFront, boolean scrimSrcEnabled) {
+    public ScrimController(ScrimView scrimBehind, ScrimView scrimInFront, View headsUpScrim,
+            boolean scrimSrcEnabled) {
         mScrimBehind = scrimBehind;
         mScrimInFront = scrimInFront;
+        mHeadsUpScrim = headsUpScrim;
         final Context context = scrimBehind.getContext();
         mUnlockMethodCache = UnlockMethodCache.getInstance(context);
         mLinearOutSlowInInterpolator = AnimationUtils.loadInterpolator(context,
                 android.R.interpolator.linear_out_slow_in);
         mScrimSrcEnabled = scrimSrcEnabled;
+        updateHeadsUpScrim(false);
     }
 
     public void setKeyguardShowing(boolean showing) {
@@ -217,7 +233,7 @@
         }
     }
 
-    private void setScrimColor(ScrimView scrim, float alpha) {
+    private void setScrimColor(View scrim, float alpha) {
         Object runningAnim = scrim.getTag(TAG_KEY_ANIM);
         if (runningAnim instanceof ValueAnimator) {
             ((ValueAnimator) runningAnim).cancel();
@@ -236,25 +252,34 @@
     }
 
     private float getCurrentScrimAlpha(View scrim) {
-        return scrim == mScrimBehind ? mCurrentBehindAlpha : mCurrentInFrontAlpha;
+        return scrim == mScrimBehind ? mCurrentBehindAlpha
+                : scrim == mScrimInFront ? mCurrentInFrontAlpha
+                : mCurrentHeadsUpAlpha;
     }
 
     private void setCurrentScrimAlpha(View scrim, float alpha) {
         if (scrim == mScrimBehind) {
             mCurrentBehindAlpha = alpha;
-        } else {
+        } else if (scrim == mScrimInFront) {
             mCurrentInFrontAlpha = alpha;
+        } else {
+            alpha = Math.max(0.0f, Math.min(1.0f, alpha));
+            mCurrentHeadsUpAlpha = alpha;
         }
     }
 
-    private void updateScrimColor(ScrimView scrim) {
+    private void updateScrimColor(View scrim) {
         float alpha1 = getCurrentScrimAlpha(scrim);
-        float alpha2 = getDozeAlpha(scrim);
-        float alpha = 1 - (1 - alpha1) * (1 - alpha2);
-        scrim.setScrimColor(Color.argb((int) (alpha * 255), 0, 0, 0));
+        if (scrim instanceof ScrimView) {
+            float alpha2 = getDozeAlpha(scrim);
+            float alpha = 1 - (1 - alpha1) * (1 - alpha2);
+            ((ScrimView) scrim).setScrimColor(Color.argb((int) (alpha * 255), 0, 0, 0));
+        } else {
+            scrim.setAlpha(alpha1);
+        }
     }
 
-    private void startScrimAnimation(final ScrimView scrim, float target) {
+    private void startScrimAnimation(final View scrim, float target) {
         float current = getCurrentScrimAlpha(scrim);
         ValueAnimator anim = ValueAnimator.ofFloat(current, target);
         anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@@ -320,4 +345,84 @@
         boolean asSrc = mBackDropView.getVisibility() != View.VISIBLE && mScrimSrcEnabled;
         mScrimBehind.setDrawAsSrc(asSrc);
     }
+
+    @Override
+    public void OnPinnedHeadsUpExistChanged(boolean exist, boolean changeImmediatly) {
+    }
+
+    @Override
+    public void OnHeadsUpPinnedChanged(ExpandableNotificationRow headsUp, boolean isHeadsUp) {
+        if (isHeadsUp) {
+            mAmountOfPinnedHeadsUps++;
+        } else {
+            mAmountOfPinnedHeadsUps--;
+            if (headsUp == mDraggedHeadsUpView) {
+                mDraggedHeadsUpView = null;
+                mTopHeadsUpDragAmount = 0.0f;
+            }
+        }
+        updateHeadsUpScrim(true);
+    }
+
+    @Override
+    public void OnHeadsUpStateChanged(NotificationData.Entry entry, boolean isHeadsUp) {
+    }
+
+    private void updateHeadsUpScrim(boolean animate) {
+        float alpha = calculateHeadsUpAlpha();
+        ValueAnimator previousAnimator = StackStateAnimator.getChildTag(mHeadsUpScrim,
+                TAG_KEY_ANIM);
+        float animEndValue = -1;
+        if (previousAnimator != null) {
+            if ((animate || alpha == mCurrentHeadsUpAlpha)) {
+                // lets cancel any running animators
+                previousAnimator.cancel();
+            }
+            animEndValue = StackStateAnimator.getChildTag(mHeadsUpScrim,
+                    TAG_HUN_START_ALPHA);
+        }
+        if (alpha != mCurrentHeadsUpAlpha && alpha != animEndValue) {
+            if (animate) {
+                startScrimAnimation(mHeadsUpScrim, alpha);
+                mHeadsUpScrim.setTag(TAG_HUN_START_ALPHA, mCurrentHeadsUpAlpha);
+                mHeadsUpScrim.setTag(TAG_HUN_END_ALPHA, alpha);
+            } else {
+                if (previousAnimator != null) {
+                    float previousStartValue = StackStateAnimator.getChildTag(mHeadsUpScrim,
+                            TAG_HUN_START_ALPHA);
+                   float previousEndValue = StackStateAnimator.getChildTag(mHeadsUpScrim,
+                           TAG_HUN_END_ALPHA);
+                    // we need to increase all animation keyframes of the previous animator by the
+                    // relative change to the end value
+                    PropertyValuesHolder[] values = previousAnimator.getValues();
+                    float relativeDiff = alpha - previousEndValue;
+                    float newStartValue = previousStartValue + relativeDiff;
+                    values[0].setFloatValues(newStartValue, alpha);
+                    mHeadsUpScrim.setTag(TAG_HUN_START_ALPHA, newStartValue);
+                    mHeadsUpScrim.setTag(TAG_HUN_END_ALPHA, alpha);
+                    previousAnimator.setCurrentPlayTime(previousAnimator.getCurrentPlayTime());
+                } else {
+                    // update the alpha directly
+                    setCurrentScrimAlpha(mHeadsUpScrim, alpha);
+                    updateScrimColor(mHeadsUpScrim);
+                }
+            }
+        }
+    }
+
+    public void setTopHeadsUpDragAmount(View draggedHeadsUpView, float topHeadsUpDragAmount) {
+        mTopHeadsUpDragAmount = topHeadsUpDragAmount;
+        mDraggedHeadsUpView = draggedHeadsUpView;
+        updateHeadsUpScrim(false);
+    }
+
+    private float calculateHeadsUpAlpha() {
+        if (mAmountOfPinnedHeadsUps >= 2) {
+            return 1.0f;
+        } else if (mAmountOfPinnedHeadsUps == 0) {
+            return 0.0f;
+        } else {
+            return 1.0f - mTopHeadsUpDragAmount;
+        }
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
index 6369d5e..194a19a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -441,7 +441,8 @@
         mPhoneStatusBar.keyguardGoingAway();
     }
 
-    public void animateCollapsePanels() {
-        mPhoneStatusBar.animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_NONE, true /* force */);
+    public void animateCollapsePanels(float speedUpFactor) {
+        mPhoneStatusBar.animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_NONE, true /* force */,
+                false /* delayed */, speedUpFactor);
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowManager.java
index 63bbf97..84a9f64 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowManager.java
@@ -130,7 +130,7 @@
 
     private void applyHeight(State state) {
         boolean expanded = state.isKeyguardShowingAndNotOccluded() || state.statusBarExpanded
-                || state.keyguardFadingAway || state.bouncerShowing;
+                || state.keyguardFadingAway || state.bouncerShowing || state.headsUpShowing;
         if (expanded) {
             mLpChanged.height = ViewGroup.LayoutParams.MATCH_PARENT;
         } else {
@@ -172,11 +172,20 @@
         applyUserActivityTimeout(state);
         applyInputFeatures(state);
         applyFitsSystemWindows(state);
+        applyModalFlag(state);
         if (mLp.copyFrom(mLpChanged) != 0) {
             mWindowManager.updateViewLayout(mStatusBarView, mLp);
         }
     }
 
+    private void applyModalFlag(State state) {
+        if (state.headsUpShowing) {
+            mLpChanged.flags |= WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
+        } else {
+            mLpChanged.flags &= ~WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
+        }
+    }
+
     public void setKeyguardShowing(boolean showing) {
         mCurrentState.keyguardShowing = showing;
         apply(mCurrentState);
@@ -218,6 +227,11 @@
         apply(mCurrentState);
     }
 
+    public void setHeadsUpShowing(boolean showing) {
+        mCurrentState.headsUpShowing = showing;
+        apply(mCurrentState);
+    }
+
     /**
      * @param state The {@link StatusBarState} of the status bar.
      */
@@ -235,6 +249,7 @@
         boolean bouncerShowing;
         boolean keyguardFadingAway;
         boolean qsExpanded;
+        boolean headsUpShowing;
 
         /**
          * The {@link BaseStatusBar} state from the status bar.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java
new file mode 100644
index 0000000..dccf2e21
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java
@@ -0,0 +1,525 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.policy;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.database.ContentObserver;
+import android.os.Handler;
+import android.os.SystemClock;
+import android.provider.Settings;
+import android.util.ArrayMap;
+import android.util.Log;
+import android.util.Pools;
+import android.view.ViewTreeObserver;
+import android.view.accessibility.AccessibilityEvent;
+
+import com.android.systemui.R;
+import com.android.systemui.statusbar.ExpandableNotificationRow;
+import com.android.systemui.statusbar.NotificationData;
+import com.android.systemui.statusbar.phone.PhoneStatusBar;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Stack;
+import java.util.TreeSet;
+
+public class HeadsUpManager implements ViewTreeObserver.OnComputeInternalInsetsListener {
+    private static final String TAG = "HeadsUpManager";
+    private static final boolean DEBUG = false;
+    private static final String SETTING_HEADS_UP_SNOOZE_LENGTH_MS = "heads_up_snooze_length_ms";
+
+    private final int mHeadsUpNotificationDecay;
+    private final int mMinimumDisplayTime;
+
+    private final int mTouchSensitivityDelay;
+    private final ArrayMap<String, Long> mSnoozedPackages;
+    private final HashSet<OnHeadsUpChangedListener> mListeners = new HashSet<>();
+    private final int mDefaultSnoozeLengthMs;
+    private final Handler mHandler = new Handler();
+    private final Pools.Pool<HeadsUpEntry> mEntryPool = new Pools.Pool<HeadsUpEntry>() {
+
+        private Stack<HeadsUpEntry> mPoolObjects = new Stack<>();
+
+        @Override
+        public HeadsUpEntry acquire() {
+            if (!mPoolObjects.isEmpty()) {
+                return mPoolObjects.pop();
+            }
+            return new HeadsUpEntry();
+        }
+
+        @Override
+        public boolean release(HeadsUpEntry instance) {
+            instance.removeAutoCancelCallbacks();
+            mPoolObjects.push(instance);
+            return true;
+        }
+    };
+
+
+    private PhoneStatusBar mBar;
+    private int mSnoozeLengthMs;
+    private ContentObserver mSettingsObserver;
+    private HashMap<String, HeadsUpEntry> mHeadsUpEntries = new HashMap<>();
+    private TreeSet<HeadsUpEntry> mSortedEntries = new TreeSet<>();
+    private HashSet<String> mSwipedOutKeys = new HashSet<>();
+    private int mUser;
+    private Clock mClock;
+    private boolean mReleaseOnExpandFinish;
+    private boolean mTrackingHeadsUp;
+    private HashSet<NotificationData.Entry> mEntriesToRemoveAfterExpand = new HashSet<>();
+    private boolean mIsExpanded;
+    private boolean mHasPinnedHeadsUp;
+    private int[] mTmpTwoArray = new int[2];
+
+    public HeadsUpManager(final Context context, ViewTreeObserver observer) {
+        Resources resources = context.getResources();
+        mTouchSensitivityDelay = resources.getInteger(R.integer.heads_up_sensitivity_delay);
+        if (DEBUG) Log.v(TAG, "create() " + mTouchSensitivityDelay);
+        mSnoozedPackages = new ArrayMap<>();
+        mDefaultSnoozeLengthMs = resources.getInteger(R.integer.heads_up_default_snooze_length_ms);
+        mSnoozeLengthMs = mDefaultSnoozeLengthMs;
+        mMinimumDisplayTime = resources.getInteger(R.integer.heads_up_notification_minimum_time);
+        mHeadsUpNotificationDecay = resources.getInteger(R.integer.heads_up_notification_decay);
+        mClock = new Clock();
+
+        mSnoozeLengthMs = Settings.Global.getInt(context.getContentResolver(),
+                SETTING_HEADS_UP_SNOOZE_LENGTH_MS, mDefaultSnoozeLengthMs);
+        mSettingsObserver = new ContentObserver(mHandler) {
+            @Override
+            public void onChange(boolean selfChange) {
+                final int packageSnoozeLengthMs = Settings.Global.getInt(
+                        context.getContentResolver(), SETTING_HEADS_UP_SNOOZE_LENGTH_MS, -1);
+                if (packageSnoozeLengthMs > -1 && packageSnoozeLengthMs != mSnoozeLengthMs) {
+                    mSnoozeLengthMs = packageSnoozeLengthMs;
+                    if (DEBUG) Log.v(TAG, "mSnoozeLengthMs = " + mSnoozeLengthMs);
+                }
+            }
+        };
+        context.getContentResolver().registerContentObserver(
+                Settings.Global.getUriFor(SETTING_HEADS_UP_SNOOZE_LENGTH_MS), false,
+                mSettingsObserver);
+        if (DEBUG) Log.v(TAG, "mSnoozeLengthMs = " + mSnoozeLengthMs);
+        observer.addOnComputeInternalInsetsListener(this);
+    }
+
+    public void setBar(PhoneStatusBar bar) {
+        mBar = bar;
+    }
+
+    public void addListener(OnHeadsUpChangedListener listener) {
+        mListeners.add(listener);
+    }
+
+    public PhoneStatusBar getBar() {
+        return mBar;
+    }
+
+    /**
+     * Called when posting a new notification to the heads up.
+     */
+    public void showNotification(NotificationData.Entry headsUp) {
+        if (DEBUG) Log.v(TAG, "showNotification");
+        addHeadsUpEntry(headsUp);
+        updateNotification(headsUp, true);
+        headsUp.setInterruption();
+    }
+
+    /**
+     * Called when updating or posting a notification to the heads up.
+     */
+    public void updateNotification(NotificationData.Entry headsUp, boolean alert) {
+        if (DEBUG) Log.v(TAG, "updateNotification");
+
+        headsUp.row.setChildrenExpanded(false /* expanded */, false /* animated */);
+        headsUp.row.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED);
+
+        if (alert) {
+            HeadsUpEntry headsUpEntry = mHeadsUpEntries.get(headsUp.key);
+            headsUpEntry.updateEntry();
+            setEntryToShade(headsUpEntry, mIsExpanded, false /* justAdded */, false);
+        }
+    }
+
+    private void addHeadsUpEntry(NotificationData.Entry entry) {
+        HeadsUpEntry headsUpEntry = mEntryPool.acquire();
+
+        // This will also add the entry to the sortedList
+        headsUpEntry.setEntry(entry);
+        mHeadsUpEntries.put(entry.key, headsUpEntry);
+        entry.row.setHeadsUp(true);
+        setEntryToShade(headsUpEntry, mIsExpanded /* inShade */, true /* justAdded */, false);
+        for (OnHeadsUpChangedListener listener : mListeners) {
+            listener.OnHeadsUpStateChanged(entry, true);
+        }
+        entry.row.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED);
+    }
+
+    private void setEntryToShade(HeadsUpEntry headsUpEntry, boolean inShade, boolean justAdded,
+            boolean forceImmediate) {
+        ExpandableNotificationRow row = headsUpEntry.entry.row;
+        if (row.isInShade() != inShade || justAdded) {
+            row.setInShade(inShade);
+            if (!justAdded || !inShade) {
+                updatePinnedHeadsUpState(forceImmediate);
+                for (OnHeadsUpChangedListener listener : mListeners) {
+                    listener.OnHeadsUpPinnedChanged(row, !inShade);
+                }
+            }
+        }
+    }
+
+    private void removeHeadsUpEntry(NotificationData.Entry entry) {
+        HeadsUpEntry remove = mHeadsUpEntries.remove(entry.key);
+        mSortedEntries.remove(remove);
+        mEntryPool.release(remove);
+        entry.row.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED);
+        entry.row.setHeadsUp(false);
+        setEntryToShade(remove, true /* inShade */, false /* justAdded */,
+                false /* forceImmediate */);
+        for (OnHeadsUpChangedListener listener : mListeners) {
+            listener.OnHeadsUpStateChanged(entry, false);
+        }
+    }
+
+    private void updatePinnedHeadsUpState(boolean forceImmediate) {
+        boolean hasPinnedHeadsUp = hasPinnedHeadsUpInternal();
+        if (hasPinnedHeadsUp == mHasPinnedHeadsUp) {
+            return;
+        }
+        mHasPinnedHeadsUp = hasPinnedHeadsUp;
+        for (OnHeadsUpChangedListener listener :mListeners) {
+            listener.OnPinnedHeadsUpExistChanged(hasPinnedHeadsUp, forceImmediate);
+        }
+    }
+
+    /**
+     * React to the removal of the notification in the heads up.
+     *
+     * @return true if the notification was removed and false if it still needs to be kept around
+     * for a bit since it wasn't shown long enough
+     */
+    public boolean removeNotification(String key) {
+        if (DEBUG) Log.v(TAG, "remove");
+        if (wasShownLongEnough(key)) {
+            releaseImmediately(key);
+            return true;
+        } else {
+            getHeadsUpEntry(key).hideAsSoonAsPossible();
+            return false;
+        }
+    }
+
+    private boolean wasShownLongEnough(String key) {
+        HeadsUpEntry headsUpEntry = getHeadsUpEntry(key);
+        HeadsUpEntry topEntry = getTopEntry();
+        if (mSwipedOutKeys.contains(key)) {
+            // We always instantly dismiss views being manually swiped out.
+            mSwipedOutKeys.remove(key);
+            return true;
+        }
+        if (headsUpEntry != topEntry) {
+            return true;
+        }
+        return headsUpEntry.wasShownLongEnough();
+    }
+
+    public boolean isHeadsUp(String key) {
+        return mHeadsUpEntries.containsKey(key);
+    }
+
+
+    /**
+     * Push any current Heads Up notification down into the shade.
+     */
+    public void releaseAllImmediately() {
+        if (DEBUG) Log.v(TAG, "releaseAllImmediately");
+        HashSet<String> keys = new HashSet<>(mHeadsUpEntries.keySet());
+        for (String key: keys) {
+            releaseImmediately(key);
+        }
+    }
+
+    public void releaseImmediately(String key) {
+        HeadsUpEntry headsUpEntry = getHeadsUpEntry(key);
+        if (headsUpEntry == null) {
+            return;
+        }
+        NotificationData.Entry shadeEntry = headsUpEntry.entry;
+        removeHeadsUpEntry(shadeEntry);
+    }
+
+    public boolean isSnoozed(String packageName) {
+        final String key = snoozeKey(packageName, mUser);
+        Long snoozedUntil = mSnoozedPackages.get(key);
+        if (snoozedUntil != null) {
+            if (snoozedUntil > SystemClock.elapsedRealtime()) {
+                if (DEBUG) Log.v(TAG, key + " snoozed");
+                return true;
+            }
+            mSnoozedPackages.remove(packageName);
+        }
+        return false;
+    }
+
+    public void snooze() {
+        for (String key: mHeadsUpEntries.keySet()) {
+            HeadsUpEntry entry = mHeadsUpEntries.get(key);
+            String packageName = entry.entry.notification.getPackageName();
+            mSnoozedPackages.put(snoozeKey(packageName, mUser),
+                    SystemClock.elapsedRealtime() + mSnoozeLengthMs);
+        }
+        mReleaseOnExpandFinish = true;
+    }
+
+    private static String snoozeKey(String packageName, int user) {
+        return user + "," + packageName;
+    }
+
+    private HeadsUpEntry getHeadsUpEntry(String key) {
+        return mHeadsUpEntries.get(key);
+    }
+
+    public NotificationData.Entry getEntry(String key) {
+        return mHeadsUpEntries.get(key).entry;
+    }
+
+    public TreeSet<HeadsUpEntry> getSortedEntries() {
+        return mSortedEntries;
+    }
+
+    public HeadsUpEntry getTopEntry() {
+        return mSortedEntries.isEmpty() ? null : mSortedEntries.first();
+    }
+
+    /**
+     * @param key the key of the touched notification
+     * @return whether the touch is valid and should not be discarded
+     */
+    public boolean shouldSwallowClick(String key) {
+        if (mClock.currentTimeMillis() < mHeadsUpEntries.get(key).postTime) {
+            return true;
+        }
+        return false;
+    }
+
+    public void onComputeInternalInsets(ViewTreeObserver.InternalInsetsInfo info) {
+        if (!mIsExpanded && mHasPinnedHeadsUp) {
+            int minX = Integer.MAX_VALUE;
+            int maxX = 0;
+            int minY = Integer.MAX_VALUE;
+            int maxY = 0;
+            for (HeadsUpEntry entry: mSortedEntries) {
+                ExpandableNotificationRow row = entry.entry.row;
+                if (!row.isInShade()) {
+                    row.getLocationOnScreen(mTmpTwoArray);
+                    minX = Math.min(minX, mTmpTwoArray[0]);
+                    minY = Math.min(minY, 0);
+                    maxX = Math.max(maxX, mTmpTwoArray[0] + row.getWidth());
+                    maxY = Math.max(maxY, row.getHeadsUpHeight());
+                }
+            }
+
+            info.setTouchableInsets(ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION);
+            info.touchableRegion.set(minX, minY, maxX, maxY);
+        }
+    }
+
+    public void setUser(int user) {
+        mUser = user;
+    }
+
+    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+        pw.println("HeadsUpManager state:");
+        pw.print("  mTouchSensitivityDelay="); pw.println(mTouchSensitivityDelay);
+        pw.print("  mSnoozeLengthMs="); pw.println(mSnoozeLengthMs);
+        pw.print("  now="); pw.println(SystemClock.elapsedRealtime());
+        pw.print("  mUser="); pw.println(mUser);
+        for (HeadsUpEntry entry: mSortedEntries) {
+            pw.print("  HeadsUpEntry="); pw.println(entry.entry);
+        }
+        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));
+        }
+    }
+
+    public boolean hasPinnedHeadsUp() {
+        return mHasPinnedHeadsUp;
+    }
+
+    private boolean hasPinnedHeadsUpInternal() {
+        for (String key: mHeadsUpEntries.keySet()) {
+            HeadsUpEntry entry = mHeadsUpEntries.get(key);
+            if (!entry.entry.row.isInShade()) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    public void addSwipedOutKey(String key) {
+        mSwipedOutKeys.add(key);
+    }
+
+    public float getHighestPinnedHeadsUp() {
+        float max = 0;
+        for (HeadsUpEntry entry: mSortedEntries) {
+            if (!entry.entry.row.isInShade()) {
+                max = Math.max(max, entry.entry.row.getActualHeight());
+            }
+        }
+        return max;
+    }
+
+    public void releaseAllToShade() {
+        for (String key: mHeadsUpEntries.keySet()) {
+            HeadsUpEntry entry = mHeadsUpEntries.get(key);
+            setEntryToShade(entry, true /* toShade */, false /* justAdded */,
+                    true /* forceImmediate */);
+        }
+    }
+
+    public void onExpandingFinished() {
+        if (mReleaseOnExpandFinish) {
+            releaseAllImmediately();
+            mReleaseOnExpandFinish = false;
+        } else {
+            for (NotificationData.Entry entry : mEntriesToRemoveAfterExpand) {
+                removeHeadsUpEntry(entry);
+            }
+            mEntriesToRemoveAfterExpand.clear();
+        }
+    }
+
+    public void setTrackingHeadsUp(boolean trackingHeadsUp) {
+        mTrackingHeadsUp = trackingHeadsUp;
+    }
+
+    public void setIsExpanded(boolean isExpanded) {
+        if (isExpanded != mIsExpanded) {
+            mIsExpanded = isExpanded;
+            if (isExpanded) {
+                releaseAllToShade();
+            }
+        }
+    }
+
+    public int getTopHeadsUpHeight() {
+        HeadsUpEntry topEntry = getTopEntry();
+        return topEntry != null ? topEntry.entry.row.getHeadsUpHeight() : 0;
+    }
+
+    public int compare(NotificationData.Entry a, NotificationData.Entry b) {
+        HeadsUpEntry aEntry = getHeadsUpEntry(a.key);
+        HeadsUpEntry bEntry = getHeadsUpEntry(b.key);
+        if (aEntry == null || bEntry == null) {
+            return aEntry == null ? 1 : -1;
+        }
+        return aEntry.compareTo(bEntry);
+    }
+
+    public class HeadsUpEntry implements Comparable<HeadsUpEntry> {
+        public NotificationData.Entry entry;
+        public long postTime;
+        public long earliestRemovaltime;
+        private Runnable mRemoveHeadsUpRunnable;
+
+        public void setEntry(final NotificationData.Entry entry) {
+            this.entry = entry;
+
+            // The actual post time will be just after the heads-up really slided in
+            postTime = mClock.currentTimeMillis() + mTouchSensitivityDelay;
+            mRemoveHeadsUpRunnable = new Runnable() {
+                @Override
+                public void run() {
+                    if (!mTrackingHeadsUp) {
+                        removeHeadsUpEntry(entry);
+                    } else {
+                        mEntriesToRemoveAfterExpand.add(entry);
+                    }
+                }
+            };
+            updateEntry();
+        }
+
+        public void updateEntry() {
+            long currentTime = mClock.currentTimeMillis();
+            earliestRemovaltime = currentTime + mMinimumDisplayTime;
+            postTime = Math.max(postTime, currentTime);
+            removeAutoCancelCallbacks();
+            if (canEntryDecay()) {
+                long finishTime = postTime + mHeadsUpNotificationDecay;
+                long removeDelay = Math.max(finishTime - currentTime, mMinimumDisplayTime);
+                mHandler.postDelayed(mRemoveHeadsUpRunnable, removeDelay);
+            }
+            updateSortOrder(HeadsUpEntry.this);
+        }
+
+        private boolean canEntryDecay() {
+            return entry.notification.getNotification().fullScreenIntent == null;
+        }
+
+        @Override
+        public int compareTo(HeadsUpEntry o) {
+            return postTime < o.postTime ? 1
+                    : postTime == o.postTime ? 0
+                            : -1;
+        }
+
+        public void removeAutoCancelCallbacks() {
+            mHandler.removeCallbacks(mRemoveHeadsUpRunnable);
+        }
+
+        public boolean wasShownLongEnough() {
+            return earliestRemovaltime < mClock.currentTimeMillis();
+        }
+
+        public void hideAsSoonAsPossible() {
+            removeAutoCancelCallbacks();
+            mHandler.postDelayed(mRemoveHeadsUpRunnable,
+                    earliestRemovaltime - mClock.currentTimeMillis());
+        }
+    }
+
+    /**
+     * Update the sorted heads up order.
+     *
+     * @param headsUpEntry the headsUp that changed
+     */
+    private void updateSortOrder(HeadsUpEntry headsUpEntry) {
+        mSortedEntries.remove(headsUpEntry);
+        mSortedEntries.add(headsUpEntry);
+    }
+
+    public static class Clock {
+        public long currentTimeMillis() {
+            return SystemClock.elapsedRealtime();
+        }
+    }
+
+    public interface OnHeadsUpChangedListener {
+        void OnPinnedHeadsUpExistChanged(boolean exist, boolean changeImmediatly);
+        void OnHeadsUpPinnedChanged(ExpandableNotificationRow headsUp, boolean isHeadsUp);
+        void OnHeadsUpStateChanged(NotificationData.Entry entry, boolean isHeadsUp);
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpNotificationView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpNotificationView.java
deleted file mode 100644
index 1e40bab..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpNotificationView.java
+++ /dev/null
@@ -1,622 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.statusbar.policy;
-
-import android.content.Context;
-import android.content.res.Configuration;
-import android.content.res.Resources;
-import android.database.ContentObserver;
-import android.graphics.Outline;
-import android.graphics.Rect;
-import android.os.SystemClock;
-import android.provider.Settings;
-import android.util.ArrayMap;
-import android.util.AttributeSet;
-import android.util.Log;
-import android.view.MotionEvent;
-import android.view.View;
-import android.view.ViewConfiguration;
-import android.view.ViewGroup;
-import android.view.ViewOutlineProvider;
-import android.view.ViewTreeObserver;
-import android.view.accessibility.AccessibilityEvent;
-import android.widget.FrameLayout;
-
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.systemui.ExpandHelper;
-import com.android.systemui.Gefingerpoken;
-import com.android.systemui.R;
-import com.android.systemui.SwipeHelper;
-import com.android.systemui.statusbar.ExpandableView;
-import com.android.systemui.statusbar.NotificationData;
-import com.android.systemui.statusbar.phone.PhoneStatusBar;
-
-import java.io.FileDescriptor;
-import java.io.PrintWriter;
-
-public class HeadsUpNotificationView extends FrameLayout implements SwipeHelper.Callback, ExpandHelper.Callback,
-        ViewTreeObserver.OnComputeInternalInsetsListener {
-    private static final String TAG = "HeadsUpNotificationView";
-    private static final boolean DEBUG = false;
-    private static final boolean SPEW = DEBUG;
-    private static final String SETTING_HEADS_UP_SNOOZE_LENGTH_MS = "heads_up_snooze_length_ms";
-
-    Rect mTmpRect = new Rect();
-    int[] mTmpTwoArray = new int[2];
-
-    private final int mHeadsUpNotificationDecay;
-    private final int mMinimumDisplayTime;
-
-    private final int mTouchSensitivityDelay;
-    private final float mMaxAlpha = 1f;
-    private final ArrayMap<String, Long> mSnoozedPackages;
-    private final int mDefaultSnoozeLengthMs;
-
-    private SwipeHelper mSwipeHelper;
-    private EdgeSwipeHelper mEdgeSwipeHelper;
-
-    private PhoneStatusBar mBar;
-
-    private long mLingerUntilMs;
-    private long mStartTouchTime;
-    private ViewGroup mContentHolder;
-    private int mSnoozeLengthMs;
-    private ContentObserver mSettingsObserver;
-
-    private NotificationData.Entry mHeadsUp;
-    private int mUser;
-    private String mMostRecentPackageName;
-    private boolean mTouched;
-    private Clock mClock;
-
-    public static class Clock {
-        public long currentTimeMillis() {
-            return SystemClock.elapsedRealtime();
-        }
-    }
-
-    public HeadsUpNotificationView(Context context, AttributeSet attrs) {
-        this(context, attrs, 0);
-    }
-
-    public HeadsUpNotificationView(Context context, AttributeSet attrs, int defStyle) {
-        super(context, attrs, defStyle);
-        Resources resources = context.getResources();
-        mTouchSensitivityDelay = resources.getInteger(R.integer.heads_up_sensitivity_delay);
-        if (DEBUG) Log.v(TAG, "create() " + mTouchSensitivityDelay);
-        mSnoozedPackages = new ArrayMap<>();
-        mDefaultSnoozeLengthMs = resources.getInteger(R.integer.heads_up_default_snooze_length_ms);
-        mSnoozeLengthMs = mDefaultSnoozeLengthMs;
-        mMinimumDisplayTime = resources.getInteger(R.integer.heads_up_notification_minimum_time);
-        mHeadsUpNotificationDecay = resources.getInteger(R.integer.heads_up_notification_decay);
-        mClock = new Clock();
-    }
-
-    @VisibleForTesting
-    public HeadsUpNotificationView(Context context, Clock clock, SwipeHelper swipeHelper,
-            EdgeSwipeHelper edgeSwipeHelper, int headsUpNotificationDecay, int minimumDisplayTime,
-            int touchSensitivityDelay, int snoozeLength) {
-        super(context, null);
-        mClock = clock;
-        mSwipeHelper = swipeHelper;
-        mEdgeSwipeHelper = edgeSwipeHelper;
-        mMinimumDisplayTime = minimumDisplayTime;
-        mHeadsUpNotificationDecay = headsUpNotificationDecay;
-        mTouchSensitivityDelay = touchSensitivityDelay;
-        mSnoozedPackages = new ArrayMap<>();
-        mDefaultSnoozeLengthMs = snoozeLength;
-    }
-
-    public void updateResources() {
-        if (mContentHolder != null) {
-            final LayoutParams lp = (LayoutParams) mContentHolder.getLayoutParams();
-            lp.width = getResources().getDimensionPixelSize(R.dimen.notification_panel_width);
-            lp.gravity = getResources().getInteger(R.integer.notification_panel_layout_gravity);
-            mContentHolder.setLayoutParams(lp);
-        }
-    }
-
-    public void setBar(PhoneStatusBar bar) {
-        mBar = bar;
-    }
-
-    public PhoneStatusBar getBar() {
-        return mBar;
-    }
-
-    public ViewGroup getHolder() {
-        return mContentHolder;
-    }
-
-    /**
-     * Called when posting a new notification to the heads up.
-     */
-    public void showNotification(NotificationData.Entry headsUp) {
-        if (DEBUG) Log.v(TAG, "showNotification");
-        if (mHeadsUp != null) {
-            // bump any previous heads up back to the shade
-            releaseImmediately();
-        }
-        mTouched = false;
-        updateNotification(headsUp, true);
-        mLingerUntilMs = mClock.currentTimeMillis() + mMinimumDisplayTime;
-    }
-
-    /**
-     * Called when updating or posting a notification to the heads up.
-     */
-    public void updateNotification(NotificationData.Entry headsUp, boolean alert) {
-        if (DEBUG) Log.v(TAG, "updateNotification");
-
-        if (mHeadsUp == headsUp) {
-            resetViewForHeadsup();
-            // This is an in-place update.  Noting more to do.
-            return;
-        }
-
-        mHeadsUp = headsUp;
-
-        if (mContentHolder != null) {
-            mContentHolder.removeAllViews();
-        }
-
-        if (mHeadsUp != null) {
-            mMostRecentPackageName = mHeadsUp.notification.getPackageName();
-            if (mHeadsUp.row != null) {
-                resetViewForHeadsup();
-            }
-
-            mStartTouchTime = SystemClock.elapsedRealtime() + mTouchSensitivityDelay;
-            if (mContentHolder != null) {  // only null in tests and before we are attached to a window
-                mContentHolder.setX(0);
-                mContentHolder.setVisibility(View.VISIBLE);
-                mContentHolder.setAlpha(mMaxAlpha);
-                mContentHolder.addView(mHeadsUp.row);
-                sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED);
-
-                mSwipeHelper.snapChild(mContentHolder, 1f);
-            }
-
-            mHeadsUp.setInterruption();
-        }
-        if (alert) {
-            // Make sure the heads up window is open.
-            mBar.scheduleHeadsUpOpen();
-            mBar.scheduleHeadsUpDecay(mHeadsUpNotificationDecay);
-        }
-    }
-
-    private void resetViewForHeadsup() {
-        if (mHeadsUp.row.areChildrenExpanded()) {
-            mHeadsUp.row.setChildrenExpanded(false /* expanded */, false /* animated */);
-        }
-        mHeadsUp.row.setSystemExpanded(true);
-        mHeadsUp.row.setSensitive(false);
-        mHeadsUp.row.setHeadsUp(true);
-        mHeadsUp.row.setTranslationY(0);
-        mHeadsUp.row.setTranslationZ(0);
-        mHeadsUp.row.setHideSensitive(
-                false, false /* animated */, 0 /* delay */, 0 /* duration */);
-    }
-
-    /**
-     * Possibly enter the lingering state by delaying the closing of the window.
-     *
-     * @return true if the notification has entered the lingering state.
-     */
-    private boolean startLingering(boolean removed) {
-        final long now = mClock.currentTimeMillis();
-        if (!mTouched && mHeadsUp != null && now < mLingerUntilMs) {
-            if (removed) {
-                mHeadsUp = null;
-            }
-            mBar.scheduleHeadsUpDecay(mLingerUntilMs - now);
-            return true;
-        }
-        return false;
-    }
-
-    /**
-     * React to the removal of the notification in the heads up.
-     */
-    public void removeNotification(String key) {
-        if (DEBUG) Log.v(TAG, "remove");
-        if (mHeadsUp == null || !mHeadsUp.key.equals(key)) {
-            return;
-        }
-        if (!startLingering(/* removed */ true)) {
-            mHeadsUp = null;
-            releaseImmediately();
-        }
-    }
-
-    /**
-     * Ask for any current Heads Up notification to be pushed down into the shade.
-     */
-    public void release() {
-        if (DEBUG) Log.v(TAG, "release");
-        if (!startLingering(/* removed */ false)) {
-            releaseImmediately();
-        }
-    }
-
-    /**
-     * Push any current Heads Up notification down into the shade.
-     */
-    public void releaseImmediately() {
-        if (DEBUG) Log.v(TAG, "releaseImmediately");
-        if (mHeadsUp != null) {
-            mContentHolder.removeView(mHeadsUp.row);
-            mBar.displayNotificationFromHeadsUp(mHeadsUp);
-        }
-        mHeadsUp = null;
-        mBar.scheduleHeadsUpClose();
-    }
-
-    @Override
-    protected void onVisibilityChanged(View changedView, int visibility) {
-        super.onVisibilityChanged(changedView, visibility);
-        if (DEBUG) Log.v(TAG, "onVisibilityChanged: " + visibility);
-        if (changedView.getVisibility() == VISIBLE) {
-            mStartTouchTime = mClock.currentTimeMillis() + mTouchSensitivityDelay;
-            sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED);
-        }
-    }
-
-    public boolean isSnoozed(String packageName) {
-        final String key = snoozeKey(packageName, mUser);
-        Long snoozedUntil = mSnoozedPackages.get(key);
-        if (snoozedUntil != null) {
-            if (snoozedUntil > SystemClock.elapsedRealtime()) {
-                if (DEBUG) Log.v(TAG, key + " snoozed");
-                return true;
-            }
-            mSnoozedPackages.remove(packageName);
-        }
-        return false;
-    }
-
-    private void snooze() {
-        if (mMostRecentPackageName != null) {
-            mSnoozedPackages.put(snoozeKey(mMostRecentPackageName, mUser),
-                    SystemClock.elapsedRealtime() + mSnoozeLengthMs);
-        }
-        releaseImmediately();
-    }
-
-    private static String snoozeKey(String packageName, int user) {
-        return user + "," + packageName;
-    }
-
-    public boolean isShowing(String key) {
-        return mHeadsUp != null && mHeadsUp.key.equals(key);
-    }
-
-    public NotificationData.Entry getEntry() {
-        return mHeadsUp;
-    }
-
-    public boolean isClearable() {
-        return mHeadsUp == null || mHeadsUp.notification.isClearable();
-    }
-
-    // ViewGroup methods
-
-private static final ViewOutlineProvider CONTENT_HOLDER_OUTLINE_PROVIDER =
-        new ViewOutlineProvider() {
-            @Override
-            public void getOutline(View view, Outline outline) {
-                int outlineLeft = view.getPaddingLeft();
-                int outlineTop = view.getPaddingTop();
-
-                // Apply padding to shadow.
-                outline.setRect(outlineLeft, outlineTop,
-                        view.getWidth() - outlineLeft - view.getPaddingRight(),
-                        view.getHeight() - outlineTop - view.getPaddingBottom());
-            }
-        };
-
-    @Override
-    public void onAttachedToWindow() {
-        final ViewConfiguration viewConfiguration = ViewConfiguration.get(getContext());
-        float touchSlop = viewConfiguration.getScaledTouchSlop();
-        mSwipeHelper = new SwipeHelper(SwipeHelper.X, this, getContext());
-        mSwipeHelper.setMaxSwipeProgress(mMaxAlpha);
-        mEdgeSwipeHelper = new EdgeSwipeHelper(this, touchSlop);
-
-        int minHeight = getResources().getDimensionPixelSize(R.dimen.notification_min_height);
-        int maxHeight = getResources().getDimensionPixelSize(R.dimen.notification_max_height);
-
-        mContentHolder = (ViewGroup) findViewById(R.id.content_holder);
-        mContentHolder.setOutlineProvider(CONTENT_HOLDER_OUTLINE_PROVIDER);
-
-        mSnoozeLengthMs = Settings.Global.getInt(mContext.getContentResolver(),
-                SETTING_HEADS_UP_SNOOZE_LENGTH_MS, mDefaultSnoozeLengthMs);
-        mSettingsObserver = new ContentObserver(getHandler()) {
-            @Override
-            public void onChange(boolean selfChange) {
-                final int packageSnoozeLengthMs = Settings.Global.getInt(
-                        mContext.getContentResolver(), SETTING_HEADS_UP_SNOOZE_LENGTH_MS, -1);
-                if (packageSnoozeLengthMs > -1 && packageSnoozeLengthMs != mSnoozeLengthMs) {
-                    mSnoozeLengthMs = packageSnoozeLengthMs;
-                    if (DEBUG) Log.v(TAG, "mSnoozeLengthMs = " + mSnoozeLengthMs);
-                }
-            }
-        };
-        mContext.getContentResolver().registerContentObserver(
-                Settings.Global.getUriFor(SETTING_HEADS_UP_SNOOZE_LENGTH_MS), false,
-                mSettingsObserver);
-        if (DEBUG) Log.v(TAG, "mSnoozeLengthMs = " + mSnoozeLengthMs);
-
-        if (mHeadsUp != null) {
-            // whoops, we're on already!
-            showNotification(mHeadsUp);
-        }
-
-        getViewTreeObserver().addOnComputeInternalInsetsListener(this);
-    }
-
-
-    @Override
-    protected void onDetachedFromWindow() {
-        mContext.getContentResolver().unregisterContentObserver(mSettingsObserver);
-    }
-
-    @Override
-    public boolean onInterceptTouchEvent(MotionEvent ev) {
-        if (DEBUG) Log.v(TAG, "onInterceptTouchEvent()");
-        if (mClock.currentTimeMillis() < mStartTouchTime) {
-            return true;
-        }
-        mTouched = true;
-        return mEdgeSwipeHelper.onInterceptTouchEvent(ev)
-                || mSwipeHelper.onInterceptTouchEvent(ev)
-                || mHeadsUp == null // lingering
-                || super.onInterceptTouchEvent(ev);
-    }
-
-    // View methods
-
-    @Override
-    public void onDraw(android.graphics.Canvas c) {
-        super.onDraw(c);
-        if (DEBUG) {
-            //Log.d(TAG, "onDraw: canvas height: " + c.getHeight() + "px; measured height: "
-            //        + getMeasuredHeight() + "px");
-            c.save();
-            c.clipRect(6, 6, c.getWidth() - 6, getMeasuredHeight() - 6,
-                    android.graphics.Region.Op.DIFFERENCE);
-            c.drawColor(0xFFcc00cc);
-            c.restore();
-        }
-    }
-
-    @Override
-    public boolean onTouchEvent(MotionEvent ev) {
-        if (mClock.currentTimeMillis() < mStartTouchTime) {
-            return false;
-        }
-
-        final boolean wasRemoved = mHeadsUp == null;
-        if (!wasRemoved) {
-            mBar.scheduleHeadsUpDecay(mHeadsUpNotificationDecay);
-        }
-        return mEdgeSwipeHelper.onTouchEvent(ev)
-                || mSwipeHelper.onTouchEvent(ev)
-                || wasRemoved
-                || super.onTouchEvent(ev);
-    }
-
-    @Override
-    protected void onConfigurationChanged(Configuration newConfig) {
-        super.onConfigurationChanged(newConfig);
-        float densityScale = getResources().getDisplayMetrics().density;
-        mSwipeHelper.setDensityScale(densityScale);
-        float pagingTouchSlop = ViewConfiguration.get(getContext()).getScaledPagingTouchSlop();
-        mSwipeHelper.setPagingTouchSlop(pagingTouchSlop);
-    }
-
-    // ExpandHelper.Callback methods
-
-    @Override
-    public ExpandableView getChildAtRawPosition(float x, float y) {
-        return getChildAtPosition(x, y);
-    }
-
-    @Override
-    public ExpandableView getChildAtPosition(float x, float y) {
-        return mHeadsUp == null ? null : mHeadsUp.row;
-    }
-
-    @Override
-    public boolean canChildBeExpanded(View v) {
-        return mHeadsUp != null && mHeadsUp.row == v && mHeadsUp.row.isExpandable();
-    }
-
-    @Override
-    public void setUserExpandedChild(View v, boolean userExpanded) {
-        if (mHeadsUp != null && mHeadsUp.row == v) {
-            mHeadsUp.row.setUserExpanded(userExpanded);
-        }
-    }
-
-    @Override
-    public void setUserLockedChild(View v, boolean userLocked) {
-        if (mHeadsUp != null && mHeadsUp.row == v) {
-            mHeadsUp.row.setUserLocked(userLocked);
-        }
-    }
-
-    @Override
-    public void expansionStateChanged(boolean isExpanding) {
-
-    }
-
-    // SwipeHelper.Callback methods
-
-    @Override
-    public boolean canChildBeDismissed(View v) {
-        return true;
-    }
-
-    @Override
-    public boolean isAntiFalsingNeeded() {
-        return false;
-    }
-
-    @Override
-    public float getFalsingThresholdFactor() {
-        return 1.0f;
-    }
-
-    @Override
-    public void onChildDismissed(View v) {
-        Log.v(TAG, "User swiped heads up to dismiss");
-        if (mHeadsUp != null && mHeadsUp.notification.isClearable()) {
-            mBar.onNotificationClear(mHeadsUp.notification);
-            mHeadsUp = null;
-        }
-        releaseImmediately();
-    }
-
-    @Override
-    public void onBeginDrag(View v) {
-    }
-
-    @Override
-    public void onDragCancelled(View v) {
-        mContentHolder.setAlpha(mMaxAlpha); // sometimes this isn't quite reset
-    }
-
-    @Override
-    public void onChildSnappedBack(View animView) {
-    }
-
-    @Override
-    public boolean updateSwipeProgress(View animView, boolean dismissable, float swipeProgress) {
-        getBackground().setAlpha((int) (255 * swipeProgress));
-        return false;
-    }
-
-    @Override
-    public View getChildAtPosition(MotionEvent ev) {
-        return mContentHolder;
-    }
-
-    @Override
-    public View getChildContentView(View v) {
-        return mContentHolder;
-    }
-
-    @Override
-    public void onComputeInternalInsets(ViewTreeObserver.InternalInsetsInfo info) {
-        mContentHolder.getLocationOnScreen(mTmpTwoArray);
-
-        info.setTouchableInsets(ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION);
-        info.touchableRegion.set(mTmpTwoArray[0], mTmpTwoArray[1],
-                mTmpTwoArray[0] + mContentHolder.getWidth(),
-                mTmpTwoArray[1] + mContentHolder.getHeight());
-    }
-
-    public void escalate() {
-        mBar.scheduleHeadsUpEscalation();
-    }
-
-    public String getKey() {
-        return mHeadsUp == null ? null : mHeadsUp.notification.getKey();
-    }
-
-    public void setUser(int user) {
-        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("  mLingerUntilMs="); pw.println(mLingerUntilMs);
-        pw.print("  mTouched="); pw.println(mTouched);
-        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));
-        }
-    }
-
-    public static class EdgeSwipeHelper implements Gefingerpoken {
-        private static final boolean DEBUG_EDGE_SWIPE = false;
-        private final float mTouchSlop;
-        private final HeadsUpNotificationView mHeadsUpView;
-        private boolean mConsuming;
-        private float mFirstY;
-        private float mFirstX;
-
-        public EdgeSwipeHelper(HeadsUpNotificationView headsUpView, float touchSlop) {
-            mHeadsUpView = headsUpView;
-            mTouchSlop = touchSlop;
-        }
-
-        @Override
-        public boolean onInterceptTouchEvent(MotionEvent ev) {
-            switch (ev.getActionMasked()) {
-                case MotionEvent.ACTION_DOWN:
-                    if (DEBUG_EDGE_SWIPE) Log.d(TAG, "action down " + ev.getY());
-                    mFirstX = ev.getX();
-                    mFirstY = ev.getY();
-                    mConsuming = false;
-                    break;
-
-                case MotionEvent.ACTION_MOVE:
-                    if (DEBUG_EDGE_SWIPE) Log.d(TAG, "action move " + ev.getY());
-                    final float dY = ev.getY() - mFirstY;
-                    final float daX = Math.abs(ev.getX() - mFirstX);
-                    final float daY = Math.abs(dY);
-                    if (!mConsuming && daX < daY && daY > mTouchSlop) {
-                        mHeadsUpView.snooze();
-                        if (dY > 0) {
-                            if (DEBUG_EDGE_SWIPE) Log.d(TAG, "found an open");
-                            mHeadsUpView.getBar().animateExpandNotificationsPanel();
-                        }
-                        mConsuming = true;
-                    }
-                    break;
-
-                case MotionEvent.ACTION_UP:
-                case MotionEvent.ACTION_CANCEL:
-                    if (DEBUG_EDGE_SWIPE) Log.d(TAG, "action done");
-                    mConsuming = false;
-                    break;
-            }
-            return mConsuming;
-        }
-
-        @Override
-        public boolean onTouchEvent(MotionEvent ev) {
-            return mConsuming;
-        }
-    }
-}
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 ba938cc..c3c6b12 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java
@@ -114,6 +114,11 @@
         setInetCondition(inetCondition);
     }
 
+    public void setCarrierNetworkChangeMode(boolean carrierNetworkChangeMode) {
+        mCurrentState.carrierNetworkChangeMode = carrierNetworkChangeMode;
+        notifyListenersIfNecessary();
+    }
+
     /**
      * Start listening for phone state changes.
      */
@@ -123,7 +128,8 @@
                         | PhoneStateListener.LISTEN_SIGNAL_STRENGTHS
                         | PhoneStateListener.LISTEN_CALL_STATE
                         | PhoneStateListener.LISTEN_DATA_CONNECTION_STATE
-                        | PhoneStateListener.LISTEN_DATA_ACTIVITY);
+                        | PhoneStateListener.LISTEN_DATA_ACTIVITY
+                        | PhoneStateListener.LISTEN_CARRIER_NETWORK_CHANGE);
     }
 
     /**
@@ -201,8 +207,12 @@
                         && !mCurrentState.isEmergency,
                         getQsCurrentIconId(), contentDescription,
                         qsTypeIcon,
-                        mCurrentState.dataConnected && mCurrentState.activityIn,
-                        mCurrentState.dataConnected && mCurrentState.activityOut,
+                        mCurrentState.dataConnected
+                            && !mCurrentState.carrierNetworkChangeMode
+                            && mCurrentState.activityIn,
+                        mCurrentState.dataConnected
+                            && !mCurrentState.carrierNetworkChangeMode
+                            && mCurrentState.activityOut,
                         dataContentDescription,
                         mCurrentState.isEmergency ? null : mCurrentState.networkName,
                         // Only wide if actually showing something.
@@ -215,6 +225,7 @@
             mSignalClusters.get(i).setMobileDataIndicators(
                     mCurrentState.enabled && !mCurrentState.airplaneMode,
                     getCurrentIconId(),
+                    getCurrentDarkIconId(),
                     typeIcon,
                     contentDescription,
                     dataContentDescription,
@@ -224,6 +235,10 @@
         }
     }
 
+    private int getCurrentDarkIconId() {
+        return getCurrentIconId(false /* light */);
+    }
+
     @Override
     protected MobileState cleanState() {
         return new MobileState();
@@ -270,6 +285,10 @@
         }
     }
 
+    private boolean isCarrierNetworkChangeActive() {
+        return !hasService() && mCurrentState.carrierNetworkChangeMode;
+    }
+
     public void handleBroadcast(Intent intent) {
         String action = intent.getAction();
         if (action.equals(TelephonyIntents.SPN_STRINGS_UPDATED_ACTION)) {
@@ -351,7 +370,9 @@
         mCurrentState.dataConnected = mCurrentState.connected
                 && mDataState == TelephonyManager.DATA_CONNECTED;
 
-        if (isRoaming()) {
+        if (isCarrierNetworkChangeActive()) {
+            mCurrentState.iconGroup = TelephonyIcons.CARRIER_NETWORK_CHANGE;
+        } else if (isRoaming()) {
             mCurrentState.iconGroup = TelephonyIcons.ROAMING;
         }
         if (isEmergencyOnly() != mCurrentState.isEmergency) {
@@ -363,6 +384,7 @@
                 && mServiceState.getOperatorAlphaShort() != null) {
             mCurrentState.networkName = mServiceState.getOperatorAlphaShort();
         }
+
         notifyListenersIfNecessary();
     }
 
@@ -428,6 +450,16 @@
             }
             setActivity(direction);
         }
+
+        @Override
+        public void onCarrierNetworkChange(boolean active) {
+            if (DEBUG) {
+                Log.d(mTag, "onCarrierNetworkChange: active=" + active);
+            }
+            mCurrentState.carrierNetworkChangeMode = active;
+
+            updateTelephony();
+        }
     };
 
     static class MobileIconGroup extends SignalController.IconGroup {
@@ -440,8 +472,17 @@
                 int sbNullState, int qsNullState, int sbDiscState, int qsDiscState,
                 int discContentDesc, int dataContentDesc, int dataType, boolean isWide,
                 int[] qsDataType) {
-            super(name, sbIcons, qsIcons, contentDesc, sbNullState, qsNullState, sbDiscState,
-                    qsDiscState, discContentDesc);
+            this(name, sbIcons, sbIcons, qsIcons, contentDesc, sbNullState, qsNullState,
+                    sbDiscState, sbDiscState, qsDiscState, discContentDesc, dataContentDesc,
+                    dataType, isWide, qsDataType);
+        }
+
+        public MobileIconGroup(String name, int[][] sbIcons, int[][] sbDarkIcons, int[][] qsIcons,
+                int[] contentDesc, int sbNullState, int qsNullState, int sbDiscState,
+                int sbDarkDiscState, int qsDiscState, int discContentDesc, int dataContentDesc,
+                int dataType, boolean isWide, int[] qsDataType) {
+            super(name, sbIcons, sbDarkIcons, qsIcons, contentDesc, sbNullState, qsNullState,
+                    sbDiscState, sbDarkDiscState, qsDiscState, discContentDesc);
             mDataContentDescription = dataContentDesc;
             mDataType = dataType;
             mIsWide = isWide;
@@ -455,6 +496,7 @@
         boolean dataConnected;
         boolean isEmergency;
         boolean airplaneMode;
+        boolean carrierNetworkChangeMode;
         int inetForNetwork;
 
         @Override
@@ -467,6 +509,7 @@
             inetForNetwork = state.inetForNetwork;
             isEmergency = state.isEmergency;
             airplaneMode = state.airplaneMode;
+            carrierNetworkChangeMode = state.carrierNetworkChangeMode;
         }
 
         @Override
@@ -478,7 +521,8 @@
             builder.append("dataConnected=").append(dataConnected).append(',');
             builder.append("inetForNetwork=").append(inetForNetwork).append(',');
             builder.append("isEmergency=").append(isEmergency).append(',');
-            builder.append("airplaneMode=").append(airplaneMode);
+            builder.append("airplaneMode=").append(airplaneMode).append(',');
+            builder.append("carrierNetworkChangeMode=").append(carrierNetworkChangeMode);
         }
 
         @Override
@@ -489,6 +533,7 @@
                     && ((MobileState) o).dataConnected == dataConnected
                     && ((MobileState) o).isEmergency == isEmergency
                     && ((MobileState) o).airplaneMode == airplaneMode
+                    && ((MobileState) o).carrierNetworkChangeMode == carrierNetworkChangeMode
                     && ((MobileState) o).inetForNetwork == inetForNetwork;
         }
     }
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 bb3eb7ad..5cf6a6e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
@@ -704,6 +704,13 @@
                 controller.getState().enabled = show;
                 controller.notifyListeners();
             }
+            String carrierNetworkChange = args.getString("carriernetworkchange");
+            if (carrierNetworkChange != null) {
+                boolean show = carrierNetworkChange.equals("show");
+                for (MobileSignalController controller : mMobileSignalControllers.values()) {
+                    controller.setCarrierNetworkChangeMode(show);
+                }
+            }
         }
     }
 
@@ -718,9 +725,9 @@
     public interface SignalCluster {
         void setWifiIndicators(boolean visible, int strengthIcon, String contentDescription);
 
-        void setMobileDataIndicators(boolean visible, int strengthIcon, int typeIcon,
-                String contentDescription, String typeContentDescription, boolean isTypeIconWide,
-                int subId);
+        void setMobileDataIndicators(boolean visible, int strengthIcon, int darkStrengthIcon,
+                int typeIcon, String contentDescription, String typeContentDescription,
+                boolean isTypeIconWide, int subId);
         void setSubs(List<SubscriptionInfo> subs);
         void setNoSims(boolean show);
 
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 1d96c6b..c204814 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SignalController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SignalController.java
@@ -142,8 +142,16 @@
      * Gets the signal icon for SB based on current state of connected, enabled, and level.
      */
     public int getCurrentIconId() {
+        return getCurrentIconId(true /* light */);
+    }
+
+    protected int getCurrentIconId(boolean light) {
         if (mCurrentState.connected) {
-            return getIcons().mSbIcons[mCurrentState.inetCondition][mCurrentState.level];
+            if (light) {
+                return getIcons().mSbIcons[mCurrentState.inetCondition][mCurrentState.level];
+            } else {
+                return getIcons().mSbDarkIcons[mCurrentState.inetCondition][mCurrentState.level];
+            }
         } else if (mCurrentState.enabled) {
             return getIcons().mSbDiscState;
         } else {
@@ -226,11 +234,13 @@
      */
     static class IconGroup {
         final int[][] mSbIcons;
+        final int[][] mSbDarkIcons;
         final int[][] mQsIcons;
         final int[] mContentDesc;
         final int mSbNullState;
         final int mQsNullState;
         final int mSbDiscState;
+        final int mSbDarkDiscState;
         final int mQsDiscState;
         final int mDiscContentDesc;
         // For logging.
@@ -239,13 +249,22 @@
         public IconGroup(String name, int[][] sbIcons, int[][] qsIcons, int[] contentDesc,
                 int sbNullState, int qsNullState, int sbDiscState, int qsDiscState,
                 int discContentDesc) {
+            this(name, sbIcons, sbIcons, qsIcons, contentDesc, sbNullState, qsNullState,
+                    sbDiscState, sbDiscState, qsDiscState, discContentDesc);
+        }
+
+        public IconGroup(String name, int[][] sbIcons, int[][] sbDarkIcons, int[][] qsIcons,
+                int[] contentDesc, int sbNullState, int qsNullState, int sbDiscState,
+                int sbDarkDiscState, int qsDiscState, int discContentDesc) {
             mName = name;
             mSbIcons = sbIcons;
+            mSbDarkIcons = sbDarkIcons;
             mQsIcons = qsIcons;
             mContentDesc = contentDesc;
             mSbNullState = sbNullState;
             mQsNullState = qsNullState;
             mSbDiscState = sbDiscState;
+            mSbDarkDiscState = sbDarkDiscState;
             mQsDiscState = qsDiscState;
             mDiscContentDesc = discContentDesc;
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/TelephonyIcons.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/TelephonyIcons.java
index d266ed8..053feb12 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/TelephonyIcons.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/TelephonyIcons.java
@@ -68,6 +68,42 @@
           R.drawable.stat_sys_signal_4_fully }
     };
 
+    //CarrierNetworkChange
+    static final int[][] TELEPHONY_CARRIER_NETWORK_CHANGE = {
+            { R.drawable.stat_sys_signal_carrier_network_change_animation,
+              R.drawable.stat_sys_signal_carrier_network_change_animation,
+              R.drawable.stat_sys_signal_carrier_network_change_animation,
+              R.drawable.stat_sys_signal_carrier_network_change_animation },
+            { R.drawable.stat_sys_signal_carrier_network_change_animation,
+              R.drawable.stat_sys_signal_carrier_network_change_animation,
+              R.drawable.stat_sys_signal_carrier_network_change_animation,
+              R.drawable.stat_sys_signal_carrier_network_change_animation }
+        };
+
+    static final int[][] TELEPHONY_CARRIER_NETWORK_CHANGE_DARK = {
+            { R.drawable.stat_sys_signal_dark_carrier_network_change_animation,
+              R.drawable.stat_sys_signal_dark_carrier_network_change_animation,
+              R.drawable.stat_sys_signal_dark_carrier_network_change_animation,
+              R.drawable.stat_sys_signal_dark_carrier_network_change_animation },
+            { R.drawable.stat_sys_signal_dark_carrier_network_change_animation,
+              R.drawable.stat_sys_signal_dark_carrier_network_change_animation,
+              R.drawable.stat_sys_signal_dark_carrier_network_change_animation,
+              R.drawable.stat_sys_signal_dark_carrier_network_change_animation }
+        };
+
+    static final int[][] QS_TELEPHONY_CARRIER_NETWORK_CHANGE = {
+        { R.drawable.ic_qs_signal_carrier_network_change_animation,
+          R.drawable.ic_qs_signal_carrier_network_change_animation,
+          R.drawable.ic_qs_signal_carrier_network_change_animation,
+          R.drawable.ic_qs_signal_carrier_network_change_animation,
+          R.drawable.ic_qs_signal_carrier_network_change_animation },
+        { R.drawable.ic_qs_signal_carrier_network_change_animation,
+          R.drawable.ic_qs_signal_carrier_network_change_animation,
+          R.drawable.ic_qs_signal_carrier_network_change_animation,
+          R.drawable.ic_qs_signal_carrier_network_change_animation,
+          R.drawable.ic_qs_signal_carrier_network_change_animation }
+    };
+
     static final int[] QS_DATA_R = {
         R.drawable.ic_qs_signal_r,
         R.drawable.ic_qs_signal_r
@@ -202,11 +238,34 @@
     static final int ICON_3G = R.drawable.stat_sys_data_fully_connected_3g;
     static final int ICON_4G = R.drawable.stat_sys_data_fully_connected_4g;
     static final int ICON_1X = R.drawable.stat_sys_data_fully_connected_1x;
+    static final int ICON_CARRIER_NETWORK_CHANGE =
+            R.drawable.stat_sys_signal_carrier_network_change_animation;
+    static final int ICON_CARRIER_NETWORK_CHANGE_DARK =
+            R.drawable.stat_sys_signal_dark_carrier_network_change_animation;
 
     static final int QS_ICON_LTE = R.drawable.ic_qs_signal_lte;
     static final int QS_ICON_3G = R.drawable.ic_qs_signal_3g;
     static final int QS_ICON_4G = R.drawable.ic_qs_signal_4g;
     static final int QS_ICON_1X = R.drawable.ic_qs_signal_1x;
+    static final int QS_ICON_CARRIER_NETWORK_CHANGE =
+            R.drawable.ic_qs_signal_carrier_network_change_animation;
+
+    static final MobileIconGroup CARRIER_NETWORK_CHANGE = new MobileIconGroup(
+            "CARRIER_NETWORK_CHANGE",
+            TelephonyIcons.TELEPHONY_CARRIER_NETWORK_CHANGE,
+            TelephonyIcons.TELEPHONY_CARRIER_NETWORK_CHANGE_DARK,
+            TelephonyIcons.QS_TELEPHONY_CARRIER_NETWORK_CHANGE,
+            AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH,
+            0, 0,
+            TelephonyIcons.ICON_CARRIER_NETWORK_CHANGE,
+            TelephonyIcons.ICON_CARRIER_NETWORK_CHANGE_DARK,
+            TelephonyIcons.QS_ICON_CARRIER_NETWORK_CHANGE,
+            AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH[0],
+            R.string.accessibility_carrier_network_change_mode,
+            0,
+            false,
+            null
+            );
 
     static final MobileIconGroup THREE_G = new MobileIconGroup(
             "3G",
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/AmbientState.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/AmbientState.java
index 8e677f1..f2b971f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/AmbientState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/AmbientState.java
@@ -17,9 +17,13 @@
 package com.android.systemui.statusbar.stack;
 
 import android.view.View;
+
 import com.android.systemui.statusbar.ActivatableNotificationView;
+import com.android.systemui.statusbar.ExpandableNotificationRow;
+import com.android.systemui.statusbar.policy.HeadsUpManager;
 
 import java.util.ArrayList;
+import java.util.TreeSet;
 
 /**
  * A global state to track all input states for the algorithm.
@@ -34,6 +38,12 @@
     private int mSpeedBumpIndex = -1;
     private boolean mDark;
     private boolean mHideSensitive;
+    private HeadsUpManager mHeadsUpManager;
+    private float mStackTranslation;
+    private int mLayoutHeight;
+    private int mTopPadding;
+    private boolean mShadeExpanded;
+    private float mMaxHeadsUpTranslation;
 
     public int getScrollY() {
         return mScrollY;
@@ -115,4 +125,67 @@
     public void setSpeedBumpIndex(int speedBumpIndex) {
         mSpeedBumpIndex = speedBumpIndex;
     }
+
+    public void setHeadsUpManager(HeadsUpManager headsUpManager) {
+        mHeadsUpManager = headsUpManager;
+    }
+
+    public TreeSet<HeadsUpManager.HeadsUpEntry> getSortedHeadsUpEntries() {
+        return mHeadsUpManager.getSortedEntries();
+    }
+
+    public float getStackTranslation() {
+        return mStackTranslation;
+    }
+
+    public void setStackTranslation(float stackTranslation) {
+        mStackTranslation = stackTranslation;
+    }
+
+    public int getLayoutHeight() {
+        return mLayoutHeight;
+    }
+
+    public void setLayoutHeight(int layoutHeight) {
+        mLayoutHeight = layoutHeight;
+    }
+
+    public float getTopPadding() {
+        return mTopPadding;
+    }
+
+    public void setTopPadding(int topPadding) {
+        mTopPadding = topPadding;
+    }
+
+    public int getInnerHeight() {
+        return mLayoutHeight - mTopPadding - getTopHeadsUpPushIn();
+    }
+
+    private int getTopHeadsUpPushIn() {
+        ExpandableNotificationRow topHeadsUpEntry = getTopHeadsUpEntry();
+        return topHeadsUpEntry != null ? topHeadsUpEntry.getHeadsUpHeight()
+                - topHeadsUpEntry.getMinHeight(): 0;
+    }
+
+    public boolean isShadeExpanded() {
+        return mShadeExpanded;
+    }
+
+    public void setShadeExpanded(boolean shadeExpanded) {
+        mShadeExpanded = shadeExpanded;
+    }
+
+    public void setMaxHeadsUpTranslation(float maxHeadsUpTranslation) {
+        mMaxHeadsUpTranslation = maxHeadsUpTranslation;
+    }
+
+    public float getMaxHeadsUpTranslation() {
+        return mMaxHeadsUpTranslation;
+    }
+
+    public ExpandableNotificationRow getTopHeadsUpEntry() {
+        HeadsUpManager.HeadsUpEntry topEntry = mHeadsUpManager.getTopEntry();
+        return topEntry == null ? null : topEntry.entry.row;
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
index 2eafd57..88fc602 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
@@ -24,6 +24,7 @@
 import android.graphics.PointF;
 import android.util.AttributeSet;
 import android.util.Log;
+import android.util.Pair;
 import android.view.MotionEvent;
 import android.view.VelocityTracker;
 import android.view.View;
@@ -47,6 +48,8 @@
 import com.android.systemui.statusbar.StatusBarState;
 import com.android.systemui.statusbar.phone.NotificationGroupManager;
 import com.android.systemui.statusbar.phone.PhoneStatusBar;
+import com.android.systemui.statusbar.phone.ScrimController;
+import com.android.systemui.statusbar.policy.HeadsUpManager;
 import com.android.systemui.statusbar.policy.ScrollAdapter;
 
 import java.util.ArrayList;
@@ -121,15 +124,15 @@
     private StackScrollState mCurrentStackScrollState = new StackScrollState(this);
     private AmbientState mAmbientState = new AmbientState();
     private NotificationGroupManager mGroupManager;
-    private ArrayList<View> mChildrenToAddAnimated = new ArrayList<View>();
-    private ArrayList<View> mChildrenToRemoveAnimated = new ArrayList<View>();
-    private ArrayList<View> mSnappedBackChildren = new ArrayList<View>();
-    private ArrayList<View> mDragAnimPendingChildren = new ArrayList<View>();
-    private ArrayList<View> mChildrenChangingPositions = new ArrayList<View>();
+    private ArrayList<View> mChildrenToAddAnimated = new ArrayList<>();
+    private ArrayList<View> mAddedHeadsUpChildren = new ArrayList<>();
+    private ArrayList<View> mChildrenToRemoveAnimated = new ArrayList<>();
+    private ArrayList<View> mSnappedBackChildren = new ArrayList<>();
+    private ArrayList<View> mDragAnimPendingChildren = new ArrayList<>();
+    private ArrayList<View> mChildrenChangingPositions = new ArrayList<>();
     private HashSet<View> mFromMoreCardAdditions = new HashSet<>();
-    private ArrayList<AnimationEvent> mAnimationEvents
-            = new ArrayList<AnimationEvent>();
-    private ArrayList<View> mSwipedOutViews = new ArrayList<View>();
+    private ArrayList<AnimationEvent> mAnimationEvents = new ArrayList<>();
+    private ArrayList<View> mSwipedOutViews = new ArrayList<>();
     private final StackStateAnimator mStateAnimator = new StackStateAnimator(this);
     private boolean mAnimationsEnabled;
     private boolean mChangePositionInProgress;
@@ -143,7 +146,6 @@
      * The raw amount of the overScroll on the bottom, which is not rubber-banded.
      */
     private float mOverScrolledBottomPixels;
-
     private OnChildLocationsChangedListener mListener;
     private OnOverscrollTopChangedListener mOverscrollTopChangedListener;
     private ExpandableView.OnHeightChangedListener mOnHeightChangedListener;
@@ -171,7 +173,6 @@
      * Was the scroller scrolled to the top when the down motion was observed?
      */
     private boolean mScrolledToTopOnFirstDown;
-
     /**
      * The minimal amount of over scroll which is needed in order to switch to the quick settings
      * when over scrolling on a expanded card.
@@ -179,6 +180,7 @@
     private float mMinTopOverScrollToEscape;
     private int mIntrinsicPadding;
     private int mNotificationTopPadding;
+    private float mStackTranslation;
     private float mTopPaddingOverflow;
     private boolean mDontReportNextOverScroll;
     private boolean mRequestViewResizeAnimationOnLayout;
@@ -202,9 +204,9 @@
     private ViewGroup mScrollView;
     private boolean mInterceptDelegateEnabled;
     private boolean mDelegateToScrollView;
+
     private boolean mDisallowScrollingInThisMotion;
     private long mGoToFullShadeDelay;
-
     private ViewTreeObserver.OnPreDrawListener mChildrenUpdater
             = new ViewTreeObserver.OnPreDrawListener() {
         @Override
@@ -218,7 +220,12 @@
     private PhoneStatusBar mPhoneStatusBar;
     private int[] mTempInt2 = new int[2];
     private boolean mGenerateChildOrderChangedEvent;
-    private boolean mRemoveAnimationEnabled;
+    private HashSet<Runnable> mAnimationFinishedRunnables = new HashSet<>();
+    private HashSet<Pair<ExpandableNotificationRow, Boolean>> mHeadsUpChangeAnimations
+            = new HashSet<>();
+    private HeadsUpManager mHeadsUpManager;
+    private boolean mTrackingHeadsUp;
+    private ScrimController mScrimController;
 
     public NotificationStackScrollLayout(Context context) {
         this(context, null);
@@ -404,8 +411,8 @@
     }
 
     private void updateAlgorithmHeightAndPadding() {
-        mStackScrollAlgorithm.setLayoutHeight(getLayoutHeight());
-        mStackScrollAlgorithm.setTopPadding(mTopPadding);
+        mAmbientState.setLayoutHeight(getLayoutHeight());
+        mAmbientState.setTopPadding(mTopPadding);
     }
 
     /**
@@ -478,9 +485,13 @@
         int newStackHeight = (int) height;
         int minStackHeight = getMinStackHeight();
         int stackHeight;
-        if (newStackHeight - mTopPadding - mTopPaddingOverflow >= minStackHeight
+        float paddingOffset;
+        boolean trackingHeadsUp = mTrackingHeadsUp;
+        int normalExpandPositionStart = trackingHeadsUp ? mHeadsUpManager.getTopHeadsUpHeight()
+                : minStackHeight;
+        if (newStackHeight - mTopPadding - mTopPaddingOverflow >= normalExpandPositionStart
                 || getNotGoneChildCount() == 0) {
-            setTranslationY(mTopPaddingOverflow);
+            paddingOffset = mTopPaddingOverflow;
             stackHeight = newStackHeight;
         } else {
 
@@ -492,9 +503,13 @@
             float partiallyThere = (newStackHeight - mTopPadding - mTopPaddingOverflow)
                     / minStackHeight;
             partiallyThere = Math.max(0, partiallyThere);
-            translationY += (1 - partiallyThere) * (mBottomStackPeekSize +
-                    mCollapseSecondCardPadding);
-            setTranslationY(translationY - mTopPadding);
+            if (!trackingHeadsUp) {
+                translationY += (1 - partiallyThere) * (mBottomStackPeekSize +
+                        mCollapseSecondCardPadding);
+            } else {
+                translationY = (int) (height - mHeadsUpManager.getTopHeadsUpHeight());
+            }
+            paddingOffset = translationY - mTopPadding;
             stackHeight = (int) (height - (translationY - mTopPadding));
         }
         if (stackHeight != mCurrentStackHeight) {
@@ -502,6 +517,19 @@
             updateAlgorithmHeightAndPadding();
             requestChildrenUpdate();
         }
+        setStackTranslation(paddingOffset);
+    }
+
+    public float getStackTranslation() {
+        return mStackTranslation;
+    }
+
+    private void setStackTranslation(float stackTranslation) {
+        if (stackTranslation != mStackTranslation) {
+            mStackTranslation = stackTranslation;
+            mAmbientState.setStackTranslation(stackTranslation);
+            requestChildrenUpdate();
+        }
     }
 
     /**
@@ -543,11 +571,6 @@
         if (mDismissAllInProgress) {
             return;
         }
-        if (DEBUG) Log.v(TAG, "onChildDismissed: " + v);
-        final View veto = v.findViewById(R.id.veto);
-        if (veto != null && veto.getVisibility() != View.GONE) {
-            veto.performClick();
-        }
         setSwipingInProgress(false);
         if (mDragAnimPendingChildren.contains(v)) {
             // We start the swipe and finish it in the same frame, we don't want any animation
@@ -556,6 +579,17 @@
         }
         mSwipedOutViews.add(v);
         mAmbientState.onDragFinished(v);
+        if (v instanceof ExpandableNotificationRow) {
+            ExpandableNotificationRow row = (ExpandableNotificationRow) v;
+            if (row.isHeadsUp()) {
+                mHeadsUpManager.addSwipedOutKey(row.getStatusBarNotification().getKey());
+            }
+        }
+        final View veto = v.findViewById(R.id.veto);
+        if (veto != null && veto.getVisibility() != View.GONE) {
+            veto.performClick();
+        }
+        if (DEBUG) Log.v(TAG, "onChildDismissed: " + v);
     }
 
     @Override
@@ -575,28 +609,48 @@
 
     @Override
     public boolean updateSwipeProgress(View animView, boolean dismissable, float swipeProgress) {
+        if (isPinnedHeadsUp(animView) && canChildBeDismissed(animView)) {
+            mScrimController.setTopHeadsUpDragAmount(animView,
+                    Math.min(Math.abs(swipeProgress - 1.0f), 1.0f));
+        }
         return false;
     }
 
-    @Override
-    public float getFalsingThresholdFactor() {
-        return mPhoneStatusBar.isScreenOnComingFromTouch() ? 1.5f : 1.0f;
-    }
-
     public void onBeginDrag(View v) {
         setSwipingInProgress(true);
         mAmbientState.onBeginDrag(v);
-        if (mAnimationsEnabled) {
+        if (mAnimationsEnabled && !isPinnedHeadsUp(v)) {
             mDragAnimPendingChildren.add(v);
             mNeedsAnimation = true;
         }
         requestChildrenUpdate();
     }
 
+    public boolean isPinnedHeadsUp(View v) {
+        if (v instanceof ExpandableNotificationRow) {
+            ExpandableNotificationRow row = (ExpandableNotificationRow) v;
+            return row.isHeadsUp() && !row.isInShade();
+        }
+        return false;
+    }
+
+    private boolean isHeadsUp(View v) {
+        if (v instanceof ExpandableNotificationRow) {
+            ExpandableNotificationRow row = (ExpandableNotificationRow) v;
+            return row.isHeadsUp();
+        }
+        return false;
+    }
+
     public void onDragCancelled(View v) {
         setSwipingInProgress(false);
     }
 
+    @Override
+    public float getFalsingThresholdFactor() {
+        return mPhoneStatusBar.isScreenOnComingFromTouch() ? 1.5f : 1.0f;
+    }
+
     public View getChildAtPosition(MotionEvent ev) {
         return getChildAtPosition(ev.getX(), ev.getY());
     }
@@ -657,6 +711,10 @@
             if (touchY >= top && touchY <= bottom && touchX >= left && touchX <= right) {
                 if (slidingChild instanceof ExpandableNotificationRow) {
                     ExpandableNotificationRow row = (ExpandableNotificationRow) slidingChild;
+                    if (row.isHeadsUp() && !row.isInShade()
+                            && mHeadsUpManager.getTopEntry().entry.row != row) {
+                        continue;
+                    }
                     return row.getViewAtPosition(touchY - childTop);
                 }
                 return slidingChild;
@@ -667,7 +725,8 @@
 
     public boolean canChildBeExpanded(View v) {
         return v instanceof ExpandableNotificationRow
-                && ((ExpandableNotificationRow) v).isExpandable();
+                && ((ExpandableNotificationRow) v).isExpandable()
+                && !((ExpandableNotificationRow) v).isHeadsUp();
     }
 
     public void setUserExpandedChild(View v, boolean userExpanded) {
@@ -1343,12 +1402,9 @@
                     // add the padding before this element
                     height += mPaddingBetweenElements;
                 }
-                if (child instanceof ExpandableNotificationRow) {
-                    ExpandableNotificationRow row = (ExpandableNotificationRow) child;
-                    height += row.getIntrinsicHeight();
-                } else if (child instanceof ExpandableView) {
+                if (child instanceof ExpandableView) {
                     ExpandableView expandableView = (ExpandableView) child;
-                    height += expandableView.getActualHeight();
+                    height += expandableView.getIntrinsicHeight();
                 }
             }
         }
@@ -1573,16 +1629,11 @@
         ((ExpandableView) child).setOnHeightChangedListener(null);
         mCurrentStackScrollState.removeViewStateForView(child);
         updateScrollStateForRemovedChild(child);
-        if (mRemoveAnimationEnabled) {
-            boolean animationGenerated = generateRemoveAnimation(child);
-            if (animationGenerated && !mSwipedOutViews.contains(child)) {
-                // Add this view to an overlay in order to ensure that it will still be temporary
-                // drawn when removed
-                getOverlay().add(child);
-            }
-        } else {
-            // TODO: handle this more cleanly when HEADS-up and the shade are merged
-            requestAnimateEverything();
+        boolean animationGenerated = generateRemoveAnimation(child);
+        if (animationGenerated && !mSwipedOutViews.contains(child)) {
+            // Add this view to an overlay in order to ensure that it will still be temporary
+            // drawn when removed
+            getOverlay().add(child);
         }
         updateAnimationState(false, child);
 
@@ -1713,16 +1764,17 @@
     }
 
     private void updateNotificationAnimationStates() {
-        boolean running = mIsExpanded && mAnimationsEnabled;
+        boolean running = mAnimationsEnabled;
         int childCount = getChildCount();
         for (int i = 0; i < childCount; i++) {
             View child = getChildAt(i);
+            running &= mIsExpanded || isPinnedHeadsUp(child);
             updateAnimationState(running, child);
         }
     }
 
     private void updateAnimationState(View child) {
-        updateAnimationState(mAnimationsEnabled && mIsExpanded, child);
+        updateAnimationState((mAnimationsEnabled || isPinnedHeadsUp(child)) && mIsExpanded, child);
     }
 
 
@@ -1752,6 +1804,10 @@
             }
             mNeedsAnimation = true;
         }
+        if (isHeadsUp(child)) {
+            mAddedHeadsUpChildren.add(child);
+            mChildrenToAddAnimated.remove(child);
+        }
     }
 
     /**
@@ -1790,6 +1846,7 @@
     }
 
     private void generateChildHierarchyEvents() {
+        generateHeadsUpAnimationEvents();
         generateChildRemovalEvents();
         generateChildAdditionEvents();
         generatePositionChangeEvents();
@@ -1807,6 +1864,40 @@
         mNeedsAnimation = false;
     }
 
+    private void generateHeadsUpAnimationEvents() {
+        for (Pair<ExpandableNotificationRow, Boolean> eventPair : mHeadsUpChangeAnimations) {
+            ExpandableNotificationRow row = eventPair.first;
+            boolean isHeadsUp = eventPair.second;
+            int type = AnimationEvent.ANIMATION_TYPE_HEADS_UP_OTHER;
+            boolean onBottom = false;
+            if (!mIsExpanded && !isHeadsUp) {
+                type = AnimationEvent.ANIMATION_TYPE_HEADS_UP_DISAPPEAR;
+            } else if (mAddedHeadsUpChildren.contains(row) || (!row.isInShade() && !mIsExpanded)) {
+                if (!row.isInShade() || shouldHunAppearFromBottom(row)) {
+                    // Our custom add animation
+                    type = AnimationEvent.ANIMATION_TYPE_HEADS_UP_APPEAR;
+                } else {
+                    // Normal add animation
+                    type = AnimationEvent.ANIMATION_TYPE_ADD;
+                }
+                onBottom = row.isInShade();
+            }
+            AnimationEvent event = new AnimationEvent(row, type);
+            event.headsUpFromBottom = onBottom;
+            mAnimationEvents.add(event);
+        }
+        mHeadsUpChangeAnimations.clear();
+        mAddedHeadsUpChildren.clear();
+    }
+
+    private boolean shouldHunAppearFromBottom(ExpandableNotificationRow row) {
+        StackViewState viewState = mCurrentStackScrollState.getViewStateForView(row);
+        if (viewState.yTranslation + viewState.height < mAmbientState.getMaxHeadsUpTranslation()) {
+            return false;
+        }
+        return true;
+    }
+
     private void generateGroupExpansionEvent() {
         // Generate a group expansion/collapsing event if there is such a group at all
         if (mExpandedGroupView != null) {
@@ -2182,6 +2273,10 @@
 
     public void onChildAnimationFinished() {
         requestChildrenUpdate();
+        for (Runnable runnable : mAnimationFinishedRunnables) {
+            runnable.run();
+        }
+        mAnimationFinishedRunnables.clear();
     }
 
     /**
@@ -2283,7 +2378,7 @@
      * @return the y position of the first notification
      */
     public float getNotificationsTopY() {
-        return mTopPadding + getTranslationY();
+        return mTopPadding + getStackTranslation();
     }
 
     @Override
@@ -2470,7 +2565,7 @@
                 max = bottom;
             }
         }
-        return max + getTranslationY();
+        return max + getStackTranslation();
     }
 
     /**
@@ -2530,10 +2625,6 @@
         return touchY > mIntrinsicPadding;
     }
 
-    public void setRemoveAnimationEnabled(boolean enabled) {
-        mRemoveAnimationEnabled = enabled;
-    }
-
     private void updateExpandButtons() {
         for (int i = 0; i < getChildCount(); i++) {
             View child = getChildAt(i);
@@ -2579,6 +2670,50 @@
         }
     }
 
+    public void performOnAnimationFinished(Runnable runnable) {
+        mAnimationFinishedRunnables.add(runnable);
+    }
+
+    public void setHeadsUpManager(HeadsUpManager headsUpManager) {
+        mHeadsUpManager = headsUpManager;
+        mAmbientState.setHeadsUpManager(headsUpManager);
+        mStackScrollAlgorithm.setHeadsUpManager(headsUpManager);
+    }
+
+    public void generateHeadsUpAnimation(ExpandableNotificationRow row, boolean isHeadsUp) {
+        if (mAnimationsEnabled) {
+            mHeadsUpChangeAnimations.add(new Pair<>(row, isHeadsUp));
+            mNeedsAnimation = true;
+            requestChildrenUpdate();
+        }
+    }
+
+    public void setShadeExpanded(boolean shadeExpanded) {
+        mAmbientState.setShadeExpanded(shadeExpanded);
+        mStateAnimator.setShadeExpanded(shadeExpanded);
+    }
+
+    /**
+     * Set the boundary for the bottom heads up position. The heads up will always be above this
+     * position.
+     *
+     * @param height the height of the screen
+     * @param bottomBarHeight the height of the bar on the bottom
+     */
+    public void setHeadsUpBoundaries(int height, int bottomBarHeight) {
+        mAmbientState.setMaxHeadsUpTranslation(height - bottomBarHeight);
+        mStateAnimator.setHeadsUpAppearHeightBottom(height);
+        requestChildrenUpdate();
+    }
+
+    public void setTrackingHeadsUp(boolean trackingHeadsUp) {
+        mTrackingHeadsUp = trackingHeadsUp;
+    }
+
+    public void setScrimController(ScrimController scrimController) {
+        mScrimController = scrimController;
+    }
+
     /**
      * A listener that is notified when some child locations might have changed.
      */
@@ -2723,6 +2858,30 @@
                         .animateY()
                         .animateZ(),
 
+                // ANIMATION_TYPE_HEADS_UP_APPEAR
+                new AnimationFilter()
+                        .animateAlpha()
+                        .animateHeight()
+                        .animateTopInset()
+                        .animateY()
+                        .animateZ(),
+
+                // ANIMATION_TYPE_HEADS_UP_DISAPPEAR
+                new AnimationFilter()
+                        .animateAlpha()
+                        .animateHeight()
+                        .animateTopInset()
+                        .animateY()
+                        .animateZ(),
+
+                // ANIMATION_TYPE_HEADS_UP_OTHER
+                new AnimationFilter()
+                        .animateAlpha()
+                        .animateHeight()
+                        .animateTopInset()
+                        .animateY()
+                        .animateZ(),
+
                 // ANIMATION_TYPE_EVERYTHING
                 new AnimationFilter()
                         .animateAlpha()
@@ -2780,6 +2939,15 @@
                 // ANIMATION_TYPE_GROUP_EXPANSION_CHANGED
                 StackStateAnimator.ANIMATION_DURATION_EXPAND_CLICKED,
 
+                // ANIMATION_TYPE_HEADS_UP_APPEAR
+                StackStateAnimator.ANIMATION_DURATION_HEADS_UP_APPEAR,
+
+                // ANIMATION_TYPE_HEADS_UP_DISAPPEAR
+                StackStateAnimator.ANIMATION_DURATION_HEADS_UP_DISAPPEAR,
+
+                // ANIMATION_TYPE_HEADS_UP_OTHER
+                StackStateAnimator.ANIMATION_DURATION_STANDARD,
+
                 // ANIMATION_TYPE_EVERYTHING
                 StackStateAnimator.ANIMATION_DURATION_STANDARD,
         };
@@ -2798,7 +2966,10 @@
         static final int ANIMATION_TYPE_HIDE_SENSITIVE = 11;
         static final int ANIMATION_TYPE_VIEW_RESIZE = 12;
         static final int ANIMATION_TYPE_GROUP_EXPANSION_CHANGED = 13;
-        static final int ANIMATION_TYPE_EVERYTHING = 14;
+        static final int ANIMATION_TYPE_HEADS_UP_APPEAR = 14;
+        static final int ANIMATION_TYPE_HEADS_UP_DISAPPEAR = 15;
+        static final int ANIMATION_TYPE_HEADS_UP_OTHER = 16;
+        static final int ANIMATION_TYPE_EVERYTHING = 17;
 
         static final int DARK_ANIMATION_ORIGIN_INDEX_ABOVE = -1;
         static final int DARK_ANIMATION_ORIGIN_INDEX_BELOW = -2;
@@ -2810,6 +2981,7 @@
         final long length;
         View viewAfterChangingView;
         int darkAnimationOriginIndex;
+        boolean headsUpFromBottom;
 
         AnimationEvent(View view, int type) {
             this(view, type, LENGTHS[type]);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java
index e7bf47b..d98bcfe 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java
@@ -25,9 +25,11 @@
 import com.android.systemui.R;
 import com.android.systemui.statusbar.ExpandableNotificationRow;
 import com.android.systemui.statusbar.ExpandableView;
+import com.android.systemui.statusbar.policy.HeadsUpManager;
 
 import java.util.ArrayList;
 import java.util.List;
+import java.util.TreeSet;
 
 /**
  * The Algorithm of the {@link com.android.systemui.statusbar.stack
@@ -54,11 +56,6 @@
     private StackIndentationFunctor mTopStackIndentationFunctor;
     private StackIndentationFunctor mBottomStackIndentationFunctor;
 
-    private int mLayoutHeight;
-
-    /** mLayoutHeight - mTopPadding */
-    private int mInnerHeight;
-    private int mTopPadding;
     private StackScrollAlgorithmState mTempAlgorithmState = new StackScrollAlgorithmState();
     private boolean mIsExpansionChanging;
     private int mFirstChildMaxHeight;
@@ -74,6 +71,7 @@
     private boolean mIsSmallScreen;
     private int mMaxNotificationHeight;
     private boolean mScaleDimmed;
+    private HeadsUpManager mHeadsUpManager;
 
     public StackScrollAlgorithm(Context context) {
         initConstants(context);
@@ -157,20 +155,20 @@
         scrollY = Math.max(0, scrollY);
         algorithmState.scrollY = (int) (scrollY + mCollapsedSize + bottomOverScroll);
 
-        updateVisibleChildren(resultState, algorithmState);
+        updateVisibleChildren(resultState, algorithmState, ambientState);
 
         // Phase 1:
-        findNumberOfItemsInTopStackAndUpdateState(resultState, algorithmState);
+        findNumberOfItemsInTopStackAndUpdateState(resultState, algorithmState, ambientState);
 
         // Phase 2:
-        updatePositionsForState(resultState, algorithmState);
+        updatePositionsForState(resultState, algorithmState, ambientState);
 
         // Phase 3:
         updateZValuesForState(resultState, algorithmState);
 
         handleDraggedViews(ambientState, resultState, algorithmState);
         updateDimmedActivatedHideSensitive(ambientState, resultState, algorithmState);
-        updateClipping(resultState, algorithmState);
+        updateClipping(resultState, algorithmState, ambientState);
         updateSpeedBumpState(resultState, algorithmState, ambientState.getSpeedBumpIndex());
         getNotificationChildrenStates(resultState, algorithmState);
     }
@@ -201,7 +199,7 @@
     }
 
     private void updateClipping(StackScrollState resultState,
-            StackScrollAlgorithmState algorithmState) {
+            StackScrollAlgorithmState algorithmState, AmbientState ambientState) {
         float previousNotificationEnd = 0;
         float previousNotificationStart = 0;
         boolean previousNotificationIsSwiped = false;
@@ -242,7 +240,7 @@
                 // otherwise we would clip to a transparent view.
                 previousNotificationStart = newYTranslation + state.clipTopAmount * state.scale;
                 previousNotificationEnd = newNotificationEnd;
-                previousNotificationIsSwiped = child.getTranslationX() != 0;
+                previousNotificationIsSwiped = ambientState.getDraggedViews().contains(child);
             }
         }
     }
@@ -314,7 +312,9 @@
                     StackViewState viewState = resultState.getViewStateForView(
                             nextChild);
                     // The child below the dragged one must be fully visible
-                    viewState.alpha = 1;
+                    if (!isPinnedHeadsUpView(draggedView) || isPinnedHeadsUpView(nextChild)) {
+                        viewState.alpha = 1;
+                    }
                 }
 
                 // Lets set the alpha to the one it currently has, as its currently being dragged
@@ -325,27 +325,41 @@
         }
     }
 
+    private boolean isPinnedHeadsUpView(View view) {
+        if (view instanceof ExpandableNotificationRow) {
+            ExpandableNotificationRow row = (ExpandableNotificationRow) view;
+            return row.isHeadsUp() && !row.isInShade();
+        }
+        return false;
+    }
+
     /**
      * Update the visible children on the state.
      */
     private void updateVisibleChildren(StackScrollState resultState,
-            StackScrollAlgorithmState state) {
+            StackScrollAlgorithmState state, AmbientState ambientState) {
         ViewGroup hostView = resultState.getHostView();
         int childCount = hostView.getChildCount();
         state.visibleChildren.clear();
         state.visibleChildren.ensureCapacity(childCount);
         int notGoneIndex = 0;
+        TreeSet<HeadsUpManager.HeadsUpEntry> headsUpEntries
+                = ambientState.getSortedHeadsUpEntries();
+        for (HeadsUpManager.HeadsUpEntry entry: headsUpEntries) {
+            ExpandableView v = entry.entry.row;
+            notGoneIndex = updateNotGoneIndex(resultState, state, notGoneIndex, v);
+        }
         for (int i = 0; i < childCount; i++) {
             ExpandableView v = (ExpandableView) hostView.getChildAt(i);
             if (v.getVisibility() != View.GONE) {
-                StackViewState viewState = resultState.getViewStateForView(v);
-                viewState.notGoneIndex = notGoneIndex;
-                state.visibleChildren.add(v);
-                notGoneIndex++;
-
-                // handle the notgoneIndex for the children as well
                 if (v instanceof ExpandableNotificationRow) {
                     ExpandableNotificationRow row = (ExpandableNotificationRow) v;
+                    if (row.isHeadsUp()) {
+                        continue;
+                    }
+                    notGoneIndex = updateNotGoneIndex(resultState, state, notGoneIndex, v);
+
+                    // handle the notgoneIndex for the children as well
                     List<ExpandableNotificationRow> children =
                             row.getNotificationChildren();
                     if (row.areChildrenExpanded() && children != null) {
@@ -358,22 +372,35 @@
                             }
                         }
                     }
+                } else {
+                    notGoneIndex = updateNotGoneIndex(resultState, state, notGoneIndex, v);
                 }
             }
         }
     }
 
+    private int updateNotGoneIndex(StackScrollState resultState,
+            StackScrollAlgorithmState state, int notGoneIndex,
+            ExpandableView v) {
+        StackViewState viewState = resultState.getViewStateForView(v);
+        viewState.notGoneIndex = notGoneIndex;
+        state.visibleChildren.add(v);
+        notGoneIndex++;
+        return notGoneIndex;
+    }
+
     /**
      * Determine the positions for the views. This is the main part of the algorithm.
      *
-     * @param resultState The result state to update if a change to the properties of a child occurs
+     *  @param resultState The result state to update if a change to the properties of a child occurs
      * @param algorithmState The state in which the current pass of the algorithm is currently in
+     * @param ambientState The current ambient state
      */
     private void updatePositionsForState(StackScrollState resultState,
-            StackScrollAlgorithmState algorithmState) {
+            StackScrollAlgorithmState algorithmState, AmbientState ambientState) {
 
         // The starting position of the bottom stack peek
-        float bottomPeekStart = mInnerHeight - mBottomStackPeekSize;
+        float bottomPeekStart = ambientState.getInnerHeight() - mBottomStackPeekSize;
 
         // The position where the bottom stack starts.
         float bottomStackStart = bottomPeekStart - mBottomStackSlowDownLength;
@@ -384,13 +411,17 @@
         // How far in is the element currently transitioning into the bottom stack.
         float yPositionInScrollView = 0.0f;
 
+        // If we have a heads-up higher than the collapsed height we need to add the difference to
+        // the padding of all other elements, i.e push in the top stack slightly.
+        ExpandableNotificationRow topHeadsUpEntry = ambientState.getTopHeadsUpEntry();
+
         int childCount = algorithmState.visibleChildren.size();
         int numberOfElementsCompletelyIn = (int) algorithmState.itemsInTopStack;
         for (int i = 0; i < childCount; i++) {
             ExpandableView child = algorithmState.visibleChildren.get(i);
             StackViewState childViewState = resultState.getViewStateForView(child);
             childViewState.location = StackViewState.LOCATION_UNKNOWN;
-            int childHeight = getMaxAllowedChildHeight(child);
+            int childHeight = getMaxAllowedChildHeight(child, ambientState);
             float yPositionInScrollViewAfterElement = yPositionInScrollView
                     + childHeight
                     + mPaddingBetweenElements;
@@ -427,7 +458,8 @@
                             bottomPeekStart, childViewState.yTranslation, childViewState,
                             childHeight);
                 }
-                clampPositionToBottomStackStart(childViewState, childViewState.height);
+                clampPositionToBottomStackStart(childViewState, childViewState.height,
+                        ambientState);
             } else if (nextYPosition >= bottomStackStart) {
                 // Case 2:
                 // We are in the bottom stack.
@@ -435,7 +467,7 @@
                     // According to the regular scroll view we are fully translated out of the
                     // bottom of the screen so we are fully in the bottom stack
                     updateStateForChildFullyInBottomStack(algorithmState,
-                            bottomStackStart, childViewState, childHeight);
+                            bottomStackStart, childViewState, childHeight, ambientState);
                 } else {
                     // According to the regular scroll view we are currently translating out of /
                     // into the bottom of the screen
@@ -447,7 +479,7 @@
                 // Case 3:
                 // We are in the regular scroll area.
                 childViewState.location = StackViewState.LOCATION_MAIN_AREA;
-                clampYTranslation(childViewState, childHeight);
+                clampYTranslation(childViewState, childHeight, ambientState);
             }
 
             // The first card is always rendered.
@@ -468,7 +500,44 @@
             currentYPosition = childViewState.yTranslation + childHeight + mPaddingBetweenElements;
             yPositionInScrollView = yPositionInScrollViewAfterElement;
 
-            childViewState.yTranslation += mTopPadding;
+            if (ambientState.isShadeExpanded() && topHeadsUpEntry != null
+                    && child != topHeadsUpEntry) {
+                childViewState.yTranslation += topHeadsUpEntry.getHeadsUpHeight() - mCollapsedSize;
+            }
+            childViewState.yTranslation += ambientState.getTopPadding()
+                    + ambientState.getStackTranslation();
+        }
+        updateHeadsUpStates(resultState, ambientState);
+    }
+
+    private void updateHeadsUpStates(StackScrollState resultState, AmbientState ambientState) {
+        TreeSet<HeadsUpManager.HeadsUpEntry> headsUpEntries = ambientState.getSortedHeadsUpEntries();
+        for (HeadsUpManager.HeadsUpEntry entry: headsUpEntries) {
+            ExpandableNotificationRow row = entry.entry.row;
+            StackViewState childState = resultState.getViewStateForView(row);
+            ExpandableNotificationRow topHeadsUpEntry = ambientState.getTopHeadsUpEntry();
+            boolean isTopEntry = topHeadsUpEntry == row;
+            if (!row.isInShade()) {
+                childState.yTranslation = 0;
+                childState.height = row.getHeadsUpHeight();
+                if (!isTopEntry) {
+                    // Ensure that a headsUp is never below the topmost headsUp
+                    StackViewState topState = resultState.getViewStateForView(topHeadsUpEntry);
+                    childState.height = row.getHeadsUpHeight();
+                    childState.yTranslation = topState.yTranslation + topState.height
+                            - childState.height;
+                }
+            } else if (mIsExpanded) {
+                if (isTopEntry) {
+                    childState.height += row.getHeadsUpHeight() - mCollapsedSize;
+                }
+                childState.height = Math.max(childState.height, row.getHeadsUpHeight());
+                // Ensure that the heads up is always visible even when scrolled of from the bottom
+                float bottomPosition = ambientState.getMaxHeadsUpTranslation() - childState.height;
+                childState.yTranslation = Math.min(childState.yTranslation,
+                        bottomPosition);
+            }
+
         }
     }
 
@@ -478,8 +547,9 @@
      * @param childViewState the view state of the child
      * @param childHeight the height of this child
      */
-    private void clampYTranslation(StackViewState childViewState, int childHeight) {
-        clampPositionToBottomStackStart(childViewState, childHeight);
+    private void clampYTranslation(StackViewState childViewState, int childHeight,
+            AmbientState ambientState) {
+        clampPositionToBottomStackStart(childViewState, childHeight, ambientState);
         clampPositionToTopStackEnd(childViewState, childHeight);
     }
 
@@ -491,14 +561,15 @@
      * @param childHeight the height of this child
      */
     private void clampPositionToBottomStackStart(StackViewState childViewState,
-            int childHeight) {
+            int childHeight, AmbientState ambientState) {
         childViewState.yTranslation = Math.min(childViewState.yTranslation,
-                mInnerHeight - mBottomStackPeekSize - mCollapseSecondCardPadding - childHeight);
+                ambientState.getInnerHeight() - mBottomStackPeekSize - mCollapseSecondCardPadding
+                        - childHeight);
     }
 
     /**
      * Clamp the yTranslation of the child up such that its end is at lest on the end of the top
-     * stack.get
+     * stack.
      *
      * @param childViewState the view state of the child
      * @param childHeight the height of this child
@@ -509,9 +580,14 @@
                 mCollapsedSize - childHeight);
     }
 
-    private int getMaxAllowedChildHeight(View child) {
+    private int getMaxAllowedChildHeight(View child, AmbientState ambientState) {
         if (child instanceof ExpandableNotificationRow) {
             ExpandableNotificationRow row = (ExpandableNotificationRow) child;
+            if (ambientState == null && row.isHeadsUp()
+                    || ambientState != null && ambientState.getTopHeadsUpEntry() == child) {
+                int extraSize = row.getIntrinsicHeight() - row.getHeadsUpHeight();
+                return mCollapsedSize + extraSize;
+            }
             return row.getIntrinsicHeight();
         } else if (child instanceof ExpandableView) {
             ExpandableView expandableView = (ExpandableView) child;
@@ -548,8 +624,7 @@
 
     private void updateStateForChildFullyInBottomStack(StackScrollAlgorithmState algorithmState,
             float transitioningPositionStart, StackViewState childViewState,
-            int childHeight) {
-
+            int childHeight, AmbientState ambientState) {
         float currentYPosition;
         algorithmState.itemsInBottomStack += 1.0f;
         if (algorithmState.itemsInBottomStack < MAX_ITEMS_IN_BOTTOM_STACK) {
@@ -567,7 +642,7 @@
                 childViewState.alpha = 1.0f - algorithmState.partialInBottom;
             }
             childViewState.location = StackViewState.LOCATION_BOTTOM_STACK_HIDDEN;
-            currentYPosition = mInnerHeight;
+            currentYPosition = ambientState.getInnerHeight();
         }
         childViewState.yTranslation = currentYPosition - childHeight;
         clampPositionToTopStackEnd(childViewState, childHeight);
@@ -629,7 +704,7 @@
      * @param algorithmState The state in which the current pass of the algorithm is currently in
      */
     private void findNumberOfItemsInTopStackAndUpdateState(StackScrollState resultState,
-            StackScrollAlgorithmState algorithmState) {
+            StackScrollAlgorithmState algorithmState, AmbientState ambientState) {
 
         // The y Position if the element would be in a regular scrollView
         float yPositionInScrollView = 0.0f;
@@ -639,7 +714,7 @@
         for (int i = 0; i < childCount; i++) {
             ExpandableView child = algorithmState.visibleChildren.get(i);
             StackViewState childViewState = resultState.getViewStateForView(child);
-            int childHeight = getMaxAllowedChildHeight(child);
+            int childHeight = getMaxAllowedChildHeight(child, ambientState);
             float yPositionInScrollViewAfterElement = yPositionInScrollView
                     + childHeight
                     + mPaddingBetweenElements;
@@ -647,7 +722,7 @@
                 if (i == 0 && algorithmState.scrollY <= mCollapsedSize) {
 
                     // The starting position of the bottom stack peek
-                    int bottomPeekStart = mInnerHeight - mBottomStackPeekSize -
+                    int bottomPeekStart = ambientState.getInnerHeight() - mBottomStackPeekSize -
                             mCollapseSecondCardPadding;
                     // Collapse and expand the first child while the shade is being expanded
                     float maxHeight = mIsExpansionChanging && child == mFirstChildWhileExpanding
@@ -744,21 +819,6 @@
         }
     }
 
-    public void setLayoutHeight(int layoutHeight) {
-        this.mLayoutHeight = layoutHeight;
-        updateInnerHeight();
-    }
-
-    public void setTopPadding(int topPadding) {
-        mTopPadding = topPadding;
-        updateInnerHeight();
-    }
-
-    private void updateInnerHeight() {
-        mInnerHeight = mLayoutHeight - mTopPadding;
-    }
-
-
     /**
      * Update whether the device is very small, i.e. Notifications can be in both the top and the
      * bottom stack at the same time
@@ -788,6 +848,13 @@
                 // current height or the end value of the animation.
                 mFirstChildMaxHeight = StackStateAnimator.getFinalActualHeight(
                         mFirstChildWhileExpanding);
+                if (mFirstChildWhileExpanding instanceof ExpandableNotificationRow) {
+                    ExpandableNotificationRow row =
+                            (ExpandableNotificationRow) mFirstChildWhileExpanding;
+                    if (row.isHeadsUp()) {
+                        mFirstChildMaxHeight += mCollapsedSize - row.getHeadsUpHeight();
+                    }
+                }
             } else {
                 updateFirstChildMaxSizeToMaxHeight();
             }
@@ -809,7 +876,7 @@
                                 int oldBottom) {
                             if (mFirstChildWhileExpanding != null) {
                                 mFirstChildMaxHeight = getMaxAllowedChildHeight(
-                                        mFirstChildWhileExpanding);
+                                        mFirstChildWhileExpanding, null);
                             } else {
                                 mFirstChildMaxHeight = 0;
                             }
@@ -817,7 +884,7 @@
                         }
                     });
         } else {
-            mFirstChildMaxHeight = getMaxAllowedChildHeight(mFirstChildWhileExpanding);
+            mFirstChildMaxHeight = getMaxAllowedChildHeight(mFirstChildWhileExpanding, null);
         }
     }
 
@@ -830,6 +897,9 @@
     }
 
     private View findFirstVisibleChild(ViewGroup container) {
+        if (mHeadsUpManager != null && mHeadsUpManager.getTopEntry() != null) {
+            return mHeadsUpManager.getTopEntry().entry.row;
+        }
         int childCount = container.getChildCount();
         for (int i = 0; i < childCount; i++) {
             View child = container.getChildAt(i);
@@ -870,6 +940,10 @@
         }
     }
 
+    public void setHeadsUpManager(HeadsUpManager headsUpManager) {
+        mHeadsUpManager = headsUpManager;
+    }
+
     class StackScrollAlgorithmState {
 
         /**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java
index b249fbf..f5d94c8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java
@@ -21,9 +21,11 @@
 import android.animation.ObjectAnimator;
 import android.animation.PropertyValuesHolder;
 import android.animation.ValueAnimator;
+import android.graphics.Path;
 import android.view.View;
 import android.view.animation.AnimationUtils;
 import android.view.animation.Interpolator;
+import android.view.animation.PathInterpolator;
 
 import com.android.systemui.R;
 import com.android.systemui.statusbar.ExpandableNotificationRow;
@@ -32,7 +34,6 @@
 
 import java.util.ArrayList;
 import java.util.HashSet;
-import java.util.Set;
 import java.util.Stack;
 
 /**
@@ -45,6 +46,8 @@
     public static final int ANIMATION_DURATION_APPEAR_DISAPPEAR = 464;
     public static final int ANIMATION_DURATION_EXPAND_CLICKED = 360;
     public static final int ANIMATION_DURATION_DIMMED_ACTIVATED = 220;
+    public static final int ANIMATION_DURATION_HEADS_UP_APPEAR = 650;
+    public static final int ANIMATION_DURATION_HEADS_UP_DISAPPEAR = 230;
     public static final int ANIMATION_DELAY_PER_ELEMENT_INTERRUPTING = 80;
     public static final int ANIMATION_DELAY_PER_ELEMENT_EXPAND_CHILDREN = 54;
     public static final int ANIMATION_DELAY_PER_ELEMENT_MANUAL = 32;
@@ -73,12 +76,15 @@
     private static final int TAG_START_TOP_INSET = R.id.top_inset_animator_start_value_tag;
 
     private final Interpolator mFastOutSlowInInterpolator;
+    private final Interpolator mHeadsUpAppearInterpolator;
     private final int mGoToFullShadeAppearingTranslation;
     public NotificationStackScrollLayout mHostLayout;
     private ArrayList<NotificationStackScrollLayout.AnimationEvent> mNewEvents =
             new ArrayList<>();
     private ArrayList<View> mNewAddChildren = new ArrayList<>();
-    private Set<Animator> mAnimatorSet = new HashSet<>();
+    private HashSet<View> mHeadsUpAppearChildren = new HashSet<>();
+    private HashSet<View> mHeadsUpDisappearChildren = new HashSet<>();
+    private HashSet<Animator> mAnimatorSet = new HashSet<>();
     private Stack<AnimatorListenerAdapter> mAnimationListenerPool = new Stack<>();
     private AnimationFilter mAnimationFilter = new AnimationFilter();
     private long mCurrentLength;
@@ -86,10 +92,12 @@
 
     /** The current index for the last child which was not added in this event set. */
     private int mCurrentLastNotAddedIndex;
-
     private ValueAnimator mTopOverScrollAnimator;
     private ValueAnimator mBottomOverScrollAnimator;
     private ExpandableNotificationRow mChildExpandingView;
+    private StackViewState mTmpState = new StackViewState();
+    private int mHeadsUpAppearHeightBottom;
+    private boolean mShadeExpanded;
 
     public StackStateAnimator(NotificationStackScrollLayout hostLayout) {
         mHostLayout = hostLayout;
@@ -98,6 +106,25 @@
         mGoToFullShadeAppearingTranslation =
                 hostLayout.getContext().getResources().getDimensionPixelSize(
                         R.dimen.go_to_full_shade_appearing_translation);
+        Path path = new Path();
+        path.moveTo(0, 0);
+        float x1 = 250f;
+        float x2 = 150f;
+        float x3 = 100f;
+        float y1 = 90f;
+        float y2 = 78f;
+        float y3 = 80f;
+        float xTot = (x1 + x2 + x3);
+        path.cubicTo(x1 * 0.9f / xTot, 0f,
+                x1 * 0.8f / xTot, y1 / y3,
+                x1 / xTot , y1 / y3);
+        path.cubicTo((x1 + x2 * 0.4f) / xTot, y1 / y3,
+                (x1 + x2 * 0.2f) / xTot, y2 / y3,
+                (x1 + x2) / xTot, y2 / y3);
+        path.cubicTo((x1 + x2 + x3 * 0.4f) / xTot, y2 / y3,
+                (x1 + x2 + x3 * 0.2f) / xTot, 1f,
+                1f, 1f);
+        mHeadsUpAppearInterpolator = new PathInterpolator(path);
     }
 
     public boolean isRunning() {
@@ -119,7 +146,8 @@
             final ExpandableView child = (ExpandableView) mHostLayout.getChildAt(i);
 
             StackViewState viewState = finalState.getViewStateForView(child);
-            if (viewState == null || child.getVisibility() == View.GONE) {
+            if (viewState == null || child.getVisibility() == View.GONE
+                    || applyWithoutAnimation(child, viewState, finalState)) {
                 continue;
             }
 
@@ -130,11 +158,39 @@
             // no child has preformed any animation, lets finish
             onAnimationFinished();
         }
+        mHeadsUpAppearChildren.clear();
+        mHeadsUpDisappearChildren.clear();
         mNewEvents.clear();
         mNewAddChildren.clear();
         mChildExpandingView = null;
     }
 
+    /**
+     * Determines if a view should not perform an animation and applies it directly.
+     *
+     * @return true if no animation should be performed
+     */
+    private boolean applyWithoutAnimation(ExpandableView child, StackViewState viewState,
+            StackScrollState finalState) {
+        if (mShadeExpanded) {
+            return false;
+        }
+        if (getChildTag(child, TAG_ANIMATOR_TRANSLATION_Y) != null) {
+            // A Y translation animation is running
+            return false;
+        }
+        if (mHeadsUpDisappearChildren.contains(child) || mHeadsUpAppearChildren.contains(child)) {
+            // This is a heads up animation
+            return false;
+        }
+        if (mHostLayout.isPinnedHeadsUp(child)) {
+            // This is another headsUp which might move. Let's animate!
+            return false;
+        }
+        finalState.applyState(child, viewState);
+        return true;
+    }
+
     private int findLastNotAddedIndex(StackScrollState finalState) {
         int childCount = mHostLayout.getChildCount();
         for (int i = childCount - 1; i >= 0; i--) {
@@ -616,7 +672,9 @@
 
         ObjectAnimator animator = ObjectAnimator.ofFloat(child, View.TRANSLATION_Y,
                 child.getTranslationY(), newEndValue);
-        animator.setInterpolator(mFastOutSlowInInterpolator);
+        Interpolator interpolator = mHeadsUpAppearChildren.contains(child) ?
+                mHeadsUpAppearInterpolator :mFastOutSlowInInterpolator;
+        animator.setInterpolator(interpolator);
         long newDuration = cancelAnimatorAndGetNewDuration(duration, previousAnimator);
         animator.setDuration(newDuration);
         if (delay > 0 && (previousAnimator == null || !previousAnimator.isRunning())) {
@@ -731,7 +789,7 @@
         };
     }
 
-    private static <T> T getChildTag(View child, int tag) {
+    public static <T> T getChildTag(View child, int tag) {
         return (T) child.getTag(tag);
     }
 
@@ -828,6 +886,22 @@
                 ExpandableNotificationRow row = (ExpandableNotificationRow) event.changingView;
                 row.prepareExpansionChanged(finalState);
                 mChildExpandingView = row;
+            } else if (event.animationType == NotificationStackScrollLayout
+                    .AnimationEvent.ANIMATION_TYPE_HEADS_UP_APPEAR) {
+                // This item is added, initialize it's properties.
+                StackViewState viewState = finalState.getViewStateForView(changingView);
+                mTmpState.copyFrom(viewState);
+                if (event.headsUpFromBottom) {
+                    mTmpState.yTranslation = mHeadsUpAppearHeightBottom;
+                } else {
+                    mTmpState.yTranslation = -mTmpState.height;
+                }
+                mHeadsUpAppearChildren.add(changingView);
+                finalState.applyState(changingView, mTmpState);
+            } else if (event.animationType == NotificationStackScrollLayout
+                    .AnimationEvent.ANIMATION_TYPE_HEADS_UP_DISAPPEAR) {
+                // This item is added, initialize it's properties.
+                mHeadsUpDisappearChildren.add(changingView);
             }
             mNewEvents.add(event);
         }
@@ -893,4 +967,12 @@
             return getChildTag(view, TAG_END_HEIGHT);
         }
     }
+
+    public void setHeadsUpAppearHeightBottom(int headsUpAppearHeightBottom) {
+        mHeadsUpAppearHeightBottom = headsUpAppearHeightBottom;
+    }
+
+    public void setShadeExpanded(boolean shadeExpanded) {
+        mShadeExpanded = shadeExpanded;
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java
index c272e48..dce695d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java
@@ -20,8 +20,6 @@
 import android.service.notification.NotificationListenerService.RankingMap;
 import android.service.notification.StatusBarNotification;
 import android.view.View;
-import android.view.ViewGroup.LayoutParams;
-import android.view.WindowManager;
 
 import com.android.internal.statusbar.StatusBarIcon;
 import com.android.systemui.statusbar.ActivatableNotificationView;
@@ -123,19 +121,7 @@
     }
 
     @Override
-    public void scheduleHeadsUpDecay(long delay) {
-    }
-
-    @Override
-    public void scheduleHeadsUpOpen() {
-    }
-
-    @Override
-    public void scheduleHeadsUpEscalation() {
-    }
-
-    @Override
-    public void scheduleHeadsUpClose() {
+    public void escalateHeadsUp() {
     }
 
     @Override
@@ -178,4 +164,17 @@
     @Override
     public void appTransitionStarting(long startTime, long duration) {
     }
+
+    @Override
+    protected void updateHeadsUp(String key, NotificationData.Entry entry, boolean shouldInterrupt,
+            boolean alertAgain) {
+    }
+
+    @Override
+    protected void setHeadsUpUser(int newUserId) {
+    }
+
+    protected boolean isSnoozedPackage(StatusBarNotification sbn) {
+        return false;
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumePanel.java b/packages/SystemUI/src/com/android/systemui/volume/VolumePanel.java
deleted file mode 100644
index 45cb4a1..0000000
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumePanel.java
+++ /dev/null
@@ -1,1538 +0,0 @@
-/*
- * Copyright (C) 2007 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.volume;
-
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.animation.ValueAnimator;
-import android.app.AlertDialog;
-import android.app.Dialog;
-import android.content.BroadcastReceiver;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.DialogInterface;
-import android.content.DialogInterface.OnDismissListener;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.content.pm.PackageManager;
-import android.content.pm.ServiceInfo;
-import android.content.res.Configuration;
-import android.content.res.Resources;
-import android.content.res.TypedArray;
-import android.graphics.PixelFormat;
-import android.graphics.drawable.ColorDrawable;
-import android.media.AudioAttributes;
-import android.media.AudioManager;
-import android.media.AudioSystem;
-import android.media.RingtoneManager;
-import android.media.ToneGenerator;
-import android.media.VolumeProvider;
-import android.media.session.MediaController;
-import android.media.session.MediaController.PlaybackInfo;
-import android.net.Uri;
-import android.os.AsyncTask;
-import android.os.Bundle;
-import android.os.Debug;
-import android.os.Handler;
-import android.os.Message;
-import android.os.Vibrator;
-import android.util.Log;
-import android.util.SparseArray;
-import android.view.LayoutInflater;
-import android.view.MotionEvent;
-import android.view.View;
-import android.view.View.OnClickListener;
-import android.view.ViewGroup;
-import android.view.Window;
-import android.view.WindowManager;
-import android.view.WindowManager.LayoutParams;
-import android.view.accessibility.AccessibilityEvent;
-import android.view.accessibility.AccessibilityManager;
-import android.view.animation.AnimationUtils;
-import android.view.animation.Interpolator;
-import android.widget.ImageView;
-import android.widget.SeekBar;
-import android.widget.SeekBar.OnSeekBarChangeListener;
-import android.widget.TextView;
-
-import com.android.internal.R;
-import com.android.systemui.DemoMode;
-import com.android.systemui.statusbar.policy.ZenModeController;
-
-import java.io.FileDescriptor;
-import java.io.PrintWriter;
-
-/**
- * Handles the user interface for the volume keys.
- *
- * @hide
- */
-public class VolumePanel extends Handler implements DemoMode {
-    private static final String TAG = "VolumePanel";
-    private static boolean LOGD = Log.isLoggable(TAG, Log.DEBUG);
-
-    private static final int PLAY_SOUND_DELAY = AudioSystem.PLAY_SOUND_DELAY;
-
-    /**
-     * The delay before vibrating. This small period exists so if the user is
-     * moving to silent mode, it will not emit a short vibrate (it normally
-     * would since vibrate is between normal mode and silent mode using hardware
-     * keys).
-     */
-    public static final int VIBRATE_DELAY = 300;
-
-    private static final int VIBRATE_DURATION = 300;
-    private static final int BEEP_DURATION = 150;
-    private static final int MAX_VOLUME = 100;
-    private static final int FREE_DELAY = 10000;
-    private static final int TIMEOUT_DELAY = 3000;
-    private static final int TIMEOUT_DELAY_SHORT = 1500;
-    private static final int TIMEOUT_DELAY_COLLAPSED = 4500;
-    private static final int TIMEOUT_DELAY_SAFETY_WARNING = 5000;
-    private static final int TIMEOUT_DELAY_EXPANDED = 10000;
-
-    private static final int MSG_VOLUME_CHANGED = 0;
-    private static final int MSG_FREE_RESOURCES = 1;
-    private static final int MSG_PLAY_SOUND = 2;
-    private static final int MSG_STOP_SOUNDS = 3;
-    private static final int MSG_VIBRATE = 4;
-    private static final int MSG_TIMEOUT = 5;
-    private static final int MSG_RINGER_MODE_CHANGED = 6;
-    private static final int MSG_MUTE_CHANGED = 7;
-    private static final int MSG_REMOTE_VOLUME_CHANGED = 8;
-    private static final int MSG_REMOTE_VOLUME_UPDATE_IF_SHOWN = 9;
-    private static final int MSG_SLIDER_VISIBILITY_CHANGED = 10;
-    private static final int MSG_DISPLAY_SAFE_VOLUME_WARNING = 11;
-    private static final int MSG_LAYOUT_DIRECTION = 12;
-    private static final int MSG_ZEN_MODE_AVAILABLE_CHANGED = 13;
-    private static final int MSG_USER_ACTIVITY = 14;
-    private static final int MSG_NOTIFICATION_EFFECTS_SUPPRESSOR_CHANGED = 15;
-    private static final int MSG_INTERNAL_RINGER_MODE_CHANGED = 16;
-
-    // Pseudo stream type for remote volume
-    private static final int STREAM_REMOTE_MUSIC = -200;
-
-    private static final AudioAttributes VIBRATION_ATTRIBUTES = new AudioAttributes.Builder()
-            .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
-            .setUsage(AudioAttributes.USAGE_ASSISTANCE_SONIFICATION)
-            .build();
-
-    private static final int IC_AUDIO_VOL = com.android.systemui.R.drawable.ic_audio_vol;
-    private static final int IC_AUDIO_VOL_MUTE = com.android.systemui.R.drawable.ic_audio_vol_mute;
-    private static final int IC_AUDIO_BT = com.android.systemui.R.drawable.ic_audio_bt;
-    private static final int IC_AUDIO_BT_MUTE = com.android.systemui.R.drawable.ic_audio_bt_mute;
-
-    private final String mTag;
-    protected final Context mContext;
-    private final AudioManager mAudioManager;
-    private final ZenModeController mZenController;
-    private boolean mRingIsSilent;
-    private boolean mVoiceCapable;
-    private boolean mZenModeAvailable;
-    private boolean mZenPanelExpanded;
-    private int mTimeoutDelay = TIMEOUT_DELAY;
-    private float mDisabledAlpha;
-    private int mLastRingerMode = AudioManager.RINGER_MODE_NORMAL;
-    private int mLastRingerProgress = 0;
-    private int mDemoIcon;
-
-    /** Volume panel content view */
-    private final View mView;
-    /** Dialog hosting the panel */
-    private final Dialog mDialog;
-
-    /** The visible portion of the volume overlay */
-    private final ViewGroup mPanel;
-    /** Contains the slider and its touchable icons */
-    private final ViewGroup mSliderPanel;
-    /** The zen mode configuration panel view */
-    private ZenModePanel mZenPanel;
-    /** The component currently suppressing notification stream effects */
-    private ComponentName mNotificationEffectsSuppressor;
-
-    private Callback mCallback;
-
-    /** Currently active stream that shows up at the top of the list of sliders */
-    private int mActiveStreamType = -1;
-    /** All the slider controls mapped by stream type */
-    private SparseArray<StreamControl> mStreamControls;
-    private final AccessibilityManager mAccessibilityManager;
-    private final SecondaryIconTransition mSecondaryIconTransition;
-    private final IconPulser mIconPulser;
-
-    private enum StreamResources {
-        BluetoothSCOStream(AudioManager.STREAM_BLUETOOTH_SCO,
-                R.string.volume_icon_description_bluetooth,
-                IC_AUDIO_BT,
-                IC_AUDIO_BT_MUTE,
-                false),
-        RingerStream(AudioManager.STREAM_RING,
-                R.string.volume_icon_description_ringer,
-                com.android.systemui.R.drawable.ic_ringer_audible,
-                com.android.systemui.R.drawable.ic_ringer_mute,
-                false),
-        VoiceStream(AudioManager.STREAM_VOICE_CALL,
-                R.string.volume_icon_description_incall,
-                com.android.systemui.R.drawable.ic_audio_phone,
-                com.android.systemui.R.drawable.ic_audio_phone,
-                false),
-        AlarmStream(AudioManager.STREAM_ALARM,
-                R.string.volume_alarm,
-                com.android.systemui.R.drawable.ic_audio_alarm,
-                com.android.systemui.R.drawable.ic_audio_alarm_mute,
-                false),
-        MediaStream(AudioManager.STREAM_MUSIC,
-                R.string.volume_icon_description_media,
-                IC_AUDIO_VOL,
-                IC_AUDIO_VOL_MUTE,
-                true),
-        NotificationStream(AudioManager.STREAM_NOTIFICATION,
-                R.string.volume_icon_description_notification,
-                com.android.systemui.R.drawable.ic_ringer_audible,
-                com.android.systemui.R.drawable.ic_ringer_mute,
-                true),
-        RemoteStream(STREAM_REMOTE_MUSIC,
-                R.string.volume_icon_description_media, //FIXME should have its own description
-                com.android.systemui.R.drawable.ic_audio_remote,
-                com.android.systemui.R.drawable.ic_audio_remote,
-                false);// will be dynamically updated
-
-        int streamType;
-        int descRes;
-        int iconRes;
-        int iconMuteRes;
-        // RING, VOICE_CALL & BLUETOOTH_SCO are hidden unless explicitly requested
-        boolean show;
-
-        StreamResources(int streamType, int descRes, int iconRes, int iconMuteRes, boolean show) {
-            this.streamType = streamType;
-            this.descRes = descRes;
-            this.iconRes = iconRes;
-            this.iconMuteRes = iconMuteRes;
-            this.show = show;
-        }
-    }
-
-    // List of stream types and their order
-    private static final StreamResources[] STREAMS = {
-        StreamResources.BluetoothSCOStream,
-        StreamResources.RingerStream,
-        StreamResources.VoiceStream,
-        StreamResources.MediaStream,
-        StreamResources.NotificationStream,
-        StreamResources.AlarmStream,
-        StreamResources.RemoteStream
-    };
-
-    /** Object that contains data for each slider */
-    private class StreamControl {
-        int streamType;
-        MediaController controller;
-        ViewGroup group;
-        ImageView icon;
-        SeekBar seekbarView;
-        TextView suppressorView;
-        View divider;
-        ImageView secondaryIcon;
-        int iconRes;
-        int iconMuteRes;
-        int iconSuppressedRes;
-        int minVolume;
-    }
-
-    // Synchronize when accessing this
-    private ToneGenerator mToneGenerators[];
-    private Vibrator mVibrator;
-    private boolean mHasVibrator;
-
-    private static AlertDialog sSafetyWarning;
-    private static Object sSafetyWarningLock = new Object();
-
-    protected LayoutParams getDialogLayoutParams(Window window, Resources res) {
-        final LayoutParams lp = window.getAttributes();
-        lp.token = null;
-        lp.y = res.getDimensionPixelOffset(com.android.systemui.R.dimen.volume_panel_top);
-        lp.type = LayoutParams.TYPE_STATUS_BAR_PANEL;
-        lp.format = PixelFormat.TRANSLUCENT;
-        lp.windowAnimations = com.android.systemui.R.style.VolumePanelAnimation;
-        lp.setTitle(TAG);
-        return lp;
-    }
-
-    public VolumePanel(Context context, ZenModeController zenController) {
-        mTag = String.format("%s.%08x", TAG, hashCode());
-        mContext = context;
-        mZenController = zenController;
-        mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
-        mAccessibilityManager = (AccessibilityManager) context.getSystemService(
-                Context.ACCESSIBILITY_SERVICE);
-        mSecondaryIconTransition = new SecondaryIconTransition();
-        mIconPulser = new IconPulser(context);
-
-        if (LOGD) Log.d(mTag, "new VolumePanel");
-
-        mDisabledAlpha = 0.5f;
-        if (mContext.getTheme() != null) {
-            final TypedArray arr = mContext.getTheme().obtainStyledAttributes(
-                    new int[] { android.R.attr.disabledAlpha });
-            mDisabledAlpha = arr.getFloat(0, mDisabledAlpha);
-            arr.recycle();
-        }
-
-        mDialog = new Dialog(context) {
-            @Override
-            public boolean onTouchEvent(MotionEvent event) {
-                if (isShowing() && event.getAction() == MotionEvent.ACTION_OUTSIDE &&
-                        sSafetyWarning == null) {
-                    forceTimeout(0);
-                    return true;
-                }
-                return false;
-            }
-        };
-
-        final Window window = mDialog.getWindow();
-        window.requestFeature(Window.FEATURE_NO_TITLE);
-        mDialog.setCanceledOnTouchOutside(true);
-        mDialog.setContentView(com.android.systemui.R.layout.volume_panel_dialog);
-        mDialog.setOnDismissListener(new OnDismissListener() {
-            @Override
-            public void onDismiss(DialogInterface dialog) {
-                mActiveStreamType = -1;
-                mAudioManager.forceVolumeControlStream(mActiveStreamType);
-                setZenPanelVisible(false);
-                mDemoIcon = 0;
-                mSecondaryIconTransition.cancel();
-            }
-        });
-
-        mDialog.create();
-
-        final Resources res = context.getResources();
-        window.setAttributes(getDialogLayoutParams(window, res));
-
-        updateWidth();
-
-        window.setBackgroundDrawable(new ColorDrawable(0x00000000));
-        window.clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND);
-        window.addFlags(LayoutParams.FLAG_NOT_FOCUSABLE
-                | LayoutParams.FLAG_NOT_TOUCH_MODAL
-                | LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
-                | LayoutParams.FLAG_HARDWARE_ACCELERATED);
-        mView = window.findViewById(R.id.content);
-        Interaction.register(mView, new Interaction.Callback() {
-            @Override
-            public void onInteraction() {
-                resetTimeout();
-            }
-        });
-
-        mPanel = (ViewGroup) mView.findViewById(com.android.systemui.R.id.visible_panel);
-        mSliderPanel = (ViewGroup) mView.findViewById(com.android.systemui.R.id.slider_panel);
-        mZenPanel = (ZenModePanel) mView.findViewById(com.android.systemui.R.id.zen_mode_panel);
-        initZenModePanel();
-
-        mToneGenerators = new ToneGenerator[AudioSystem.getNumStreamTypes()];
-        mVibrator = (Vibrator) context.getSystemService(Context.VIBRATOR_SERVICE);
-        mHasVibrator = mVibrator != null && mVibrator.hasVibrator();
-        mVoiceCapable = context.getResources().getBoolean(R.bool.config_voice_capable);
-
-        if (mZenController != null) {
-            mZenModeAvailable = mZenController.isZenAvailable();
-            mNotificationEffectsSuppressor = mZenController.getEffectsSuppressor();
-            mZenController.addCallback(mZenCallback);
-        }
-
-        registerReceiver();
-    }
-
-    public void onConfigurationChanged(Configuration newConfig) {
-        updateWidth();
-        if (mZenPanel != null) {
-            mZenPanel.updateLocale();
-        }
-    }
-
-    private void updateWidth() {
-        final Resources res = mContext.getResources();
-        final LayoutParams lp = mDialog.getWindow().getAttributes();
-        lp.width = res.getDimensionPixelSize(com.android.systemui.R.dimen.notification_panel_width);
-        lp.gravity =
-                res.getInteger(com.android.systemui.R.integer.notification_panel_layout_gravity);
-        mDialog.getWindow().setAttributes(lp);
-    }
-
-    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
-        pw.println("VolumePanel state:");
-        pw.print("  mTag="); pw.println(mTag);
-        pw.print("  mRingIsSilent="); pw.println(mRingIsSilent);
-        pw.print("  mVoiceCapable="); pw.println(mVoiceCapable);
-        pw.print("  mHasVibrator="); pw.println(mHasVibrator);
-        pw.print("  mZenModeAvailable="); pw.println(mZenModeAvailable);
-        pw.print("  mZenPanelExpanded="); pw.println(mZenPanelExpanded);
-        pw.print("  mNotificationEffectsSuppressor="); pw.println(mNotificationEffectsSuppressor);
-        pw.print("  mTimeoutDelay="); pw.println(mTimeoutDelay);
-        pw.print("  mDisabledAlpha="); pw.println(mDisabledAlpha);
-        pw.print("  mLastRingerMode="); pw.println(mLastRingerMode);
-        pw.print("  mLastRingerProgress="); pw.println(mLastRingerProgress);
-        pw.print("  isShowing()="); pw.println(isShowing());
-        pw.print("  mCallback="); pw.println(mCallback);
-        pw.print("  sConfirmSafeVolumeDialog=");
-        pw.println(sSafetyWarning != null ? "<not null>" : null);
-        pw.print("  mActiveStreamType="); pw.println(mActiveStreamType);
-        pw.print("  mStreamControls=");
-        if (mStreamControls == null) {
-            pw.println("null");
-        } else {
-            final int N = mStreamControls.size();
-            pw.print("<size "); pw.print(N); pw.println('>');
-            for (int i = 0; i < N; i++) {
-                final StreamControl sc = mStreamControls.valueAt(i);
-                pw.print("    stream "); pw.print(sc.streamType); pw.print(":");
-                if (sc.seekbarView != null) {
-                    pw.print(" progress="); pw.print(sc.seekbarView.getProgress());
-                    pw.print(" of "); pw.print(sc.seekbarView.getMax());
-                    if (!sc.seekbarView.isEnabled()) pw.print(" (disabled)");
-                }
-                if (sc.icon != null && sc.icon.isClickable()) pw.print(" (clickable)");
-                pw.println();
-            }
-        }
-        if (mZenPanel != null) {
-            mZenPanel.dump(fd, pw, args);
-        }
-    }
-
-    private void initZenModePanel() {
-        mZenPanel.init(mZenController);
-        mZenPanel.setCallback(new ZenModePanel.Callback() {
-            @Override
-            public void onMoreSettings() {
-                if (mCallback != null) {
-                    mCallback.onZenSettings();
-                }
-            }
-
-            @Override
-            public void onPrioritySettings() {
-                if (mCallback != null) {
-                    mCallback.onZenPrioritySettings();
-                }
-            }
-
-            @Override
-            public void onInteraction() {
-                resetTimeout();
-            }
-
-            @Override
-            public void onExpanded(boolean expanded) {
-                if (mZenPanelExpanded == expanded) return;
-                mZenPanelExpanded = expanded;
-                updateTimeoutDelay();
-                resetTimeout();
-            }
-        });
-    }
-
-    private void setLayoutDirection(int layoutDirection) {
-        mPanel.setLayoutDirection(layoutDirection);
-        updateStates();
-    }
-
-    private void registerReceiver() {
-        final IntentFilter filter = new IntentFilter();
-        filter.addAction(AudioManager.RINGER_MODE_CHANGED_ACTION);
-        filter.addAction(AudioManager.INTERNAL_RINGER_MODE_CHANGED_ACTION);
-        filter.addAction(Intent.ACTION_SCREEN_OFF);
-        mContext.registerReceiver(new BroadcastReceiver() {
-            @Override
-            public void onReceive(Context context, Intent intent) {
-                final String action = intent.getAction();
-
-                if (AudioManager.RINGER_MODE_CHANGED_ACTION.equals(action)) {
-                    removeMessages(MSG_RINGER_MODE_CHANGED);
-                    sendEmptyMessage(MSG_RINGER_MODE_CHANGED);
-                }
-
-                if (AudioManager.INTERNAL_RINGER_MODE_CHANGED_ACTION.equals(action)) {
-                    removeMessages(MSG_INTERNAL_RINGER_MODE_CHANGED);
-                    sendEmptyMessage(MSG_INTERNAL_RINGER_MODE_CHANGED);
-                }
-
-                if (Intent.ACTION_SCREEN_OFF.equals(action)) {
-                    postDismiss(0);
-                }
-            }
-        }, filter);
-    }
-
-    private boolean isMuted(int streamType) {
-        if (streamType == STREAM_REMOTE_MUSIC) {
-            // TODO do we need to support a distinct mute property for remote?
-            return false;
-        } else {
-            return mAudioManager.isStreamMute(streamType);
-        }
-    }
-
-    private int getStreamMinVolume(int streamType) {
-        if (streamType == STREAM_REMOTE_MUSIC) {
-            return 0;
-        } else {
-            return mAudioManager.getStreamMinVolume(streamType);
-        }
-    }
-
-    private int getStreamMaxVolume(int streamType) {
-        if (streamType == STREAM_REMOTE_MUSIC) {
-            if (mStreamControls != null) {
-                StreamControl sc = mStreamControls.get(streamType);
-                if (sc != null && sc.controller != null) {
-                    PlaybackInfo ai = sc.controller.getPlaybackInfo();
-                    return ai.getMaxVolume();
-                }
-            }
-            return -1;
-        } else {
-            return mAudioManager.getStreamMaxVolume(streamType);
-        }
-    }
-
-    private int getStreamVolume(int streamType) {
-        if (streamType == STREAM_REMOTE_MUSIC) {
-            if (mStreamControls != null) {
-                StreamControl sc = mStreamControls.get(streamType);
-                if (sc != null && sc.controller != null) {
-                    PlaybackInfo ai = sc.controller.getPlaybackInfo();
-                    return ai.getCurrentVolume();
-                }
-            }
-            return -1;
-        } else {
-            return mAudioManager.getLastAudibleStreamVolume(streamType);
-        }
-    }
-
-    private void setStreamVolume(StreamControl sc, int index, int flags) {
-        if (sc.streamType == STREAM_REMOTE_MUSIC) {
-            if (sc.controller != null) {
-                sc.controller.setVolumeTo(index, flags);
-            } else {
-                Log.w(mTag, "Adjusting remote volume without a controller!");
-            }
-        } else if (getStreamVolume(sc.streamType) != index) {
-            mAudioManager.setStreamVolume(sc.streamType, index, flags);
-        }
-    }
-
-    private void createSliders() {
-        final Resources res = mContext.getResources();
-        final LayoutInflater inflater = (LayoutInflater) mContext.getSystemService(
-                Context.LAYOUT_INFLATER_SERVICE);
-
-        mStreamControls = new SparseArray<StreamControl>(STREAMS.length);
-
-        final StreamResources notificationStream = StreamResources.NotificationStream;
-        for (int i = 0; i < STREAMS.length; i++) {
-            StreamResources streamRes = STREAMS[i];
-
-            final int streamType = streamRes.streamType;
-            final boolean isNotification = isNotificationOrRing(streamType);
-
-            final StreamControl sc = new StreamControl();
-            sc.streamType = streamType;
-            sc.group = (ViewGroup) inflater.inflate(
-                    com.android.systemui.R.layout.volume_panel_item, null);
-            sc.group.setTag(sc);
-            sc.icon = (ImageView) sc.group.findViewById(com.android.systemui.R.id.stream_icon);
-            sc.icon.setTag(sc);
-            sc.icon.setContentDescription(res.getString(streamRes.descRes));
-            sc.iconRes = streamRes.iconRes;
-            sc.iconMuteRes = streamRes.iconMuteRes;
-            sc.icon.setImageResource(sc.iconRes);
-            sc.icon.setClickable(isNotification && mHasVibrator);
-            if (isNotification) {
-                if (mHasVibrator) {
-                    sc.icon.setSoundEffectsEnabled(false);
-                    sc.iconMuteRes = com.android.systemui.R.drawable.ic_ringer_vibrate;
-                    sc.icon.setOnClickListener(new OnClickListener() {
-                        @Override
-                        public void onClick(View v) {
-                            resetTimeout();
-                            toggleRinger(sc);
-                        }
-                    });
-                }
-                sc.iconSuppressedRes = com.android.systemui.R.drawable.ic_ringer_mute;
-            }
-            sc.seekbarView = (SeekBar) sc.group.findViewById(com.android.systemui.R.id.seekbar);
-            sc.suppressorView =
-                    (TextView) sc.group.findViewById(com.android.systemui.R.id.suppressor);
-            sc.suppressorView.setVisibility(View.GONE);
-            final boolean showSecondary = !isNotification && notificationStream.show;
-            sc.divider = sc.group.findViewById(com.android.systemui.R.id.divider);
-            sc.secondaryIcon = (ImageView) sc.group
-                    .findViewById(com.android.systemui.R.id.secondary_icon);
-            sc.secondaryIcon.setImageResource(com.android.systemui.R.drawable.ic_ringer_audible);
-            sc.secondaryIcon.setContentDescription(res.getString(notificationStream.descRes));
-            sc.secondaryIcon.setClickable(showSecondary);
-            sc.divider.setVisibility(showSecondary ? View.VISIBLE : View.GONE);
-            sc.secondaryIcon.setVisibility(showSecondary ? View.VISIBLE : View.GONE);
-            if (showSecondary) {
-                sc.secondaryIcon.setOnClickListener(new OnClickListener() {
-                    @Override
-                    public void onClick(View v) {
-                        mSecondaryIconTransition.start(sc);
-                    }
-                });
-            }
-            sc.minVolume = getStreamMinVolume(streamType);
-            sc.seekbarView.setMax(getStreamMaxVolume(streamType) - sc.minVolume);
-            sc.seekbarView.setOnSeekBarChangeListener(mSeekListener);
-            sc.seekbarView.setTag(sc);
-            mStreamControls.put(streamType, sc);
-        }
-    }
-
-    private void toggleRinger(StreamControl sc) {
-        if (!mHasVibrator) return;
-        if (mAudioManager.getRingerModeInternal() == AudioManager.RINGER_MODE_NORMAL) {
-            mAudioManager.setRingerModeInternal(AudioManager.RINGER_MODE_VIBRATE);
-            postVolumeChanged(sc.streamType, AudioManager.FLAG_SHOW_UI | AudioManager.FLAG_VIBRATE);
-        } else {
-            mAudioManager.setRingerModeInternal(AudioManager.RINGER_MODE_NORMAL);
-            postVolumeChanged(sc.streamType, AudioManager.FLAG_PLAY_SOUND);
-        }
-    }
-
-    private void reorderSliders(int activeStreamType) {
-        mSliderPanel.removeAllViews();
-
-        final StreamControl active = mStreamControls.get(activeStreamType);
-        if (active == null) {
-            Log.e(TAG, "Missing stream type! - " + activeStreamType);
-            mActiveStreamType = -1;
-        } else {
-            mSliderPanel.addView(active.group);
-            mActiveStreamType = activeStreamType;
-            active.group.setVisibility(View.VISIBLE);
-            updateSlider(active, true /*forceReloadIcon*/);
-            updateTimeoutDelay();
-            updateZenPanelVisible();
-        }
-    }
-
-    private void updateSliderProgress(StreamControl sc, int progress) {
-        final boolean isRinger = isNotificationOrRing(sc.streamType);
-        if (isRinger && mAudioManager.getRingerModeInternal() == AudioManager.RINGER_MODE_SILENT) {
-            progress = mLastRingerProgress;
-        }
-        if (progress < 0) {
-            progress = getStreamVolume(sc.streamType);
-        }
-        sc.seekbarView.setProgress(progress - sc.minVolume);
-        if (isRinger) {
-            mLastRingerProgress = progress;
-        }
-    }
-
-    private void updateSliderIcon(StreamControl sc, boolean muted) {
-        ComponentName suppressor = null;
-        if (isNotificationOrRing(sc.streamType)) {
-            suppressor = mNotificationEffectsSuppressor;
-            int ringerMode = mAudioManager.getRingerModeInternal();
-            if (ringerMode == AudioManager.RINGER_MODE_SILENT) {
-                ringerMode = mLastRingerMode;
-            } else {
-                mLastRingerMode = ringerMode;
-            }
-            if (mHasVibrator) {
-                muted = ringerMode == AudioManager.RINGER_MODE_VIBRATE;
-            } else {
-                muted = false;
-            }
-        }
-        sc.icon.setImageResource(mDemoIcon != 0 ? mDemoIcon
-                : suppressor != null ? sc.iconSuppressedRes
-                : muted ? sc.iconMuteRes
-                : sc.iconRes);
-    }
-
-    private void updateSliderSuppressor(StreamControl sc) {
-        final ComponentName suppressor = isNotificationOrRing(sc.streamType)
-                ? mNotificationEffectsSuppressor : null;
-        if (suppressor == null) {
-            sc.seekbarView.setVisibility(View.VISIBLE);
-            sc.suppressorView.setVisibility(View.GONE);
-        } else {
-            sc.seekbarView.setVisibility(View.GONE);
-            sc.suppressorView.setVisibility(View.VISIBLE);
-            sc.suppressorView.setText(mContext.getString(R.string.muted_by,
-                    getSuppressorCaption(suppressor)));
-        }
-    }
-
-    private String getSuppressorCaption(ComponentName suppressor) {
-        final PackageManager pm = mContext.getPackageManager();
-        try {
-            final ServiceInfo info = pm.getServiceInfo(suppressor, 0);
-            if (info != null) {
-                final CharSequence seq = info.loadLabel(pm);
-                if (seq != null) {
-                    final String str = seq.toString().trim();
-                    if (str.length() > 0) {
-                        return str;
-                    }
-                }
-            }
-        } catch (Throwable e) {
-            Log.w(TAG, "Error loading suppressor caption", e);
-        }
-        return suppressor.getPackageName();
-    }
-
-    /** Update the mute and progress state of a slider */
-    private void updateSlider(StreamControl sc, boolean forceReloadIcon) {
-        updateSliderProgress(sc, -1);
-        final boolean muted = isMuted(sc.streamType);
-        if (forceReloadIcon) {
-            sc.icon.setImageDrawable(null);
-        }
-        updateSliderIcon(sc, muted);
-        updateSliderEnabled(sc, muted, false);
-        updateSliderSuppressor(sc);
-    }
-
-    private void updateSliderEnabled(final StreamControl sc, boolean muted, boolean fixedVolume) {
-        final boolean wasEnabled = sc.seekbarView.isEnabled();
-        final boolean isRinger = isNotificationOrRing(sc.streamType);
-        if (sc.streamType == STREAM_REMOTE_MUSIC) {
-            // never disable touch interactions for remote playback, the muting is not tied to
-            // the state of the phone.
-            sc.seekbarView.setEnabled(!fixedVolume);
-        } else if (isRinger && mNotificationEffectsSuppressor != null) {
-            sc.icon.setEnabled(true);
-            sc.icon.setAlpha(1f);
-            sc.icon.setClickable(false);
-        } else if (isRinger
-                && mAudioManager.getRingerModeInternal() == AudioManager.RINGER_MODE_SILENT) {
-            sc.seekbarView.setEnabled(false);
-            sc.icon.setEnabled(false);
-            sc.icon.setAlpha(mDisabledAlpha);
-            sc.icon.setClickable(false);
-        } else if (fixedVolume ||
-                (sc.streamType != mAudioManager.getUiSoundsStreamType() && !isRinger && muted) ||
-                (sSafetyWarning != null)) {
-            sc.seekbarView.setEnabled(false);
-        } else {
-            sc.seekbarView.setEnabled(true);
-            sc.icon.setEnabled(true);
-            sc.icon.setAlpha(1f);
-        }
-        // show the silent hint when the disabled slider is touched in silent mode
-        if (isRinger && wasEnabled != sc.seekbarView.isEnabled()) {
-            if (sc.seekbarView.isEnabled()) {
-                sc.group.setOnTouchListener(null);
-                sc.icon.setClickable(mHasVibrator);
-            } else {
-                final View.OnTouchListener showHintOnTouch = new View.OnTouchListener() {
-                    @Override
-                    public boolean onTouch(View v, MotionEvent event) {
-                        resetTimeout();
-                        showSilentHint();
-                        return false;
-                    }
-                };
-                sc.group.setOnTouchListener(showHintOnTouch);
-            }
-        }
-    }
-
-    private void showSilentHint() {
-        if (mZenPanel != null) {
-            mZenPanel.showSilentHint();
-        }
-    }
-
-    private void showVibrateHint() {
-        final StreamControl active = mStreamControls.get(mActiveStreamType);
-        if (active != null) {
-            mIconPulser.start(active.icon);
-            if (!hasMessages(MSG_VIBRATE)) {
-                sendEmptyMessageDelayed(MSG_VIBRATE, VIBRATE_DELAY);
-            }
-        }
-    }
-
-    private static boolean isNotificationOrRing(int streamType) {
-        return streamType == AudioManager.STREAM_RING
-                || streamType == AudioManager.STREAM_NOTIFICATION;
-    }
-
-    public void setCallback(Callback callback) {
-        mCallback = callback;
-    }
-
-    private void updateTimeoutDelay() {
-        mTimeoutDelay = mDemoIcon != 0 ? TIMEOUT_DELAY_EXPANDED
-                : sSafetyWarning != null ? TIMEOUT_DELAY_SAFETY_WARNING
-                : mActiveStreamType == AudioManager.STREAM_MUSIC ? TIMEOUT_DELAY_SHORT
-                : mZenPanelExpanded ? TIMEOUT_DELAY_EXPANDED
-                : isZenPanelVisible() ? TIMEOUT_DELAY_COLLAPSED
-                : TIMEOUT_DELAY;
-    }
-
-    private boolean isZenPanelVisible() {
-        return mZenPanel != null && mZenPanel.getVisibility() == View.VISIBLE;
-    }
-
-    private void setZenPanelVisible(boolean visible) {
-        if (LOGD) Log.d(mTag, "setZenPanelVisible " + visible + " mZenPanel=" + mZenPanel);
-        final boolean changing = visible != isZenPanelVisible();
-        if (visible) {
-            mZenPanel.setHidden(false);
-            resetTimeout();
-        } else {
-            mZenPanel.setHidden(true);
-        }
-        if (changing) {
-            updateTimeoutDelay();
-            resetTimeout();
-        }
-    }
-
-    private void updateStates() {
-        final int count = mSliderPanel.getChildCount();
-        for (int i = 0; i < count; i++) {
-            StreamControl sc = (StreamControl) mSliderPanel.getChildAt(i).getTag();
-            updateSlider(sc, true /*forceReloadIcon*/);
-        }
-    }
-
-    private void updateActiveSlider() {
-        final StreamControl active = mStreamControls.get(mActiveStreamType);
-        if (active != null) {
-            updateSlider(active, false /*forceReloadIcon*/);
-        }
-    }
-
-    private void updateZenPanelVisible() {
-        setZenPanelVisible(mZenModeAvailable && isNotificationOrRing(mActiveStreamType));
-    }
-
-    public void postVolumeChanged(int streamType, int flags) {
-        if (hasMessages(MSG_VOLUME_CHANGED)) return;
-        synchronized (this) {
-            if (mStreamControls == null) {
-                createSliders();
-            }
-        }
-        removeMessages(MSG_FREE_RESOURCES);
-        obtainMessage(MSG_VOLUME_CHANGED, streamType, flags).sendToTarget();
-    }
-
-    public void postRemoteVolumeChanged(MediaController controller, int flags) {
-        if (hasMessages(MSG_REMOTE_VOLUME_CHANGED)) return;
-        synchronized (this) {
-            if (mStreamControls == null) {
-                createSliders();
-            }
-        }
-        removeMessages(MSG_FREE_RESOURCES);
-        obtainMessage(MSG_REMOTE_VOLUME_CHANGED, flags, 0, controller).sendToTarget();
-    }
-
-    public void postRemoteSliderVisibility(boolean visible) {
-        obtainMessage(MSG_SLIDER_VISIBILITY_CHANGED,
-                STREAM_REMOTE_MUSIC, visible ? 1 : 0).sendToTarget();
-    }
-
-    /**
-     * Called by AudioService when it has received new remote playback information that
-     * would affect the VolumePanel display (mainly volumes). The difference with
-     * {@link #postRemoteVolumeChanged(int, int)} is that the handling of the posted message
-     * (MSG_REMOTE_VOLUME_UPDATE_IF_SHOWN) will only update the volume slider if it is being
-     * displayed.
-     * This special code path is due to the fact that remote volume updates arrive to AudioService
-     * asynchronously. So after AudioService has sent the volume update (which should be treated
-     * as a request to update the volume), the application will likely set a new volume. If the UI
-     * is still up, we need to refresh the display to show this new value.
-     */
-    public void postHasNewRemotePlaybackInfo() {
-        if (hasMessages(MSG_REMOTE_VOLUME_UPDATE_IF_SHOWN)) return;
-        // don't create or prevent resources to be freed, if they disappear, this update came too
-        //   late and shouldn't warrant the panel to be displayed longer
-        obtainMessage(MSG_REMOTE_VOLUME_UPDATE_IF_SHOWN).sendToTarget();
-    }
-
-    public void postMuteChanged(int streamType, int flags) {
-        if (hasMessages(MSG_VOLUME_CHANGED)) return;
-        synchronized (this) {
-            if (mStreamControls == null) {
-                createSliders();
-            }
-        }
-        removeMessages(MSG_FREE_RESOURCES);
-        obtainMessage(MSG_MUTE_CHANGED, streamType, flags).sendToTarget();
-    }
-
-    public void postDisplaySafeVolumeWarning(int flags) {
-        if (hasMessages(MSG_DISPLAY_SAFE_VOLUME_WARNING)) return;
-        obtainMessage(MSG_DISPLAY_SAFE_VOLUME_WARNING, flags, 0).sendToTarget();
-    }
-
-    public void postDismiss(long delay) {
-        forceTimeout(delay);
-    }
-
-    public void postLayoutDirection(int layoutDirection) {
-        removeMessages(MSG_LAYOUT_DIRECTION);
-        obtainMessage(MSG_LAYOUT_DIRECTION, layoutDirection, 0).sendToTarget();
-    }
-
-    private static String flagsToString(int flags) {
-        return flags == 0 ? "0" : (flags + "=" + AudioManager.flagsToString(flags));
-    }
-
-    private static String streamToString(int stream) {
-        return AudioSystem.streamToString(stream);
-    }
-
-    /**
-     * Override this if you have other work to do when the volume changes (for
-     * example, vibrating, playing a sound, etc.). Make sure to call through to
-     * the superclass implementation.
-     */
-    protected void onVolumeChanged(int streamType, int flags) {
-
-        if (LOGD) Log.d(mTag, "onVolumeChanged(streamType: " + streamToString(streamType)
-                + ", flags: " + flagsToString(flags) + ")");
-
-        if ((flags & AudioManager.FLAG_SHOW_UI) != 0) {
-            synchronized (this) {
-                if (mActiveStreamType != streamType) {
-                    reorderSliders(streamType);
-                }
-                onShowVolumeChanged(streamType, flags, null);
-            }
-        }
-
-        if ((flags & AudioManager.FLAG_PLAY_SOUND) != 0 && ! mRingIsSilent) {
-            removeMessages(MSG_PLAY_SOUND);
-            sendMessageDelayed(obtainMessage(MSG_PLAY_SOUND, streamType, flags), PLAY_SOUND_DELAY);
-        }
-
-        if ((flags & AudioManager.FLAG_REMOVE_SOUND_AND_VIBRATE) != 0) {
-            removeMessages(MSG_PLAY_SOUND);
-            removeMessages(MSG_VIBRATE);
-            onStopSounds();
-        }
-
-        removeMessages(MSG_FREE_RESOURCES);
-        sendMessageDelayed(obtainMessage(MSG_FREE_RESOURCES), FREE_DELAY);
-        resetTimeout();
-    }
-
-    protected void onMuteChanged(int streamType, int flags) {
-
-        if (LOGD) Log.d(mTag, "onMuteChanged(streamType: " + streamToString(streamType)
-                + ", flags: " + flagsToString(flags) + ")");
-
-        StreamControl sc = mStreamControls.get(streamType);
-        if (sc != null) {
-            updateSliderIcon(sc, isMuted(sc.streamType));
-        }
-
-        onVolumeChanged(streamType, flags);
-    }
-
-    protected void onShowVolumeChanged(int streamType, int flags, MediaController controller) {
-        int index = getStreamVolume(streamType);
-
-        mRingIsSilent = false;
-
-        if (LOGD) {
-            Log.d(mTag, "onShowVolumeChanged(streamType: " + streamToString(streamType)
-                    + ", flags: " + flagsToString(flags) + "), index: " + index);
-        }
-
-        // get max volume for progress bar
-
-        int max = getStreamMaxVolume(streamType) - getStreamMinVolume(streamType);
-        StreamControl sc = mStreamControls.get(streamType);
-
-        switch (streamType) {
-
-            case AudioManager.STREAM_RING: {
-                Uri ringuri = RingtoneManager.getActualDefaultRingtoneUri(
-                        mContext, RingtoneManager.TYPE_RINGTONE);
-                if (ringuri == null) {
-                    mRingIsSilent = true;
-                }
-                break;
-            }
-
-            case AudioManager.STREAM_MUSIC: {
-                // Special case for when Bluetooth is active for music
-                if ((mAudioManager.getDevicesForStream(AudioManager.STREAM_MUSIC) &
-                        (AudioManager.DEVICE_OUT_BLUETOOTH_A2DP |
-                        AudioManager.DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES |
-                        AudioManager.DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER)) != 0) {
-                    setMusicIcon(IC_AUDIO_BT, IC_AUDIO_BT_MUTE);
-                } else {
-                    setMusicIcon(IC_AUDIO_VOL, IC_AUDIO_VOL_MUTE);
-                }
-                break;
-            }
-
-            case AudioManager.STREAM_ALARM: {
-                break;
-            }
-
-            case AudioManager.STREAM_NOTIFICATION: {
-                Uri ringuri = RingtoneManager.getActualDefaultRingtoneUri(
-                        mContext, RingtoneManager.TYPE_NOTIFICATION);
-                if (ringuri == null) {
-                    mRingIsSilent = true;
-                }
-                break;
-            }
-
-            case STREAM_REMOTE_MUSIC: {
-                if (controller == null && sc != null) {
-                    // If we weren't passed one try using the last one set.
-                    controller = sc.controller;
-                }
-                if (controller == null) {
-                    // We still don't have one, ignore the command.
-                    Log.w(mTag, "sent remote volume change without a controller!");
-                } else {
-                    PlaybackInfo vi = controller.getPlaybackInfo();
-                    index = vi.getCurrentVolume();
-                    max = vi.getMaxVolume();
-                    if ((vi.getVolumeControl() & VolumeProvider.VOLUME_CONTROL_FIXED) != 0) {
-                        // if the remote volume is fixed add the flag for the UI
-                        flags |= AudioManager.FLAG_FIXED_VOLUME;
-                    }
-                }
-                if (LOGD) { Log.d(mTag, "showing remote volume "+index+" over "+ max); }
-                break;
-            }
-        }
-
-        if (sc != null) {
-            if (streamType == STREAM_REMOTE_MUSIC && controller != sc.controller) {
-                if (sc.controller != null) {
-                    sc.controller.unregisterCallback(mMediaControllerCb);
-                }
-                sc.controller = controller;
-                if (controller != null) {
-                    sc.controller.registerCallback(mMediaControllerCb);
-                }
-            }
-            if (sc.seekbarView.getMax() != max) {
-                sc.seekbarView.setMax(max);
-            }
-            updateSliderProgress(sc, index);
-            final boolean muted = isMuted(streamType);
-            updateSliderEnabled(sc, muted, (flags & AudioManager.FLAG_FIXED_VOLUME) != 0);
-            if (isNotificationOrRing(streamType)) {
-                // check for secondary-icon transition completion
-                if (mSecondaryIconTransition.isRunning()) {
-                    mSecondaryIconTransition.cancel();  // safe to reset
-                    sc.seekbarView.setAlpha(0); sc.seekbarView.animate().alpha(1);
-                    mZenPanel.setAlpha(0); mZenPanel.animate().alpha(1);
-                }
-                updateSliderIcon(sc, muted);
-            }
-        }
-
-        if (!isShowing()) {
-            int stream = (streamType == STREAM_REMOTE_MUSIC) ? -1 : streamType;
-            // when the stream is for remote playback, use -1 to reset the stream type evaluation
-            mAudioManager.forceVolumeControlStream(stream);
-            mDialog.show();
-            if (mCallback != null) {
-                mCallback.onVisible(true);
-            }
-            announceDialogShown();
-        }
-
-        // Do a little vibrate if applicable (only when going into vibrate mode)
-        if ((streamType != STREAM_REMOTE_MUSIC) &&
-                ((flags & AudioManager.FLAG_VIBRATE) != 0) &&
-                isNotificationOrRing(streamType) &&
-                mAudioManager.getRingerModeInternal() == AudioManager.RINGER_MODE_VIBRATE) {
-            sendMessageDelayed(obtainMessage(MSG_VIBRATE), VIBRATE_DELAY);
-        }
-
-        // Pulse the zen icon if an adjustment was suppressed due to silent mode.
-        if ((flags & AudioManager.FLAG_SHOW_SILENT_HINT) != 0) {
-            showSilentHint();
-        }
-
-        // Pulse the slider icon & vibrate if an adjustment down was suppressed due to vibrate mode.
-        if ((flags & AudioManager.FLAG_SHOW_VIBRATE_HINT) != 0) {
-            showVibrateHint();
-        }
-    }
-
-    private void announceDialogShown() {
-        mView.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
-    }
-
-    private boolean isShowing() {
-        return mDialog.isShowing();
-    }
-
-    protected void onPlaySound(int streamType, int flags) {
-
-        if (hasMessages(MSG_STOP_SOUNDS)) {
-            removeMessages(MSG_STOP_SOUNDS);
-            // Force stop right now
-            onStopSounds();
-        }
-
-        synchronized (this) {
-            ToneGenerator toneGen = getOrCreateToneGenerator(streamType);
-            if (toneGen != null) {
-                toneGen.startTone(ToneGenerator.TONE_PROP_BEEP);
-                sendMessageDelayed(obtainMessage(MSG_STOP_SOUNDS), BEEP_DURATION);
-            }
-        }
-    }
-
-    protected void onStopSounds() {
-
-        synchronized (this) {
-            int numStreamTypes = AudioSystem.getNumStreamTypes();
-            for (int i = numStreamTypes - 1; i >= 0; i--) {
-                ToneGenerator toneGen = mToneGenerators[i];
-                if (toneGen != null) {
-                    toneGen.stopTone();
-                }
-            }
-        }
-    }
-
-    protected void onVibrate() {
-
-        // Make sure we ended up in vibrate ringer mode
-        if (mAudioManager.getRingerModeInternal() != AudioManager.RINGER_MODE_VIBRATE) {
-            return;
-        }
-        if (mVibrator != null) {
-            mVibrator.vibrate(VIBRATE_DURATION, VIBRATION_ATTRIBUTES);
-        }
-    }
-
-    protected void onRemoteVolumeChanged(MediaController controller, int flags) {
-        if (LOGD) Log.d(mTag, "onRemoteVolumeChanged(controller:" + controller + ", flags: "
-                + flagsToString(flags) + ")");
-
-        if (((flags & AudioManager.FLAG_SHOW_UI) != 0) || isShowing()) {
-            synchronized (this) {
-                if (mActiveStreamType != STREAM_REMOTE_MUSIC) {
-                    reorderSliders(STREAM_REMOTE_MUSIC);
-                }
-                onShowVolumeChanged(STREAM_REMOTE_MUSIC, flags, controller);
-            }
-        } else {
-            if (LOGD) Log.d(mTag, "not calling onShowVolumeChanged(), no FLAG_SHOW_UI or no UI");
-        }
-
-        removeMessages(MSG_FREE_RESOURCES);
-        sendMessageDelayed(obtainMessage(MSG_FREE_RESOURCES), FREE_DELAY);
-        resetTimeout();
-    }
-
-    protected void onRemoteVolumeUpdateIfShown() {
-        if (LOGD) Log.d(mTag, "onRemoteVolumeUpdateIfShown()");
-        if (isShowing()
-                && (mActiveStreamType == STREAM_REMOTE_MUSIC)
-                && (mStreamControls != null)) {
-            onShowVolumeChanged(STREAM_REMOTE_MUSIC, 0, null);
-        }
-    }
-
-    /**
-     * Clear the current remote stream controller.
-     */
-    private void clearRemoteStreamController() {
-        if (mStreamControls != null) {
-            StreamControl sc = mStreamControls.get(STREAM_REMOTE_MUSIC);
-            if (sc != null) {
-                if (sc.controller != null) {
-                    sc.controller.unregisterCallback(mMediaControllerCb);
-                    sc.controller = null;
-                }
-            }
-        }
-    }
-
-    /**
-     * Handler for MSG_SLIDER_VISIBILITY_CHANGED Hide or show a slider
-     *
-     * @param streamType can be a valid stream type value, or
-     *            VolumePanel.STREAM_MASTER, or VolumePanel.STREAM_REMOTE_MUSIC
-     * @param visible
-     */
-    synchronized protected void onSliderVisibilityChanged(int streamType, int visible) {
-        if (LOGD) Log.d(mTag, "onSliderVisibilityChanged(stream="+streamType+", visi="+visible+")");
-        boolean isVisible = (visible == 1);
-        for (int i = STREAMS.length - 1 ; i >= 0 ; i--) {
-            StreamResources streamRes = STREAMS[i];
-            if (streamRes.streamType == streamType) {
-                streamRes.show = isVisible;
-                if (!isVisible && (mActiveStreamType == streamType)) {
-                    mActiveStreamType = -1;
-                }
-                break;
-            }
-        }
-    }
-
-    protected void onDisplaySafeVolumeWarning(int flags) {
-        if ((flags & (AudioManager.FLAG_SHOW_UI | AudioManager.FLAG_SHOW_UI_WARNINGS)) != 0
-                || isShowing()) {
-            synchronized (sSafetyWarningLock) {
-                if (sSafetyWarning != null) {
-                    return;
-                }
-                sSafetyWarning = new SafetyWarningDialog(mContext, mAudioManager) {
-                    @Override
-                    protected void cleanUp() {
-                        synchronized (sSafetyWarningLock) {
-                            sSafetyWarning = null;
-                        }
-                        forceTimeout(0);
-                        updateStates();
-                    }
-                };
-                sSafetyWarning.show();
-            }
-            updateStates();
-        }
-        if (mAccessibilityManager.isTouchExplorationEnabled()) {
-            removeMessages(MSG_TIMEOUT);
-        } else {
-            updateTimeoutDelay();
-            resetTimeout();
-        }
-    }
-
-    /**
-     * Lock on this VolumePanel instance as long as you use the returned ToneGenerator.
-     */
-    private ToneGenerator getOrCreateToneGenerator(int streamType) {
-        synchronized (this) {
-            if (mToneGenerators[streamType] == null) {
-                try {
-                    mToneGenerators[streamType] = new ToneGenerator(streamType, MAX_VOLUME);
-                } catch (RuntimeException e) {
-                    if (LOGD) {
-                        Log.d(mTag, "ToneGenerator constructor failed with "
-                                + "RuntimeException: " + e);
-                    }
-                }
-            }
-            return mToneGenerators[streamType];
-        }
-    }
-
-
-    /**
-     * Switch between icons because Bluetooth music is same as music volume, but with
-     * different icons.
-     */
-    private void setMusicIcon(int resId, int resMuteId) {
-        StreamControl sc = mStreamControls.get(AudioManager.STREAM_MUSIC);
-        if (sc != null) {
-            sc.iconRes = resId;
-            sc.iconMuteRes = resMuteId;
-            updateSliderIcon(sc, isMuted(sc.streamType));
-        }
-    }
-
-    protected void onFreeResources() {
-        synchronized (this) {
-            for (int i = mToneGenerators.length - 1; i >= 0; i--) {
-                if (mToneGenerators[i] != null) {
-                    mToneGenerators[i].release();
-                }
-                mToneGenerators[i] = null;
-            }
-        }
-    }
-
-    @Override
-    public void handleMessage(Message msg) {
-        switch (msg.what) {
-
-            case MSG_VOLUME_CHANGED: {
-                onVolumeChanged(msg.arg1, msg.arg2);
-                break;
-            }
-
-            case MSG_MUTE_CHANGED: {
-                onMuteChanged(msg.arg1, msg.arg2);
-                break;
-            }
-
-            case MSG_FREE_RESOURCES: {
-                onFreeResources();
-                break;
-            }
-
-            case MSG_STOP_SOUNDS: {
-                onStopSounds();
-                break;
-            }
-
-            case MSG_PLAY_SOUND: {
-                onPlaySound(msg.arg1, msg.arg2);
-                break;
-            }
-
-            case MSG_VIBRATE: {
-                onVibrate();
-                break;
-            }
-
-            case MSG_TIMEOUT: {
-                if (isShowing()) {
-                    mDialog.dismiss();
-                    clearRemoteStreamController();
-                    mActiveStreamType = -1;
-                    if (mCallback != null) {
-                        mCallback.onVisible(false);
-                    }
-                }
-                synchronized (sSafetyWarningLock) {
-                    if (sSafetyWarning != null) {
-                        if (LOGD) Log.d(mTag, "SafetyWarning timeout");
-                        sSafetyWarning.dismiss();
-                    }
-                }
-                break;
-            }
-
-            case MSG_RINGER_MODE_CHANGED:
-            case MSG_INTERNAL_RINGER_MODE_CHANGED:
-            case MSG_NOTIFICATION_EFFECTS_SUPPRESSOR_CHANGED: {
-                if (isShowing()) {
-                    updateActiveSlider();
-                }
-                break;
-            }
-
-            case MSG_REMOTE_VOLUME_CHANGED: {
-                onRemoteVolumeChanged((MediaController) msg.obj, msg.arg1);
-                break;
-            }
-
-            case MSG_REMOTE_VOLUME_UPDATE_IF_SHOWN:
-                onRemoteVolumeUpdateIfShown();
-                break;
-
-            case MSG_SLIDER_VISIBILITY_CHANGED:
-                onSliderVisibilityChanged(msg.arg1, msg.arg2);
-                break;
-
-            case MSG_DISPLAY_SAFE_VOLUME_WARNING:
-                onDisplaySafeVolumeWarning(msg.arg1);
-                break;
-
-            case MSG_LAYOUT_DIRECTION:
-                setLayoutDirection(msg.arg1);
-                break;
-
-            case MSG_ZEN_MODE_AVAILABLE_CHANGED:
-                mZenModeAvailable = msg.arg1 != 0;
-                updateZenPanelVisible();
-                break;
-
-            case MSG_USER_ACTIVITY:
-                if (mCallback != null) {
-                    mCallback.onInteraction();
-                }
-                break;
-        }
-    }
-
-    private void resetTimeout() {
-        final boolean touchExploration = mAccessibilityManager.isTouchExplorationEnabled();
-        if (LOGD) Log.d(mTag, "resetTimeout at " + System.currentTimeMillis()
-                + " delay=" + mTimeoutDelay + " touchExploration=" + touchExploration);
-        if (sSafetyWarning == null || !touchExploration) {
-            removeMessages(MSG_TIMEOUT);
-            sendEmptyMessageDelayed(MSG_TIMEOUT, mTimeoutDelay);
-            removeMessages(MSG_USER_ACTIVITY);
-            sendEmptyMessage(MSG_USER_ACTIVITY);
-        }
-    }
-
-    private void forceTimeout(long delay) {
-        if (LOGD) Log.d(mTag, "forceTimeout delay=" + delay + " callers=" + Debug.getCallers(3));
-        removeMessages(MSG_TIMEOUT);
-        sendEmptyMessageDelayed(MSG_TIMEOUT, delay);
-    }
-
-    public ZenModeController getZenController() {
-        return mZenController;
-    }
-
-    @Override
-    public void dispatchDemoCommand(String command, Bundle args) {
-        if (!COMMAND_VOLUME.equals(command)) return;
-        String icon = args.getString("icon");
-        final String iconMute = args.getString("iconmute");
-        final boolean mute = iconMute != null;
-        icon = mute ? iconMute : icon;
-        icon = icon.endsWith("Stream") ? icon : (icon + "Stream");
-        final StreamResources sr = StreamResources.valueOf(icon);
-        mDemoIcon = mute ? sr.iconMuteRes : sr.iconRes;
-        final int forcedStreamType = StreamResources.MediaStream.streamType;
-        mAudioManager.forceVolumeControlStream(forcedStreamType);
-        mAudioManager.adjustStreamVolume(forcedStreamType, AudioManager.ADJUST_SAME,
-                AudioManager.FLAG_SHOW_UI);
-    }
-
-    private final OnSeekBarChangeListener mSeekListener = new OnSeekBarChangeListener() {
-        @Override
-        public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
-            final Object tag = seekBar.getTag();
-            if (fromUser && tag instanceof StreamControl) {
-                StreamControl sc = (StreamControl) tag;
-                setStreamVolume(sc, progress + sc.minVolume,
-                        AudioManager.FLAG_SHOW_UI | AudioManager.FLAG_VIBRATE);
-            }
-            resetTimeout();
-        }
-
-        @Override
-        public void onStartTrackingTouch(SeekBar seekBar) {
-        }
-
-        @Override
-        public void onStopTrackingTouch(SeekBar seekBar) {
-        }
-    };
-
-    private final ZenModeController.Callback mZenCallback = new ZenModeController.Callback() {
-        @Override
-        public void onZenAvailableChanged(boolean available) {
-            obtainMessage(MSG_ZEN_MODE_AVAILABLE_CHANGED, available ? 1 : 0, 0).sendToTarget();
-        }
-
-        @Override
-        public void onEffectsSupressorChanged() {
-            mNotificationEffectsSuppressor = mZenController.getEffectsSuppressor();
-            sendEmptyMessage(MSG_NOTIFICATION_EFFECTS_SUPPRESSOR_CHANGED);
-        }
-    };
-
-    private final MediaController.Callback mMediaControllerCb = new MediaController.Callback() {
-        public void onAudioInfoChanged(PlaybackInfo info) {
-            onRemoteVolumeUpdateIfShown();
-        }
-    };
-
-    private final class SecondaryIconTransition extends AnimatorListenerAdapter
-            implements Runnable {
-        private static final int ANIMATION_TIME = 400;
-        private static final int WAIT_FOR_SWITCH_TIME = 1000;
-
-        private final int mAnimationTime = (int)(ANIMATION_TIME * ValueAnimator.getDurationScale());
-        private final int mFadeOutTime = mAnimationTime / 2;
-        private final int mDelayTime = mAnimationTime / 3;
-
-        private final Interpolator mIconInterpolator =
-                AnimationUtils.loadInterpolator(mContext, android.R.interpolator.fast_out_slow_in);
-
-        private StreamControl mTarget;
-
-        public void start(StreamControl sc) {
-            if (sc == null) throw new IllegalArgumentException();
-            if (LOGD) Log.d(mTag, "Secondary icon animation start");
-            if (mTarget != null) {
-                cancel();
-            }
-            mTarget = sc;
-            mTimeoutDelay = mAnimationTime + WAIT_FOR_SWITCH_TIME;
-            resetTimeout();
-            mTarget.secondaryIcon.setClickable(false);
-            final int N = mTarget.group.getChildCount();
-            for (int i = 0; i < N; i++) {
-                final View child = mTarget.group.getChildAt(i);
-                if (child != mTarget.secondaryIcon) {
-                    child.animate().alpha(0).setDuration(mFadeOutTime).start();
-                }
-            }
-            mTarget.secondaryIcon.animate()
-                    .translationXBy(mTarget.icon.getX() - mTarget.secondaryIcon.getX())
-                    .setInterpolator(mIconInterpolator)
-                    .setStartDelay(mDelayTime)
-                    .setDuration(mAnimationTime - mDelayTime)
-                    .setListener(this)
-                    .start();
-        }
-
-        public boolean isRunning() {
-            return mTarget != null;
-        }
-
-        public void cancel() {
-            if (mTarget == null) return;
-            mTarget.secondaryIcon.setClickable(true);
-            final int N = mTarget.group.getChildCount();
-            for (int i = 0; i < N; i++) {
-                final View child = mTarget.group.getChildAt(i);
-                if (child != mTarget.secondaryIcon) {
-                    child.animate().cancel();
-                    child.setAlpha(1);
-                }
-            }
-            mTarget.secondaryIcon.animate().cancel();
-            mTarget.secondaryIcon.setTranslationX(0);
-            mTarget = null;
-        }
-
-        @Override
-        public void onAnimationEnd(Animator animation) {
-            if (mTarget == null) return;
-            AsyncTask.execute(this);
-        }
-
-        @Override
-        public void run() {
-            if (mTarget == null) return;
-            if (LOGD) Log.d(mTag, "Secondary icon animation complete, show notification slider");
-            mAudioManager.forceVolumeControlStream(StreamResources.NotificationStream.streamType);
-            mAudioManager.adjustStreamVolume(StreamResources.NotificationStream.streamType,
-                    AudioManager.ADJUST_SAME, AudioManager.FLAG_SHOW_UI);
-        }
-    }
-
-    public interface Callback {
-        void onZenSettings();
-        void onZenPrioritySettings();
-        void onInteraction();
-        void onVisible(boolean visible);
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumePanelComponent.java b/packages/SystemUI/src/com/android/systemui/volume/VolumePanelComponent.java
deleted file mode 100644
index b072cab..0000000
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumePanelComponent.java
+++ /dev/null
@@ -1,202 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.volume;
-
-import android.content.Context;
-import android.content.Intent;
-import android.content.res.Configuration;
-import android.media.AudioManager;
-import android.media.IRemoteVolumeController;
-import android.media.IVolumeController;
-import android.media.VolumePolicy;
-import android.media.session.ISessionController;
-import android.media.session.MediaController;
-import android.media.session.MediaSessionManager;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.RemoteException;
-
-import com.android.systemui.R;
-import com.android.systemui.SystemUI;
-import com.android.systemui.keyguard.KeyguardViewMediator;
-import com.android.systemui.qs.tiles.DndTile;
-import com.android.systemui.statusbar.phone.PhoneStatusBar;
-import com.android.systemui.statusbar.policy.ZenModeController;
-
-import java.io.FileDescriptor;
-import java.io.PrintWriter;
-
-/**
- * Implementation of VolumeComponent backed by the old volume panel.
- */
-public class VolumePanelComponent implements VolumeComponent {
-
-    private final SystemUI mSysui;
-    private final Context mContext;
-    private final Handler mHandler;
-    private final VolumeController mVolumeController;
-    private final RemoteVolumeController mRemoteVolumeController;
-    private final AudioManager mAudioManager;
-    private final MediaSessionManager mMediaSessionManager;
-
-    private VolumePanel mPanel;
-    private int mDismissDelay;
-
-    public VolumePanelComponent(SystemUI sysui, Context context, Handler handler,
-            ZenModeController controller) {
-        mSysui = sysui;
-        mContext = context;
-        mHandler = handler;
-        mAudioManager = context.getSystemService(AudioManager.class);
-        mMediaSessionManager = context.getSystemService(MediaSessionManager.class);
-        mVolumeController = new VolumeController();
-        mRemoteVolumeController = new RemoteVolumeController();
-        mDismissDelay = mContext.getResources().getInteger(R.integer.volume_panel_dismiss_delay);
-        mPanel = new VolumePanel(mContext, controller);
-        mPanel.setCallback(new VolumePanel.Callback() {
-            @Override
-            public void onZenSettings() {
-                mHandler.removeCallbacks(mStartZenSettings);
-                mHandler.post(mStartZenSettings);
-            }
-
-            @Override
-            public void onZenPrioritySettings() {
-                mHandler.removeCallbacks(mStartZenPrioritySettings);
-                mHandler.post(mStartZenPrioritySettings);
-            }
-
-            @Override
-            public void onInteraction() {
-                final KeyguardViewMediator kvm = mSysui.getComponent(KeyguardViewMediator.class);
-                if (kvm != null) {
-                    kvm.userActivity();
-                }
-            }
-
-            @Override
-            public void onVisible(boolean visible) {
-                if (mAudioManager != null && mVolumeController != null) {
-                    mAudioManager.notifyVolumeControllerVisible(mVolumeController, visible);
-                }
-            }
-        });
-    }
-
-    @Override
-    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
-        if (mPanel != null) {
-            mPanel.dump(fd, pw, args);
-        }
-    }
-
-    public void register() {
-        mAudioManager.setVolumeController(mVolumeController);
-        mAudioManager.setVolumePolicy(VolumePolicy.DEFAULT);
-        mMediaSessionManager.setRemoteVolumeController(mRemoteVolumeController);
-        DndTile.setVisible(mContext, false);
-    }
-
-    @Override
-    public void onConfigurationChanged(Configuration newConfig) {
-        if (mPanel != null) {
-            mPanel.onConfigurationChanged(newConfig);
-        }
-    }
-
-    @Override
-    public ZenModeController getZenController() {
-        return mPanel.getZenController();
-    }
-
-    @Override
-    public void dispatchDemoCommand(String command, Bundle args) {
-        mPanel.dispatchDemoCommand(command, args);
-    }
-
-    @Override
-    public void dismissNow() {
-        mPanel.postDismiss(0);
-    }
-
-    private void startSettings(Intent intent) {
-        mSysui.getComponent(PhoneStatusBar.class).startActivityDismissingKeyguard(intent,
-                true /* onlyProvisioned */, true /* dismissShade */);
-        mPanel.postDismiss(mDismissDelay);
-    }
-
-    private final Runnable mStartZenSettings = new Runnable() {
-        @Override
-        public void run() {
-            startSettings(ZenModePanel.ZEN_SETTINGS);
-        }
-    };
-
-    private final Runnable mStartZenPrioritySettings = new Runnable() {
-        @Override
-        public void run() {
-            startSettings(ZenModePanel.ZEN_PRIORITY_SETTINGS);
-        }
-    };
-
-    private final class RemoteVolumeController extends IRemoteVolumeController.Stub {
-        @Override
-        public void remoteVolumeChanged(ISessionController binder, int flags)
-                throws RemoteException {
-            MediaController controller = new MediaController(mContext, binder);
-            mPanel.postRemoteVolumeChanged(controller, flags);
-        }
-
-        @Override
-        public void updateRemoteController(ISessionController session) throws RemoteException {
-            mPanel.postRemoteSliderVisibility(session != null);
-            // TODO stash default session in case the slider can be opened other
-            // than by remoteVolumeChanged.
-        }
-    }
-
-    /** For now, simply host an unmodified base volume panel in this process. */
-    private final class VolumeController extends IVolumeController.Stub {
-
-        @Override
-        public void displaySafeVolumeWarning(int flags) throws RemoteException {
-            mPanel.postDisplaySafeVolumeWarning(flags);
-        }
-
-        @Override
-        public void volumeChanged(int streamType, int flags)
-                throws RemoteException {
-            mPanel.postVolumeChanged(streamType, flags);
-        }
-
-        @Override
-        public void masterMuteChanged(int flags) throws RemoteException {
-            // no-op
-        }
-
-        @Override
-        public void setLayoutDirection(int layoutDirection)
-                throws RemoteException {
-            mPanel.postLayoutDirection(layoutDirection);
-        }
-
-        @Override
-        public void dismiss() throws RemoteException {
-            dismissNow();
-        }
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeUI.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeUI.java
index e979786..5f04aaf 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeUI.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeUI.java
@@ -31,7 +31,6 @@
 import android.media.AudioManager;
 import android.media.session.MediaSessionManager;
 import android.os.Handler;
-import android.os.SystemProperties;
 import android.provider.Settings;
 import android.text.TextUtils;
 import android.util.Log;
@@ -52,8 +51,6 @@
     private static final String TAG = "VolumeUI";
     private static boolean LOGD = Log.isLoggable(TAG, Log.DEBUG);
 
-    private static final boolean USE_OLD_VOLUME = SystemProperties.getBoolean("volume.old", false);
-
     private final Handler mHandler = new Handler();
     private final Receiver mReceiver = new Receiver();
     private final RestorationNotification mRestorationNotification = new RestorationNotification();
@@ -64,8 +61,7 @@
     private MediaSessionManager mMediaSessionManager;
     private ServiceMonitor mVolumeControllerService;
 
-    private VolumePanelComponent mOldVolume;
-    private VolumeDialogComponent mNewVolume;
+    private VolumeDialogComponent mVolumeComponent;
 
     @Override
     public void start() {
@@ -77,8 +73,7 @@
         mMediaSessionManager = (MediaSessionManager) mContext
                 .getSystemService(Context.MEDIA_SESSION_SERVICE);
         final ZenModeController zenController = new ZenModeControllerImpl(mContext, mHandler);
-        mOldVolume = new VolumePanelComponent(this, mContext, mHandler, zenController);
-        mNewVolume = new VolumeDialogComponent(this, mContext, null, zenController);
+        mVolumeComponent = new VolumeDialogComponent(this, mContext, null, zenController);
         putComponent(VolumeComponent.class, getVolumeComponent());
         mReceiver.start();
         mVolumeControllerService = new ServiceMonitor(TAG, LOGD,
@@ -88,7 +83,7 @@
     }
 
     private VolumeComponent getVolumeComponent() {
-        return USE_OLD_VOLUME ? mOldVolume : mNewVolume;
+        return mVolumeComponent;
     }
 
     @Override
@@ -235,7 +230,7 @@
                     .putExtra(Receiver.EXTRA_COMPONENT, component);
             mNotificationManager.notify(R.id.notification_volumeui,
                     new Notification.Builder(mContext)
-                            .setSmallIcon(R.drawable.ic_ringer_audible)
+                            .setSmallIcon(R.drawable.ic_volume_media)
                             .setWhen(0)
                             .setShowWhen(false)
                             .setOngoing(true)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/HeadsUpNotificationTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/HeadsUpNotificationTest.java
deleted file mode 100644
index e8a80d9..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/HeadsUpNotificationTest.java
+++ /dev/null
@@ -1,261 +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.statusbar.policy;
-
-import static org.mockito.Matchers.anyInt;
-import static org.mockito.Mockito.inOrder;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.reset;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.when;
-
-import android.app.Notification;
-import android.os.*;
-import android.service.notification.StatusBarNotification;
-import com.android.systemui.SwipeHelper;
-import com.android.systemui.SysuiTestCase;
-import com.android.systemui.statusbar.ExpandableNotificationRow;
-import com.android.systemui.statusbar.NotificationData;
-import com.android.systemui.statusbar.phone.PhoneStatusBar;
-
-import org.mockito.ArgumentCaptor;
-import org.mockito.InOrder;
-import org.mockito.Mock;
-import org.mockito.Mockito;
-import org.mockito.MockitoAnnotations;
-
-/**
- * Test the Heads Up Notification.
- *
- * Specifically the policy that a notificaiton must remain visibile for a minimum period of time.
- */
-public class HeadsUpNotificationTest extends SysuiTestCase {
-    private static final String TAG = "HeadsUpNotificationTest";
-
-    private static int TOUCH_SENSITIVITY = 100;
-    private static int NOTIFICATION_DECAY = 10000;
-    private static int MINIMUM_DISPLAY_TIME = 3000;
-    private static int SNOOZE_TIME = 60000;
-    private static long TOO_SOON = 1000L;  // less than MINIMUM_DISPLAY_TIME
-    private static long LATER = 5000L;  // more than MINIMUM_DISPLAY_TIME
-    private static long REMAINING_VISIBILITY = MINIMUM_DISPLAY_TIME - TOO_SOON;
-
-    protected HeadsUpNotificationView mHeadsUp;
-
-    @Mock protected PhoneStatusBar mMockStatusBar;
-    @Mock private HeadsUpNotificationView.Clock mClock;
-    @Mock private SwipeHelper mMockSwipeHelper;
-    @Mock private HeadsUpNotificationView.EdgeSwipeHelper mMockEdgeSwipeHelper;
-
-    @Override
-    protected void setUp() throws Exception {
-        super.setUp();
-
-        MockitoAnnotations.initMocks(this);
-
-        mHeadsUp = new HeadsUpNotificationView(mContext,
-                mClock, mMockSwipeHelper, mMockEdgeSwipeHelper,
-                NOTIFICATION_DECAY, MINIMUM_DISPLAY_TIME, TOUCH_SENSITIVITY, SNOOZE_TIME);
-        mHeadsUp.setBar(mMockStatusBar);
-    }
-
-    private NotificationData.Entry makeNotification(String key) {
-        StatusBarNotification sbn = mock(StatusBarNotification.class);
-        when(sbn.getKey()).thenReturn(key);
-        return new NotificationData.Entry(sbn, null);
-    }
-
-    public void testPostAndDecay() {
-        NotificationData.Entry a = makeNotification("a");
-        mHeadsUp.showNotification(a);
-        Mockito.verify(mMockStatusBar, never()).scheduleHeadsUpClose();
-        Mockito.verify(mMockStatusBar, times(1)).scheduleHeadsUpOpen();
-        ArgumentCaptor<Long> decayArg = ArgumentCaptor.forClass(Long.class);
-        Mockito.verify(mMockStatusBar).scheduleHeadsUpDecay(decayArg.capture());
-        // New notification gets a full decay time.
-        assertEquals(NOTIFICATION_DECAY, (long) decayArg.getValue());
-    }
-
-    public void testPostAndDeleteTooSoon() {
-        when(mClock.currentTimeMillis()).thenReturn(0L);
-        NotificationData.Entry a = makeNotification("a");
-        mHeadsUp.showNotification(a);
-        reset(mMockStatusBar);
-
-        when(mClock.currentTimeMillis()).thenReturn(TOO_SOON);
-        mHeadsUp.removeNotification(a.key);
-        ArgumentCaptor<Long> decayArg = ArgumentCaptor.forClass(Long.class);
-        Mockito.verify(mMockStatusBar, never()).scheduleHeadsUpClose();
-        Mockito.verify(mMockStatusBar).scheduleHeadsUpDecay(decayArg.capture());
-        // Leave the window up for the balance of the minumum time.
-        assertEquals(REMAINING_VISIBILITY, (long) decayArg.getValue());
-    }
-
-    public void testPostAndDeleteLater() {
-        when(mClock.currentTimeMillis()).thenReturn(0L);
-        NotificationData.Entry a = makeNotification("a");
-        mHeadsUp.showNotification(a);
-        reset(mMockStatusBar);
-
-        when(mClock.currentTimeMillis()).thenReturn(LATER);
-        mHeadsUp.removeNotification(a.key);
-        // Delete closes immediately if the minimum time window is satisfied.
-        Mockito.verify(mMockStatusBar, times(1)).scheduleHeadsUpClose();
-        Mockito.verify(mMockStatusBar, never()).scheduleHeadsUpDecay(anyInt());
-    }
-
-    // This is a bad test.  It should not care that there is a call to scheduleHeadsUpClose(),
-    // but it happens that there will be one, so it is important that it happen before the
-    // call to scheduleHeadsUpOpen(), so that the final state is open.
-    // Maybe mMockStatusBar should instead be a fake that tracks the open/closed state.
-    public void testPostAndReplaceTooSoon() {
-        InOrder callOrder = inOrder(mMockStatusBar);
-        when(mClock.currentTimeMillis()).thenReturn(0L);
-        NotificationData.Entry a = makeNotification("a");
-        mHeadsUp.showNotification(a);
-        reset(mMockStatusBar);
-
-        when(mClock.currentTimeMillis()).thenReturn(TOO_SOON);
-        NotificationData.Entry b = makeNotification("b");
-        mHeadsUp.showNotification(b);
-        Mockito.verify(mMockStatusBar, times(1)).scheduleHeadsUpClose();
-        ArgumentCaptor<Long> decayArg = ArgumentCaptor.forClass(Long.class);
-        Mockito.verify(mMockStatusBar, times(1)).scheduleHeadsUpDecay(decayArg.capture());
-        // New notification gets a full decay time.
-        assertEquals(NOTIFICATION_DECAY, (long) decayArg.getValue());
-
-        // Make sure close was called before open, so that the heads up stays open.
-        callOrder.verify(mMockStatusBar).scheduleHeadsUpClose();
-        callOrder.verify(mMockStatusBar).scheduleHeadsUpOpen();
-    }
-
-    public void testPostAndUpdateAlertAgain() {
-        when(mClock.currentTimeMillis()).thenReturn(0L);
-        NotificationData.Entry a = makeNotification("a");
-        mHeadsUp.showNotification(a);
-        reset(mMockStatusBar);
-
-        when(mClock.currentTimeMillis()).thenReturn(TOO_SOON);
-        mHeadsUp.updateNotification(a, true);
-        Mockito.verify(mMockStatusBar, never()).scheduleHeadsUpClose();
-        ArgumentCaptor<Long> decayArg = ArgumentCaptor.forClass(Long.class);
-        Mockito.verify(mMockStatusBar, times(1)).scheduleHeadsUpDecay(decayArg.capture());
-        // Alert again gets a full decay time.
-        assertEquals(NOTIFICATION_DECAY, (long) decayArg.getValue());
-    }
-
-    public void testPostAndUpdateAlertAgainFastFail() {
-        when(mClock.currentTimeMillis()).thenReturn(0L);
-        NotificationData.Entry a = makeNotification("a");
-        mHeadsUp.showNotification(a);
-        reset(mMockStatusBar);
-
-        when(mClock.currentTimeMillis()).thenReturn(TOO_SOON);
-        NotificationData.Entry a_prime = makeNotification("a");
-        mHeadsUp.updateNotification(a_prime, true);
-        Mockito.verify(mMockStatusBar, never()).scheduleHeadsUpClose();
-        ArgumentCaptor<Long> decayArg = ArgumentCaptor.forClass(Long.class);
-        Mockito.verify(mMockStatusBar, times(1)).scheduleHeadsUpDecay(decayArg.capture());
-        // Alert again gets a full decay time.
-        assertEquals(NOTIFICATION_DECAY, (long) decayArg.getValue());
-    }
-
-    public void testPostAndUpdateNoAlertAgain() {
-        when(mClock.currentTimeMillis()).thenReturn(0L);
-        NotificationData.Entry a = makeNotification("a");
-        mHeadsUp.showNotification(a);
-        reset(mMockStatusBar);
-
-        when(mClock.currentTimeMillis()).thenReturn(TOO_SOON);
-        mHeadsUp.updateNotification(a, false);
-        Mockito.verify(mMockStatusBar, never()).scheduleHeadsUpClose();
-        Mockito.verify(mMockStatusBar, never()).scheduleHeadsUpDecay(anyInt());
-    }
-
-    public void testPostAndUpdateNoAlertAgainFastFail() {
-        when(mClock.currentTimeMillis()).thenReturn(0L);
-        NotificationData.Entry a = makeNotification("a");
-        mHeadsUp.showNotification(a);
-        reset(mMockStatusBar);
-
-        when(mClock.currentTimeMillis()).thenReturn(TOO_SOON);
-        NotificationData.Entry a_prime = makeNotification("a");
-        mHeadsUp.updateNotification(a_prime, false);
-        Mockito.verify(mMockStatusBar, never()).scheduleHeadsUpClose();
-        Mockito.verify(mMockStatusBar, never()).scheduleHeadsUpDecay(anyInt());
-    }
-
-    public void testPostAndUpdateLowPriorityTooSoon() {
-        when(mClock.currentTimeMillis()).thenReturn(0L);
-        NotificationData.Entry a = makeNotification("a");
-        mHeadsUp.showNotification(a);
-        reset(mMockStatusBar);
-
-        when(mClock.currentTimeMillis()).thenReturn(TOO_SOON);
-        mHeadsUp.release();
-        Mockito.verify(mMockStatusBar, never()).scheduleHeadsUpClose();
-        ArgumentCaptor<Long> decayArg = ArgumentCaptor.forClass(Long.class);
-        Mockito.verify(mMockStatusBar, times(1)).scheduleHeadsUpDecay(decayArg.capture());
-        // Down grade on update leaves the window up for the balance of the minumum time.
-        assertEquals(REMAINING_VISIBILITY, (long) decayArg.getValue());
-    }
-
-    public void testPostAndUpdateLowPriorityTooSoonFastFail() {
-        when(mClock.currentTimeMillis()).thenReturn(0L);
-        NotificationData.Entry a = makeNotification("a");
-        mHeadsUp.showNotification(a);
-        reset(mMockStatusBar);
-
-        when(mClock.currentTimeMillis()).thenReturn(TOO_SOON);
-        NotificationData.Entry a_prime = makeNotification("a");
-        mHeadsUp.updateNotification(a_prime, false);
-        mHeadsUp.release();
-        Mockito.verify(mMockStatusBar, never()).scheduleHeadsUpClose();
-        ArgumentCaptor<Long> decayArg = ArgumentCaptor.forClass(Long.class);
-        Mockito.verify(mMockStatusBar, times(1)).scheduleHeadsUpDecay(decayArg.capture());
-        // Down grade on update leaves the window up for the balance of the minumum time.
-        assertEquals(REMAINING_VISIBILITY, (long) decayArg.getValue());
-    }
-
-    public void testPostAndUpdateLowPriorityLater() {
-        when(mClock.currentTimeMillis()).thenReturn(0L);
-        NotificationData.Entry a = makeNotification("a");
-        mHeadsUp.showNotification(a);
-        reset(mMockStatusBar);
-
-        when(mClock.currentTimeMillis()).thenReturn(LATER);
-        mHeadsUp.release();
-        // Down grade on update closes immediately if the minimum time window is satisfied.
-        Mockito.verify(mMockStatusBar, times(1)).scheduleHeadsUpClose();
-        Mockito.verify(mMockStatusBar, never()).scheduleHeadsUpDecay(anyInt());
-    }
-
-    public void testPostAndUpdateLowPriorityLaterFastFail() {
-        when(mClock.currentTimeMillis()).thenReturn(0L);
-        NotificationData.Entry a = makeNotification("a");
-        mHeadsUp.showNotification(a);
-        reset(mMockStatusBar);
-
-        when(mClock.currentTimeMillis()).thenReturn(LATER);
-        NotificationData.Entry a_prime = makeNotification("a");
-        mHeadsUp.updateNotification(a_prime, false);
-        mHeadsUp.release();
-        // Down grade on update closes immediately if the minimum time window is satisfied.
-        Mockito.verify(mMockStatusBar, times(1)).scheduleHeadsUpClose();
-        Mockito.verify(mMockStatusBar, never()).scheduleHeadsUpDecay(anyInt());
-    }
-}
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 5d88407..5d40eed 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
@@ -279,7 +279,7 @@
 
         // TODO: Verify all fields.
         Mockito.verify(mSignalCluster, Mockito.atLeastOnce()).setMobileDataIndicators(
-                visibleArg.capture(), iconArg.capture(), typeIconArg.capture(),
+                visibleArg.capture(), iconArg.capture(), iconArg.capture(), typeIconArg.capture(),
                 ArgumentCaptor.forClass(String.class).capture(),
                 ArgumentCaptor.forClass(String.class).capture(),
                 ArgumentCaptor.forClass(Boolean.class).capture(),
diff --git a/rs/java/android/renderscript/Allocation.java b/rs/java/android/renderscript/Allocation.java
index 2203850..3b61f9d 100644
--- a/rs/java/android/renderscript/Allocation.java
+++ b/rs/java/android/renderscript/Allocation.java
@@ -276,7 +276,7 @@
      * Enable/Disable AutoPadding for Vec3 elements.
      * By default: Diabled.
      *
-     * @param useAutoPadding True: enable AutoPadding; flase: disable AutoPadding
+     * @param useAutoPadding True: enable AutoPadding; False: disable AutoPadding
      *
      */
     public void setAutoPadding(boolean useAutoPadding) {
diff --git a/rs/java/android/renderscript/AllocationAdapter.java b/rs/java/android/renderscript/AllocationAdapter.java
index 35d59dd..9bfd6ec 100644
--- a/rs/java/android/renderscript/AllocationAdapter.java
+++ b/rs/java/android/renderscript/AllocationAdapter.java
@@ -208,7 +208,7 @@
     }
 
     /**
-     *
+     * @hide
      */
     public void setArray(int arrayNum, int arrayVal) {
         if (mAdaptedAllocation.getType().getArray(arrayNum) == 0) {
diff --git a/rs/java/android/renderscript/RenderScript.java b/rs/java/android/renderscript/RenderScript.java
index 7c927fd..e7f210b 100644
--- a/rs/java/android/renderscript/RenderScript.java
+++ b/rs/java/android/renderscript/RenderScript.java
@@ -951,6 +951,17 @@
         rsnScriptIntrinsicBLAS_Z(mContext, id, func, TransA, TransB, Side, Uplo, Diag, M, N, K, alphaX, alphaY, A, B, betaX, betaY, C, incX, incY, KL, KU);
     }
 
+    native void rsnScriptIntrinsicBLAS_BNNM(long con, long id, int M, int N, int K,
+                                             long A, int a_offset, long B, int b_offset, long C, int c_offset,
+                                             int c_mult_int);
+    synchronized void nScriptIntrinsicBLAS_BNNM(long id, int M, int N, int K,
+                                             long A, int a_offset, long B, int b_offset, long C, int c_offset,
+                                             int c_mult_int) {
+        validate();
+        rsnScriptIntrinsicBLAS_BNNM(mContext, id, M, N, K, A, a_offset, B, b_offset, C, c_offset, c_mult_int);
+    }
+
+
 
     long     mDev;
     long     mContext;
diff --git a/rs/java/android/renderscript/ScriptIntrinsicBLAS.java b/rs/java/android/renderscript/ScriptIntrinsicBLAS.java
index 90d2300..6cfdfee 100644
--- a/rs/java/android/renderscript/ScriptIntrinsicBLAS.java
+++ b/rs/java/android/renderscript/ScriptIntrinsicBLAS.java
@@ -24,7 +24,6 @@
  *
  * BLAS
  *
- * @hide
  **/
 public final class ScriptIntrinsicBLAS extends ScriptIntrinsic {
     private Allocation mLUT;
@@ -176,6 +175,9 @@
     private static final int RsBlas_zherk = 141;
     private static final int RsBlas_zher2k = 142;
 
+    // BLAS extensions start here
+    private static final int RsBlas_bnnm = 1000;
+
     /**
      */
     public static ScriptIntrinsicBLAS create(RenderScript rs) {
@@ -1485,5 +1487,22 @@
     }
 
 
+    /**
+     *
+     * 8-bit GEMM-like operation for neural networks
+     *
+     **/
+    public void BNNM(Allocation A, int a_offset, Allocation B, int b_offset, Allocation C, int c_offset, int c_mult) {
+        validateL3(Element.U8(mRS), NO_TRANSPOSE, TRANSPOSE, 0, A, B, C);
+
+        int M = -1, N = -1, K = -1;
+        M = A.getType().getY();
+        N = B.getType().getY();
+        K = A.getType().getX();
+
+
+        mRS.nScriptIntrinsicBLAS_BNNM(getID(mRS), M, N, K, A.getID(mRS), a_offset, B.getID(mRS), b_offset, C.getID(mRS), c_offset, c_mult);
+
+    }
 
 }
diff --git a/rs/java/android/renderscript/Type.java b/rs/java/android/renderscript/Type.java
index cc9b58b..dc23785 100644
--- a/rs/java/android/renderscript/Type.java
+++ b/rs/java/android/renderscript/Type.java
@@ -150,6 +150,7 @@
     }
 
     /**
+     * @hide
       * Return the dimension of the specified array.
       *
       * @param arrayNum  The array dimension to query
@@ -169,6 +170,7 @@
     }
 
     /**
+     * @hide
       * Return the number of array dimensions.
       *
       * @return int
@@ -382,6 +384,7 @@
         }
 
         /**
+         * @hide
          * Adds an array dimension to the builder
          *
          * @param dim
diff --git a/rs/jni/android_renderscript_RenderScript.cpp b/rs/jni/android_renderscript_RenderScript.cpp
index 49afa6d..ae48a5f 100644
--- a/rs/jni/android_renderscript_RenderScript.cpp
+++ b/rs/jni/android_renderscript_RenderScript.cpp
@@ -579,6 +579,32 @@
 
 
 static void
+nScriptIntrinsicBLAS_BNNM(JNIEnv *_env, jobject _this, jlong con, jlong id, jint M, jint N, jint K,
+                                             jlong A, jint a_offset, jlong B, jint b_offset, jlong C, jint c_offset,
+                                             jint c_mult_int) {
+    RsBlasCall call;
+    memset(&call, 0, sizeof(call));
+    call.func = RsBlas_bnnm;
+    call.M = M;
+    call.N = N;
+    call.K = K;
+    call.a_offset = a_offset;
+    call.b_offset = b_offset;
+    call.c_offset = c_offset;
+    call.c_mult_int = c_mult_int;
+
+    RsAllocation in_allocs[3];
+    in_allocs[0] = (RsAllocation)A;
+    in_allocs[1] = (RsAllocation)B;
+    in_allocs[2] = (RsAllocation)C;
+
+    rsScriptForEachMulti((RsContext)con, (RsScript)id, 0,
+                         in_allocs, sizeof(in_allocs), nullptr,
+                         &call, sizeof(call), nullptr, 0);
+}
+
+
+static void
 nAssignName(JNIEnv *_env, jobject _this, jlong con, jlong obj, jbyteArray str)
 {
     if (kLogApi) {
@@ -2417,6 +2443,8 @@
 {"rsnScriptIntrinsicBLAS_Complex",   "(JJIIIIIIIIIFFJJFFJIIII)V",             (void*)nScriptIntrinsicBLAS_Complex },
 {"rsnScriptIntrinsicBLAS_Z",         "(JJIIIIIIIIIDDJJDDJIIII)V",             (void*)nScriptIntrinsicBLAS_Z },
 
+{"rsnScriptIntrinsicBLAS_BNNM",      "(JJIIIJIJIJII)V",                       (void*)nScriptIntrinsicBLAS_BNNM },
+
 {"rsnProgramStoreCreate",            "(JZZZZZZIII)J",                         (void*)nProgramStoreCreate },
 
 {"rsnProgramBindConstants",          "(JJIJ)V",                               (void*)nProgramBindConstants },
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index bb5ff1b..82a77d2 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -3154,6 +3154,7 @@
                 case WindowManager.LayoutParams.TYPE_APPLICATION_PANEL:
                 case WindowManager.LayoutParams.TYPE_APPLICATION_STARTING:
                 case WindowManager.LayoutParams.TYPE_APPLICATION_SUB_PANEL:
+                case WindowManager.LayoutParams.TYPE_APPLICATION_ABOVE_SUB_PANEL:
                 case WindowManager.LayoutParams.TYPE_BASE_APPLICATION:
                 case WindowManager.LayoutParams.TYPE_PHONE:
                 case WindowManager.LayoutParams.TYPE_PRIORITY_PHONE:
diff --git a/services/core/java/com/android/server/BluetoothManagerService.java b/services/core/java/com/android/server/BluetoothManagerService.java
index ef51ad6..3e5eee8 100644
--- a/services/core/java/com/android/server/BluetoothManagerService.java
+++ b/services/core/java/com/android/server/BluetoothManagerService.java
@@ -112,6 +112,7 @@
     private static final int SERVICE_IBLUETOOTHGATT = 2;
 
     private final Context mContext;
+    private static int mBleAppCount = 0;
 
     // Locks are not provided for mName and mAddress.
     // They are accessed in handler or broadcast receiver, same thread context.
@@ -184,11 +185,40 @@
                             persistBluetoothSetting(BLUETOOTH_ON_BLUETOOTH);
                         }
                     }
+
+                    int st = BluetoothAdapter.STATE_OFF;
+                    if (mBluetooth != null) {
+                        try {
+                            st = mBluetooth.getState();
+                        } catch (RemoteException e) {
+                            Log.e(TAG,"Unable to call getState", e);
+                        }
+                    }
+                    Log.d(TAG, "state" + st);
+
                     if (isAirplaneModeOn()) {
-                        // disable without persisting the setting
-                        sendDisableMsg();
+                        // Clear registered LE apps to force shut-off
+                        synchronized (this) {
+                            mBleAppCount = 0;
+                        }
+                        if (st == BluetoothAdapter.STATE_BLE_ON) {
+                            //if state is BLE_ON make sure you trigger disableBLE part
+                            try {
+                                if (mBluetooth != null) {
+                                    mBluetooth.onBrEdrDown();
+                                    mEnableExternal = false;
+                                }
+                            } catch(RemoteException e) {
+                                Log.e(TAG,"Unable to call onBrEdrDown", e);
+                            }
+                        } else if (st == BluetoothAdapter.STATE_ON){
+                            // disable without persisting the setting
+                            Log.d(TAG, "Calling disable");
+                            sendDisableMsg();
+                        }
                     } else if (mEnableExternal) {
                         // enable without persisting the setting
+                        Log.d(TAG, "Calling enable");
                         sendEnableMsg(mQuietEnableExternal);
                     }
                 }
@@ -203,12 +233,6 @@
                         sendEnableMsg(mQuietEnableExternal);
                     }
                 }
-
-                if (!isNameAndAddressSet()) {
-                    //Sync the Bluetooth name and address from the Bluetooth Adapter
-                    if (DBG) Log.d(TAG,"Retrieving Bluetooth Adapter name and address...");
-                    getNameAndAddress();
-                }
             }
         }
     };
@@ -218,6 +242,7 @@
 
         mContext = context;
         mBluetooth = null;
+        mBluetoothGatt = null;
         mBinding = false;
         mUnbinding = false;
         mEnable = false;
@@ -396,6 +421,133 @@
         return false;
     }
 
+    class ClientDeathRecipient implements IBinder.DeathRecipient {
+        public void binderDied() {
+            if (DBG) Log.d(TAG, "Binder is dead -  unregister Ble App");
+            if (mBleAppCount > 0) --mBleAppCount;
+
+            if (mBleAppCount == 0) {
+                if (DBG) Log.d(TAG, "Disabling LE only mode after application crash");
+                try {
+                    if (mBluetooth != null) {
+                        mBluetooth.onBrEdrDown();
+                    }
+                } catch(RemoteException e) {
+                     Log.e(TAG,"Unable to call onBrEdrDown", e);
+                }
+            }
+        }
+    }
+
+    /** Internal death rec list */
+    Map<IBinder, ClientDeathRecipient> mBleApps = new HashMap<IBinder, ClientDeathRecipient>();
+
+    public int updateBleAppCount(IBinder token, boolean enable) {
+        if (enable) {
+            ClientDeathRecipient r = mBleApps.get(token);
+            if (r == null) {
+                ClientDeathRecipient deathRec = new ClientDeathRecipient();
+                try {
+                    token.linkToDeath(deathRec, 0);
+                } catch (RemoteException ex) {
+                    throw new IllegalArgumentException("Wake lock is already dead.");
+                }
+                mBleApps.put(token, deathRec);
+                synchronized (this) {
+                    ++mBleAppCount;
+                }
+                if (DBG) Log.d(TAG, "Registered for death Notification");
+            }
+
+        } else  {
+            ClientDeathRecipient r = mBleApps.get(token);
+            if (r != null) {
+                try {
+                    token.linkToDeath(r, 0);
+                } catch (RemoteException ex) {
+                    throw new IllegalArgumentException("Wake lock is already dead.");
+                }
+                mBleApps.remove(token);
+                synchronized (this) {
+                    if (mBleAppCount > 0) --mBleAppCount;
+                }
+                if (DBG) Log.d(TAG, "Unregistered for death Notification");
+            }
+        }
+        if (DBG) Log.d(TAG, "Updated BleAppCount" + mBleAppCount);
+        if (mBleAppCount == 0 && mEnable) {
+            try {
+                if (mBluetooth != null && (mBluetooth.getState() != BluetoothAdapter.STATE_ON)) {
+                    if (DBG) Log.d(TAG, "Reseting the mEnable flag for clean disable");
+                    mEnable = false;
+                }
+            } catch (RemoteException e) {
+                Log.e(TAG, "getState()", e);
+            }
+        }
+        return mBleAppCount;
+    }
+
+    /** @hide*/
+    public boolean isBleAppPresent() {
+        if (DBG) Log.d(TAG, "isBleAppPresent() count: " + mBleAppCount);
+        return (mBleAppCount > 0);
+    }
+
+    /**
+     * Action taken when GattService is turned off
+     */
+    private void onBluetoothGattServiceUp() {
+        if (DBG) Log.d(TAG,"BluetoothGatt Service is Up");
+        try{
+            if (isBleAppPresent() == false && mBluetooth.getState() == BluetoothAdapter.STATE_BLE_ON) {
+                mBluetooth.onLeServiceUp();
+
+                // waive WRITE_SECURE_SETTINGS permission check
+                long callingIdentity = Binder.clearCallingIdentity();
+                persistBluetoothSetting(BLUETOOTH_ON_BLUETOOTH);
+                Binder.restoreCallingIdentity(callingIdentity);
+            }
+        } catch(RemoteException e) {
+                Log.e(TAG,"Unable to call onServiceUp", e);
+        }
+    }
+
+    /**
+     * Inform BluetoothAdapter instances that BREDR part is down
+     * and turn off all service and stack if no LE app needs it
+     */
+    private void sendBrEdrDownCallback() {
+        if (DBG) Log.d(TAG,"Calling sendBrEdrDownCallback callbacks");
+        int n = mCallbacks.beginBroadcast();
+
+        if (isBleAppPresent() == false) {
+            try {
+                mBluetooth.onBrEdrDown();
+            } catch(RemoteException e) {
+                Log.e(TAG,"Unable to call onBrEdrDown", e);
+            }
+        }
+        else{//need to stay at BLE ON. disconnect all Gatt connections
+            try{
+                mBluetoothGatt.unregAll();//disconnectAll();
+            } catch(RemoteException e) {
+                Log.e(TAG,"Unable to disconn all", e);
+            }
+        }
+
+        Log.d(TAG,"Broadcasting onBrEdrDown() to " + n + " receivers.");
+        for (int i=0; i <n; i++) {
+            try {
+                mCallbacks.getBroadcastItem(i).onBrEdrDown();
+            }  catch (RemoteException e) {
+                Log.e(TAG, "Unable to call sendBrEdrDownCallback() on callback #" + i, e);
+            }
+        }
+        mCallbacks.finishBroadcast();
+    }
+
+    /** @hide*/
     public void getNameAndAddress() {
         if (DBG) {
             Log.d(TAG,"getNameAndAddress(): mBluetooth = " + mBluetooth +
@@ -445,11 +597,9 @@
             mQuietEnableExternal = false;
             mEnableExternal = true;
             // waive WRITE_SECURE_SETTINGS permission check
-            long callingIdentity = Binder.clearCallingIdentity();
-            persistBluetoothSetting(BLUETOOTH_ON_BLUETOOTH);
-            Binder.restoreCallingIdentity(callingIdentity);
             sendEnableMsg(false);
         }
+        if (DBG) Log.d(TAG, "enable returning");
         return true;
     }
 
@@ -508,6 +658,7 @@
             } else {
                 mUnbinding=false;
             }
+            mBluetoothGatt = null;
         }
     }
 
@@ -1034,6 +1185,7 @@
                     synchronized(mConnection) {
                         if (msg.arg1 == SERVICE_IBLUETOOTHGATT) {
                             mBluetoothGatt = IBluetoothGatt.Stub.asInterface(service);
+                            onBluetoothGattServiceUp();
                             break;
                         } // else must be SERVICE_IBLUETOOTH
 
@@ -1111,12 +1263,18 @@
                     bluetoothStateChangeHandler(prevState, newState);
                     // handle error state transition case from TURNING_ON to OFF
                     // unbind and rebind bluetooth service and enable bluetooth
-                    if ((prevState == BluetoothAdapter.STATE_TURNING_ON) &&
+                    if ((prevState == BluetoothAdapter.STATE_BLE_TURNING_ON) &&
                         (newState == BluetoothAdapter.STATE_OFF) &&
                         (mBluetooth != null) && mEnable) {
                         recoverBluetoothServiceFromError();
                     }
-                    if (newState == BluetoothAdapter.STATE_ON) {
+                    if ((prevState == BluetoothAdapter.STATE_TURNING_ON) &&
+                        (newState == BluetoothAdapter.STATE_BLE_ON) &&
+                        (mBluetooth != null) && mEnable) {
+                        recoverBluetoothServiceFromError();
+                    }
+                    if (newState == BluetoothAdapter.STATE_ON ||
+                        newState == BluetoothAdapter.STATE_BLE_ON) {
                         // bluetooth is working, reset the counter
                         if (mErrorRecoveryRetryCounter != 0) {
                             Log.w(TAG, "bluetooth is recovered from error");
@@ -1376,39 +1534,90 @@
         return valid;
     }
 
+    private void sendBleStateChanged(int prevState, int newState) {
+        if (DBG) Log.d(TAG,"BLE State Change Intent: " + prevState + " -> " + newState);
+        // Send broadcast message to everyone else
+        Intent intent = new Intent(BluetoothAdapter.ACTION_BLE_STATE_CHANGED);
+        intent.putExtra(BluetoothAdapter.EXTRA_PREVIOUS_STATE, prevState);
+        intent.putExtra(BluetoothAdapter.EXTRA_STATE, newState);
+        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
+        mContext.sendBroadcastAsUser(intent, UserHandle.ALL, BLUETOOTH_PERM);
+    }
+
     private void bluetoothStateChangeHandler(int prevState, int newState) {
+        boolean isStandardBroadcast = true;
         if (prevState != newState) {
             //Notify all proxy objects first of adapter state change
-            if (newState == BluetoothAdapter.STATE_ON || newState == BluetoothAdapter.STATE_OFF) {
-                boolean isUp = (newState==BluetoothAdapter.STATE_ON);
-                sendBluetoothStateCallback(isUp);
+            if (newState == BluetoothAdapter.STATE_BLE_ON
+                   || newState == BluetoothAdapter.STATE_OFF) {
+                boolean intermediate_off = (prevState == BluetoothAdapter.STATE_TURNING_OFF
+                   && newState == BluetoothAdapter.STATE_BLE_ON);
 
-                if (isUp) {
-                    // connect to GattService
-                    if (mContext.getPackageManager().hasSystemFeature(
-                                                     PackageManager.FEATURE_BLUETOOTH_LE)) {
-                        Intent i = new Intent(IBluetoothGatt.class.getName());
-                        doBind(i, mConnection, Context.BIND_AUTO_CREATE | Context.BIND_IMPORTANT,
-                                UserHandle.CURRENT);
-                    }
-                } else {
-                    //If Bluetooth is off, send service down event to proxy objects, and unbind
-                    if (!isUp && canUnbindBluetoothService()) {
-                        unbindAllBluetoothProfileServices();
+                if (newState == BluetoothAdapter.STATE_OFF) {
+                    // If Bluetooth is off, send service down event to proxy objects, and unbind
+                    if (DBG) Log.d(TAG, "Bluetooth is complete turn off");
+                    if (canUnbindBluetoothService()) {
+                        if (DBG) Log.d(TAG, "Good to unbind!");
                         sendBluetoothServiceDownCallback();
                         unbindAndFinish();
+                        sendBleStateChanged(prevState, newState);
+                        // Don't broadcast as it has already been broadcast before
+                        isStandardBroadcast = false;
                     }
+
+                } else if (!intermediate_off) {
+                    // connect to GattService
+                    if (DBG) Log.d(TAG, "Bluetooth is in LE only mode");
+                    if (mBluetoothGatt != null) {
+                        if (DBG) Log.d(TAG, "Calling BluetoothGattServiceUp");
+                        onBluetoothGattServiceUp();
+                    } else {
+                        if (DBG) Log.d(TAG, "Binding Bluetooth GATT service");
+                        if (mContext.getPackageManager().hasSystemFeature(
+                                                        PackageManager.FEATURE_BLUETOOTH_LE)) {
+                            Intent i = new Intent(IBluetoothGatt.class.getName());
+                            doBind(i, mConnection, Context.BIND_AUTO_CREATE | Context.BIND_IMPORTANT, UserHandle.CURRENT);
+                        }
+                    }
+                    sendBleStateChanged(prevState, newState);
+                    //Don't broadcase this as std intent
+                    isStandardBroadcast = false;
+
+                } else if (intermediate_off){
+                    if (DBG) Log.d(TAG, "Intermediate off, back to LE only mode");
+                    // For LE only mode, broadcast as is
+                    sendBleStateChanged(prevState, newState);
+                    sendBluetoothStateCallback(false); // BT is OFF for general users
+                    // Broadcast as STATE_OFF
+                    newState = BluetoothAdapter.STATE_OFF;
+                    sendBrEdrDownCallback();
                 }
+            } else if (newState == BluetoothAdapter.STATE_ON) {
+                boolean isUp = (newState==BluetoothAdapter.STATE_ON);
+                sendBluetoothStateCallback(isUp);
+                sendBleStateChanged(prevState, newState);
+
+            } else if (newState == BluetoothAdapter.STATE_BLE_TURNING_ON
+                || newState == BluetoothAdapter.STATE_BLE_TURNING_OFF ) {
+                sendBleStateChanged(prevState, newState);
+                isStandardBroadcast = false;
+
+            } else if (newState == BluetoothAdapter.STATE_TURNING_ON
+                || newState == BluetoothAdapter.STATE_TURNING_OFF) {
+                sendBleStateChanged(prevState, newState);
             }
 
-            //Send broadcast message to everyone else
-            Intent intent = new Intent(BluetoothAdapter.ACTION_STATE_CHANGED);
-            intent.putExtra(BluetoothAdapter.EXTRA_PREVIOUS_STATE, prevState);
-            intent.putExtra(BluetoothAdapter.EXTRA_STATE, newState);
-            intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
-            if (DBG) Log.d(TAG,"Bluetooth State Change Intent: " + prevState + " -> " + newState);
-            mContext.sendBroadcastAsUser(intent, UserHandle.ALL,
-                    BLUETOOTH_PERM);
+            if (isStandardBroadcast) {
+                if (prevState == BluetoothAdapter.STATE_BLE_ON) {
+                    // Show prevState of BLE_ON as OFF to standard users
+                    prevState = BluetoothAdapter.STATE_OFF;
+                }
+                Intent intent = new Intent(BluetoothAdapter.ACTION_STATE_CHANGED);
+                intent.putExtra(BluetoothAdapter.EXTRA_PREVIOUS_STATE, prevState);
+                intent.putExtra(BluetoothAdapter.EXTRA_STATE, newState);
+                intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
+                mContext.sendBroadcastAsUser(intent, UserHandle.ALL, BLUETOOTH_PERM);
+            }
         }
     }
 
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index b5796c9..b785d3d 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -19,6 +19,7 @@
 import static android.Manifest.permission.RECEIVE_DATA_ACTIVITY_CHANGE;
 import static android.net.ConnectivityManager.CONNECTIVITY_ACTION;
 import static android.net.ConnectivityManager.CONNECTIVITY_ACTION_IMMEDIATE;
+import static android.net.ConnectivityManager.NETID_UNSET;
 import static android.net.ConnectivityManager.TYPE_NONE;
 import static android.net.ConnectivityManager.TYPE_VPN;
 import static android.net.ConnectivityManager.getNetworkTypeName;
@@ -89,6 +90,7 @@
 import android.text.TextUtils;
 import android.util.Slog;
 import android.util.SparseArray;
+import android.util.SparseBooleanArray;
 import android.util.SparseIntArray;
 import android.util.Xml;
 
@@ -709,16 +711,15 @@
         return mNextNetworkRequestId++;
     }
 
-    private void assignNextNetId(NetworkAgentInfo nai) {
+    private int reserveNetId() {
         synchronized (mNetworkForNetId) {
             for (int i = MIN_NET_ID; i <= MAX_NET_ID; i++) {
                 int netId = mNextNetId;
                 if (++mNextNetId > MAX_NET_ID) mNextNetId = MIN_NET_ID;
                 // Make sure NetID unused.  http://b/16815182
-                if (mNetworkForNetId.get(netId) == null) {
-                    nai.network = new Network(netId);
-                    mNetworkForNetId.put(netId, nai);
-                    return;
+                if (!mNetIdInUse.get(netId)) {
+                    mNetIdInUse.put(netId, true);
+                    return netId;
                 }
             }
         }
@@ -739,7 +740,9 @@
                     info = new NetworkInfo(nai.networkInfo);
                     lp = new LinkProperties(nai.linkProperties);
                     nc = new NetworkCapabilities(nai.networkCapabilities);
-                    network = new Network(nai.network);
+                    // Network objects are outwardly immutable so there is no point to duplicating.
+                    // Duplicating also precludes sharing socket factories and connection pools.
+                    network = nai.network;
                     subscriberId = (nai.networkMisc != null) ? nai.networkMisc.subscriberId : null;
                 }
                 info.setType(networkType);
@@ -807,7 +810,9 @@
                 info = new NetworkInfo(nai.networkInfo);
                 lp = new LinkProperties(nai.linkProperties);
                 nc = new NetworkCapabilities(nai.networkCapabilities);
-                network = new Network(nai.network);
+                // Network objects are outwardly immutable so there is no point to duplicating.
+                // Duplicating also precludes sharing socket factories and connection pools.
+                network = nai.network;
                 subscriberId = (nai.networkMisc != null) ? nai.networkMisc.subscriberId : null;
             }
         }
@@ -873,6 +878,28 @@
         return getFilteredNetworkInfo(state.networkInfo, state.linkProperties, uid);
     }
 
+    @Override
+    public Network getActiveNetwork() {
+        enforceAccessPermission();
+        final int uid = Binder.getCallingUid();
+        final int user = UserHandle.getUserId(uid);
+        int vpnNetId = NETID_UNSET;
+        synchronized (mVpns) {
+            final Vpn vpn = mVpns.get(user);
+            if (vpn != null && vpn.appliesToUid(uid)) vpnNetId = vpn.getNetId();
+        }
+        NetworkAgentInfo nai;
+        if (vpnNetId != NETID_UNSET) {
+            synchronized (mNetworkForNetId) {
+                nai = mNetworkForNetId.get(vpnNetId);
+            }
+            if (nai != null) return nai.network;
+        }
+        nai = getDefaultNetwork();
+        if (nai != null && isNetworkWithLinkPropertiesBlocked(nai.linkProperties, uid)) nai = null;
+        return nai != null ? nai.network : null;
+    }
+
     /**
      * Find the first Provisioning network.
      *
@@ -985,13 +1012,13 @@
     @Override
     public Network[] getAllNetworks() {
         enforceAccessPermission();
-        final ArrayList<Network> result = new ArrayList();
         synchronized (mNetworkForNetId) {
+            final Network[] result = new Network[mNetworkForNetId.size()];
             for (int i = 0; i < mNetworkForNetId.size(); i++) {
-                result.add(new Network(mNetworkForNetId.valueAt(i).network));
+                result[i] = mNetworkForNetId.valueAt(i).network;
             }
+            return result;
         }
-        return result.toArray(new Network[result.size()]);
     }
 
     private NetworkCapabilities getNetworkCapabilitiesAndValidation(NetworkAgentInfo nai) {
@@ -1960,6 +1987,7 @@
                 if (nai != null) {
                     synchronized (mNetworkForNetId) {
                         mNetworkForNetId.remove(nai.network.netId);
+                        mNetIdInUse.delete(nai.network.netId);
                     }
                     // Just in case.
                     mLegacyTypeTracker.remove(nai);
@@ -2003,6 +2031,7 @@
             mLegacyTypeTracker.remove(nai);
             synchronized (mNetworkForNetId) {
                 mNetworkForNetId.remove(nai.network.netId);
+                mNetIdInUse.delete(nai.network.netId);
             }
             // Since we've lost the network, go through all the requests that
             // it was satisfying and see if any other factory can satisfy them.
@@ -2549,25 +2578,27 @@
     public void reportInetCondition(int networkType, int percentage) {
         NetworkAgentInfo nai = mLegacyTypeTracker.getNetworkForType(networkType);
         if (nai == null) return;
-        boolean isGood = percentage > 50;
-        // Revalidate if the app report does not match our current validated state.
-        if (isGood != nai.lastValidated) {
-            // Make the message logged by reportBadNetwork below less confusing.
-            if (DBG && isGood) log("reportInetCondition: type=" + networkType + " ok, revalidate");
-            reportBadNetwork(nai.network);
-        }
+        reportNetworkConnectivity(nai.network, percentage > 50);
     }
 
-    public void reportBadNetwork(Network network) {
+    public void reportNetworkConnectivity(Network network, boolean hasConnectivity) {
         enforceAccessPermission();
         enforceInternetPermission();
 
-        if (network == null) return;
-
-        final int uid = Binder.getCallingUid();
-        NetworkAgentInfo nai = getNetworkAgentInfoForNetwork(network);
+        NetworkAgentInfo nai;
+        if (network == null) {
+            nai = getDefaultNetwork();
+        } else {
+            nai = getNetworkAgentInfoForNetwork(network);
+        }
         if (nai == null) return;
-        if (DBG) log("reportBadNetwork(" + nai.name() + ") by " + uid);
+        // Revalidate if the app report does not match our current validated state.
+        if (hasConnectivity == nai.lastValidated) return;
+        final int uid = Binder.getCallingUid();
+        if (DBG) {
+            log("reportNetworkConnectivity(" + nai.network.netId + ", " + hasConnectivity +
+                    ") by " + uid);
+        }
         synchronized (nai) {
             // Validating an uncreated network could result in a call to rematchNetworkAndRequests()
             // which isn't meant to work on uncreated networks.
@@ -3026,23 +3057,6 @@
         }
     }
 
-    public int findConnectionTypeForIface(String iface) {
-        enforceConnectivityInternalPermission();
-
-        if (TextUtils.isEmpty(iface)) return ConnectivityManager.TYPE_NONE;
-
-        synchronized(mNetworkForNetId) {
-            for (int i = 0; i < mNetworkForNetId.size(); i++) {
-                NetworkAgentInfo nai = mNetworkForNetId.valueAt(i);
-                LinkProperties lp = nai.linkProperties;
-                if (lp != null && iface.equals(lp.getInterfaceName()) && nai.networkInfo != null) {
-                    return nai.networkInfo.getType();
-                }
-            }
-        }
-        return ConnectivityManager.TYPE_NONE;
-    }
-
     @Override
     public int checkMobileProvisioning(int suggestedTimeOutMs) {
         // TODO: Remove?  Any reason to trigger a provisioning check?
@@ -3296,7 +3310,7 @@
                 loge("Starting user already has a VPN");
                 return;
             }
-            userVpn = new Vpn(mHandler.getLooper(), mContext, mNetd, this, userId);
+            userVpn = new Vpn(mHandler.getLooper(), mContext, mNetd, userId);
             mVpns.put(userId, userVpn);
         }
     }
@@ -3441,6 +3455,24 @@
         }
     }
 
+    @Override
+    public boolean requestBwUpdate(Network network) {
+        enforceAccessPermission();
+        NetworkAgentInfo nai = null;
+        if (network == null) {
+            return false;
+        }
+        synchronized (mNetworkForNetId) {
+            nai = mNetworkForNetId.get(network.netId);
+        }
+        if (nai != null) {
+            nai.asyncChannel.sendMessage(android.net.NetworkAgent.CMD_REQUEST_BANDWIDTH_UPDATE);
+            return true;
+        }
+        return false;
+    }
+
+
     private void enforceMeteredApnPolicy(NetworkCapabilities networkCapabilities) {
         // if UID is restricted, don't allow them to bring up metered APNs
         if (networkCapabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED)
@@ -3548,14 +3580,23 @@
      * and the are the highest scored network available.
      * the are keyed off the Requests requestId.
      */
+    // TODO: Yikes, this is accessed on multiple threads: add synchronization.
     private final SparseArray<NetworkAgentInfo> mNetworkForRequestId =
             new SparseArray<NetworkAgentInfo>();
 
+    // NOTE: Accessed on multiple threads, must be synchronized on itself.
+    @GuardedBy("mNetworkForNetId")
     private final SparseArray<NetworkAgentInfo> mNetworkForNetId =
             new SparseArray<NetworkAgentInfo>();
+    // NOTE: Accessed on multiple threads, synchronized with mNetworkForNetId.
+    // An entry is first added to mNetIdInUse, prior to mNetworkForNetId, so
+    // there may not be a strict 1:1 correlation between the two.
+    @GuardedBy("mNetworkForNetId")
+    private final SparseBooleanArray mNetIdInUse = new SparseBooleanArray();
 
     // NetworkAgentInfo keyed off its connecting messenger
     // TODO - eval if we can reduce the number of lists/hashmaps/sparsearrays
+    // NOTE: Only should be accessed on ConnectivityServiceThread, except dump().
     private final HashMap<Messenger, NetworkAgentInfo> mNetworkAgentInfos =
             new HashMap<Messenger, NetworkAgentInfo>();
 
@@ -3570,7 +3611,7 @@
         return nai == getDefaultNetwork();
     }
 
-    public void registerNetworkAgent(Messenger messenger, NetworkInfo networkInfo,
+    public int registerNetworkAgent(Messenger messenger, NetworkInfo networkInfo,
             LinkProperties linkProperties, NetworkCapabilities networkCapabilities,
             int currentScore, NetworkMisc networkMisc) {
         enforceConnectivityInternalPermission();
@@ -3578,20 +3619,23 @@
         // TODO: Instead of passing mDefaultRequest, provide an API to determine whether a Network
         // satisfies mDefaultRequest.
         NetworkAgentInfo nai = new NetworkAgentInfo(messenger, new AsyncChannel(),
-            new NetworkInfo(networkInfo), new LinkProperties(linkProperties),
-            new NetworkCapabilities(networkCapabilities), currentScore, mContext, mTrackerHandler,
-            new NetworkMisc(networkMisc), mDefaultRequest);
+                new Network(reserveNetId()), new NetworkInfo(networkInfo), new LinkProperties(
+                linkProperties), new NetworkCapabilities(networkCapabilities), currentScore,
+                mContext, mTrackerHandler, new NetworkMisc(networkMisc), mDefaultRequest);
         synchronized (this) {
             nai.networkMonitor.systemReady = mSystemReady;
         }
         if (DBG) log("registerNetworkAgent " + nai);
         mHandler.sendMessage(mHandler.obtainMessage(EVENT_REGISTER_NETWORK_AGENT, nai));
+        return nai.network.netId;
     }
 
     private void handleRegisterNetworkAgent(NetworkAgentInfo na) {
         if (VDBG) log("Got NetworkAgent Messenger");
         mNetworkAgentInfos.put(na.messenger, na);
-        assignNextNetId(na);
+        synchronized (mNetworkForNetId) {
+            mNetworkForNetId.put(na.network.netId, na);
+        }
         na.asyncChannel.connect(mContext, mTrackerHandler, na.messenger);
         NetworkInfo networkInfo = na.networkInfo;
         na.networkInfo = null;
@@ -4211,9 +4255,10 @@
             networkAgent.created = true;
             updateLinkProperties(networkAgent, null);
             notifyIfacesChanged();
-            notifyNetworkCallbacks(networkAgent, ConnectivityManager.CALLBACK_PRECHECK);
+
             networkAgent.networkMonitor.sendMessage(NetworkMonitor.CMD_NETWORK_CONNECTED);
             scheduleUnvalidatedPrompt(networkAgent);
+
             if (networkAgent.isVPN()) {
                 // Temporarily disable the default proxy (not global).
                 synchronized (mProxyLock) {
@@ -4226,9 +4271,13 @@
                 }
                 // TODO: support proxy per network.
             }
+
             // Consider network even though it is not yet validated.
             rematchNetworkAndRequests(networkAgent, NascentState.NOT_JUST_VALIDATED,
                     ReapUnvalidatedNetworks.REAP);
+
+            // This has to happen after matching the requests, because callbacks are just requests.
+            notifyNetworkCallbacks(networkAgent, ConnectivityManager.CALLBACK_PRECHECK);
         } else if (state == NetworkInfo.State.DISCONNECTED ||
                 state == NetworkInfo.State.SUSPENDED) {
             networkAgent.asyncChannel.disconnect();
diff --git a/services/core/java/com/android/server/LockSettingsService.java b/services/core/java/com/android/server/LockSettingsService.java
index ee73b1a..a31a1a7 100644
--- a/services/core/java/com/android/server/LockSettingsService.java
+++ b/services/core/java/com/android/server/LockSettingsService.java
@@ -134,7 +134,11 @@
 
     public void systemReady() {
         migrateOldData();
-        getGateKeeperService();
+        try {
+            getGateKeeperService();
+        } catch (RemoteException e) {
+            Slog.e(TAG, "Failure retrieving IGateKeeperService", e);
+        }
         mStorage.prefetchUser(UserHandle.USER_OWNER);
     }
 
@@ -388,7 +392,9 @@
         byte[] currentHandle = getCurrentHandle(userId);
 
         if (pattern == null) {
+            getGateKeeperService().clearSecureUserId(userId);
             mStorage.writePatternHash(null, userId);
+            maybeUpdateKeystore(null, userId);
             return;
         }
 
@@ -414,7 +420,9 @@
         byte[] currentHandle = getCurrentHandle(userId);
 
         if (password == null) {
+            getGateKeeperService().clearSecureUserId(userId);
             mStorage.writePasswordHash(null, userId);
+            maybeUpdateKeystore(null, userId);
             return;
         }
 
@@ -691,7 +699,16 @@
         return null;
     }
 
-    private synchronized IGateKeeperService getGateKeeperService() {
+    private class GateKeeperDiedRecipient implements IBinder.DeathRecipient {
+        @Override
+        public void binderDied() {
+            mGateKeeperService.asBinder().unlinkToDeath(this, 0);
+            mGateKeeperService = null;
+        }
+    }
+
+    private synchronized IGateKeeperService getGateKeeperService()
+            throws RemoteException {
         if (mGateKeeperService != null) {
             return mGateKeeperService;
         }
@@ -699,6 +716,7 @@
         final IBinder service =
             ServiceManager.getService("android.service.gatekeeper.IGateKeeperService");
         if (service != null) {
+            service.linkToDeath(new GateKeeperDiedRecipient(), 0);
             mGateKeeperService = IGateKeeperService.Stub.asInterface(service);
             return mGateKeeperService;
         }
diff --git a/services/core/java/com/android/server/MountService.java b/services/core/java/com/android/server/MountService.java
index 4c937f7..7e4df58 100644
--- a/services/core/java/com/android/server/MountService.java
+++ b/services/core/java/com/android/server/MountService.java
@@ -61,6 +61,7 @@
 import android.os.storage.StorageVolume;
 import android.os.storage.VolumeInfo;
 import android.text.TextUtils;
+import android.text.format.DateUtils;
 import android.util.ArrayMap;
 import android.util.AtomicFile;
 import android.util.DebugUtils;
@@ -533,7 +534,11 @@
                     break;
                 }
                 case H_FSTRIM: {
-                    waitForReady();
+                    if (!isReady()) {
+                        Slog.i(TAG, "fstrim requested, but no daemon connection yet; trying again");
+                        sendMessageDelayed(obtainMessage(H_FSTRIM), DateUtils.SECOND_IN_MILLIS);
+                    }
+
                     Slog.i(TAG, "Running fstrim idle maintenance");
 
                     // Remember when we kicked it off
diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java
index d153233..908ee22 100644
--- a/services/core/java/com/android/server/TelephonyRegistry.java
+++ b/services/core/java/com/android/server/TelephonyRegistry.java
@@ -181,6 +181,8 @@
 
     private PreciseCallState mPreciseCallState = new PreciseCallState();
 
+    private boolean mCarrierNetworkChangeState = false;
+
     private PreciseDataConnectionState mPreciseDataConnectionState =
                 new PreciseDataConnectionState();
 
@@ -607,6 +609,13 @@
                             remove(r.binder);
                         }
                     }
+                    if ((events & PhoneStateListener.LISTEN_CARRIER_NETWORK_CHANGE) != 0) {
+                        try {
+                            r.callback.onCarrierNetworkChange(mCarrierNetworkChangeState);
+                        } catch (RemoteException ex) {
+                            remove(r.binder);
+                        }
+                    }
                 }
             }
         } else {
@@ -790,6 +799,31 @@
         broadcastSignalStrengthChanged(signalStrength, subId);
     }
 
+    @Override
+    public void notifyCarrierNetworkChange(boolean active) {
+        if (!checkNotifyPermissionOrCarrierPrivilege("notifyCarrierNetworkChange()")) {
+            return;
+        }
+        if (VDBG) {
+            log("notifyCarrierNetworkChange: active=" + active);
+        }
+
+        synchronized (mRecords) {
+            mCarrierNetworkChangeState = active;
+            for (Record r : mRecords) {
+                if (r.matchPhoneStateListenerEvent(
+                        PhoneStateListener.LISTEN_CARRIER_NETWORK_CHANGE)) {
+                    try {
+                        r.callback.onCarrierNetworkChange(active);
+                    } catch (RemoteException ex) {
+                        mRemoveList.add(r.binder);
+                    }
+                }
+            }
+            handleRemoveListLocked();
+        }
+    }
+
     public void notifyCellInfo(List<CellInfo> cellInfo) {
          notifyCellInfoForSubscriber(SubscriptionManager.DEFAULT_SUBSCRIPTION_ID, cellInfo);
     }
@@ -1422,9 +1456,19 @@
                 android.Manifest.permission.READ_PRECISE_PHONE_STATE);
     }
 
+    private boolean checkNotifyPermissionOrCarrierPrivilege(String method) {
+        if  (checkNotifyPermission() || checkCarrierPrivilege()) {
+            return true;
+        }
+
+        String msg = "Modify Phone State or Carrier Privilege Permission Denial: " + method
+                + " from pid=" + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid();
+        if (DBG) log(msg);
+        return false;
+    }
+
     private boolean checkNotifyPermission(String method) {
-        if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
-                == PackageManager.PERMISSION_GRANTED) {
+        if (checkNotifyPermission()) {
             return true;
         }
         String msg = "Modify Phone State Permission Denial: " + method + " from pid="
@@ -1433,6 +1477,24 @@
         return false;
     }
 
+    private boolean checkNotifyPermission() {
+        return mContext.checkCallingOrSelfPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+                == PackageManager.PERMISSION_GRANTED;
+    }
+
+    private boolean checkCarrierPrivilege() {
+        TelephonyManager tm = TelephonyManager.getDefault();
+        String[] pkgs = mContext.getPackageManager().getPackagesForUid(Binder.getCallingUid());
+        for (String pkg : pkgs) {
+            if (tm.checkCarrierPrivilegesForPackage(pkg) ==
+                    TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
     private void checkListenerPermission(int events) {
         if ((events & PhoneStateListener.LISTEN_CELL_LOCATION) != 0) {
             mContext.enforceCallingOrSelfPermission(
diff --git a/services/core/java/com/android/server/Watchdog.java b/services/core/java/com/android/server/Watchdog.java
index 69e61f6..772a15c 100644
--- a/services/core/java/com/android/server/Watchdog.java
+++ b/services/core/java/com/android/server/Watchdog.java
@@ -190,6 +190,17 @@
         }
     }
 
+    /** Monitor for checking the availability of binder threads. The monitor will block until
+     * there is a binder thread available to process in coming IPCs to make sure other processes
+     * can still communicate with the service.
+     */
+    private static final class BinderThreadMonitor implements Watchdog.Monitor {
+        @Override
+        public void monitor() {
+            Binder.blockUntilThreadAvailable();
+        }
+    }
+
     public interface Monitor {
         void monitor();
     }
@@ -227,6 +238,9 @@
         // And the display thread.
         mHandlerCheckers.add(new HandlerChecker(DisplayThread.getHandler(),
                 "display thread", DEFAULT_TIMEOUT));
+
+        // Initialize monitor for Binder threads.
+        addMonitor(new BinderThreadMonitor());
     }
 
     public void init(Context context, ActivityManagerService activity) {
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 37f0e35..b658932 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -26,10 +26,14 @@
 import static com.android.internal.util.XmlUtils.writeBooleanAttribute;
 import static com.android.internal.util.XmlUtils.writeIntAttribute;
 import static com.android.internal.util.XmlUtils.writeLongAttribute;
-import static com.android.server.am.ActivityStackSupervisor.HOME_STACK_ID;
-import static com.android.server.am.ActivityManagerDebugConfig.*;
-import static com.android.server.am.TaskRecord.INVALID_TASK_ID;
 import static com.android.server.Watchdog.NATIVE_STACKS_OF_INTEREST;
+import static com.android.server.am.ActivityManagerDebugConfig.*;
+import static com.android.server.am.ActivityStackSupervisor.HOME_STACK_ID;
+import static com.android.server.am.TaskRecord.INVALID_TASK_ID;
+import static com.android.server.am.TaskRecord.LOCK_TASK_AUTH_DONT_LOCK;
+import static com.android.server.am.TaskRecord.LOCK_TASK_AUTH_LAUNCHABLE;
+import static com.android.server.am.TaskRecord.LOCK_TASK_AUTH_PINNABLE;
+import static com.android.server.am.TaskRecord.LOCK_TASK_AUTH_WHITELISTED;
 import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT;
 import static org.xmlpull.v1.XmlPullParser.START_TAG;
 
@@ -2179,15 +2183,6 @@
         }
     }
 
-    public static final class BinderThreadMonitor implements Watchdog.Monitor {
-        /** This method will block until there is a binder thread available to process
-         * in coming IPCs to make sure other processes can still communicate with the service.
-         */
-        @Override
-        public void monitor() {
-            Binder.blockUntilThreadAvailable();
-        }
-    }
     // Note: This method is invoked on the main thread but may need to attach various
     // handlers to other threads.  So take care to be explicit about the looper.
     public ActivityManagerService(Context systemContext) {
@@ -2282,7 +2277,6 @@
         };
 
         Watchdog.getInstance().addMonitor(this);
-        Watchdog.getInstance().addMonitor(new BinderThreadMonitor());
         Watchdog.getInstance().addThread(mHandler);
     }
 
@@ -3992,13 +3986,13 @@
             if (rootR == null) {
                 Slog.w(TAG, "Finishing task with all activities already finished");
             }
-            // Do not allow task to finish in Lock Task mode.
-            if (tr == mStackSupervisor.mLockTaskModeTask) {
-                if (rootR == r) {
-                    Slog.i(TAG, "Not finishing task in lock task mode");
-                    mStackSupervisor.showLockTaskToast();
-                    return false;
-                }
+            // Do not allow task to finish if last task in lockTask mode. Launchable apps can
+            // finish themselves.
+            if (tr.mLockTaskAuth != LOCK_TASK_AUTH_LAUNCHABLE && rootR == r &&
+                    mStackSupervisor.isLastLockedTask(tr)) {
+                Slog.i(TAG, "Not finishing task in lock task mode");
+                mStackSupervisor.showLockTaskToast();
+                return false;
             }
             if (mController != null) {
                 // Find the first activity that is not finishing.
@@ -4152,20 +4146,18 @@
             final long origId = Binder.clearCallingIdentity();
             try {
                 ActivityRecord r = ActivityRecord.isInStackLocked(token);
+                if (r == null) {
+                    return false;
+                }
 
-                ActivityRecord rootR = r.task.getRootActivity();
-                // Do not allow task to finish in Lock Task mode.
-                if (r.task == mStackSupervisor.mLockTaskModeTask) {
-                    if (rootR == r) {
-                        mStackSupervisor.showLockTaskToast();
-                        return false;
-                    }
+                // Do not allow the last non-launchable task to finish in Lock Task mode.
+                final TaskRecord task = r.task;
+                if (task.mLockTaskAuth != LOCK_TASK_AUTH_LAUNCHABLE &&
+                        mStackSupervisor.isLastLockedTask(task) && task.getRootActivity() == r) {
+                    mStackSupervisor.showLockTaskToast();
+                    return false;
                 }
-                boolean res = false;
-                if (r != null) {
-                    res = r.task.stack.finishActivityAffinityLocked(r);
-                }
-                return res;
+                return task.stack.finishActivityAffinityLocked(r);
             } finally {
                 Binder.restoreCallingIdentity(origId);
             }
@@ -7859,6 +7851,27 @@
         rti.lastActiveTime = tr.lastActiveTime;
         rti.affiliatedTaskId = tr.mAffiliatedTaskId;
         rti.affiliatedTaskColor = tr.mAffiliatedTaskColor;
+        rti.numActivities = 0;
+
+        ActivityRecord base = null;
+        ActivityRecord top = null;
+        ActivityRecord tmp;
+
+        for (int i = tr.mActivities.size() - 1; i >= 0; --i) {
+            tmp = tr.mActivities.get(i);
+            if (tmp.finishing) {
+                continue;
+            }
+            base = tmp;
+            if (top == null || (top.state == ActivityState.INITIALIZING)) {
+                top = base;
+            }
+            rti.numActivities++;
+        }
+
+        rti.baseActivity = (base != null) ? base.intent.getComponent() : null;
+        rti.topActivity = (top != null) ? top.intent.getComponent() : null;
+
         return rti;
     }
 
@@ -8346,9 +8359,9 @@
             final long origId = Binder.clearCallingIdentity();
             try {
                 int taskId = ActivityRecord.getTaskForActivityLocked(token, !nonRoot);
-                if (taskId >= 0) {
-                    if ((mStackSupervisor.mLockTaskModeTask != null)
-                            && (mStackSupervisor.mLockTaskModeTask.taskId == taskId)) {
+                final TaskRecord task = mRecentTasks.taskForIdLocked(taskId);
+                if (task != null) {
+                    if (mStackSupervisor.isLockedTask(task)) {
                         mStackSupervisor.showLockTaskToast();
                         return false;
                     }
@@ -8530,47 +8543,45 @@
 
     @Override
     public void updateLockTaskPackages(int userId, String[] packages) {
-        if (Binder.getCallingUid() != Process.SYSTEM_UID) {
+        final int callingUid = Binder.getCallingUid();
+        if (callingUid != 0 && callingUid != Process.SYSTEM_UID) {
             throw new SecurityException("updateLockTaskPackage called from non-system process");
         }
         synchronized (this) {
             mLockTaskPackages.put(userId, packages);
+            mStackSupervisor.onLockTaskPackagesUpdatedLocked();
         }
     }
 
-    private boolean isLockTaskAuthorizedLocked(String pkg) {
-        String[] packages = mLockTaskPackages.get(mCurrentUserId);
-        if (packages == null) {
-            return false;
-        }
-        for (int i = packages.length - 1; i >= 0; --i) {
-            if (pkg.equals(packages[i])) {
-                return true;
-            }
-        }
-        return false;
-    }
 
     void startLockTaskModeLocked(TaskRecord task) {
-        final String pkg = task.intent.getComponent().getPackageName();
+        if (task.mLockTaskAuth == LOCK_TASK_AUTH_DONT_LOCK) {
+            return;
+        }
+
         // isSystemInitiated is used to distinguish between locked and pinned mode, as pinned mode
         // is initiated by system after the pinning request was shown and locked mode is initiated
         // by an authorized app directly
-        boolean isSystemInitiated = Binder.getCallingUid() == Process.SYSTEM_UID;
+        final int callingUid = Binder.getCallingUid();
+        boolean isSystemInitiated = callingUid == Process.SYSTEM_UID;
         long ident = Binder.clearCallingIdentity();
         try {
-            if (!isSystemInitiated && !isLockTaskAuthorizedLocked(pkg)) {
-                StatusBarManagerInternal statusBarManager =
-                        LocalServices.getService(StatusBarManagerInternal.class);
-                if (statusBarManager != null) {
-                    statusBarManager.showScreenPinningRequest();
-                }
-                return;
-            }
-
             final ActivityStack stack = mStackSupervisor.getFocusedStack();
-            if (!isSystemInitiated && (stack == null || task != stack.topTask())) {
-                throw new IllegalArgumentException("Invalid task, not in foreground");
+            if (!isSystemInitiated) {
+                task.mLockTaskUid = callingUid;
+                if (task.mLockTaskAuth == LOCK_TASK_AUTH_PINNABLE) {
+                    // startLockTask() called by app and task mode is lockTaskModeDefault.
+                    StatusBarManagerInternal statusBarManager =
+                            LocalServices.getService(StatusBarManagerInternal.class);
+                    if (statusBarManager != null) {
+                        statusBarManager.showScreenPinningRequest();
+                    }
+                    return;
+                }
+
+                if (stack == null || task != stack.topTask()) {
+                    throw new IllegalArgumentException("Invalid task, not in foreground");
+                }
             }
             mStackSupervisor.setLockTaskModeLocked(task, isSystemInitiated ?
                     ActivityManager.LOCK_TASK_MODE_PINNED :
@@ -8624,23 +8635,15 @@
 
     @Override
     public void stopLockTaskMode() {
-        // Verify that the user matches the package of the intent for the TaskRecord
-        // we are locked to or systtem.  This will ensure the same caller for startLockTaskMode
-        // and stopLockTaskMode.
-        final int callingUid = Binder.getCallingUid();
-        if (callingUid != Process.SYSTEM_UID) {
-            try {
-                String pkg =
-                        mStackSupervisor.mLockTaskModeTask.intent.getComponent().getPackageName();
-                int uid = mContext.getPackageManager().getPackageUid(pkg,
-                        Binder.getCallingUserHandle().getIdentifier());
-                if (uid != callingUid) {
-                    throw new SecurityException("Invalid uid, expected " + uid);
-                }
-            } catch (NameNotFoundException e) {
-                Log.d(TAG, "stopLockTaskMode " + e);
-                return;
-            }
+        final TaskRecord lockTask = mStackSupervisor.getLockedTaskLocked();
+        if (lockTask == null) {
+            // Our work here is done.
+            return;
+        }
+        // Ensure the same caller for startLockTaskMode and stopLockTaskMode.
+        if (getLockTaskModeState() == ActivityManager.LOCK_TASK_MODE_LOCKED &&
+                Binder.getCallingUid() != lockTask.mLockTaskUid) {
+            throw new SecurityException("Invalid uid, expected " + lockTask.mLockTaskUid);
         }
         long ident = Binder.clearCallingIdentity();
         try {
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index 2362d28..33f915f 100644
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -16,7 +16,7 @@
 
 package com.android.server.am;
 
-import static android.content.pm.ActivityInfo.FLAG_SHOW_ON_LOCK_SCREEN;
+import static android.content.pm.ActivityInfo.FLAG_SHOW_FOR_ALL_USERS;
 
 import static com.android.server.am.ActivityManagerDebugConfig.*;
 
@@ -363,19 +363,9 @@
         mOverrideConfig = Configuration.EMPTY;
     }
 
-    /**
-     * Checks whether the userid is a profile of the current user.
-     */
-    private boolean isCurrentProfileLocked(int userId) {
-        if (userId == mCurrentUser) return true;
-        for (int i = 0; i < mService.mCurrentProfileIds.length; i++) {
-            if (mService.mCurrentProfileIds[i] == userId) return true;
-        }
-        return false;
-    }
-
     boolean okToShowLocked(ActivityRecord r) {
-        return isCurrentProfileLocked(r.userId) || (r.info.flags & FLAG_SHOW_ON_LOCK_SCREEN) != 0;
+        return mStackSupervisor.isCurrentProfileLocked(r.userId)
+                || (r.info.flags & FLAG_SHOW_FOR_ALL_USERS) != 0;
     }
 
     final ActivityRecord topRunningActivityLocked(ActivityRecord notTop) {
@@ -619,12 +609,13 @@
 
         for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
             final TaskRecord task = mTaskHistory.get(taskNdx);
-            final boolean notCurrentUserTask = !isCurrentProfileLocked(task.userId);
+            final boolean notCurrentUserTask =
+                    !mStackSupervisor.isCurrentProfileLocked(task.userId);
             final ArrayList<ActivityRecord> activities = task.mActivities;
 
             for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) {
                 ActivityRecord r = activities.get(activityNdx);
-                if (notCurrentUserTask && (r.info.flags & FLAG_SHOW_ON_LOCK_SCREEN) == 0) {
+                if (notCurrentUserTask && (r.info.flags & FLAG_SHOW_FOR_ALL_USERS) == 0) {
                     return null;
                 }
                 if (!r.finishing && r.intent.getComponent().equals(cls) && r.userId == userId) {
@@ -655,7 +646,7 @@
 
             // NOTE: If {@link TaskRecord#topRunningActivityLocked} return is not null then it is
             // okay to show the activity when locked.
-            if (isCurrentProfileLocked(task.userId)
+            if (mStackSupervisor.isCurrentProfileLocked(task.userId)
                     || task.topRunningActivityLocked(null) != null) {
                 if (DEBUG_TASKS) Slog.d(TAG_TASKS, "switchUserLocked: stack=" + getStackId() +
                         " moving " + task + " to top");
@@ -817,8 +808,14 @@
     final boolean startPausingLocked(boolean userLeaving, boolean uiSleeping, boolean resuming,
             boolean dontWait) {
         if (mPausingActivity != null) {
-            Slog.wtf(TAG, "Going to pause when pause is already pending for " + mPausingActivity);
-            completePauseLocked(false);
+            Slog.wtf(TAG, "Going to pause when pause is already pending for " + mPausingActivity
+                    + " state=" + mPausingActivity.state);
+            if (!mService.isSleeping()) {
+                // Avoid recursion among check for sleep and complete pause during sleeping.
+                // Because activity will be paused immediately after resume, just let pause
+                // be completed by the order of activity paused from clients.
+                completePauseLocked(false);
+            }
         }
         ActivityRecord prev = mResumedActivity;
         if (prev == null) {
@@ -2024,13 +2021,13 @@
         // Now put task at top.
         int taskNdx = mTaskHistory.size();
         final boolean notShownWhenLocked =
-                (newActivity != null && (newActivity.info.flags & FLAG_SHOW_ON_LOCK_SCREEN) == 0)
+                (newActivity != null && (newActivity.info.flags & FLAG_SHOW_FOR_ALL_USERS) == 0)
                 || (newActivity == null && task.topRunningActivityLocked(null) == null);
-        if (!isCurrentProfileLocked(task.userId) && notShownWhenLocked) {
+        if (!mStackSupervisor.isCurrentProfileLocked(task.userId) && notShownWhenLocked) {
             // Put non-current user tasks below current user tasks.
             while (--taskNdx >= 0) {
                 final TaskRecord tmpTask = mTaskHistory.get(taskNdx);
-                if (!isCurrentProfileLocked(tmpTask.userId)
+                if (!mStackSupervisor.isCurrentProfileLocked(tmpTask.userId)
                         || tmpTask.topRunningActivityLocked(null) == null) {
                     break;
                 }
@@ -2074,7 +2071,7 @@
                         r.putInHistory();
                         mWindowManager.addAppToken(task.mActivities.indexOf(r), r.appToken,
                                 r.task.taskId, mStackId, r.info.screenOrientation, r.fullscreen,
-                                (r.info.flags & ActivityInfo.FLAG_SHOW_ON_LOCK_SCREEN) != 0,
+                                (r.info.flags & ActivityInfo.FLAG_SHOW_FOR_ALL_USERS) != 0,
                                 r.userId, r.info.configChanges, task.voiceSession != null,
                                 r.mLaunchTaskBehind);
                         if (VALIDATE_TOKENS) {
@@ -2138,7 +2135,7 @@
             }
             mWindowManager.addAppToken(task.mActivities.indexOf(r),
                     r.appToken, r.task.taskId, mStackId, r.info.screenOrientation, r.fullscreen,
-                    (r.info.flags & ActivityInfo.FLAG_SHOW_ON_LOCK_SCREEN) != 0, r.userId,
+                    (r.info.flags & ActivityInfo.FLAG_SHOW_FOR_ALL_USERS) != 0, r.userId,
                     r.info.configChanges, task.voiceSession != null, r.mLaunchTaskBehind);
             boolean doShow = true;
             if (newTask) {
@@ -2190,7 +2187,7 @@
             // because there is nothing for it to animate on top of.
             mWindowManager.addAppToken(task.mActivities.indexOf(r), r.appToken,
                     r.task.taskId, mStackId, r.info.screenOrientation, r.fullscreen,
-                    (r.info.flags & ActivityInfo.FLAG_SHOW_ON_LOCK_SCREEN) != 0, r.userId,
+                    (r.info.flags & ActivityInfo.FLAG_SHOW_FOR_ALL_USERS) != 0, r.userId,
                     r.info.configChanges, task.voiceSession != null, r.mLaunchTaskBehind);
             ActivityOptions.abort(options);
             options = null;
@@ -2875,7 +2872,7 @@
             }
 
             if (endTask) {
-                mStackSupervisor.endLockTaskModeIfTaskEnding(task);
+                mStackSupervisor.removeLockedTaskLocked(task);
             }
         } else if (r.state != ActivityState.PAUSING) {
             // If the activity is PAUSING, we will complete the finish once
@@ -3674,8 +3671,7 @@
         }
 
         Slog.i(TAG, "moveTaskToBack: " + tr);
-
-        mStackSupervisor.endLockTaskModeIfTaskEnding(tr);
+        mStackSupervisor.removeLockedTaskLocked(tr);
 
         // If we have a watcher, preflight the move before committing to it.  First check
         // for *other* available tasks, but if none are available, then try again allowing the
@@ -4062,6 +4058,7 @@
             }
             ActivityRecord r = null;
             ActivityRecord top = null;
+            ActivityRecord tmp;
             int numActivities = 0;
             int numRunning = 0;
             final ArrayList<ActivityRecord> activities = task.mActivities;
@@ -4069,7 +4066,11 @@
                 continue;
             }
             for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) {
-                r = activities.get(activityNdx);
+                tmp = activities.get(activityNdx);
+                if (tmp.finishing) {
+                    continue;
+                }
+                r = tmp;
 
                 // Initialize state for next task if needed.
                 if (top == null || (top.state == ActivityState.INITIALIZING)) {
@@ -4240,7 +4241,7 @@
      */
     void removeTask(TaskRecord task, String reason, boolean notMoving) {
         if (notMoving) {
-            mStackSupervisor.endLockTaskModeIfTaskEnding(task);
+            mStackSupervisor.removeLockedTaskLocked(task);
             mWindowManager.removeTask(task.taskId);
         }
 
@@ -4345,4 +4346,10 @@
         mFullscreen = Configuration.EMPTY.equals(mOverrideConfig);
         return !mOverrideConfig.equals(oldConfig);
     }
+
+    void onLockTaskPackagesUpdatedLocked() {
+        for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
+            mTaskHistory.get(taskNdx).setLockTaskAuth();
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index c2f6bfd..1585f61 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -17,10 +17,15 @@
 package com.android.server.am;
 
 import static android.Manifest.permission.START_ANY_ACTIVITY;
+import static android.app.ActivityManager.LOCK_TASK_MODE_LOCKED;
+import static android.app.ActivityManager.LOCK_TASK_MODE_NONE;
+import static android.app.ActivityManager.LOCK_TASK_MODE_PINNED;
 import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TASK;
 import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TOP;
 import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
 import static android.content.Intent.FLAG_ACTIVITY_TASK_ON_HOME;
+import static android.content.pm.ActivityInfo.FLAG_SHOW_FOR_ALL_USERS;
+import static android.content.pm.ActivityInfo.LOCK_TASK_LAUNCH_MODE_IF_WHITELISTED;
 import static android.content.pm.PackageManager.PERMISSION_GRANTED;
 import static com.android.server.am.ActivityManagerDebugConfig.*;
 import static com.android.server.am.ActivityManagerService.FIRST_SUPERVISOR_STACK_MSG;
@@ -28,6 +33,10 @@
 import static com.android.server.am.ActivityRecord.RECENTS_ACTIVITY_TYPE;
 import static com.android.server.am.ActivityRecord.APPLICATION_ACTIVITY_TYPE;
 import static com.android.server.am.ActivityStack.ActivityState.*;
+import static com.android.server.am.TaskRecord.LOCK_TASK_AUTH_DONT_LOCK;
+import static com.android.server.am.TaskRecord.LOCK_TASK_AUTH_LAUNCHABLE;
+import static com.android.server.am.TaskRecord.LOCK_TASK_AUTH_PINNABLE;
+import static com.android.server.am.TaskRecord.LOCK_TASK_AUTH_WHITELISTED;
 
 import android.app.Activity;
 import android.app.ActivityManager;
@@ -261,17 +270,17 @@
 
     // TODO: Add listener for removal of references.
     /** Mapping from (ActivityStack/TaskStack).mStackId to their current state */
-    private SparseArray<ActivityContainer> mActivityContainers = new SparseArray<ActivityContainer>();
+    private SparseArray<ActivityContainer> mActivityContainers = new SparseArray<>();
 
     /** Mapping from displayId to display current state */
-    private final SparseArray<ActivityDisplay> mActivityDisplays =
-            new SparseArray<ActivityDisplay>();
+    private final SparseArray<ActivityDisplay> mActivityDisplays = new SparseArray<>();
 
     InputManagerInternal mInputManagerInternal;
 
-    /** If non-null then the task specified remains in front and no other tasks may be started
-     * until the task exits or #stopLockTaskMode() is called. */
-    TaskRecord mLockTaskModeTask;
+    /** The chain of tasks in lockTask mode. The current frontmost task is at the top, and tasks
+     * may be finished until there is only one entry left. If this is empty the system is not
+     * in lockTask mode. */
+    ArrayList<TaskRecord> mLockTaskModeTasks = new ArrayList<>();
     /** Store the current lock task mode. Possible values:
      * {@link ActivityManager#LOCK_TASK_MODE_NONE}, {@link ActivityManager#LOCK_TASK_MODE_LOCKED},
      * {@link ActivityManager#LOCK_TASK_MODE_PINNED}
@@ -282,8 +291,7 @@
      */
     private LockTaskNotify mLockTaskNotify;
 
-    final ArrayList<PendingActivityLaunch> mPendingActivityLaunches
-            = new ArrayList<PendingActivityLaunch>();
+    final ArrayList<PendingActivityLaunch> mPendingActivityLaunches = new ArrayList<>();
 
     /** Used to keep resumeTopActivityLocked() from being entered recursively */
     boolean inResumeTopActivity;
@@ -796,7 +804,7 @@
             ArrayList<ActivityStack> stacks = mActivityDisplays.valueAt(displayNdx).mStacks;
             for (int stackNdx = stacks.size() - 1; stackNdx >= 0; --stackNdx) {
                 final ActivityStack stack = stacks.get(stackNdx);
-                ArrayList<RunningTaskInfo> stackTaskList = new ArrayList<RunningTaskInfo>();
+                ArrayList<RunningTaskInfo> stackTaskList = new ArrayList<>();
                 runningTaskLists.add(stackTaskList);
                 stack.getTasksLocked(stackTaskList, callingUid, allowed);
             }
@@ -894,8 +902,8 @@
         intent = new Intent(intent);
 
         // Collect information about the target of the Intent.
-        ActivityInfo aInfo = resolveActivity(intent, resolvedType, startFlags,
-                profilerInfo, userId);
+        ActivityInfo aInfo =
+                resolveActivity(intent, resolvedType, startFlags, profilerInfo, userId);
 
         ActivityContainer container = (ActivityContainer)iContainer;
         synchronized (mService) {
@@ -1170,7 +1178,12 @@
         mService.updateLruProcessLocked(app, true, null);
         mService.updateOomAdjLocked();
 
-        final ActivityStack stack = r.task.stack;
+        final TaskRecord task = r.task;
+        if (task.mLockTaskAuth == LOCK_TASK_AUTH_LAUNCHABLE) {
+            setLockTaskModeLocked(task, LOCK_TASK_MODE_LOCKED, "lockTaskLaunchMode attribute");
+        }
+
+        final ActivityStack stack = task.stack;
         try {
             if (app.thread == null) {
                 throw new RemoteException();
@@ -1187,11 +1200,11 @@
             if (andResume) {
                 EventLog.writeEvent(EventLogTags.AM_RESTART_ACTIVITY,
                         r.userId, System.identityHashCode(r),
-                        r.task.taskId, r.shortComponentName);
+                        task.taskId, r.shortComponentName);
             }
             if (r.isHomeActivity() && r.isNotResolverActivity()) {
                 // Home process is the root process of the task.
-                mService.mHomeProcess = r.task.mActivities.get(0).app;
+                mService.mHomeProcess = task.mActivities.get(0).app;
             }
             mService.ensurePackageDexOpt(r.intent.getComponent().getPackageName());
             r.sleeping = false;
@@ -1233,7 +1246,7 @@
             app.thread.scheduleLaunchActivity(new Intent(r.intent), r.appToken,
                     System.identityHashCode(r), r.info, new Configuration(mService.mConfiguration),
                     new Configuration(stack.mOverrideConfig), r.compat, r.launchedFromPackage,
-                    r.task.voiceInteractor, app.repProcState, r.icicle, r.persistentState, results,
+                    task.voiceInteractor, app.repProcState, r.icicle, r.persistentState, results,
                     newIntents, !andResume, mService.isNextTransitionForward(), profilerInfo);
 
             if ((app.info.privateFlags&ApplicationInfo.PRIVATE_FLAG_CANT_SAVE_STATE) != 0) {
@@ -1369,8 +1382,9 @@
             }
         }
 
+        final int userId = aInfo != null ? UserHandle.getUserId(aInfo.applicationInfo.uid) : 0;
+
         if (err == ActivityManager.START_SUCCESS) {
-            final int userId = aInfo != null ? UserHandle.getUserId(aInfo.applicationInfo.uid) : 0;
             Slog.i(TAG, "START u" + userId + " {" + intent.toShortString(true, true, true, false)
                     + "} from uid " + callingUid
                     + " on display " + (container == null ? (mFocusedStack == null ?
@@ -1394,7 +1408,7 @@
 
         final int launchFlags = intent.getFlags();
 
-        if ((launchFlags&Intent.FLAG_ACTIVITY_FORWARD_RESULT) != 0 && sourceRecord != null) {
+        if ((launchFlags & Intent.FLAG_ACTIVITY_FORWARD_RESULT) != 0 && sourceRecord != null) {
             // Transfer the result target from the source activity to the new
             // one being started, including any failures.
             if (requestCode >= 0) {
@@ -1438,6 +1452,13 @@
             err = ActivityManager.START_CLASS_NOT_FOUND;
         }
 
+        if (err == ActivityManager.START_SUCCESS
+                && !isCurrentProfileLocked(userId)
+                && (aInfo.flags & FLAG_SHOW_FOR_ALL_USERS) == 0) {
+            // Trying to launch a background activity that doesn't show for all users.
+            err = ActivityManager.START_NOT_CURRENT_USER_ACTIVITY;
+        }
+
         if (err == ActivityManager.START_SUCCESS && sourceRecord != null
                 && sourceRecord.task.voiceSession != null) {
             // If this activity is being launched as part of a voice session, we need
@@ -1946,7 +1967,7 @@
                     if ((launchFlags&Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED) != 0) {
                         intentActivity = targetStack.resetTaskIfNeededLocked(intentActivity, r);
                     }
-                    if ((startFlags&ActivityManager.START_FLAG_ONLY_IF_NEEDED) != 0) {
+                    if ((startFlags & ActivityManager.START_FLAG_ONLY_IF_NEEDED) != 0) {
                         // We don't need to start a new activity, and
                         // the client said not to do anything if that
                         // is the case, so this is it!  And for paranoia, make
@@ -1964,8 +1985,7 @@
                         }
                         return ActivityManager.START_RETURN_INTENT_TO_CALLER;
                     }
-                    if ((launchFlags &
-                            (FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TASK))
+                    if ((launchFlags & (FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TASK))
                             == (FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TASK)) {
                         // The caller has requested to completely replace any
                         // existing task with its new activity.  Well that should
@@ -2128,10 +2148,6 @@
         // Should this be considered a new task?
         if (r.resultTo == null && inTask == null && !addingToTask
                 && (launchFlags & Intent.FLAG_ACTIVITY_NEW_TASK) != 0) {
-            if (isLockTaskModeViolation(reuseTask)) {
-                Slog.e(TAG, "Attempted Lock Task Mode violation r=" + r);
-                return ActivityManager.START_RETURN_LOCK_TASK_MODE_VIOLATION;
-            }
             newTask = true;
             targetStack = computeStackFocus(r, newTask);
             targetStack.moveToFront("startingNewTask");
@@ -2147,6 +2163,10 @@
             } else {
                 r.setTask(reuseTask, taskToAffiliate);
             }
+            if (isLockTaskModeViolation(r.task)) {
+                Slog.e(TAG, "Attempted Lock Task Mode violation r=" + r);
+                return ActivityManager.START_RETURN_LOCK_TASK_MODE_VIOLATION;
+            }
             if (!movedHome) {
                 if ((launchFlags &
                         (FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_TASK_ON_HOME))
@@ -2815,7 +2835,7 @@
             final ActivityRecord r = activities.get(activityNdx);
             mWindowManager.addAppToken(0, r.appToken, task.taskId, stack.mStackId,
                     r.info.screenOrientation, r.fullscreen,
-                    (r.info.flags & ActivityInfo.FLAG_SHOW_ON_LOCK_SCREEN) != 0,
+                    (r.info.flags & ActivityInfo.FLAG_SHOW_FOR_ALL_USERS) != 0,
                     r.userId, r.info.configChanges, task.voiceSession != null,
                     r.mLaunchTaskBehind);
         }
@@ -3220,6 +3240,15 @@
         mStartingBackgroundUsers.add(uss);
     }
 
+    /** Checks whether the userid is a profile of the current user. */
+    boolean isCurrentProfileLocked(int userId) {
+        if (userId == mCurrentUser) return true;
+        for (int i = 0; i < mService.mCurrentProfileIds.length; i++) {
+            if (mService.mCurrentProfileIds[i] == userId) return true;
+        }
+        return false;
+    }
+
     final ArrayList<ActivityRecord> processStoppingActivitiesLocked(boolean remove) {
         ArrayList<ActivityRecord> stops = null;
 
@@ -3292,6 +3321,7 @@
         pw.print(prefix); pw.println("mCurTaskId=" + mCurTaskId);
         pw.print(prefix); pw.println("mUserStackInFront=" + mUserStackInFront);
         pw.print(prefix); pw.println("mActivityContainers=" + mActivityContainers);
+        pw.print(prefix); pw.println("mLockTaskModeTasks" + mLockTaskModeTasks);
     }
 
     ArrayList<ActivityRecord> getDumpActivitiesLocked(String name) {
@@ -3592,6 +3622,32 @@
         return list;
     }
 
+    TaskRecord getLockedTaskLocked() {
+        final int top = mLockTaskModeTasks.size() - 1;
+        if (top >= 0) {
+            return mLockTaskModeTasks.get(top);
+        }
+        return null;
+    }
+
+    boolean isLockedTask(TaskRecord task) {
+        return mLockTaskModeTasks.contains(task);
+    }
+
+    boolean isLastLockedTask(TaskRecord task) {
+        return mLockTaskModeTasks.size() == 1 && mLockTaskModeTasks.contains(task);
+    }
+
+    void removeLockedTaskLocked(final TaskRecord task) {
+        if (mLockTaskModeTasks.remove(task) && mLockTaskModeTasks.isEmpty()) {
+            // Last one.
+            final Message lockTaskMsg = Message.obtain();
+            lockTaskMsg.arg1 = task.userId;
+            lockTaskMsg.what = LOCK_TASK_END_MSG;
+            mHandler.sendMessage(lockTaskMsg);
+        }
+    }
+
     void showLockTaskToast() {
         mLockTaskNotify.showToast(mLockTaskModeState);
     }
@@ -3599,42 +3655,93 @@
     void setLockTaskModeLocked(TaskRecord task, int lockTaskModeState, String reason) {
         if (task == null) {
             // Take out of lock task mode if necessary
-            if (mLockTaskModeTask != null) {
-                final Message lockTaskMsg = Message.obtain();
-                lockTaskMsg.arg1 = mLockTaskModeTask.userId;
-                lockTaskMsg.what = LOCK_TASK_END_MSG;
-                mLockTaskModeTask = null;
-                mHandler.sendMessage(lockTaskMsg);
+            final TaskRecord lockedTask = getLockedTaskLocked();
+            if (lockedTask != null) {
+                removeLockedTaskLocked(lockedTask);
+                if (!mLockTaskModeTasks.isEmpty()) {
+                    // There are locked tasks remaining, can only finish this task, not unlock it.
+                    lockedTask.performClearTaskLocked();
+                    resumeTopActivitiesLocked();
+                    return;
+                }
             }
             return;
         }
-        if (isLockTaskModeViolation(task)) {
-            Slog.e(TAG, "setLockTaskMode: Attempt to start a second Lock Task Mode task.");
+
+        // Should have already been checked, but do it again.
+        if (task.mLockTaskAuth == LOCK_TASK_AUTH_DONT_LOCK) {
             return;
         }
-        mLockTaskModeTask = task;
+        if (isLockTaskModeViolation(task)) {
+            Slog.e(TAG, "setLockTaskMode: Attempt to start an unauthorized lock task.");
+            return;
+        }
+
+        if (mLockTaskModeTasks.isEmpty()) {
+            // First locktask.
+            final Message lockTaskMsg = Message.obtain();
+            lockTaskMsg.obj = task.intent.getComponent().getPackageName();
+            lockTaskMsg.arg1 = task.userId;
+            lockTaskMsg.what = LOCK_TASK_START_MSG;
+            lockTaskMsg.arg2 = lockTaskModeState;
+            mHandler.sendMessage(lockTaskMsg);
+        }
+        // Add it or move it to the top.
+        mLockTaskModeTasks.remove(task);
+        mLockTaskModeTasks.add(task);
+
+        if (task.mLockTaskUid == -1) {
+            task.mLockTaskUid = task.mCallingUid;
+        }
         findTaskToMoveToFrontLocked(task, 0, null, reason);
         resumeTopActivitiesLocked();
-
-        final Message lockTaskMsg = Message.obtain();
-        lockTaskMsg.obj = mLockTaskModeTask.intent.getComponent().getPackageName();
-        lockTaskMsg.arg1 = mLockTaskModeTask.userId;
-        lockTaskMsg.what = LOCK_TASK_START_MSG;
-        lockTaskMsg.arg2 = lockTaskModeState;
-        mHandler.sendMessage(lockTaskMsg);
     }
 
     boolean isLockTaskModeViolation(TaskRecord task) {
-        return mLockTaskModeTask != null && mLockTaskModeTask != task;
+        if (getLockedTaskLocked() == task) {
+            return false;
+        }
+        final int lockTaskAuth = task.mLockTaskAuth;
+        switch (lockTaskAuth) {
+            case LOCK_TASK_AUTH_DONT_LOCK:
+                return !mLockTaskModeTasks.isEmpty();
+            case LOCK_TASK_AUTH_LAUNCHABLE:
+            case LOCK_TASK_AUTH_WHITELISTED:
+                return false;
+            case LOCK_TASK_AUTH_PINNABLE:
+                // Pinnable tasks can't be launched on top of locktask tasks.
+                return !mLockTaskModeTasks.isEmpty();
+            default:
+                Slog.w(TAG, "isLockTaskModeViolation: invalid lockTaskAuth value=" + lockTaskAuth);
+                return true;
+        }
     }
 
-    void endLockTaskModeIfTaskEnding(TaskRecord task) {
-        if (mLockTaskModeTask != null && mLockTaskModeTask == task) {
-            final Message lockTaskMsg = Message.obtain();
-            lockTaskMsg.arg1 = mLockTaskModeTask.userId;
-            lockTaskMsg.what = LOCK_TASK_END_MSG;
-            mLockTaskModeTask = null;
-            mHandler.sendMessage(lockTaskMsg);
+    void onLockTaskPackagesUpdatedLocked() {
+        boolean didSomething = false;
+        for (int taskNdx = mLockTaskModeTasks.size() - 1; taskNdx >= 0; --taskNdx) {
+            final TaskRecord lockedTask = mLockTaskModeTasks.get(taskNdx);
+            if (lockedTask.mLockTaskMode != LOCK_TASK_LAUNCH_MODE_IF_WHITELISTED) {
+                continue;
+            }
+            final boolean wasLaunchable = lockedTask.mLockTaskAuth == LOCK_TASK_AUTH_LAUNCHABLE;
+            lockedTask.setLockTaskAuth();
+            if (wasLaunchable && lockedTask.mLockTaskAuth != LOCK_TASK_AUTH_LAUNCHABLE) {
+                // Lost whitelisting authorization. End it now.
+                removeLockedTaskLocked(lockedTask);
+                lockedTask.performClearTaskLocked();
+                didSomething = true;
+            }
+        }
+        for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
+            ArrayList<ActivityStack> stacks = mActivityDisplays.valueAt(displayNdx).mStacks;
+            for (int stackNdx = stacks.size() - 1; stackNdx >= 0; --stackNdx) {
+                final ActivityStack stack = stacks.get(stackNdx);
+                stack.onLockTaskPackagesUpdatedLocked();
+            }
+        }
+        if (didSomething) {
+            resumeTopActivitiesLocked();
         }
     }
 
@@ -3734,11 +3841,10 @@
                         mLockTaskModeState = msg.arg2;
                         if (getStatusBarService() != null) {
                             int flags = 0;
-                            if (mLockTaskModeState == ActivityManager.LOCK_TASK_MODE_LOCKED) {
+                            if (mLockTaskModeState == LOCK_TASK_MODE_LOCKED) {
                                 flags = StatusBarManager.DISABLE_MASK
                                         & (~StatusBarManager.DISABLE_BACK);
-                            } else if (mLockTaskModeState ==
-                                    ActivityManager.LOCK_TASK_MODE_PINNED) {
+                            } else if (mLockTaskModeState == LOCK_TASK_MODE_PINNED) {
                                 flags = StatusBarManager.DISABLE_MASK
                                         & (~StatusBarManager.DISABLE_BACK)
                                         & (~StatusBarManager.DISABLE_HOME)
@@ -3776,8 +3882,7 @@
                             boolean shouldLockKeyguard = Settings.Secure.getInt(
                                     mService.mContext.getContentResolver(),
                                     Settings.Secure.LOCK_TO_APP_EXIT_LOCKED) != 0;
-                            if (mLockTaskModeState == ActivityManager.LOCK_TASK_MODE_PINNED &&
-                                    shouldLockKeyguard) {
+                            if (mLockTaskModeState == LOCK_TASK_MODE_PINNED && shouldLockKeyguard) {
                                 mWindowManager.lockNow(null);
                                 mWindowManager.dismissKeyguard();
                                 new LockPatternUtils(mService.mContext)
@@ -3789,7 +3894,7 @@
                     } catch (RemoteException ex) {
                         throw new RuntimeException(ex);
                     } finally {
-                        mLockTaskModeState = ActivityManager.LOCK_TASK_MODE_NONE;
+                        mLockTaskModeState = LOCK_TASK_MODE_NONE;
                     }
                 } break;
                 case CONTAINER_CALLBACK_TASK_LIST_EMPTY: {
diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java
index bfc4fc7..905adc0 100644
--- a/services/core/java/com/android/server/am/BatteryStatsService.java
+++ b/services/core/java/com/android/server/am/BatteryStatsService.java
@@ -18,8 +18,6 @@
 
 import android.bluetooth.BluetoothActivityEnergyInfo;
 import android.bluetooth.BluetoothAdapter;
-import android.bluetooth.BluetoothHeadset;
-import android.bluetooth.BluetoothProfile;
 import android.content.Context;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
@@ -40,7 +38,6 @@
 import android.os.SystemClock;
 import android.os.UserHandle;
 import android.os.WorkSource;
-import android.telephony.DataConnectionRealTimeInfo;
 import android.telephony.SignalStrength;
 import android.telephony.TelephonyManager;
 import android.util.Slog;
@@ -71,8 +68,6 @@
     final BatteryStatsImpl mStats;
     final BatteryStatsHandler mHandler;
     Context mContext;
-    private boolean mBluetoothPendingStats;
-    private BluetoothHeadset mBluetoothHeadset;
     PowerManagerInternal mPowerManagerInternal;
 
     class BatteryStatsHandler extends Handler implements BatteryStatsImpl.ExternalStatsSync {
@@ -87,11 +82,11 @@
         public void handleMessage(Message msg) {
             switch (msg.what) {
                 case MSG_SYNC_EXTERNAL_STATS:
-                    updateExternalStats();
+                    updateExternalStats((String)msg.obj);
                     break;
 
                 case MSG_WRITE_TO_DISK:
-                    updateExternalStats();
+                    updateExternalStats("write");
                     synchronized (mStats) {
                         mStats.writeAsyncLocked();
                     }
@@ -100,9 +95,10 @@
         }
 
         @Override
-        public void scheduleSync() {
+        public void scheduleSync(String reason) {
             if (!hasMessages(MSG_SYNC_EXTERNAL_STATS)) {
-                sendEmptyMessage(MSG_SYNC_EXTERNAL_STATS);
+                Message msg = Message.obtain(this, MSG_SYNC_EXTERNAL_STATS, reason);
+                sendMessage(msg);
             }
         }
     }
@@ -140,7 +136,7 @@
     public void shutdown() {
         Slog.w("BatteryStats", "Writing battery stats before shutdown...");
 
-        updateExternalStats();
+        updateExternalStats("shutdown");
         synchronized (mStats) {
             mStats.shutdownLocked();
         }
@@ -231,7 +227,7 @@
         //Slog.i("foo", "SENDING BATTERY INFO:");
         //mStats.dumpLocked(new LogPrinter(Log.INFO, "foo", Log.LOG_ID_SYSTEM));
         Parcel out = Parcel.obtain();
-        updateExternalStats();
+        updateExternalStats("get-stats");
         synchronized (mStats) {
             mStats.writeToParcel(out, 0);
         }
@@ -246,7 +242,7 @@
         //Slog.i("foo", "SENDING BATTERY INFO:");
         //mStats.dumpLocked(new LogPrinter(Log.INFO, "foo", Log.LOG_ID_SYSTEM));
         Parcel out = Parcel.obtain();
-        updateExternalStats();
+        updateExternalStats("get-stats");
         synchronized (mStats) {
             mStats.writeToParcel(out, 0);
         }
@@ -569,7 +565,10 @@
 
         // There was a change in WiFi power state.
         // Collect data now for the past activity.
-        mHandler.scheduleSync();
+        mHandler.scheduleSync("wifi-data");
+        synchronized (mStats) {
+            mStats.noteWifiRadioPowerState(powerState, tsNanos);
+        }
     }
 
     public void noteWifiRunning(WorkSource ws) {
@@ -614,56 +613,6 @@
         }
     }
 
-    public void noteBluetoothOn() {
-        enforceCallingPermission();
-        BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
-        if (adapter != null) {
-            adapter.getProfileProxy(mContext, mBluetoothProfileServiceListener,
-                                    BluetoothProfile.HEADSET);
-        }
-        synchronized (mStats) {
-            if (mBluetoothHeadset != null) {
-                mStats.noteBluetoothOnLocked();
-                mStats.setBtHeadset(mBluetoothHeadset);
-            } else {
-                mBluetoothPendingStats = true;
-            }
-        }
-    }
-
-    private BluetoothProfile.ServiceListener mBluetoothProfileServiceListener =
-        new BluetoothProfile.ServiceListener() {
-        public void onServiceConnected(int profile, BluetoothProfile proxy) {
-            mBluetoothHeadset = (BluetoothHeadset) proxy;
-            synchronized (mStats) {
-                if (mBluetoothPendingStats) {
-                    mStats.noteBluetoothOnLocked();
-                    mStats.setBtHeadset(mBluetoothHeadset);
-                    mBluetoothPendingStats = false;
-                }
-            }
-        }
-
-        public void onServiceDisconnected(int profile) {
-            mBluetoothHeadset = null;
-        }
-    };
-
-    public void noteBluetoothOff() {
-        enforceCallingPermission();
-        synchronized (mStats) {
-            mBluetoothPendingStats = false;
-            mStats.noteBluetoothOffLocked();
-        }
-    }
-    
-    public void noteBluetoothState(int bluetoothState) {
-        enforceCallingPermission();
-        synchronized (mStats) {
-            mStats.noteBluetoothStateLocked(bluetoothState);
-        }
-    }
-
     public void noteFullWifiLockAcquired(int uid) {
         enforceCallingPermission();
         synchronized (mStats) {
@@ -820,7 +769,7 @@
 
         // Sync external stats first as the battery has changed states. If we don't sync
         // immediately here, we may not collect the relevant data later.
-        updateExternalStats();
+        updateExternalStats("battery-state");
         synchronized (mStats) {
             mStats.setBatteryStateLocked(status, health, plugType, level, temp, volt);
         }
@@ -974,9 +923,9 @@
                         pw.println("Battery stats reset.");
                         noOutput = true;
                     }
-                    updateExternalStats();
+                    updateExternalStats("dump");
                 } else if ("--write".equals(arg)) {
-                    updateExternalStats();
+                    updateExternalStats("dump");
                     synchronized (mStats) {
                         mStats.writeSyncLocked();
                         pw.println("Battery stats written.");
@@ -1047,7 +996,7 @@
         }
 
         // Fetch data from external sources and update the BatteryStatsImpl object with them.
-        updateExternalStats();
+        updateExternalStats("dump");
 
         if (useCheckinFormat) {
             List<ApplicationInfo> apps = mContext.getPackageManager().getInstalledApplications(0);
@@ -1178,7 +1127,7 @@
      * We first grab a lock specific to this method, then once all the data has been collected,
      * we grab the mStats lock and update the data.
      */
-    void updateExternalStats() {
+    void updateExternalStats(String reason) {
         synchronized (mExternalStatsLock) {
             if (mContext == null) {
                 // We haven't started yet (which means the BatteryStatsImpl object has
@@ -1189,6 +1138,12 @@
             final WifiActivityEnergyInfo wifiEnergyInfo = pullWifiEnergyInfoLocked();
             final BluetoothActivityEnergyInfo bluetoothEnergyInfo = pullBluetoothEnergyInfoLocked();
             synchronized (mStats) {
+                if (mStats.mRecordAllHistory) {
+                    final long elapsedRealtime = SystemClock.elapsedRealtime();
+                    final long uptime = SystemClock.uptimeMillis();
+                    mStats.addHistoryEventLocked(elapsedRealtime, uptime,
+                            BatteryStats.HistoryItem.EVENT_COLLECT_EXTERNAL_STATS, reason, 0);
+                }
                 mStats.updateKernelWakelocksLocked();
                 mStats.updateMobileRadioStateLocked(SystemClock.elapsedRealtime());
                 mStats.updateWifiStateLocked(wifiEnergyInfo);
diff --git a/services/core/java/com/android/server/am/TaskRecord.java b/services/core/java/com/android/server/am/TaskRecord.java
index 82e6d47..790a78d 100644
--- a/services/core/java/com/android/server/am/TaskRecord.java
+++ b/services/core/java/com/android/server/am/TaskRecord.java
@@ -18,6 +18,10 @@
 
 import static android.content.Intent.FLAG_ACTIVITY_NEW_DOCUMENT;
 import static android.content.Intent.FLAG_ACTIVITY_RETAIN_IN_RECENTS;
+import static android.content.pm.ActivityInfo.LOCK_TASK_LAUNCH_MODE_ALWAYS;
+import static android.content.pm.ActivityInfo.LOCK_TASK_LAUNCH_MODE_DEFAULT;
+import static android.content.pm.ActivityInfo.LOCK_TASK_LAUNCH_MODE_IF_WHITELISTED;
+import static android.content.pm.ActivityInfo.LOCK_TASK_LAUNCH_MODE_NEVER;
 import static com.android.server.am.ActivityManagerDebugConfig.*;
 import static com.android.server.am.ActivityRecord.HOME_ACTIVITY_TYPE;
 import static com.android.server.am.ActivityRecord.APPLICATION_ACTIVITY_TYPE;
@@ -121,6 +125,20 @@
 
     boolean mResizeable;    // Activities in the task resizeable. Based on the resizable setting of
                             // the root activity.
+    int mLockTaskMode;      // Which tasklock mode to launch this task in. One of
+                            // ActivityManager.LOCK_TASK_LAUNCH_MODE_*
+    /** Can't be put in lockTask mode. */
+    final static int LOCK_TASK_AUTH_DONT_LOCK = 0;
+    /** Can enter lockTask with user approval if not already in lockTask. */
+    final static int LOCK_TASK_AUTH_PINNABLE = 1;
+    /** Starts in LOCK_TASK_MODE_LOCKED automatically. Can start over existing lockTask task. */
+    final static int LOCK_TASK_AUTH_LAUNCHABLE = 2;
+    /** Enters LOCK_TASK_MODE_LOCKED via startLockTask(), enters LOCK_TASK_MODE_PINNED from
+     * Overview. Can start over existing lockTask task. */
+    final static int LOCK_TASK_AUTH_WHITELISTED = 3;
+    int mLockTaskAuth = LOCK_TASK_AUTH_PINNABLE;
+
+    int mLockTaskUid = -1;  // The uid of the application that called startLockTask().
 
     // This represents the last resolved activity values for this task
     // NOTE: This value needs to be persisted with each task
@@ -186,6 +204,8 @@
         voiceInteractor = _voiceInteractor;
         isAvailable = true;
         mActivities = new ArrayList<>();
+        mCallingUid = info.applicationInfo.uid;
+        mCallingPackage = info.packageName;
         setIntent(_intent, info);
     }
 
@@ -201,12 +221,12 @@
         voiceInteractor = null;
         isAvailable = true;
         mActivities = new ArrayList<>();
+        mCallingUid = info.applicationInfo.uid;
+        mCallingPackage = info.packageName;
         setIntent(_intent, info);
 
         taskType = ActivityRecord.APPLICATION_ACTIVITY_TYPE;
         isPersistable = true;
-        mCallingUid = info.applicationInfo.uid;
-        mCallingPackage = info.packageName;
         // Clamp to [1, max].
         maxRecents = Math.min(Math.max(info.maxRecents, 1),
                 ActivityManager.getMaxAppRecentsLimitStatic());
@@ -215,8 +235,6 @@
         mTaskToReturnTo = HOME_ACTIVITY_TYPE;
         userId = UserHandle.getUserId(info.applicationInfo.uid);
         lastTaskDescription = _taskDescription;
-        mCallingUid = info.applicationInfo.uid;
-        mCallingPackage = info.packageName;
     }
 
     private TaskRecord(ActivityManagerService service, int _taskId, Intent _intent,
@@ -278,9 +296,9 @@
 
     /** Sets the original intent, and the calling uid and package. */
     void setIntent(ActivityRecord r) {
-        setIntent(r.intent, r.info);
         mCallingUid = r.launchedFromUid;
         mCallingPackage = r.launchedFromPackage;
+        setIntent(r.intent, r.info);
     }
 
     /** Sets the original intent, _without_ updating the calling uid or package. */
@@ -362,6 +380,8 @@
             autoRemoveRecents = false;
         }
         mResizeable = info.resizeable;
+        mLockTaskMode = info.lockTaskLaunchMode;
+        setLockTaskAuth();
     }
 
     void setTaskToReturnTo(int taskToReturnTo) {
@@ -716,6 +736,53 @@
         performClearTaskAtIndexLocked(0);
     }
 
+    private boolean isPrivileged() {
+        final ProcessRecord proc = mService.mProcessNames.get(mCallingPackage, mCallingUid);
+        if (proc != null) {
+                return (proc.info.privateFlags & ApplicationInfo.PRIVATE_FLAG_PRIVILEGED) != 0;
+        }
+        return false;
+    }
+
+    void setLockTaskAuth() {
+        switch (mLockTaskMode) {
+            case LOCK_TASK_LAUNCH_MODE_DEFAULT:
+                mLockTaskAuth = isLockTaskWhitelistedLocked() ?
+                    LOCK_TASK_AUTH_WHITELISTED : LOCK_TASK_AUTH_PINNABLE;
+                break;
+
+            case LOCK_TASK_LAUNCH_MODE_NEVER:
+                mLockTaskAuth = isPrivileged() ?
+                        LOCK_TASK_AUTH_DONT_LOCK : LOCK_TASK_AUTH_PINNABLE;
+                break;
+
+            case LOCK_TASK_LAUNCH_MODE_ALWAYS:
+                mLockTaskAuth = isPrivileged() ?
+                        LOCK_TASK_AUTH_LAUNCHABLE: LOCK_TASK_AUTH_PINNABLE;
+                break;
+
+            case LOCK_TASK_LAUNCH_MODE_IF_WHITELISTED:
+                mLockTaskAuth = isLockTaskWhitelistedLocked() ?
+                        LOCK_TASK_AUTH_LAUNCHABLE : LOCK_TASK_AUTH_PINNABLE;
+                break;
+        }
+    }
+
+    boolean isLockTaskWhitelistedLocked() {
+        if (mCallingPackage == null) {
+            return false;
+        }
+        String[] packages = mService.mLockTaskPackages.get(userId);
+        if (packages == null) {
+            return false;
+        }
+        for (int i = packages.length - 1; i >= 0; --i) {
+            if (mCallingPackage.equals(packages[i])) {
+                return true;
+            }
+        }
+        return false;
+    }
     boolean isHomeTask() {
         return taskType == HOME_ACTIVITY_TYPE;
     }
diff --git a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
index dac0580..8a7c902 100644
--- a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
+++ b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
@@ -40,7 +40,10 @@
  */
 public class NetworkAgentInfo {
     public NetworkInfo networkInfo;
-    public Network network;
+    // This Network object should always be used if possible, so as to encourage reuse of the
+    // enclosed socket factory and connection pool.  Avoid creating other Network objects.
+    // This Network object is always valid.
+    public final Network network;
     public LinkProperties linkProperties;
     public NetworkCapabilities networkCapabilities;
     public final NetworkMonitor networkMonitor;
@@ -86,12 +89,12 @@
     // Used by ConnectivityService to keep track of 464xlat.
     public Nat464Xlat clatd;
 
-    public NetworkAgentInfo(Messenger messenger, AsyncChannel ac, NetworkInfo info,
+    public NetworkAgentInfo(Messenger messenger, AsyncChannel ac, Network net, NetworkInfo info,
             LinkProperties lp, NetworkCapabilities nc, int score, Context context, Handler handler,
             NetworkMisc misc, NetworkRequest defaultRequest) {
         this.messenger = messenger;
         asyncChannel = ac;
-        network = null;
+        network = net;
         networkInfo = info;
         linkProperties = lp;
         networkCapabilities = nc;
diff --git a/services/core/java/com/android/server/connectivity/NetworkMonitor.java b/services/core/java/com/android/server/connectivity/NetworkMonitor.java
index d0e1665..7e20276 100644
--- a/services/core/java/com/android/server/connectivity/NetworkMonitor.java
+++ b/services/core/java/com/android/server/connectivity/NetworkMonitor.java
@@ -216,7 +216,7 @@
     // 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.
+    // When an application calls reportNetworkConnectivity, only make one attempt.
     private static final int REEVALUATE_ATTEMPTS = 1;
     private final int mReevaluateDelayMs;
     private int mReevaluateToken = 0;
diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java
index 0b430ea..3d478f9 100644
--- a/services/core/java/com/android/server/connectivity/Vpn.java
+++ b/services/core/java/com/android/server/connectivity/Vpn.java
@@ -17,8 +17,13 @@
 package com.android.server.connectivity;
 
 import static android.Manifest.permission.BIND_VPN_SERVICE;
+import static android.net.ConnectivityManager.NETID_UNSET;
 import static android.net.RouteInfo.RTN_THROW;
 import static android.net.RouteInfo.RTN_UNREACHABLE;
+import static android.os.UserHandle.PER_USER_RANGE;
+import static android.system.OsConstants.AF_INET;
+import static android.system.OsConstants.AF_INET6;
+
 import android.Manifest;
 import android.app.AppGlobals;
 import android.app.AppOpsManager;
@@ -29,6 +34,7 @@
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.content.ServiceConnection;
+import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.pm.ResolveInfo;
@@ -64,6 +70,7 @@
 import android.os.UserManager;
 import android.security.Credentials;
 import android.security.KeyStore;
+import android.text.TextUtils;
 import android.util.Log;
 
 import com.android.internal.annotations.GuardedBy;
@@ -110,7 +117,6 @@
     private LegacyVpnRunner mLegacyVpnRunner;
     private PendingIntent mStatusIntent;
     private volatile boolean mEnableTeardown = true;
-    private final IConnectivityManager mConnService;
     private final INetworkManagementService mNetd;
     private VpnConfig mConfig;
     private NetworkAgent mNetworkAgent;
@@ -126,10 +132,9 @@
     private final int mUserHandle;
 
     public Vpn(Looper looper, Context context, INetworkManagementService netService,
-            IConnectivityManager connService, int userHandle) {
+            int userHandle) {
         mContext = context;
         mNetd = netService;
-        mConnService = connService;
         mUserHandle = userHandle;
         mLooper = looper;
 
@@ -336,6 +341,10 @@
         return mNetworkInfo;
     }
 
+    public int getNetId() {
+        return mNetworkAgent != null ? mNetworkAgent.netId : NETID_UNSET;
+    }
+
     private LinkProperties makeLinkProperties() {
         boolean allowIPv4 = mConfig.allowIPv4;
         boolean allowIPv6 = mConfig.allowIPv6;
@@ -1106,11 +1115,15 @@
             // registering
             mOuterInterface = mConfig.interfaze;
 
-            try {
-                mOuterConnection.set(
-                        mConnService.findConnectionTypeForIface(mOuterInterface));
-            } catch (Exception e) {
-                mOuterConnection.set(ConnectivityManager.TYPE_NONE);
+            if (!TextUtils.isEmpty(mOuterInterface)) {
+                final ConnectivityManager cm = ConnectivityManager.from(mContext);
+                for (Network network : cm.getAllNetworks()) {
+                    final LinkProperties lp = cm.getLinkProperties(network);
+                    if (lp != null && mOuterInterface.equals(lp.getInterfaceName())) {
+                        final NetworkInfo networkInfo = cm.getNetworkInfo(network);
+                        if (networkInfo != null) mOuterConnection.set(networkInfo.getType());
+                    }
+                }
             }
 
             IntentFilter filter = new IntentFilter();
diff --git a/services/core/java/com/android/server/display/DisplayDevice.java b/services/core/java/com/android/server/display/DisplayDevice.java
index e16be71..ee36972 100644
--- a/services/core/java/com/android/server/display/DisplayDevice.java
+++ b/services/core/java/com/android/server/display/DisplayDevice.java
@@ -47,6 +47,10 @@
     // within a transaction from performTraversalInTransactionLocked.
     private Surface mCurrentSurface;
 
+    // DEBUG STATE: Last device info which was written to the log, or null if none.
+    // Do not use for any other purpose.
+    DisplayDeviceInfo mDebugLastLoggedDeviceInfo;
+
     public DisplayDevice(DisplayAdapter displayAdapter, IBinder displayToken, String uniqueId) {
         mDisplayAdapter = displayAdapter;
         mDisplayToken = displayToken;
diff --git a/services/core/java/com/android/server/display/DisplayDeviceInfo.java b/services/core/java/com/android/server/display/DisplayDeviceInfo.java
index d1e73f0..ebf6e4e 100644
--- a/services/core/java/com/android/server/display/DisplayDeviceInfo.java
+++ b/services/core/java/com/android/server/display/DisplayDeviceInfo.java
@@ -104,6 +104,16 @@
     public static final int TOUCH_EXTERNAL = 2;
 
     /**
+     * Diff result: The {@link #state} fields differ.
+     */
+    public static final int DIFF_STATE = 1 << 0;
+
+    /**
+     * Diff result: Other fields differ.
+     */
+    public static final int DIFF_OTHER = 1 << 1;
+
+    /**
      * Gets the name of the display device, which may be derived from EDID or
      * other sources. The name may be localized and displayed to the user.
      */
@@ -238,26 +248,39 @@
     }
 
     public boolean equals(DisplayDeviceInfo other) {
-        return other != null
-                && Objects.equal(name, other.name)
-                && Objects.equal(uniqueId, other.uniqueId)
-                && width == other.width
-                && height == other.height
-                && refreshRate == other.refreshRate
-                && Arrays.equals(supportedRefreshRates, other.supportedRefreshRates)
-                && densityDpi == other.densityDpi
-                && xDpi == other.xDpi
-                && yDpi == other.yDpi
-                && appVsyncOffsetNanos == other.appVsyncOffsetNanos
-                && presentationDeadlineNanos == other.presentationDeadlineNanos
-                && flags == other.flags
-                && touch == other.touch
-                && rotation == other.rotation
-                && type == other.type
-                && Objects.equal(address, other.address)
-                && state == other.state
-                && ownerUid == other.ownerUid
-                && Objects.equal(ownerPackageName, other.ownerPackageName);
+        return other != null && diff(other) == 0;
+    }
+
+    /**
+     * Computes the difference between display device infos.
+     * Assumes other is not null.
+     */
+    public int diff(DisplayDeviceInfo other) {
+        int diff = 0;
+        if (state != other.state) {
+            diff |= DIFF_STATE;
+        }
+        if (!Objects.equal(name, other.name)
+                || !Objects.equal(uniqueId, other.uniqueId)
+                || width != other.width
+                || height != other.height
+                || refreshRate != other.refreshRate
+                || !Arrays.equals(supportedRefreshRates, other.supportedRefreshRates)
+                || densityDpi != other.densityDpi
+                || xDpi != other.xDpi
+                || yDpi != other.yDpi
+                || appVsyncOffsetNanos != other.appVsyncOffsetNanos
+                || presentationDeadlineNanos != other.presentationDeadlineNanos
+                || flags != other.flags
+                || touch != other.touch
+                || rotation != other.rotation
+                || type != other.type
+                || !Objects.equal(address, other.address)
+                || ownerUid != other.ownerUid
+                || !Objects.equal(ownerPackageName, other.ownerPackageName)) {
+            diff |= DIFF_OTHER;
+        }
+        return diff;
     }
 
     @Override
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index 9ea0444..1e87433 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -668,13 +668,14 @@
     }
 
     private void handleDisplayDeviceAddedLocked(DisplayDevice device) {
+        DisplayDeviceInfo info = device.getDisplayDeviceInfoLocked();
         if (mDisplayDevices.contains(device)) {
-            Slog.w(TAG, "Attempted to add already added display device: "
-                    + device.getDisplayDeviceInfoLocked());
+            Slog.w(TAG, "Attempted to add already added display device: " + info);
             return;
         }
 
-        Slog.i(TAG, "Display device added: " + device.getDisplayDeviceInfoLocked());
+        Slog.i(TAG, "Display device added: " + info);
+        device.mDebugLastLoggedDeviceInfo = info;
 
         mDisplayDevices.add(device);
         addLogicalDisplayLocked(device);
@@ -687,13 +688,20 @@
 
     private void handleDisplayDeviceChanged(DisplayDevice device) {
         synchronized (mSyncRoot) {
+            DisplayDeviceInfo info = device.getDisplayDeviceInfoLocked();
             if (!mDisplayDevices.contains(device)) {
-                Slog.w(TAG, "Attempted to change non-existent display device: "
-                        + device.getDisplayDeviceInfoLocked());
+                Slog.w(TAG, "Attempted to change non-existent display device: " + info);
                 return;
             }
 
-            Slog.i(TAG, "Display device changed: " + device.getDisplayDeviceInfoLocked());
+            int diff = device.mDebugLastLoggedDeviceInfo.diff(info);
+            if (diff == DisplayDeviceInfo.DIFF_STATE) {
+                Slog.i(TAG, "Display device changed state: \"" + info.name
+                        + "\", " + Display.stateToString(info.state));
+            } else if (diff != 0) {
+                Slog.i(TAG, "Display device changed: " + info);
+            }
+            device.mDebugLastLoggedDeviceInfo = info;
 
             device.applyPendingDisplayDeviceInfoChangesLocked();
             if (updateLogicalDisplaysLocked()) {
@@ -708,13 +716,14 @@
         }
     }
     private void handleDisplayDeviceRemovedLocked(DisplayDevice device) {
+        DisplayDeviceInfo info = device.getDisplayDeviceInfoLocked();
         if (!mDisplayDevices.remove(device)) {
-            Slog.w(TAG, "Attempted to remove non-existent display device: "
-                    + device.getDisplayDeviceInfoLocked());
+            Slog.w(TAG, "Attempted to remove non-existent display device: " + info);
             return;
         }
 
-        Slog.i(TAG, "Display device removed: " + device.getDisplayDeviceInfoLocked());
+        Slog.i(TAG, "Display device removed: " + info);
+        device.mDebugLastLoggedDeviceInfo = info;
 
         updateLogicalDisplaysLocked();
         scheduleTraversalLocked(false);
diff --git a/services/core/java/com/android/server/display/LogicalDisplay.java b/services/core/java/com/android/server/display/LogicalDisplay.java
index 3bb7818..65dc72f 100644
--- a/services/core/java/com/android/server/display/LogicalDisplay.java
+++ b/services/core/java/com/android/server/display/LogicalDisplay.java
@@ -302,7 +302,10 @@
         // multiplying the fractions by the product of their denominators before
         // comparing them.
         int displayRectWidth, displayRectHeight;
-        if (physWidth * displayInfo.logicalHeight
+        if ((displayInfo.flags & Display.FLAG_SCALING_DISABLED) != 0) {
+            displayRectWidth = displayInfo.logicalWidth;
+            displayRectHeight = displayInfo.logicalHeight;
+        } else if (physWidth * displayInfo.logicalHeight
                 < physHeight * displayInfo.logicalWidth) {
             // Letter box.
             displayRectWidth = physWidth;
diff --git a/services/core/java/com/android/server/fingerprint/FingerprintService.java b/services/core/java/com/android/server/fingerprint/FingerprintService.java
index 90e69d7..770df82 100644
--- a/services/core/java/com/android/server/fingerprint/FingerprintService.java
+++ b/services/core/java/com/android/server/fingerprint/FingerprintService.java
@@ -143,75 +143,67 @@
                     + ", mAuthClients = " + mAuthClient + ", mEnrollClient = " + mEnrollClient);
         if (mEnrollClient != null) {
             final IBinder token = mEnrollClient.token;
-            if (doNotify(mEnrollClient, type, arg1, arg2, arg3)) {
-                stopEnrollment(token);
+            if (dispatchNotify(mEnrollClient, type, arg1, arg2, arg3)) {
+                stopEnrollment(token, false);
+                removeClient(mEnrollClient);
             }
         }
         if (mAuthClient != null) {
             final IBinder token = mAuthClient.token;
-            if (doNotify(mAuthClient, type, arg1, arg2, arg3)) {
-                stopAuthentication(token);
+            if (dispatchNotify(mAuthClient, type, arg1, arg2, arg3)) {
+                stopAuthentication(token, false);
+                removeClient(mAuthClient);
             }
         }
         if (mRemoveClient != null) {
-            if (doNotify(mRemoveClient, type, arg1, arg2, arg3)) {
+            if (dispatchNotify(mRemoveClient, type, arg1, arg2, arg3)) {
                 removeClient(mRemoveClient);
             }
         }
     }
 
-    // Returns true if the operation is done, i.e. authentication completed
-    boolean doNotify(ClientMonitor clientMonitor, int type, int arg1, int arg2, int arg3) {
+    /*
+     * Dispatch notify events to clients.
+     *
+     * @return true if the operation is done, i.e. authentication completed
+     */
+    boolean dispatchNotify(ClientMonitor clientMonitor, int type, int arg1, int arg2, int arg3) {
         ContentResolver contentResolver = mContext.getContentResolver();
         boolean operationCompleted = false;
+        int fpId;
+        int groupId;
+        int remaining;
+        int acquireInfo;
         switch (type) {
             case FINGERPRINT_ERROR:
-                {
-                    final int error = arg1;
-                    clientMonitor.sendError(error);
-                    removeClient(clientMonitor);
-                    operationCompleted = true; // any error means the operation is done
-                }
+                fpId = arg1;
+                operationCompleted = clientMonitor.sendError(fpId);
                 break;
             case FINGERPRINT_ACQUIRED:
-                clientMonitor.sendAcquired(arg1 /* acquireInfo */);
+                acquireInfo = arg1;
+                operationCompleted = clientMonitor.sendAcquired(acquireInfo);
                 break;
             case FINGERPRINT_AUTHENTICATED:
-                {
-                    final int fpId = arg1;
-                    final int groupId = arg2;
-                    clientMonitor.sendAuthenticated(fpId, groupId);
-                    if (fpId == 0) {
-                        if (clientMonitor == mAuthClient) {
-                            operationCompleted = handleFailedAttempt(clientMonitor);
-                        }
-                    } else {
-                        mLockoutReset.run(); // a valid fingerprint resets lockout
-                    }
-                }
+                fpId = arg1;
+                groupId = arg2;
+                operationCompleted = clientMonitor.sendAuthenticated(fpId, groupId);
                 break;
             case FINGERPRINT_TEMPLATE_ENROLLING:
-                {
-                    final int fpId = arg1;
-                    final int groupId = arg2;
-                    final int remaining = arg3;
-                    clientMonitor.sendEnrollResult(fpId, groupId, remaining);
-                    if (remaining == 0) {
-                        addTemplateForUser(clientMonitor, contentResolver, fpId);
-                        operationCompleted = true; // enroll completed
-                    }
+                fpId = arg1;
+                groupId = arg2;
+                remaining = arg3;
+                operationCompleted = clientMonitor.sendEnrollResult(fpId, groupId, remaining);
+                if (remaining == 0) {
+                    addTemplateForUser(clientMonitor, contentResolver, fpId);
+                    operationCompleted = true; // enroll completed
                 }
                 break;
             case FINGERPRINT_TEMPLATE_REMOVED:
-                {
-                    final int fingerId = arg1;
-                    final int groupId = arg2;
-                    removeTemplateForUser(clientMonitor, contentResolver, fingerId);
-                    if (fingerId == 0) {
-                        operationCompleted = true; // remove completed
-                    } else {
-                        clientMonitor.sendRemoved(fingerId, groupId);
-                    }
+                fpId = arg1;
+                groupId = arg2;
+                operationCompleted = clientMonitor.sendRemoved(fpId, groupId);
+                if (fpId != 0) {
+                    removeTemplateForUser(clientMonitor, contentResolver, fpId);
                 }
                 break;
         }
@@ -235,7 +227,9 @@
     }
 
     private void resetFailedAttempts() {
-        if (DEBUG) Slog.v(TAG, "Reset fingerprint lockout");
+        if (DEBUG && inLockoutMode()) {
+            Slog.v(TAG, "Reset fingerprint lockout");
+        }
         mFailedAttempts = 0;
     }
 
@@ -283,19 +277,21 @@
 
     private void stopPendingOperations() {
         if (mEnrollClient != null) {
-            stopEnrollment(mEnrollClient.token);
+            stopEnrollment(mEnrollClient.token, true);
         }
         if (mAuthClient != null) {
-            stopAuthentication(mAuthClient.token);
+            stopAuthentication(mAuthClient.token, true);
         }
         // mRemoveClient is allowed to continue
     }
 
-    void stopEnrollment(IBinder token) {
+    void stopEnrollment(IBinder token, boolean notify) {
         final ClientMonitor client = mEnrollClient;
         if (client == null || client.token != token) return;
         int result = nativeStopEnrollment();
-        client.sendError(FingerprintManager.FINGERPRINT_ERROR_CANCELED);
+        if (notify) {
+            client.sendError(FingerprintManager.FINGERPRINT_ERROR_CANCELED);
+        }
         removeClient(mEnrollClient);
         if (result != 0) {
             Slog.w(TAG, "startEnrollCancel failed, result=" + result);
@@ -321,11 +317,13 @@
         }
     }
 
-    void stopAuthentication(IBinder token) {
+    void stopAuthentication(IBinder token, boolean notify) {
         final ClientMonitor client = mAuthClient;
         if (client == null || client.token != token) return;
         int result = nativeStopAuthentication();
-        client.sendError(FingerprintManager.FINGERPRINT_ERROR_CANCELED);
+        if (notify) {
+            client.sendError(FingerprintManager.FINGERPRINT_ERROR_CANCELED);
+        }
         removeClient(mAuthClient);
         if (result != 0) {
             Slog.w(TAG, "stopAuthentication failed, result=" + result);
@@ -358,6 +356,11 @@
         return result;
     }
 
+    public boolean hasEnrolledFingerprints(int groupId) {
+        ContentResolver resolver = mContext.getContentResolver();
+        return FingerprintUtils.getFingerprintIdsForUser(resolver, groupId).length > 0;
+    }
+
     void checkPermission(String permission) {
         getContext().enforceCallingOrSelfPermission(permission,
                 "Must have " + permission + " permission.");
@@ -403,74 +406,89 @@
             }
         }
 
+        /*
+         * @return true if we're done.
+         */
         private boolean sendRemoved(int fingerId, int groupId) {
             IFingerprintServiceReceiver rx = receiver.get();
-            if (rx != null) {
-                try {
-                    rx.onRemoved(mHalDeviceId, fingerId, groupId);
-                    return true;
-                } catch (RemoteException e) {
-                    if (DEBUG) Slog.v(TAG, "Failed to invoke sendRemoved:", e);
-                }
+            if (rx == null) return true; // client not listening
+            try {
+                rx.onRemoved(mHalDeviceId, fingerId, groupId);
+                return fingerId == 0;
+            } catch (RemoteException e) {
+                Slog.w(TAG, "Failed to notify Removed:", e);
             }
-            removeClient(this);
             return false;
         }
 
+        /*
+         * @return true if we're done.
+         */
         private boolean sendEnrollResult(int fpId, int groupId, int remaining) {
             IFingerprintServiceReceiver rx = receiver.get();
-            if (rx != null) {
-                try {
-                    rx.onEnrollResult(mHalDeviceId, fpId, groupId, remaining);
-                    return true;
-                } catch (RemoteException e) {
-                    if (DEBUG) Slog.v(TAG, "Failed to invoke sendEnrollResult:", e);
-                }
+            if (rx == null) return true; // client not listening
+            try {
+                rx.onEnrollResult(mHalDeviceId, fpId, groupId, remaining);
+                return remaining == 0;
+            } catch (RemoteException e) {
+                Slog.w(TAG, "Failed to notify EnrollResult:", e);
+                return true;
             }
-            removeClient(this);
-            return false;
         }
 
+        /*
+         * @return true if we're done.
+         */
         private boolean sendAuthenticated(int fpId, int groupId) {
             IFingerprintServiceReceiver rx = receiver.get();
+            boolean result = false;
             if (rx != null) {
                 try {
                     rx.onAuthenticated(mHalDeviceId, fpId, groupId);
-                    return true;
                 } catch (RemoteException e) {
-                    if (DEBUG) Slog.v(TAG, "Failed to invoke sendProcessed:", e);
+                    Slog.w(TAG, "Failed to notify Authenticated:", e);
+                    result = true; // client failed
                 }
+            } else {
+                result = true; // client not listening
             }
-            removeClient(this);
-            return false;
+            if (fpId <= 0) {
+                result |= handleFailedAttempt(this);
+            } else {
+                result |= true; // we have a valid fingerprint
+                mLockoutReset.run();
+            }
+            return result;
         }
 
+        /*
+         * @return true if we're done.
+         */
         private boolean sendAcquired(int acquiredInfo) {
             IFingerprintServiceReceiver rx = receiver.get();
-            if (rx != null) {
-                try {
-                    rx.onAcquired(mHalDeviceId, acquiredInfo);
-                    return true;
-                } catch (RemoteException e) {
-                    if (DEBUG) Slog.v(TAG, "Failed to invoke sendAcquired:", e);
-                }
+            if (rx == null) return true; // client not listening
+            try {
+                rx.onAcquired(mHalDeviceId, acquiredInfo);
+                return false; // acquisition continues...
+            } catch (RemoteException e) {
+                Slog.w(TAG, "Failed to invoke sendAcquired:", e);
+                return true; // client failed
             }
-            removeClient(this);
-            return false;
         }
 
+        /*
+         * @return true if we're done.
+         */
         private boolean sendError(int error) {
             IFingerprintServiceReceiver rx = receiver.get();
             if (rx != null) {
                 try {
                     rx.onError(mHalDeviceId, error);
-                    return true;
                 } catch (RemoteException e) {
-                    if (DEBUG) Slog.v(TAG, "Failed to invoke sendError:", e);
+                    Slog.w(TAG, "Failed to invoke sendError:", e);
                 }
             }
-            removeClient(this);
-            return false;
+            return true; // errors always terminate progress
         }
     }
 
@@ -502,7 +520,7 @@
             mHandler.post(new Runnable() {
                 @Override
                 public void run() {
-                    stopEnrollment(token);
+                    stopEnrollment(token, true);
                 }
             });
         }
@@ -528,7 +546,7 @@
             mHandler.post(new Runnable() {
                 @Override
                 public void run() {
-                    stopAuthentication(token);
+                    stopAuthentication(token, true);
                 }
             });
         }
@@ -572,6 +590,13 @@
             checkPermission(USE_FINGERPRINT);
             return FingerprintService.this.getEnrolledFingerprints(groupId);
         }
+
+        @Override
+        // Binder call
+        public boolean hasEnrolledFingerprints(int groupId) {
+            checkPermission(USE_FINGERPRINT);
+            return FingerprintService.this.hasEnrolledFingerprints(groupId);
+        }
     }
 
     @Override
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
index 4ac2b48..94f8dee 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
@@ -913,6 +913,12 @@
         }
     }
 
+    @ServiceThreadOnly
+    boolean isConnected(int portId) {
+        assertRunOnServiceThread();
+        return mService.isConnected(portId);
+    }
+
     private void notifyArcStatusToAudioService(boolean enabled) {
         // Note that we don't set any name to ARC.
         mService.getAudioManager().setWiredDeviceConnectionState(
diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java
index 49a96d8..2cbc1b9 100644
--- a/services/core/java/com/android/server/hdmi/HdmiControlService.java
+++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java
@@ -780,6 +780,12 @@
         return false;
     }
 
+    @ServiceThreadOnly
+    boolean isConnected(int portId) {
+        assertRunOnServiceThread();
+        return mCecController.isConnected(portId);
+    }
+
     void runOnServiceThread(Runnable runnable) {
         mHandler.post(runnable);
     }
diff --git a/services/core/java/com/android/server/hdmi/HotplugDetectionAction.java b/services/core/java/com/android/server/hdmi/HotplugDetectionAction.java
index a944a27..5f2d651 100644
--- a/services/core/java/com/android/server/hdmi/HotplugDetectionAction.java
+++ b/services/core/java/com/android/server/hdmi/HotplugDetectionAction.java
@@ -156,10 +156,13 @@
         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;
+                HdmiDeviceInfo avr = tv().getAvrDeviceInfo();
+                if (avr != null && tv().isConnected(avr.getPortId())) {
+                    ++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);
diff --git a/services/core/java/com/android/server/hdmi/SetArcTransmissionStateAction.java b/services/core/java/com/android/server/hdmi/SetArcTransmissionStateAction.java
index d200d35..9b4950b 100644
--- a/services/core/java/com/android/server/hdmi/SetArcTransmissionStateAction.java
+++ b/services/core/java/com/android/server/hdmi/SetArcTransmissionStateAction.java
@@ -54,7 +54,7 @@
     boolean start() {
         // Seq #37.
         if (mEnabled) {
-            // Enable ARC status immediately after sending <Report Arc Initiated>.
+            // Enable ARC status immediately before sending <Report Arc Initiated>.
             // If AVR responds with <Feature Abort>, disable ARC status again.
             // This is different from spec that says that turns ARC status to
             // "Enabled" if <Report ARC Initiated> is acknowledged and no
@@ -80,12 +80,21 @@
         sendCommand(command, new HdmiControlService.SendMessageCallback() {
             @Override
             public void onSendCompleted(int error) {
-                if (error != Constants.SEND_RESULT_SUCCESS) {
-                    // If fails to send <Report ARC Initiated>, disable ARC and
-                    // send <Report ARC Terminated> directly.
-                    setArcStatus(false);
-                    HdmiLogger.debug("Failed to send <Report Arc Initiated>.");
-                    finish();
+                switch (error) {
+                    case Constants.SEND_RESULT_SUCCESS:
+                    case Constants.SEND_RESULT_BUSY:
+                    case Constants.SEND_RESULT_FAILURE:
+                        // The result of the command transmission, unless it is an obvious
+                        // failure indicated by the target device (or lack thereof), should
+                        // not affect the ARC status. Ignores it silently.
+                        break;
+                    case Constants.SEND_RESULT_NAK:
+                        // If <Report ARC Initiated> is negatively ack'ed, disable ARC and
+                        // send <Report ARC Terminated> directly.
+                        setArcStatus(false);
+                        HdmiLogger.debug("Failed to send <Report Arc Initiated>.");
+                        finish();
+                        break;
                 }
             }
         });
diff --git a/services/core/java/com/android/server/job/controllers/AppIdleController.java b/services/core/java/com/android/server/job/controllers/AppIdleController.java
index 23d5c05..98fb11b 100644
--- a/services/core/java/com/android/server/job/controllers/AppIdleController.java
+++ b/services/core/java/com/android/server/job/controllers/AppIdleController.java
@@ -107,7 +107,16 @@
 
     @Override
     public void dumpControllerState(PrintWriter pw) {
-        // TODO:
+        pw.println("AppIdle");
+        pw.println("Plugged In: " + mPluggedIn);
+        synchronized (mTrackedTasks) {
+            for (JobStatus task : mTrackedTasks) {
+                pw.print(task.job.getService().getPackageName());
+                pw.print(":idle=" + !task.appNotIdleConstraintSatisfied.get());
+                pw.print(", ");
+            }
+            pw.println();
+        }
     }
 
     @Override
diff --git a/services/core/java/com/android/server/location/GpsLocationProvider.java b/services/core/java/com/android/server/location/GpsLocationProvider.java
index 7c41abbc..a279e0f 100644
--- a/services/core/java/com/android/server/location/GpsLocationProvider.java
+++ b/services/core/java/com/android/server/location/GpsLocationProvider.java
@@ -197,6 +197,8 @@
     private static final int REMOVE_LISTENER = 9;
     private static final int INJECT_NTP_TIME_FINISHED = 10;
     private static final int DOWNLOAD_XTRA_DATA_FINISHED = 11;
+    private static final int SUBSCRIPTION_OR_SIM_CHANGED = 12;
+    private static final int INITIALIZE_HANDLER = 13;
 
     // Request setid
     private static final int AGPS_RIL_REQUEST_SETID_IMSI = 1;
@@ -338,8 +340,12 @@
     // True if gps should be disabled (used to support battery saver mode in settings).
     private boolean mDisableGps = false;
 
-    // properties loaded from PROPERTIES_FILE
+    /**
+     * Properties loaded from PROPERTIES_FILE.
+     * It must be accessed only inside {@link #mHandler}.
+     */
     private Properties mProperties;
+
     private String mSuplServerHost;
     private int mSuplServerPort = TCP_MIN_PORT;
     private String mC2KServerHost;
@@ -462,7 +468,7 @@
             new OnSubscriptionsChangedListener() {
         @Override
         public void onSubscriptionsChanged() {
-            subscriptionOrSimChanged(mContext);
+            sendMessage(SUBSCRIPTION_OR_SIM_CHANGED, 0, null);
         }
     };
 
@@ -627,53 +633,22 @@
         mBatteryStats = IBatteryStats.Stub.asInterface(ServiceManager.getService(
                 BatteryStats.SERVICE_NAME));
 
-        // Load GPS configuration.
+        // Construct internal handler
+        mHandler = new ProviderHandler(looper);
+
+        // Load GPS configuration and register listeners in the background:
+        // some operations, such as opening files and registering broadcast receivers, can take a
+        // relative long time, so the ctor() is kept to create objects needed by this instance,
+        // while IO initialization and registration is delegated to our internal handler
+        // this approach is just fine because events are posted to our handler anyway
         mProperties = new Properties();
-        reloadGpsProperties(mContext, mProperties);
+        sendMessage(INITIALIZE_HANDLER, 0, null);
 
         // Create a GPS net-initiated handler.
         mNIHandler = new GpsNetInitiatedHandler(context,
                                                 mNetInitiatedListener,
                                                 mSuplEsEnabled);
 
-        // TODO: When this object "finishes" we should unregister by invoking
-        // SubscriptionManager.getInstance(mContext).unregister(mOnSubscriptionsChangedListener);
-        // This is not strictly necessary because it will be unregistered if the
-        // notification fails but it is good form.
-
-        // Register for SubscriptionInfo list changes which is guaranteed
-        // to invoke onSubscriptionsChanged the first time.
-        SubscriptionManager.from(mContext)
-            .addOnSubscriptionsChangedListener(mOnSubscriptionsChangedListener);
-
-        // construct handler, listen for events
-        mHandler = new ProviderHandler(looper);
-        listenForBroadcasts();
-
-        // also listen for PASSIVE_PROVIDER updates
-        mHandler.post(new Runnable() {
-            @Override
-            public void run() {
-                LocationManager locManager =
-                        (LocationManager) mContext.getSystemService(Context.LOCATION_SERVICE);
-                final long minTime = 0;
-                final float minDistance = 0;
-                final boolean oneShot = false;
-                LocationRequest request = LocationRequest.createFromDeprecatedProvider(
-                        LocationManager.PASSIVE_PROVIDER,
-                        minTime,
-                        minDistance,
-                        oneShot);
-                // Don't keep track of this request since it's done on behalf of other clients
-                // (which are kept track of separately).
-                request.setHideFromAppOps(true);
-                locManager.requestLocationUpdates(
-                        request,
-                        new NetworkLocationListener(),
-                        mHandler.getLooper());
-            }
-        });
-
         mListenerHelper = new GpsStatusListenerHelper(mHandler) {
             @Override
             protected boolean isAvailableInPlatform() {
@@ -731,33 +706,6 @@
         };
     }
 
-    private void listenForBroadcasts() {
-        IntentFilter intentFilter = new IntentFilter();
-        intentFilter.addAction(Intents.DATA_SMS_RECEIVED_ACTION);
-        intentFilter.addDataScheme("sms");
-        intentFilter.addDataAuthority("localhost","7275");
-        mContext.registerReceiver(mBroadcastReceiver, intentFilter, null, mHandler);
-
-        intentFilter = new IntentFilter();
-        intentFilter.addAction(Intents.WAP_PUSH_RECEIVED_ACTION);
-        try {
-            intentFilter.addDataType("application/vnd.omaloc-supl-init");
-        } catch (IntentFilter.MalformedMimeTypeException e) {
-            Log.w(TAG, "Malformed SUPL init mime type");
-        }
-        mContext.registerReceiver(mBroadcastReceiver, intentFilter, null, mHandler);
-
-        intentFilter = new IntentFilter();
-        intentFilter.addAction(ALARM_WAKEUP);
-        intentFilter.addAction(ALARM_TIMEOUT);
-        intentFilter.addAction(ConnectivityManager.CONNECTIVITY_ACTION_IMMEDIATE);
-        intentFilter.addAction(PowerManager.ACTION_POWER_SAVE_MODE_CHANGED);
-        intentFilter.addAction(Intent.ACTION_SCREEN_OFF);
-        intentFilter.addAction(Intent.ACTION_SCREEN_ON);
-        intentFilter.addAction(SIM_STATE_CHANGED);
-        mContext.registerReceiver(mBroadcastReceiver, intentFilter, null, mHandler);
-    }
-
     /**
      * Returns the name of this provider.
      */
@@ -2012,13 +1960,85 @@
                 case UPDATE_LOCATION:
                     handleUpdateLocation((Location)msg.obj);
                     break;
+                case SUBSCRIPTION_OR_SIM_CHANGED:
+                    subscriptionOrSimChanged(mContext);
+                    break;
+                case INITIALIZE_HANDLER:
+                    initialize();
+                    break;
             }
             if (msg.arg2 == 1) {
                 // wakelock was taken for this message, release it
                 mWakeLock.release();
             }
         }
-    };
+
+        /**
+         * This method is bound to {@link #GpsLocationProvider(Context, ILocationManager, Looper)}.
+         * It is in charge of loading properties and registering for events that will be posted to
+         * this handler.
+         */
+        private void initialize() {
+            // load default GPS configuration
+            // (this configuration might change in the future based on SIM changes)
+            reloadGpsProperties(mContext, mProperties);
+
+            // TODO: When this object "finishes" we should unregister by invoking
+            // SubscriptionManager.getInstance(mContext).unregister(mOnSubscriptionsChangedListener);
+            // This is not strictly necessary because it will be unregistered if the
+            // notification fails but it is good form.
+
+            // Register for SubscriptionInfo list changes which is guaranteed
+            // to invoke onSubscriptionsChanged the first time.
+            SubscriptionManager.from(mContext)
+                    .addOnSubscriptionsChangedListener(mOnSubscriptionsChangedListener);
+
+            // listen for events
+            IntentFilter intentFilter = new IntentFilter();
+            intentFilter.addAction(Intents.DATA_SMS_RECEIVED_ACTION);
+            intentFilter.addDataScheme("sms");
+            intentFilter.addDataAuthority("localhost","7275");
+            mContext.registerReceiver(mBroadcastReceiver, intentFilter, null, this);
+
+            intentFilter = new IntentFilter();
+            intentFilter.addAction(Intents.WAP_PUSH_RECEIVED_ACTION);
+            try {
+                intentFilter.addDataType("application/vnd.omaloc-supl-init");
+            } catch (IntentFilter.MalformedMimeTypeException e) {
+                Log.w(TAG, "Malformed SUPL init mime type");
+            }
+            mContext.registerReceiver(mBroadcastReceiver, intentFilter, null, this);
+
+            intentFilter = new IntentFilter();
+            intentFilter.addAction(ALARM_WAKEUP);
+            intentFilter.addAction(ALARM_TIMEOUT);
+            intentFilter.addAction(ConnectivityManager.CONNECTIVITY_ACTION_IMMEDIATE);
+            intentFilter.addAction(PowerManager.ACTION_POWER_SAVE_MODE_CHANGED);
+            intentFilter.addAction(Intent.ACTION_SCREEN_OFF);
+            intentFilter.addAction(Intent.ACTION_SCREEN_ON);
+            intentFilter.addAction(SIM_STATE_CHANGED);
+            mContext.registerReceiver(mBroadcastReceiver, intentFilter, null, this);
+
+            // listen for PASSIVE_PROVIDER updates
+            LocationManager locManager =
+                    (LocationManager) mContext.getSystemService(Context.LOCATION_SERVICE);
+            long minTime = 0;
+            float minDistance = 0;
+            boolean oneShot = false;
+            LocationRequest request = LocationRequest.createFromDeprecatedProvider(
+                    LocationManager.PASSIVE_PROVIDER,
+                    minTime,
+                    minDistance,
+                    oneShot);
+            // Don't keep track of this request since it's done on behalf of other clients
+            // (which are kept track of separately).
+            request.setHideFromAppOps(true);
+            locManager.requestLocationUpdates(
+                    request,
+                    new NetworkLocationListener(),
+                    getLooper());
+        }
+    }
 
     private final class NetworkLocationListener implements LocationListener {
         @Override
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 4cf2909..997d546 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -29,9 +29,11 @@
 import android.app.AppOpsManager;
 import android.app.IActivityManager;
 import android.app.INotificationManager;
+import android.app.INotificationManagerCallback;
 import android.app.ITransientNotification;
 import android.app.Notification;
 import android.app.NotificationManager;
+import android.app.NotificationManager.Policy;
 import android.app.PendingIntent;
 import android.app.StatusBarManager;
 import android.content.BroadcastReceiver;
@@ -227,6 +229,8 @@
             new ArrayMap<String, NotificationRecord>();
     final ArrayList<ToastRecord> mToastQueue = new ArrayList<ToastRecord>();
     final ArrayMap<String, NotificationRecord> mSummaryByGroupKey = new ArrayMap<>();
+    private final ArrayMap<String, Policy.Token> mPolicyTokens = new ArrayMap<>();
+
 
     // The last key in this list owns the hardware.
     ArrayList<String> mLights = new ArrayList<>();
@@ -893,6 +897,13 @@
                     updateInterruptionFilterLocked();
                 }
             }
+
+            @Override
+            void onPolicyChanged() {
+                getContext().sendBroadcast(
+                        new Intent(NotificationManager.ACTION_NOTIFICATION_POLICY_CHANGED)
+                                .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY));
+            }
         });
         final File systemDir = new File(Environment.getDataDirectory(), "system");
         mPolicyFile = new AtomicFile(new File(systemDir, "notification_policy.xml"));
@@ -1551,6 +1562,18 @@
                     message);
         }
 
+        private void enforcePolicyToken(Policy.Token token, String method) {
+            if (!checkPolicyToken(token)) {
+                Slog.w(TAG, "Invalid notification policy token calling " + method);
+                throw new SecurityException("Invalid notification policy token");
+            }
+        }
+
+        private boolean checkPolicyToken(Policy.Token token) {
+            return mPolicyTokens.containsValue(token)
+                    || mListeners.mPolicyTokens.containsValue(token);
+        }
+
         @Override
         protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
             if (getContext().checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
@@ -1586,24 +1609,73 @@
             enforceSystemOrSystemUIOrVolume("INotificationManager.isSystemConditionProviderEnabled");
             return mConditionProviders.isSystemProviderEnabled(path);
         }
-    };
 
-    private String[] getActiveNotificationKeys(INotificationListener token) {
-        final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
-        final ArrayList<String> keys = new ArrayList<String>();
-        if (info.isEnabledForCurrentProfiles()) {
-            synchronized (mNotificationList) {
-                final int N = mNotificationList.size();
-                for (int i = 0; i < N; i++) {
-                    final StatusBarNotification sbn = mNotificationList.get(i).sbn;
-                    if (info.enabledAndUserMatches(sbn.getUserId())) {
-                        keys.add(sbn.getKey());
-                    }
-                }
+        @Override
+        public Policy.Token getPolicyTokenFromListener(INotificationListener listener) {
+            final long identity = Binder.clearCallingIdentity();
+            try {
+                return mListeners.getPolicyToken(listener);
+            } finally {
+                Binder.restoreCallingIdentity(identity);
             }
         }
-        return keys.toArray(new String[keys.size()]);
-    }
+
+        @Override
+        public void requestNotificationPolicyToken(String pkg,
+                INotificationManagerCallback callback) throws RemoteException {
+            if (callback == null) {
+                Slog.w(TAG, "requestNotificationPolicyToken: no callback specified");
+                return;
+            }
+            if (pkg == null) {
+                Slog.w(TAG, "requestNotificationPolicyToken denied: no package specified");
+                callback.onPolicyToken(null);
+                return;
+            }
+            Policy.Token token = null;
+            final long identity = Binder.clearCallingIdentity();
+            try {
+                synchronized (mNotificationList) {
+                    token = mPolicyTokens.get(pkg);
+                    if (token == null) {
+                        token = new Policy.Token(new Binder());
+                        mPolicyTokens.put(pkg, token);
+                    }
+                    if (DBG) Slog.w(TAG, "requestNotificationPolicyToken granted for " + pkg);
+                }
+            } finally {
+                Binder.restoreCallingIdentity(identity);
+            }
+            callback.onPolicyToken(token);
+        }
+
+        @Override
+        public boolean isNotificationPolicyTokenValid(String pkg, Policy.Token token) {
+            return checkPolicyToken(token);
+        }
+
+        @Override
+        public Policy getNotificationPolicy(Policy.Token token) {
+            enforcePolicyToken(token, "getNotificationPolicy");
+            final long identity = Binder.clearCallingIdentity();
+            try {
+                return mZenModeHelper.getNotificationPolicy();
+            } finally {
+                Binder.restoreCallingIdentity(identity);
+            }
+        }
+
+        @Override
+        public void setNotificationPolicy(Policy.Token token, Policy policy) {
+            enforcePolicyToken(token, "setNotificationPolicy");
+            final long identity = Binder.clearCallingIdentity();
+            try {
+                mZenModeHelper.setNotificationPolicy(policy);
+            } finally {
+                Binder.restoreCallingIdentity(identity);
+            }
+        }
+    };
 
     private String disableNotificationEffects(NotificationRecord record) {
         if (mDisableNotificationEffects) {
@@ -1718,6 +1790,10 @@
                     pw.print(listener.component);
                 }
                 pw.println(')');
+                pw.print("    mPolicyTokens.keys: ");
+                pw.println(TextUtils.join(",", mPolicyTokens.keySet()));
+                pw.print("    mListeners.mPolicyTokens.keys: ");
+                pw.println(TextUtils.join(",", mListeners.mPolicyTokens.keySet()));
             }
 
             pw.println("\n  Condition providers:");
@@ -2970,12 +3046,18 @@
     public class NotificationListeners extends ManagedServices {
 
         private final ArraySet<ManagedServiceInfo> mLightTrimListeners = new ArraySet<>();
+        private final ArrayMap<ComponentName, Policy.Token> mPolicyTokens = new ArrayMap<>();
         private boolean mNotificationGroupsDesired;
 
         public NotificationListeners() {
             super(getContext(), mHandler, mNotificationList, mUserProfiles);
         }
 
+        public Policy.Token getPolicyToken(INotificationListener listener) {
+            final ManagedServiceInfo info = checkServiceTokenLocked(listener);
+            return info == null ? null : mPolicyTokens.get(info.component);
+        }
+
         @Override
         protected Config getConfig() {
             Config c = new Config();
@@ -3000,6 +3082,7 @@
             synchronized (mNotificationList) {
                 updateNotificationGroupsDesiredLocked();
                 update = makeRankingUpdateLocked(info);
+                mPolicyTokens.put(info.component, new Policy.Token(new Binder()));
             }
             try {
                 listener.onListenerConnected(update);
@@ -3016,6 +3099,7 @@
             }
             mLightTrimListeners.remove(removed);
             updateNotificationGroupsDesiredLocked();
+            mPolicyTokens.remove(removed.component);
         }
 
         public void setOnNotificationPostedTrimLocked(ManagedServiceInfo info, int trim) {
diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java
index 40218bb..9cb8af5 100644
--- a/services/core/java/com/android/server/notification/ZenModeHelper.java
+++ b/services/core/java/com/android/server/notification/ZenModeHelper.java
@@ -21,6 +21,7 @@
 import static android.media.AudioAttributes.USAGE_NOTIFICATION_RINGTONE;
 
 import android.app.AppOpsManager;
+import android.app.NotificationManager.Policy;
 import android.content.ComponentName;
 import android.content.ContentResolver;
 import android.content.Context;
@@ -57,6 +58,7 @@
 import java.io.IOException;
 import java.io.PrintWriter;
 import java.util.ArrayList;
+import java.util.Objects;
 
 /**
  * NotificationManagerService helper for functionality related to zen mode.
@@ -230,6 +232,21 @@
         mConfig.writeXml(out);
     }
 
+    public Policy getNotificationPolicy() {
+        return getNotificationPolicy(mConfig);
+    }
+
+    private static Policy getNotificationPolicy(ZenModeConfig config) {
+        return config == null ? null : config.toNotificationPolicy();
+    }
+
+    public void setNotificationPolicy(Policy policy) {
+        if (policy == null || mConfig == null) return;
+        final ZenModeConfig newConfig = mConfig.copy();
+        newConfig.applyNotificationPolicy(policy);
+        setConfig(newConfig, "setNotificationPolicy");
+    }
+
     public ZenModeConfig getConfig() {
         return mConfig;
     }
@@ -247,8 +264,13 @@
         if (config.equals(mConfig)) return true;
         if (DEBUG) Log.d(TAG, "setConfig reason=" + reason);
         ZenLog.traceConfig(mConfig, config);
+        final boolean policyChanged = !Objects.equals(getNotificationPolicy(mConfig),
+                getNotificationPolicy(config));
         mConfig = config;
         dispatchOnConfigChanged();
+        if (policyChanged){
+            dispatchOnPolicyChanged();
+        }
         final String val = Integer.toString(mConfig.hashCode());
         Global.putString(mContext.getContentResolver(), Global.ZEN_MODE_CONFIG_ETAG, val);
         if (!evaluateZenMode(reason, setRingerMode)) {
@@ -355,6 +377,12 @@
         }
     }
 
+    private void dispatchOnPolicyChanged() {
+        for (Callback callback : mCallbacks) {
+            callback.onPolicyChanged();
+        }
+    }
+
     private void dispatchOnZenModeChanged() {
         for (Callback callback : mCallbacks) {
             callback.onZenModeChanged();
@@ -617,6 +645,7 @@
     public static class Callback {
         void onConfigChanged() {}
         void onZenModeChanged() {}
+        void onPolicyChanged() {}
     }
 
 }
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index a260b0e..c12545b 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -6280,7 +6280,8 @@
                         !VMRuntime.is64BitAbi(pkg.applicationInfo.primaryCpuAbi)) {
                     final String nativeLibPath = pkg.applicationInfo.nativeLibraryDir;
                     for (int userId : userIds) {
-                        if (mInstaller.linkNativeLibraryDirectory(pkg.packageName, nativeLibPath, userId) < 0) {
+                        if (mInstaller.linkNativeLibraryDirectory(pkg.volumeUuid, pkg.packageName,
+                                nativeLibPath, userId) < 0) {
                             throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR,
                                     "Failed linking native library dir (user=" + userId + ")");
                         }
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 936840a..fce01e5 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -183,6 +183,7 @@
     static final int APPLICATION_MEDIA_OVERLAY_SUBLAYER = -1;
     static final int APPLICATION_PANEL_SUBLAYER = 1;
     static final int APPLICATION_SUB_PANEL_SUBLAYER = 2;
+    static final int APPLICATION_ABOVE_SUB_PANEL_SUBLAYER = 3;
 
     static public final String SYSTEM_DIALOG_REASON_KEY = "reason";
     static public final String SYSTEM_DIALOG_REASON_GLOBAL_ACTIONS = "globalactions";
@@ -2015,6 +2016,8 @@
             return APPLICATION_MEDIA_OVERLAY_SUBLAYER;
         case TYPE_APPLICATION_SUB_PANEL:
             return APPLICATION_SUB_PANEL_SUBLAYER;
+        case TYPE_APPLICATION_ABOVE_SUB_PANEL:
+            return APPLICATION_ABOVE_SUB_PANEL_SUBLAYER;
         }
         Log.e(TAG, "Unknown sub-window type: " + type);
         return 0;
diff --git a/services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java b/services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java
index 75c33af..1a52933 100644
--- a/services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java
+++ b/services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java
@@ -145,6 +145,9 @@
             if (mKeyguardState.bootCompleted) {
                 mKeyguardService.onBootCompleted();
             }
+            if (mKeyguardState.occluded) {
+                mKeyguardService.setOccluded(mKeyguardState.occluded);
+            }
         }
 
         @Override
diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
index f6df757..184224b 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
@@ -79,7 +79,7 @@
 
         public void binderDied() {
             Slog.i(TAG, "binder died for pkg=" + pkg);
-            disableInternal(userId, 0, token, pkg);
+            disableForUser(0, token, pkg, userId);
             token.unlinkToDeath(this, 0);
         }
     }
@@ -194,10 +194,11 @@
 
     @Override
     public void disable(int what, IBinder token, String pkg) {
-        disableInternal(mCurrentUserId, what, token, pkg);
+        disableForUser(what, token, pkg, mCurrentUserId);
     }
 
-    private void disableInternal(int userId, int what, IBinder token, String pkg) {
+    @Override
+    public void disableForUser(int what, IBinder token, String pkg, int userId) {
         enforceStatusBar();
 
         synchronized (mLock) {
diff --git a/services/core/java/com/android/server/trust/TrustAgentWrapper.java b/services/core/java/com/android/server/trust/TrustAgentWrapper.java
index dec195d..fb7d186 100644
--- a/services/core/java/com/android/server/trust/TrustAgentWrapper.java
+++ b/services/core/java/com/android/server/trust/TrustAgentWrapper.java
@@ -116,7 +116,7 @@
                     }
                     mTrusted = true;
                     mMessage = (CharSequence) msg.obj;
-                    boolean initiatedByUser = msg.arg1 != 0;
+                    int flags = msg.arg1;
                     long durationMs = msg.getData().getLong(DATA_DURATION);
                     if (durationMs > 0) {
                         final long duration;
@@ -141,8 +141,8 @@
                     }
                     mTrustManagerService.mArchive.logGrantTrust(mUserId, mName,
                             (mMessage != null ? mMessage.toString() : null),
-                            durationMs, initiatedByUser);
-                    mTrustManagerService.updateTrust(mUserId, initiatedByUser);
+                            durationMs, flags);
+                    mTrustManagerService.updateTrust(mUserId, flags);
                     break;
                 case MSG_TRUST_TIMEOUT:
                     if (DEBUG) Slog.v(TAG, "Trust timed out : " + mName.flattenToShortString());
@@ -156,7 +156,7 @@
                     if (msg.what == MSG_REVOKE_TRUST) {
                         mTrustManagerService.mArchive.logRevokeTrust(mUserId, mName);
                     }
-                    mTrustManagerService.updateTrust(mUserId, false);
+                    mTrustManagerService.updateTrust(mUserId, 0);
                     break;
                 case MSG_RESTART_TIMEOUT:
                     destroy();
@@ -171,7 +171,7 @@
                             if (DEBUG) Log.v(TAG, "Re-enabling agent because it acknowledged "
                                     + "enabled features: " + mName);
                             mTrustDisabledByDpm = false;
-                            mTrustManagerService.updateTrust(mUserId, false);
+                            mTrustManagerService.updateTrust(mUserId, 0);
                         }
                     } else {
                         if (DEBUG) Log.w(TAG, "Ignoring MSG_SET_TRUST_AGENT_FEATURES_COMPLETED "
@@ -185,7 +185,7 @@
                         mMessage = null;
                     }
                     mTrustManagerService.mArchive.logManagingTrust(mUserId, mName, mManagingTrust);
-                    mTrustManagerService.updateTrust(mUserId, false);
+                    mTrustManagerService.updateTrust(mUserId, 0);
                     break;
             }
         }
@@ -194,12 +194,12 @@
     private ITrustAgentServiceCallback mCallback = new ITrustAgentServiceCallback.Stub() {
 
         @Override
-        public void grantTrust(CharSequence userMessage, long durationMs, boolean initiatedByUser) {
+        public void grantTrust(CharSequence userMessage, long durationMs, int flags) {
             if (DEBUG) Slog.v(TAG, "enableTrust(" + userMessage + ", durationMs = " + durationMs
-                        + ", initiatedByUser = " + initiatedByUser + ")");
+                        + ", flags = " + flags + ")");
 
             Message msg = mHandler.obtainMessage(
-                    MSG_GRANT_TRUST, initiatedByUser ? 1 : 0, 0, userMessage);
+                    MSG_GRANT_TRUST, flags, 0, userMessage);
             msg.getData().putLong(DATA_DURATION, durationMs);
             msg.sendToTarget();
         }
@@ -381,7 +381,7 @@
         }
         if (mTrustDisabledByDpm != trustDisabled) {
             mTrustDisabledByDpm = trustDisabled;
-            mTrustManagerService.updateTrust(mUserId, false);
+            mTrustManagerService.updateTrust(mUserId, 0);
         }
         return trustDisabled;
     }
diff --git a/services/core/java/com/android/server/trust/TrustArchive.java b/services/core/java/com/android/server/trust/TrustArchive.java
index 7253716..fd63d48 100644
--- a/services/core/java/com/android/server/trust/TrustArchive.java
+++ b/services/core/java/com/android/server/trust/TrustArchive.java
@@ -19,6 +19,7 @@
 import android.content.ComponentName;
 import android.os.SystemClock;
 import android.os.UserHandle;
+import android.service.trust.TrustAgentService;
 import android.util.TimeUtils;
 
 import java.io.PrintWriter;
@@ -48,20 +49,20 @@
         // grantTrust
         final String message;
         final long duration;
-        final boolean userInitiated;
+        final int flags;
 
         // managingTrust
         final boolean managingTrust;
 
         private Event(int type, int userId, ComponentName agent, String message,
-                long duration, boolean userInitiated, boolean managingTrust) {
+                long duration, int flags, boolean managingTrust) {
             this.type = type;
             this.userId = userId;
             this.agent = agent;
             this.elapsedTimestamp = SystemClock.elapsedRealtime();
             this.message = message;
             this.duration = duration;
-            this.userInitiated = userInitiated;
+            this.flags = flags;
             this.managingTrust = managingTrust;
         }
     }
@@ -69,33 +70,33 @@
     ArrayDeque<Event> mEvents = new ArrayDeque<Event>();
 
     public void logGrantTrust(int userId, ComponentName agent, String message,
-            long duration, boolean userInitiated) {
+            long duration, int flags) {
         addEvent(new Event(TYPE_GRANT_TRUST, userId, agent, message, duration,
-                userInitiated, false));
+                flags, false));
     }
 
     public void logRevokeTrust(int userId, ComponentName agent) {
-        addEvent(new Event(TYPE_REVOKE_TRUST, userId, agent, null, 0, false, false));
+        addEvent(new Event(TYPE_REVOKE_TRUST, userId, agent, null, 0, 0, false));
     }
 
     public void logTrustTimeout(int userId, ComponentName agent) {
-        addEvent(new Event(TYPE_TRUST_TIMEOUT, userId, agent, null, 0, false, false));
+        addEvent(new Event(TYPE_TRUST_TIMEOUT, userId, agent, null, 0, 0, false));
     }
 
     public void logAgentDied(int userId, ComponentName agent) {
-        addEvent(new Event(TYPE_AGENT_DIED, userId, agent, null, 0, false, false));
+        addEvent(new Event(TYPE_AGENT_DIED, userId, agent, null, 0, 0, false));
     }
 
     public void logAgentConnected(int userId, ComponentName agent) {
-        addEvent(new Event(TYPE_AGENT_CONNECTED, userId, agent, null, 0, false, false));
+        addEvent(new Event(TYPE_AGENT_CONNECTED, userId, agent, null, 0, 0, false));
     }
 
     public void logAgentStopped(int userId, ComponentName agent) {
-        addEvent(new Event(TYPE_AGENT_STOPPED, userId, agent, null, 0, false, false));
+        addEvent(new Event(TYPE_AGENT_STOPPED, userId, agent, null, 0, 0, false));
     }
 
     public void logManagingTrust(int userId, ComponentName agent, boolean managing) {
-        addEvent(new Event(TYPE_MANAGING_TRUST, userId, agent, null, 0, false, managing));
+        addEvent(new Event(TYPE_MANAGING_TRUST, userId, agent, null, 0, 0, managing));
     }
 
     private void addEvent(Event e) {
@@ -129,8 +130,8 @@
             }
             switch (ev.type) {
                 case TYPE_GRANT_TRUST:
-                    writer.printf(", message=\"%s\", duration=%s, initiatedByUser=%d",
-                            ev.message, formatDuration(ev.duration), ev.userInitiated ? 1 : 0);
+                    writer.printf(", message=\"%s\", duration=%s, flags=%s",
+                            ev.message, formatDuration(ev.duration), dumpGrantFlags(ev.flags));
                     break;
                 case TYPE_MANAGING_TRUST:
                     writer.printf(", managingTrust=" + ev.managingTrust);
@@ -184,4 +185,20 @@
                 return "Unknown(" + type + ")";
         }
     }
+
+    private String dumpGrantFlags(int flags) {
+        StringBuilder sb = new StringBuilder();
+        if ((flags & TrustAgentService.FLAG_GRANT_TRUST_INITIATED_BY_USER) != 0) {
+            if (sb.length() != 0) sb.append('|');
+            sb.append("INITIATED_BY_USER");
+        }
+        if ((flags & TrustAgentService.FLAG_GRANT_TRUST_DISMISS_KEYGUARD) != 0) {
+            if (sb.length() != 0) sb.append('|');
+            sb.append("DISMISS_KEYGUARD");
+        }
+        if (sb.length() == 0) {
+            sb.append('0');
+        }
+        return sb.toString();
+    }
 }
diff --git a/services/core/java/com/android/server/trust/TrustManagerService.java b/services/core/java/com/android/server/trust/TrustManagerService.java
index b38d33d..7d2fb43 100644
--- a/services/core/java/com/android/server/trust/TrustManagerService.java
+++ b/services/core/java/com/android/server/trust/TrustManagerService.java
@@ -179,11 +179,11 @@
     private void updateTrustAll() {
         List<UserInfo> userInfos = mUserManager.getUsers(true /* excludeDying */);
         for (UserInfo userInfo : userInfos) {
-            updateTrust(userInfo.id, false);
+            updateTrust(userInfo.id, 0);
         }
     }
 
-    public void updateTrust(int userId, boolean initiatedByUser) {
+    public void updateTrust(int userId, int flags) {
         dispatchOnTrustManagedChanged(aggregateIsTrustManaged(userId), userId);
         boolean trusted = aggregateIsTrusted(userId);
         boolean changed;
@@ -191,7 +191,7 @@
             changed = mUserIsTrusted.get(userId) != trusted;
             mUserIsTrusted.put(userId, trusted);
         }
-        dispatchOnTrustChanged(trusted, userId, initiatedByUser);
+        dispatchOnTrustChanged(trusted, userId, flags);
         if (changed) {
             refreshDeviceLockedForUser(userId);
         }
@@ -281,7 +281,7 @@
             if (userId == UserHandle.USER_ALL) {
                 updateTrustAll();
             } else {
-                updateTrust(userId, false /* initiatedByUser */);
+                updateTrust(userId, 0);
             }
         }
     }
@@ -394,7 +394,7 @@
             }
         }
         if (trustMayHaveChanged) {
-            updateTrust(userId, false);
+            updateTrust(userId, 0);
         }
         refreshAgentList(userId);
     }
@@ -587,11 +587,11 @@
         }
     }
 
-    private void dispatchOnTrustChanged(boolean enabled, int userId, boolean initiatedByUser) {
-        if (!enabled) initiatedByUser = false;
+    private void dispatchOnTrustChanged(boolean enabled, int userId, int flags) {
+        if (!enabled) flags = 0;
         for (int i = 0; i < mTrustListeners.size(); i++) {
             try {
-                mTrustListeners.get(i).onTrustChanged(enabled, userId, initiatedByUser);
+                mTrustListeners.get(i).onTrustChanged(enabled, userId, flags);
             } catch (DeadObjectException e) {
                 Slog.d(TAG, "Removing dead TrustListener.");
                 mTrustListeners.remove(i);
diff --git a/services/core/java/com/android/server/webkit/WebViewUpdateService.java b/services/core/java/com/android/server/webkit/WebViewUpdateService.java
index d4c5f87..ac79b36 100644
--- a/services/core/java/com/android/server/webkit/WebViewUpdateService.java
+++ b/services/core/java/com/android/server/webkit/WebViewUpdateService.java
@@ -40,6 +40,8 @@
     private boolean mRelroReady32Bit = false;
     private boolean mRelroReady64Bit = false;
 
+    private String oldWebViewPackageName = null;
+
     private BroadcastReceiver mWebViewUpdatedReceiver;
 
     public WebViewUpdateService(Context context) {
@@ -51,9 +53,22 @@
         mWebViewUpdatedReceiver = new BroadcastReceiver() {
                 @Override
                 public void onReceive(Context context, Intent intent) {
-                    String webviewPackage = "package:" + WebViewFactory.getWebViewPackageName();
-                    if (webviewPackage.equals(intent.getDataString())) {
-                        onWebViewUpdateInstalled();
+
+                    for (String packageName : WebViewFactory.getWebViewPackageNames()) {
+                        String webviewPackage = "package:" + packageName;
+
+                        if (webviewPackage.equals(intent.getDataString())) {
+                            String usedPackageName =
+                                WebViewFactory.findPreferredWebViewPackage().packageName;
+                            // Only trigger update actions if the updated package is the one that
+                            // will be used, or the one that was in use before the update.
+                            if (packageName.equals(usedPackageName) ||
+                                    packageName.equals(oldWebViewPackageName)) {
+                                onWebViewUpdateInstalled();
+                                oldWebViewPackageName = usedPackageName;
+                            }
+                            return;
+                        }
                     }
                 }
         };
diff --git a/services/core/java/com/android/server/wm/AccessibilityController.java b/services/core/java/com/android/server/wm/AccessibilityController.java
index ae8832a..91ce739 100644
--- a/services/core/java/com/android/server/wm/AccessibilityController.java
+++ b/services/core/java/com/android/server/wm/AccessibilityController.java
@@ -339,6 +339,7 @@
                         case WindowManager.LayoutParams.TYPE_APPLICATION_PANEL:
                         case WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA:
                         case WindowManager.LayoutParams.TYPE_APPLICATION_SUB_PANEL:
+                        case WindowManager.LayoutParams.TYPE_APPLICATION_ABOVE_SUB_PANEL:
                         case WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG:
                         case WindowManager.LayoutParams.TYPE_SEARCH_BAR:
                         case WindowManager.LayoutParams.TYPE_PHONE:
diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java
index a04f6cb..e914cd4 100644
--- a/services/core/java/com/android/server/wm/AppWindowToken.java
+++ b/services/core/java/com/android/server/wm/AppWindowToken.java
@@ -56,7 +56,7 @@
     boolean appFullscreen;
     int requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
     boolean layoutConfigChanges;
-    boolean showWhenLocked;
+    boolean showForAllUsers;
 
     // The input dispatching timeout for this application token in nanoseconds.
     long inputDispatchingTimeoutNanos;
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index f073c23..f914369 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -66,6 +66,7 @@
     int mBaseDisplayWidth = 0;
     int mBaseDisplayHeight = 0;
     int mBaseDisplayDensity = 0;
+    boolean mDisplayScalingDisabled;
     private final DisplayInfo mDisplayInfo = new DisplayInfo();
     private final Display mDisplay;
 
@@ -360,6 +361,9 @@
                 pw.print(mBaseDisplayWidth); pw.print("x"); pw.print(mBaseDisplayHeight);
                 pw.print(" "); pw.print(mBaseDisplayDensity); pw.print("dpi");
             }
+            if (mDisplayScalingDisabled) {
+                pw.println(" noscale");
+            }
             pw.print(" cur=");
             pw.print(mDisplayInfo.logicalWidth);
             pw.print("x"); pw.print(mDisplayInfo.logicalHeight);
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 34120a0..0c3cf65 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -107,9 +107,9 @@
         }
     }
 
-    boolean showWhenLocked() {
+    boolean showForAllUsers() {
         final int tokensCount = mAppTokens.size();
-        return (tokensCount != 0) && mAppTokens.get(tokensCount - 1).showWhenLocked;
+        return (tokensCount != 0) && mAppTokens.get(tokensCount - 1).showForAllUsers;
     }
 
     @Override
diff --git a/services/core/java/com/android/server/wm/TaskStack.java b/services/core/java/com/android/server/wm/TaskStack.java
index e845f83..7cdf8b2 100644
--- a/services/core/java/com/android/server/wm/TaskStack.java
+++ b/services/core/java/com/android/server/wm/TaskStack.java
@@ -276,27 +276,26 @@
     }
 
     void addTask(Task task, boolean toTop) {
-        addTask(task, toTop, task.showWhenLocked());
+        addTask(task, toTop, task.showForAllUsers());
     }
 
     /**
      * Put a Task in this stack. Used for adding and moving.
      * @param task The task to add.
      * @param toTop Whether to add it to the top or bottom.
-     * @param showWhenLocked Whether to show the task when the device is locked
-     *                       regardless of the current user.
+     * @param showForAllUsers Whether to show the task regardless of the current user.
      */
-    void addTask(Task task, boolean toTop, boolean showWhenLocked) {
+    void addTask(Task task, boolean toTop, boolean showForAllUsers) {
         int stackNdx;
         if (!toTop) {
             stackNdx = 0;
         } else {
             stackNdx = mTasks.size();
-            if (!showWhenLocked && !mService.isCurrentProfileLocked(task.mUserId)) {
+            if (!showForAllUsers && !mService.isCurrentProfileLocked(task.mUserId)) {
                 // Place the task below all current user tasks.
                 while (--stackNdx >= 0) {
                     final Task tmpTask = mTasks.get(stackNdx);
-                    if (!tmpTask.showWhenLocked()
+                    if (!tmpTask.showForAllUsers()
                             || !mService.isCurrentProfileLocked(tmpTask.mUserId)) {
                         break;
                     }
@@ -498,7 +497,7 @@
         int top = mTasks.size();
         for (int taskNdx = 0; taskNdx < top; ++taskNdx) {
             Task task = mTasks.get(taskNdx);
-            if (mService.isCurrentProfileLocked(task.mUserId) || task.showWhenLocked()) {
+            if (mService.isCurrentProfileLocked(task.mUserId) || task.showForAllUsers()) {
                 mTasks.remove(taskNdx);
                 mTasks.add(task);
                 --top;
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index e790fb0..f29d524 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -709,6 +709,11 @@
     private WindowContentFrameStats mTempWindowRenderStats;
 
     final class DragInputEventReceiver extends InputEventReceiver {
+        // Set, if stylus button was down at the start of the drag.
+        private boolean mStylusButtonDownAtStart;
+        // Indicates the first event to check for button state.
+        private boolean mIsStartEvent = true;
+
         public DragInputEventReceiver(InputChannel inputChannel, Looper looper) {
             super(inputChannel, looper);
         }
@@ -724,6 +729,18 @@
                     boolean endDrag = false;
                     final float newX = motionEvent.getRawX();
                     final float newY = motionEvent.getRawY();
+                    final boolean isStylusButtonDown =
+                            (motionEvent.getToolType(0) == MotionEvent.TOOL_TYPE_STYLUS)
+                            && (motionEvent.getButtonState() & MotionEvent.BUTTON_SECONDARY) != 0;
+
+                    if (mIsStartEvent) {
+                        if (isStylusButtonDown) {
+                            // First event and the button was down, check for the button being
+                            // lifted in the future, if that happens we'll drop the item.
+                            mStylusButtonDownAtStart = true;
+                        }
+                        mIsStartEvent = false;
+                    }
 
                     switch (motionEvent.getAction()) {
                     case MotionEvent.ACTION_DOWN: {
@@ -733,9 +750,17 @@
                     } break;
 
                     case MotionEvent.ACTION_MOVE: {
-                        synchronized (mWindowMap) {
-                            // move the surface and tell the involved window(s) where we are
-                            mDragState.notifyMoveLw(newX, newY);
+                        if (mStylusButtonDownAtStart && !isStylusButtonDown) {
+                            if (DEBUG_DRAG) Slog.d(TAG, "Button no longer pressed; dropping at "
+                                    + newX + "," + newY);
+                            synchronized (mWindowMap) {
+                                endDrag = mDragState.notifyDropLw(newX, newY);
+                            }
+                        } else {
+                            synchronized (mWindowMap) {
+                                // move the surface and tell the involved window(s) where we are
+                                mDragState.notifyMoveLw(newX, newY);
+                            }
                         }
                     } break;
 
@@ -759,6 +784,8 @@
                         synchronized (mWindowMap) {
                             mDragState.endDragLw();
                         }
+                        mStylusButtonDownAtStart = false;
+                        mIsStartEvent = true;
                     }
 
                     handled = true;
@@ -3620,13 +3647,13 @@
         EventLog.writeEvent(EventLogTags.WM_TASK_CREATED, taskId, stackId);
         Task task = new Task(taskId, stack, userId, this);
         mTaskIdToTask.put(taskId, task);
-        stack.addTask(task, !atoken.mLaunchTaskBehind /* toTop */, atoken.showWhenLocked);
+        stack.addTask(task, !atoken.mLaunchTaskBehind /* toTop */, atoken.showForAllUsers);
         return task;
     }
 
     @Override
     public void addAppToken(int addPos, IApplicationToken token, int taskId, int stackId,
-            int requestedOrientation, boolean fullscreen, boolean showWhenLocked, int userId,
+            int requestedOrientation, boolean fullscreen, boolean showForAllUsers, int userId,
             int configChanges, boolean voiceInteraction, boolean launchTaskBehind) {
         if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS,
                 "addAppToken()")) {
@@ -3656,7 +3683,7 @@
             atoken = new AppWindowToken(this, token, voiceInteraction);
             atoken.inputDispatchingTimeoutNanos = inputDispatchingTimeoutNanos;
             atoken.appFullscreen = fullscreen;
-            atoken.showWhenLocked = showWhenLocked;
+            atoken.showForAllUsers = showForAllUsers;
             atoken.requestedOrientation = requestedOrientation;
             atoken.layoutConfigChanges = (configChanges &
                     (ActivityInfo.CONFIG_SCREEN_SIZE | ActivityInfo.CONFIG_ORIENTATION)) != 0;
@@ -7249,8 +7276,15 @@
             displayInfo.getLogicalMetrics(mRealDisplayMetrics,
                     CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO, null);
             displayInfo.getAppMetrics(mDisplayMetrics);
+            if (displayContent.mDisplayScalingDisabled) {
+                displayInfo.flags |= Display.FLAG_SCALING_DISABLED;
+            } else {
+                displayInfo.flags &= ~Display.FLAG_SCALING_DISABLED;
+            }
+
             mDisplayManagerInternal.setDisplayInfoOverrideFromWindowManager(
                     displayContent.getDisplayId(), displayInfo);
+
             displayContent.mBaseDisplayRect.set(0, 0, dw, dh);
         }
         if (false) {
@@ -7547,7 +7581,7 @@
 
         synchronized(mWindowMap) {
             final DisplayContent displayContent = getDefaultDisplayContentLocked();
-            readForcedDisplaySizeAndDensityLocked(displayContent);
+            readForcedDisplayPropertiesLocked(displayContent);
             mDisplayReady = true;
         }
 
@@ -8320,7 +8354,47 @@
         }
     }
 
-    private void readForcedDisplaySizeAndDensityLocked(final DisplayContent displayContent) {
+    @Override
+    public void setForcedDisplayScalingMode(int displayId, int mode) {
+        if (mContext.checkCallingOrSelfPermission(
+                android.Manifest.permission.WRITE_SECURE_SETTINGS) !=
+                PackageManager.PERMISSION_GRANTED) {
+            throw new SecurityException("Must hold permission " +
+                    android.Manifest.permission.WRITE_SECURE_SETTINGS);
+        }
+        if (displayId != Display.DEFAULT_DISPLAY) {
+            throw new IllegalArgumentException("Can only set the default display");
+        }
+        final long ident = Binder.clearCallingIdentity();
+        try {
+            synchronized(mWindowMap) {
+                final DisplayContent displayContent = getDisplayContentLocked(displayId);
+                if (displayContent != null) {
+                    if (mode < 0 || mode > 1) {
+                        mode = 0;
+                    }
+                    setForcedDisplayScalingModeLocked(displayContent, mode);
+                    Settings.Global.putInt(mContext.getContentResolver(),
+                            Settings.Global.DISPLAY_SCALING_FORCE, mode);
+                }
+            }
+        } finally {
+            Binder.restoreCallingIdentity(ident);
+        }
+    }
+
+    private void setForcedDisplayScalingModeLocked(DisplayContent displayContent,
+            int mode) {
+        Slog.i(TAG, "Using display scaling mode: " + (mode == 0 ? "auto" : "off"));
+
+        synchronized(displayContent.mDisplaySizeLock) {
+            displayContent.mDisplayScalingDisabled = (mode != 0);
+        }
+        reconfigureDisplayLocked(displayContent);
+    }
+
+    private void readForcedDisplayPropertiesLocked(final DisplayContent displayContent) {
+        // Display size.
         String sizeStr = Settings.Global.getString(mContext.getContentResolver(),
                 Settings.Global.DISPLAY_SIZE_FORCED);
         if (sizeStr == null || sizeStr.length() == 0) {
@@ -8345,6 +8419,8 @@
                 }
             }
         }
+
+        // Display density.
         String densityStr = Settings.Global.getString(mContext.getContentResolver(),
                 Settings.Global.DISPLAY_DENSITY_FORCED);
         if (densityStr == null || densityStr.length() == 0) {
@@ -8363,6 +8439,16 @@
             } catch (NumberFormatException ex) {
             }
         }
+
+        // Display scaling mode.
+        int mode = Settings.Global.getInt(mContext.getContentResolver(),
+                Settings.Global.DISPLAY_SCALING_FORCE, 0);
+        if (mode != 0) {
+            synchronized(displayContent.mDisplaySizeLock) {
+                Slog.i(TAG, "FORCED DISPLAY SCALING DISABLED");
+                displayContent.mDisplayScalingDisabled = true;
+            }
+        }
     }
 
     // displayContent must not be null
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index db3268d..4f795a6 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -17,6 +17,7 @@
 package com.android.server.wm;
 
 import static android.view.WindowManager.LayoutParams.FIRST_SUB_WINDOW;
+import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED;
 import static android.view.WindowManager.LayoutParams.LAST_SUB_WINDOW;
 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_COMPATIBLE_WINDOW;
 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMATION;
@@ -478,6 +479,12 @@
         if (mAppToken != null) {
             final DisplayContent appDisplay = getDisplayContent();
             mNotOnAppsDisplay = displayContent != appDisplay;
+
+            if (mAppToken.showForAllUsers) {
+                // Windows for apps that can show for all users should also show when the
+                // device is locked.
+                mAttrs.flags |= FLAG_SHOW_WHEN_LOCKED;
+            }
         }
 
         mWinAnimator = new WindowStateAnimator(this);
@@ -1353,7 +1360,7 @@
             win = win.mAttachedWindow;
         }
         if (win.mAttrs.type < WindowManager.LayoutParams.FIRST_SYSTEM_WINDOW
-                && win.mAppToken != null && win.mAppToken.showWhenLocked) {
+                && win.mAppToken != null && win.mAppToken.showForAllUsers) {
             // Save some cycles by not calling getDisplayInfo unless it is an application
             // window intended for all users.
             final DisplayContent displayContent = win.getDisplayContent();
diff --git a/services/core/jni/com_android_server_fingerprint_FingerprintService.cpp b/services/core/jni/com_android_server_fingerprint_FingerprintService.cpp
index 17f86ca..5a86923 100644
--- a/services/core/jni/com_android_server_fingerprint_FingerprintService.cpp
+++ b/services/core/jni/com_android_server_fingerprint_FingerprintService.cpp
@@ -26,6 +26,7 @@
 #include <utils/String16.h>
 #include <utils/Looper.h>
 #include <keystore/IKeystoreService.h>
+#include <keystore/keystore.h> // for error code
 
 #include <hardware/hardware.h>
 #include <hardware/fingerprint.h>
@@ -74,8 +75,9 @@
         sp<IBinder> binder = sm->getService(String16("android.security.keystore"));
         sp<IKeystoreService> service = interface_cast<IKeystoreService>(binder);
         if (service != NULL) {
-            if (service->addAuthToken(auth_token, auth_token_length) != NO_ERROR) {
-                ALOGE("Falure sending auth token to KeyStore");
+            status_t ret = service->addAuthToken(auth_token, auth_token_length);
+            if (ret != ResponseCode::NO_ERROR) {
+                ALOGE("Falure sending auth token to KeyStore: %d", ret);
             }
         } else {
             ALOGE("Unable to communicate with KeyStore");
@@ -136,14 +138,15 @@
         ALOG(LOG_VERBOSE, LOG_TAG, "nativeEnroll() : invalid token size %d\n", tokenSize);
         return -1;
     }
-    int ret = gContext.device->enroll(gContext.device, (hw_auth_token_t*) tokenData, groupId, timeout);
+    int ret = gContext.device->enroll(gContext.device,
+            reinterpret_cast<const hw_auth_token_t*>(tokenData), groupId, timeout);
     env->ReleaseByteArrayElements(token, tokenData, 0);
     return reinterpret_cast<jint>(ret);
 }
 
-static jint nativePreEnroll(JNIEnv* env, jobject clazz) {
+static jlong nativePreEnroll(JNIEnv* env, jobject clazz) {
     uint64_t ret = gContext.device->pre_enroll(gContext.device);
-    ALOG(LOG_VERBOSE, LOG_TAG, "nativePreEnroll(), result = %" PRId64 "\n", ret);
+    // ALOG(LOG_VERBOSE, LOG_TAG, "nativePreEnroll(), result = %llx", ret);
     return reinterpret_cast<jlong>((int64_t)ret);
 }
 
diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp
index 7c5980a..f3edbd1 100644
--- a/services/core/jni/com_android_server_input_InputManagerService.cpp
+++ b/services/core/jni/com_android_server_input_InputManagerService.cpp
@@ -752,7 +752,7 @@
 
 void NativeInputManager::reloadCalibration() {
     mInputManager->getReader()->requestRefreshConfiguration(
-            InputReaderConfiguration::TOUCH_AFFINE_TRANSFORMATION);
+            InputReaderConfiguration::CHANGE_TOUCH_AFFINE_TRANSFORMATION);
 }
 
 TouchAffineTransformation NativeInputManager::getTouchAffineTransformation(
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 7e59943..e22a2cc 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -32,6 +32,7 @@
 import android.app.Notification;
 import android.app.NotificationManager;
 import android.app.PendingIntent;
+import android.app.StatusBarManager;
 import android.app.admin.DeviceAdminInfo;
 import android.app.admin.DeviceAdminReceiver;
 import android.app.admin.DevicePolicyManager;
@@ -102,6 +103,7 @@
 
 import com.android.internal.R;
 import com.android.internal.os.storage.ExternalStorageFormatter;
+import com.android.internal.statusbar.IStatusBarService;
 import com.android.internal.util.FastXmlSerializer;
 import com.android.internal.util.JournaledFile;
 import com.android.internal.util.Preconditions;
@@ -152,7 +154,11 @@
 
     private static final String DEVICE_POLICIES_XML = "device_policies.xml";
 
-    private static final String LOCK_TASK_COMPONENTS_XML = "lock-task-component";
+    private static final String TAG_LOCK_TASK_COMPONENTS = "lock-task-component";
+
+    private static final String TAG_STATUS_BAR = "statusbar";
+
+    private static final String ATTR_ENABLED = "enabled";
 
     private static final int REQUEST_EXPIRE_PASSWORD = 5571;
 
@@ -172,6 +178,12 @@
 
     private static final String ATTR_DELEGATED_CERT_INSTALLER = "delegated-cert-installer";
 
+    private static final int STATUS_BAR_DISABLE_MASK =
+            StatusBarManager.DISABLE_EXPAND |
+            StatusBarManager.DISABLE_NOTIFICATION_ICONS |
+            StatusBarManager.DISABLE_NOTIFICATION_ALERTS |
+            StatusBarManager.DISABLE_SEARCH;
+
     private static final Set<String> DEVICE_OWNER_USER_RESTRICTIONS;
     static {
         DEVICE_OWNER_USER_RESTRICTIONS = new HashSet();
@@ -237,6 +249,8 @@
     // Stores and loads state on device and profile owners.
     private DeviceOwner mDeviceOwner;
 
+    private final Binder mToken = new Binder();
+
     /**
      * Whether or not device admin feature is supported. If it isn't return defaults for all
      * public methods.
@@ -287,6 +301,8 @@
         // This is the list of component allowed to start lock task mode.
         final List<String> mLockTaskPackages = new ArrayList<>();
 
+        boolean mStatusBarEnabledState = true;
+
         ComponentName mRestrictionsProvider;
 
         String mDelegatedCertInstallerPackage;
@@ -1429,9 +1445,15 @@
 
             for (int i=0; i<policy.mLockTaskPackages.size(); i++) {
                 String component = policy.mLockTaskPackages.get(i);
-                out.startTag(null, LOCK_TASK_COMPONENTS_XML);
+                out.startTag(null, TAG_LOCK_TASK_COMPONENTS);
                 out.attribute(null, "name", component);
-                out.endTag(null, LOCK_TASK_COMPONENTS_XML);
+                out.endTag(null, TAG_LOCK_TASK_COMPONENTS);
+            }
+
+            if (!policy.mStatusBarEnabledState) {
+                out.startTag(null, TAG_STATUS_BAR);
+                out.attribute(null, ATTR_ENABLED, Boolean.toString(policy.mStatusBarEnabledState));
+                out.endTag(null, TAG_STATUS_BAR);
             }
 
             out.endTag(null, "policies");
@@ -1552,9 +1574,13 @@
                     policy.mActivePasswordNonLetter = Integer.parseInt(
                             parser.getAttributeValue(null, "nonletter"));
                     XmlUtils.skipCurrentTag(parser);
-                } else if (LOCK_TASK_COMPONENTS_XML.equals(tag)) {
+                } else if (TAG_LOCK_TASK_COMPONENTS.equals(tag)) {
                     policy.mLockTaskPackages.add(parser.getAttributeValue(null, "name"));
                     XmlUtils.skipCurrentTag(parser);
+                } else if (TAG_STATUS_BAR.equals(tag)) {
+                    policy.mStatusBarEnabledState = Boolean.parseBoolean(
+                            parser.getAttributeValue(null, ATTR_ENABLED));
+                    XmlUtils.skipCurrentTag(parser);
                 } else {
                     Slog.w(LOG_TAG, "Unknown tag: " + tag);
                     XmlUtils.skipCurrentTag(parser);
@@ -1612,10 +1638,13 @@
 
     private void updateLockTaskPackagesLocked(DevicePolicyData policy, int userId) {
         IActivityManager am = ActivityManagerNative.getDefault();
+        long ident = Binder.clearCallingIdentity();
         try {
             am.updateLockTaskPackages(userId, policy.mLockTaskPackages.toArray(new String[0]));
         } catch (RemoteException e) {
             // Not gonna happen.
+        } finally {
+            Binder.restoreCallingIdentity(ident);
         }
     }
 
@@ -1679,7 +1708,7 @@
         if (!mHasFeature) {
             return;
         }
-        getUserData(UserHandle.USER_OWNER);
+        DevicePolicyData policy = getUserData(UserHandle.USER_OWNER);
         loadDeviceOwner();
         cleanUpOldUsers();
         // Register an observer for watching for user setup complete.
@@ -1696,6 +1725,10 @@
                     getScreenCaptureDisabled(null, userHandle));
         }
 
+        if (mDeviceOwner != null && mDeviceOwner.hasDeviceOwner()
+                && !policy.mStatusBarEnabledState) {
+            setStatusBarEnabledStateInternal(STATUS_BAR_DISABLE_MASK, UserHandle.USER_OWNER);
+        }
     }
 
     private void cleanUpOldUsers() {
@@ -5830,6 +5863,38 @@
         return true;
     }
 
+    @Override
+    public void setStatusBarEnabledState(ComponentName who, boolean enabled) {
+        int userId = UserHandle.getCallingUserId();
+        synchronized (this) {
+            getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER);
+            DevicePolicyData policy = getUserData(userId);
+            if (policy.mStatusBarEnabledState != enabled) {
+                policy.mStatusBarEnabledState = enabled;
+                setStatusBarEnabledStateInternal(
+                        enabled ? StatusBarManager.DISABLE_NONE : STATUS_BAR_DISABLE_MASK,
+                        userId);
+                saveSettingsLocked(userId);
+            }
+        }
+    }
+
+    private void setStatusBarEnabledStateInternal(int flags, int userId) {
+        long ident = Binder.clearCallingIdentity();
+        try {
+            IStatusBarService statusBarService = IStatusBarService.Stub.asInterface(
+                    ServiceManager.checkService(Context.STATUS_BAR_SERVICE));
+            if (statusBarService != null) {
+                statusBarService.disableForUser(flags, mToken,
+                        mDeviceOwner.getDeviceOwnerPackageName(), userId);
+            }
+        } catch (RemoteException e) {
+            Slog.e(LOG_TAG, "Failed to disable the status bar", e);
+        } finally {
+            Binder.restoreCallingIdentity(ident);
+        }
+    }
+
     /**
      * We need to update the internal state of whether a user has completed setup once. After
      * that, we ignore any changes that reset the Settings.Secure.USER_SETUP_COMPLETE changes
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
index 8a28d51..7dce83e 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
@@ -393,12 +393,9 @@
                     throw new SecurityException(
                             "Caller is not the current voice interaction service");
                 }
-                final int callingPid = Binder.getCallingPid();
-                final int callingUid = Binder.getCallingUid();
                 final long caller = Binder.clearCallingIdentity();
                 try {
-                    mImpl.showSessionLocked(callingPid, callingUid, args, flags,
-                            null /* showCallback */);
+                    mImpl.showSessionLocked(args, flags, null /* showCallback */);
                 } finally {
                     Binder.restoreCallingIdentity(caller);
                 }
@@ -432,12 +429,9 @@
                     Slog.w(TAG, "showSessionFromSession without running voice interaction service");
                     return false;
                 }
-                final int callingPid = Binder.getCallingPid();
-                final int callingUid = Binder.getCallingUid();
                 final long caller = Binder.clearCallingIdentity();
                 try {
-                    return mImpl.showSessionLocked(callingPid, callingUid, sessionArgs, flags,
-                            null /* showCallback */);
+                    return mImpl.showSessionLocked(sessionArgs, flags, null /* showCallback */);
                 } finally {
                     Binder.restoreCallingIdentity(caller);
                 }
@@ -506,11 +500,9 @@
                     Slog.w(TAG, "finish without running voice interaction service");
                     return;
                 }
-                final int callingPid = Binder.getCallingPid();
-                final int callingUid = Binder.getCallingUid();
                 final long caller = Binder.clearCallingIdentity();
                 try {
-                    mImpl.finishLocked(callingPid, callingUid, token);
+                    mImpl.finishLocked(token);
                 } finally {
                     Binder.restoreCallingIdentity(caller);
                 }
@@ -708,11 +700,9 @@
                             + "service");
                     return;
                 }
-                final int callingPid = Binder.getCallingPid();
-                final int callingUid = Binder.getCallingUid();
                 final long caller = Binder.clearCallingIdentity();
                 try {
-                    mImpl.showSessionLocked(callingPid, callingUid, new Bundle() /* sessionArgs */,
+                    mImpl.showSessionLocked(new Bundle() /* sessionArgs */,
                             VoiceInteractionService.START_SOURCE_ASSIST_GESTURE
                                     | VoiceInteractionService.START_WITH_ASSIST
                                     | VoiceInteractionService.START_WITH_SCREENSHOT,
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java
index bca757b..61ec162 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java
@@ -135,11 +135,11 @@
         mContext.registerReceiver(mBroadcastReceiver, filter, null, handler);
     }
 
-    public boolean showSessionLocked(int callingPid, int callingUid, Bundle args, int flags,
+    public boolean showSessionLocked(Bundle args, int flags,
             IVoiceInteractionSessionShowCallback showCallback) {
         if (mActiveSession == null) {
             mActiveSession = new VoiceInteractionSessionConnection(mLock, mSessionComponentName,
-                    mUser, mContext, this, callingPid, callingUid);
+                    mUser, mContext, this, mInfo.getServiceInfo().applicationInfo.uid);
         }
         return mActiveSession.showLocked(args, flags, showCallback);
     }
@@ -196,7 +196,7 @@
         }
     }
 
-    public void finishLocked(int callingPid, int callingUid, IBinder token) {
+    public void finishLocked(IBinder token) {
         if (mActiveSession == null || token != mActiveSession.mToken) {
             Slog.w(TAG, "finish does not match active session");
             return;
@@ -267,7 +267,7 @@
     @Override
     public void sessionConnectionGone(VoiceInteractionSessionConnection connection) {
         synchronized (mLock) {
-            finishLocked(connection.mCallingPid, connection.mCallingUid, connection.mToken);
+            finishLocked(connection.mToken);
         }
     }
 }
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionSessionConnection.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionSessionConnection.java
index fb83956..9634ab8 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionSessionConnection.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionSessionConnection.java
@@ -59,7 +59,6 @@
     final int mUser;
     final Context mContext;
     final Callback mCallback;
-    final int mCallingPid;
     final int mCallingUid;
     final IActivityManager mAm;
     final IWindowManager mIWindowManager;
@@ -139,13 +138,12 @@
     };
 
     public VoiceInteractionSessionConnection(Object lock, ComponentName component, int user,
-            Context context, Callback callback, int callingPid, int callingUid) {
+            Context context, Callback callback, int callingUid) {
         mLock = lock;
         mSessionComponentName = component;
         mUser = user;
         mContext = context;
         mCallback = callback;
-        mCallingPid = callingPid;
         mCallingUid = callingUid;
         mAm = ActivityManagerNative.getDefault();
         mIWindowManager = IWindowManager.Stub.asInterface(
diff --git a/telecomm/java/android/telecom/AudioState.java b/telecomm/java/android/telecom/AudioState.java
index 9c03319..465c5f4 100644
--- a/telecomm/java/android/telecom/AudioState.java
+++ b/telecomm/java/android/telecom/AudioState.java
@@ -16,7 +16,6 @@
 
 package android.telecom;
 
-import android.annotation.SystemApi;
 import android.os.Parcel;
 import android.os.Parcelable;
 
@@ -25,9 +24,7 @@
 /**
  *  Encapsulates the telecom audio state, including the current audio routing, supported audio
  *  routing and mute.
- *  @hide
  */
-@SystemApi
 public final class AudioState implements Parcelable {
     /** Direct the audio stream through the device's earpiece. */
     public static final int ROUTE_EARPIECE      = 0x00000001;
@@ -47,21 +44,13 @@
      */
     public static final int ROUTE_WIRED_OR_EARPIECE = ROUTE_EARPIECE | ROUTE_WIRED_HEADSET;
 
-    /** Bit mask of all possible audio routes.
-     *
-     * @hide
-     */
-    public static final int ROUTE_ALL = ROUTE_EARPIECE | ROUTE_BLUETOOTH | ROUTE_WIRED_HEADSET |
+    /** Bit mask of all possible audio routes. */
+    private static final int ROUTE_ALL = ROUTE_EARPIECE | ROUTE_BLUETOOTH | ROUTE_WIRED_HEADSET |
             ROUTE_SPEAKER;
 
-    /** Note: Deprecated, please do not use if possible. */
-    @SystemApi public final boolean isMuted;
-
-    /** Note: Deprecated, please do not use if possible. */
-    @SystemApi public final int route;
-
-    /** Note: Deprecated, please do not use if possible. */
-    @SystemApi public final int supportedRouteMask;
+    private final boolean isMuted;
+    private final int route;
+    private final int supportedRouteMask;
 
     public AudioState(boolean muted, int route, int supportedRouteMask) {
         this.isMuted = muted;
@@ -97,7 +86,6 @@
                 audioRouteToString(supportedRouteMask));
     }
 
-    /** @hide */
     public static String audioRouteToString(int route) {
         if (route == 0 || (route & ~ROUTE_ALL) != 0x0) {
             return "UNKNOWN";
diff --git a/telecomm/java/android/telecom/AuthenticatorService.java b/telecomm/java/android/telecom/AuthenticatorService.java
index 7aa105d..1e43c71 100644
--- a/telecomm/java/android/telecom/AuthenticatorService.java
+++ b/telecomm/java/android/telecom/AuthenticatorService.java
@@ -19,7 +19,6 @@
 import android.accounts.Account;
 import android.accounts.AccountAuthenticatorResponse;
 import android.accounts.NetworkErrorException;
-import android.annotation.SystemApi;
 import android.app.Service;
 import android.content.Context;
 import android.content.Intent;
@@ -32,7 +31,6 @@
  *
  * @hide
  */
-@SystemApi
 public class AuthenticatorService extends Service {
     private static Authenticator mAuthenticator;
 
diff --git a/telecomm/java/android/telecom/Call.java b/telecomm/java/android/telecom/Call.java
index ba0aaf3..a46585a 100644
--- a/telecomm/java/android/telecom/Call.java
+++ b/telecomm/java/android/telecom/Call.java
@@ -30,10 +30,7 @@
 
 /**
  * Represents an ongoing phone call that the in-call app should present to the user.
- *
- * {@hide}
  */
-@SystemApi
 public final class Call {
     /**
      * The state of a {@code Call} when newly created.
@@ -91,8 +88,6 @@
      * The key to retrieve the optional {@code PhoneAccount}s Telecom can bundle with its Call
      * extras. Used to pass the phone accounts to display on the front end to the user in order to
      * select phone accounts to (for example) place a call.
-     *
-     * @hide
      */
     public static final String AVAILABLE_PHONE_ACCOUNTS = "selectPhoneAccountAccounts";
 
@@ -142,38 +137,32 @@
 
         /**
          * Local device supports receiving video.
-         * @hide
          */
         public static final int CAPABILITY_SUPPORTS_VT_LOCAL_RX = 0x00000100;
 
         /**
          * Local device supports transmitting video.
-         * @hide
          */
         public static final int CAPABILITY_SUPPORTS_VT_LOCAL_TX = 0x00000200;
 
         /**
          * Local device supports bidirectional video calling.
-         * @hide
          */
         public static final int CAPABILITY_SUPPORTS_VT_LOCAL_BIDIRECTIONAL =
                 CAPABILITY_SUPPORTS_VT_LOCAL_RX | CAPABILITY_SUPPORTS_VT_LOCAL_TX;
 
         /**
          * Remote device supports receiving video.
-         * @hide
          */
         public static final int CAPABILITY_SUPPORTS_VT_REMOTE_RX = 0x00000400;
 
         /**
          * Remote device supports transmitting video.
-         * @hide
          */
         public static final int CAPABILITY_SUPPORTS_VT_REMOTE_TX = 0x00000800;
 
         /**
          * Remote device supports bidirectional video calling.
-         * @hide
          */
         public static final int CAPABILITY_SUPPORTS_VT_REMOTE_BIDIRECTIONAL =
                 CAPABILITY_SUPPORTS_VT_REMOTE_RX | CAPABILITY_SUPPORTS_VT_REMOTE_TX;
@@ -191,27 +180,21 @@
         /**
          * Whether the call is a generic conference, where we do not know the precise state of
          * participants in the conference (eg. on CDMA).
-         *
-         * @hide
          */
         public static final int CAPABILITY_GENERIC_CONFERENCE = 0x00004000;
 
         /**
          * Call is using high definition audio.
-         * @hide
          */
         public static final int CAPABILITY_HIGH_DEF_AUDIO = 0x00008000;
 
         /**
          * Call is using WIFI.
-         * @hide
          */
         public static final int CAPABILITY_WIFI = 0x00010000;
 
         /**
          * Indicates that the current device callback number should be shown.
-         *
-         * @hide
          */
         public static final int CAPABILITY_SHOW_CALLBACK_NUMBER = 0x00020000;
 
@@ -221,14 +204,21 @@
          */
         public static final int CAPABILITY_SPEED_UP_MT_AUDIO = 0x00040000;
 
-         /**
-         * Call type can be modified for IMS call
+        /**
+         * Call can be upgraded to a video call.
          * @hide
          */
         public static final int CAPABILITY_CAN_UPGRADE_TO_VIDEO = 0x00080000;
 
+        /**
+         * For video calls, indicates whether the outgoing video for the call can be paused using
+         * the {@link android.telecom.VideoProfile.VideoState#PAUSED} VideoState.
+         * @hide
+         */
+        public static final int CAPABILITY_CAN_PAUSE_VIDEO = 0x00100000;
+
         //******************************************************************************************
-        // Next CAPABILITY value: 0x00100000
+        // Next CAPABILITY value: 0x00200000
         //******************************************************************************************
 
         private final Uri mHandle;
@@ -244,7 +234,6 @@
         private final int mVideoState;
         private final StatusHints mStatusHints;
         private final Bundle mExtras;
-        private final int mCallSubstate;
 
         /**
          * Whether the supplied capabilities  supports the specified capability.
@@ -252,7 +241,6 @@
          * @param capabilities A bit field of capabilities.
          * @param capability The capability to check capabilities for.
          * @return Whether the specified capability is supported.
-         * @hide
          */
         public static boolean can(int capabilities, int capability) {
             return (capabilities & capability) != 0;
@@ -263,7 +251,6 @@
          *
          * @param capability The capability to check capabilities for.
          * @return Whether the specified capability is supported.
-         * @hide
          */
         public boolean can(int capability) {
             return can(mCallCapabilities, capability);
@@ -335,6 +322,9 @@
             if (can(capabilities, CAPABILITY_CAN_UPGRADE_TO_VIDEO)) {
                 builder.append(" CAPABILITY_CAN_UPGRADE_TO_VIDEO");
             }
+            if (can(capabilities, CAPABILITY_CAN_PAUSE_VIDEO)) {
+                builder.append(" CAPABILITY_CAN_PAUSE_VIDEO");
+            }
             builder.append("]");
             return builder.toString();
         }
@@ -407,7 +397,7 @@
          * periodically, but user interfaces should not rely on this to display any "call time
          * clock".
          */
-        public long getConnectTimeMillis() {
+        public final long getConnectTimeMillis() {
             return mConnectTimeMillis;
         }
 
@@ -440,14 +430,6 @@
             return mExtras;
         }
 
-        /**
-         * @return The substate of the {@code Call}.
-         * @hide
-         */
-        public int getCallSubstate() {
-            return mCallSubstate;
-        }
-
         @Override
         public boolean equals(Object o) {
             if (o instanceof Details) {
@@ -466,8 +448,7 @@
                         Objects.equals(mGatewayInfo, d.mGatewayInfo) &&
                         Objects.equals(mVideoState, d.mVideoState) &&
                         Objects.equals(mStatusHints, d.mStatusHints) &&
-                        Objects.equals(mExtras, d.mExtras) &&
-                        Objects.equals(mCallSubstate, d.mCallSubstate);
+                        Objects.equals(mExtras, d.mExtras);
             }
             return false;
         }
@@ -487,8 +468,7 @@
                     Objects.hashCode(mGatewayInfo) +
                     Objects.hashCode(mVideoState) +
                     Objects.hashCode(mStatusHints) +
-                    Objects.hashCode(mExtras) +
-                    Objects.hashCode(mCallSubstate);
+                    Objects.hashCode(mExtras);
         }
 
         /** {@hide} */
@@ -505,8 +485,7 @@
                 GatewayInfo gatewayInfo,
                 int videoState,
                 StatusHints statusHints,
-                Bundle extras,
-                int callSubstate) {
+                Bundle extras) {
             mHandle = handle;
             mHandlePresentation = handlePresentation;
             mCallerDisplayName = callerDisplayName;
@@ -520,11 +499,10 @@
             mVideoState = videoState;
             mStatusHints = statusHints;
             mExtras = extras;
-            mCallSubstate = callSubstate;
         }
     }
 
-    public static abstract class Listener {
+    public static abstract class Callback {
         /**
          * Invoked when the state of this {@code Call} has changed. See {@link #getState()}.
          *
@@ -583,7 +561,6 @@
          *
          * @param call The {@code Call} invoking this method.
          * @param videoCall The {@code Call.VideoCall} associated with the {@code Call}.
-         * @hide
          */
         public void onVideoCallChanged(Call call, InCallService.VideoCall videoCall) {}
 
@@ -609,13 +586,21 @@
         public void onConferenceableCallsChanged(Call call, List<Call> conferenceableCalls) {}
     }
 
+    /**
+     * @deprecated Use {@code Call.Callback} instead.
+     * @hide
+     */
+    @Deprecated
+    @SystemApi
+    public static abstract class Listener extends Callback { }
+
     private final Phone mPhone;
     private final String mTelecomCallId;
     private final InCallAdapter mInCallAdapter;
     private final List<String> mChildrenIds = new ArrayList<>();
     private final List<Call> mChildren = new ArrayList<>();
     private final List<Call> mUnmodifiableChildren = Collections.unmodifiableList(mChildren);
-    private final List<Listener> mListeners = new CopyOnWriteArrayList<>();
+    private final List<Callback> mCallbacks = new CopyOnWriteArrayList<>();
     private final List<Call> mConferenceableCalls = new ArrayList<>();
     private final List<Call> mUnmodifiableConferenceableCalls =
             Collections.unmodifiableList(mConferenceableCalls);
@@ -710,8 +695,8 @@
      * {@code Call} will temporarily pause playing the tones for a pre-defined period of time.
      *
      * If the DTMF string contains a {@link TelecomManager#DTMF_CHARACTER_WAIT} symbol, this
-     * {@code Call} will pause playing the tones and notify listeners via
-     * {@link Listener#onPostDialWait(Call, String)}. At this point, the in-call app
+     * {@code Call} will pause playing the tones and notify callbacks via
+     * {@link Callback#onPostDialWait(Call, String)}. At this point, the in-call app
      * should display to the user an indication of this state and an affordance to continue
      * the postdial sequence. When the user decides to continue the postdial sequence, the in-call
      * app should invoke the {@link #postDialContinue(boolean)} method.
@@ -836,7 +821,6 @@
      * Obtains an object that can be used to display video from this {@code Call}.
      *
      * @return An {@code Call.VideoCall}.
-     * @hide
      */
     public InCallService.VideoCall getVideoCall() {
         return mVideoCall;
@@ -853,25 +837,52 @@
     }
 
     /**
+     * Registers a callback to this {@code Call}.
+     *
+     * @param callback A {@code Callback}.
+     */
+    public void registerCallback(Callback callback) {
+        mCallbacks.add(callback);
+    }
+
+    /**
+     * Unregisters a callback from this {@code Call}.
+     *
+     * @param callback A {@code Callback}.
+     */
+    public void unregisterCallback(Callback callback) {
+        if (callback != null) {
+            mCallbacks.remove(callback);
+        }
+    }
+
+    /**
      * Adds a listener to this {@code Call}.
      *
      * @param listener A {@code Listener}.
+     * @deprecated Use {@link #registerCallback} instead.
+     * @hide
      */
+    @Deprecated
+    @SystemApi
     public void addListener(Listener listener) {
-        mListeners.add(listener);
+        registerCallback(listener);
     }
 
     /**
      * Removes a listener from this {@code Call}.
      *
      * @param listener A {@code Listener}.
+     * @deprecated Use {@link #unregisterCallback} instead.
+     * @hide
      */
+    @Deprecated
+    @SystemApi
     public void removeListener(Listener listener) {
-        if (listener != null) {
-            mListeners.remove(listener);
-        }
+        unregisterCallback(listener);
     }
 
+
     /** {@hide} */
     Call(Phone phone, String telecomCallId, InCallAdapter inCallAdapter) {
         mPhone = phone;
@@ -901,8 +912,7 @@
                 parcelableCall.getGatewayInfo(),
                 parcelableCall.getVideoState(),
                 parcelableCall.getStatusHints(),
-                parcelableCall.getExtras(),
-                parcelableCall.getCallSubstate());
+                parcelableCall.getExtras());
         boolean detailsChanged = !Objects.equals(mDetails, details);
         if (detailsChanged) {
             mDetails = details;
@@ -1003,56 +1013,56 @@
     }
 
     private void fireStateChanged(int newState) {
-        for (Listener listener : mListeners) {
-            listener.onStateChanged(this, newState);
+        for (Callback callback : mCallbacks) {
+            callback.onStateChanged(this, newState);
         }
     }
 
     private void fireParentChanged(Call newParent) {
-        for (Listener listener : mListeners) {
-            listener.onParentChanged(this, newParent);
+        for (Callback callback : mCallbacks) {
+            callback.onParentChanged(this, newParent);
         }
     }
 
     private void fireChildrenChanged(List<Call> children) {
-        for (Listener listener : mListeners) {
-            listener.onChildrenChanged(this, children);
+        for (Callback callback : mCallbacks) {
+            callback.onChildrenChanged(this, children);
         }
     }
 
     private void fireDetailsChanged(Details details) {
-        for (Listener listener : mListeners) {
-            listener.onDetailsChanged(this, details);
+        for (Callback callback : mCallbacks) {
+            callback.onDetailsChanged(this, details);
         }
     }
 
     private void fireCannedTextResponsesLoaded(List<String> cannedTextResponses) {
-        for (Listener listener : mListeners) {
-            listener.onCannedTextResponsesLoaded(this, cannedTextResponses);
+        for (Callback callback : mCallbacks) {
+            callback.onCannedTextResponsesLoaded(this, cannedTextResponses);
         }
     }
 
     private void fireVideoCallChanged(InCallService.VideoCall videoCall) {
-        for (Listener listener : mListeners) {
-            listener.onVideoCallChanged(this, videoCall);
+        for (Callback callback : mCallbacks) {
+            callback.onVideoCallChanged(this, videoCall);
         }
     }
 
     private void firePostDialWait(String remainingPostDialSequence) {
-        for (Listener listener : mListeners) {
-            listener.onPostDialWait(this, remainingPostDialSequence);
+        for (Callback callback : mCallbacks) {
+            callback.onPostDialWait(this, remainingPostDialSequence);
         }
     }
 
     private void fireCallDestroyed() {
-        for (Listener listener : mListeners) {
-            listener.onCallDestroyed(this);
+        for (Callback callback : mCallbacks) {
+            callback.onCallDestroyed(this);
         }
     }
 
     private void fireConferenceableCallsChanged() {
-        for (Listener listener : mListeners) {
-            listener.onConferenceableCallsChanged(this, mUnmodifiableConferenceableCalls);
+        for (Callback callback : mCallbacks) {
+            callback.onConferenceableCallsChanged(this, mUnmodifiableConferenceableCalls);
         }
     }
 
diff --git a/telecomm/java/android/telecom/CallProperties.java b/telecomm/java/android/telecom/CallProperties.java
index b1b82e2..1721a39 100644
--- a/telecomm/java/android/telecom/CallProperties.java
+++ b/telecomm/java/android/telecom/CallProperties.java
@@ -18,7 +18,6 @@
 
 /**
  * Defines properties of a phone call which may be affected by changes to the call.
- * @hide
  */
 public class CallProperties {
     /** Call is currently in a conference call. */
diff --git a/telecomm/java/android/telecom/CallState.java b/telecomm/java/android/telecom/CallState.java
index bd9223a..5584226 100644
--- a/telecomm/java/android/telecom/CallState.java
+++ b/telecomm/java/android/telecom/CallState.java
@@ -16,17 +16,12 @@
 
 package android.telecom;
 
-import android.annotation.SystemApi;
-
 /**
  * Defines call-state constants of the different states in which a call can exist. Although states
  * have the notion of normal transitions, due to the volatile nature of telephony systems, code
  * that uses these states should be resilient to unexpected state changes outside of what is
  * considered traditional.
- *
- * {@hide}
  */
-@SystemApi
 public final class CallState {
 
     private CallState() {}
diff --git a/telecomm/java/android/telecom/CameraCapabilities.java b/telecomm/java/android/telecom/CameraCapabilities.java
index 6eaf6a2..6242956 100644
--- a/telecomm/java/android/telecom/CameraCapabilities.java
+++ b/telecomm/java/android/telecom/CameraCapabilities.java
@@ -21,7 +21,6 @@
 
 /**
  * Represents the camera capabilities important to a Video Telephony provider.
- * @hide
  */
 public final class CameraCapabilities implements Parcelable {
 
@@ -46,7 +45,7 @@
     private final float mMaxZoom;
 
     /**
-     * Create a call camera capabilities instance that doesn't support zoom.
+     * Create a call camera capabilities instance.
      *
      * @param width The width of the camera video (in pixels).
      * @param height The height of the camera video (in pixels).
@@ -56,7 +55,8 @@
     }
 
     /**
-     * Create a call camera capabilities instance.
+     * Create a call camera capabilities instance that optionally
+     * supports zoom.
      *
      * @param width The width of the camera video (in pixels).
      * @param height The height of the camera video (in pixels).
diff --git a/telecomm/java/android/telecom/Conference.java b/telecomm/java/android/telecom/Conference.java
index ddaedcd..15a1da1 100644
--- a/telecomm/java/android/telecom/Conference.java
+++ b/telecomm/java/android/telecom/Conference.java
@@ -16,7 +16,6 @@
 
 package android.telecom;
 
-import android.annotation.SystemApi;
 import android.telecom.Connection.VideoProvider;
 
 import java.util.ArrayList;
@@ -29,16 +28,14 @@
 
 /**
  * Represents a conference call which can contain any number of {@link Connection} objects.
- * @hide
  */
-@SystemApi
 public abstract class Conference implements IConferenceable {
 
     /**
      * Used to indicate that the conference connection time is not specified.  If not specified,
      * Telecom will set the connect time.
      */
-    public static long CONNECT_TIME_NOT_SPECIFIED = 0;
+    public static final long CONNECT_TIME_NOT_SPECIFIED = 0;
 
     /** @hide */
     public abstract static class Listener {
@@ -63,7 +60,7 @@
     private final List<Connection> mUnmodifiableConferenceableConnections =
             Collections.unmodifiableList(mConferenceableConnections);
 
-    protected PhoneAccountHandle mPhoneAccount;
+    private PhoneAccountHandle mPhoneAccount;
     private AudioState mAudioState;
     private int mState = Connection.STATE_NEW;
     private DisconnectCause mDisconnectCause;
@@ -116,11 +113,6 @@
         return mState;
     }
 
-    /** @hide */
-    @Deprecated public final int getCapabilities() {
-        return getConnectionCapabilities();
-    }
-
     /**
      * Returns the capabilities of a conference. See {@code CAPABILITY_*} constants in class
      * {@link Connection} for valid values.
@@ -301,11 +293,6 @@
         return mDisconnectCause;
     }
 
-    /** @hide */
-    @Deprecated public final void setCapabilities(int connectionCapabilities) {
-        setConnectionCapabilities(connectionCapabilities);
-    }
-
     /**
      * Sets the capabilities of a conference. See {@code CAPABILITY_*} constants of class
      * {@link Connection} for valid values.
@@ -497,7 +484,7 @@
      *
      * @return The time the {@code Conference} has been connected.
      */
-    public long getConnectTimeMillis() {
+    public final long getConnectTimeMillis() {
         return mConnectTimeMillis;
     }
 
diff --git a/telecomm/java/android/telecom/Connection.java b/telecomm/java/android/telecom/Connection.java
index 6858cee..11632dc 100644
--- a/telecomm/java/android/telecom/Connection.java
+++ b/telecomm/java/android/telecom/Connection.java
@@ -19,7 +19,6 @@
 import com.android.internal.telecom.IVideoCallback;
 import com.android.internal.telecom.IVideoProvider;
 
-import android.annotation.SystemApi;
 import android.net.Uri;
 import android.os.Handler;
 import android.os.IBinder;
@@ -44,9 +43,7 @@
  * Implementations are then responsible for updating the state of the {@code Connection}, and
  * must call {@link #destroy()} to signal to the framework that the {@code Connection} is no
  * longer used and associated resources may be recovered.
- * @hide
  */
-@SystemApi
 public abstract class Connection implements IConferenceable {
 
     public static final int STATE_INITIALIZING = 0;
@@ -187,57 +184,22 @@
     public static final int CAPABILITY_SPEED_UP_MT_AUDIO = 0x00040000;
 
     /**
-     * Call type can be modified for IMS call
+     * Call can be upgraded to a video call.
      * @hide
      */
     public static final int CAPABILITY_CAN_UPGRADE_TO_VIDEO = 0x00080000;
 
+    /**
+     * For video calls, indicates whether the outgoing video for the call can be paused using
+     * the {@link android.telecom.VideoProfile.VideoState#PAUSED} VideoState.
+     * @hide
+     */
+    public static final int CAPABILITY_CAN_PAUSE_VIDEO = 0x00100000;
+
     //**********************************************************************************************
-    // Next CAPABILITY value: 0x00100000
+    // Next CAPABILITY value: 0x00200000
     //**********************************************************************************************
 
-    /**
-     * Call substate bitmask values
-     */
-
-    /* Default case */
-    /**
-     * @hide
-     */
-    public static final int SUBSTATE_NONE = 0;
-
-    /* Indicates that the call is connected but audio attribute is suspended */
-    /**
-     * @hide
-     */
-    public static final int SUBSTATE_AUDIO_CONNECTED_SUSPENDED = 0x1;
-
-    /* Indicates that the call is connected but video attribute is suspended */
-    /**
-     * @hide
-     */
-    public static final int SUBSTATE_VIDEO_CONNECTED_SUSPENDED = 0x2;
-
-    /* Indicates that the call is established but media retry is needed */
-    /**
-     * @hide
-     */
-    public static final int SUBSTATE_AVP_RETRY = 0x4;
-
-    /* Indicates that the call is multitasking */
-    /**
-     * @hide
-     */
-    public static final int SUBSTATE_MEDIA_PAUSED = 0x8;
-
-    /* Mask containing all the call substate bits set */
-    /**
-     * @hide
-     */
-    public static final int SUBSTATE_ALL = SUBSTATE_AUDIO_CONNECTED_SUSPENDED |
-        SUBSTATE_VIDEO_CONNECTED_SUSPENDED | SUBSTATE_AVP_RETRY |
-        SUBSTATE_MEDIA_PAUSED;
-
     // Flag controlling whether PII is emitted into the logs
     private static final boolean PII_DEBUG = Log.isLoggable(android.util.Log.DEBUG);
 
@@ -345,6 +307,9 @@
         if (can(capabilities, CAPABILITY_CAN_UPGRADE_TO_VIDEO)) {
             builder.append(" CAPABILITY_CAN_UPGRADE_TO_VIDEO");
         }
+        if (can(capabilities, CAPABILITY_CAN_PAUSE_VIDEO)) {
+            builder.append(" CAPABILITY_CAN_PAUSE_VIDEO");
+        }
         builder.append("]");
         return builder.toString();
     }
@@ -373,10 +338,8 @@
         public void onConferenceParticipantsChanged(Connection c,
                 List<ConferenceParticipant> participants) {}
         public void onConferenceStarted() {}
-        public void onCallSubstateChanged(Connection c, int substate) {}
     }
 
-    /** @hide */
     public static abstract class VideoProvider {
 
         /**
@@ -801,7 +764,6 @@
     private DisconnectCause mDisconnectCause;
     private Conference mConference;
     private ConnectionService mConnectionService;
-    private int mCallSubstate;
 
     /**
      * Create a new Connection.
@@ -860,21 +822,6 @@
     }
 
     /**
-     * Returns the call substate of the call.
-     * Valid values: {@link Connection#SUBSTATE_NONE},
-     * {@link Connection#SUBSTATE_AUDIO_CONNECTED_SUSPENDED},
-     * {@link Connection#SUBSTATE_VIDEO_CONNECTED_SUSPENDED},
-     * {@link Connection#SUBSTATE_AVP_RETRY},
-     * {@link Connection#SUBSTATE_MEDIA_PAUSED}.
-     *
-     * @param callSubstate The new call substate.
-     * @hide
-     */
-    public final int getCallSubstate() {
-        return mCallSubstate;
-    }
-
-    /**
      * @return The audio state of the connection, describing how its audio is currently
      *         being routed by the system. This is {@code null} if this Connection
      *         does not directly know about its audio state.
@@ -994,11 +941,6 @@
         return mConnectionCapabilities;
     }
 
-    /** @hide */
-    @SystemApi @Deprecated public final int getCallCapabilities() {
-        return getConnectionCapabilities();
-    }
-
     /**
      * Sets the value of the {@link #getAddress()} property.
      *
@@ -1053,25 +995,6 @@
     }
 
     /**
-     * Set the call substate for the connection.
-     * Valid values: {@link Connection#SUBSTATE_NONE},
-     * {@link Connection#SUBSTATE_AUDIO_CONNECTED_SUSPENDED},
-     * {@link Connection#SUBSTATE_VIDEO_CONNECTED_SUSPENDED},
-     * {@link Connection#SUBSTATE_AVP_RETRY},
-     * {@link Connection#SUBSTATE_MEDIA_PAUSED}.
-     *
-     * @param callSubstate The new call substate.
-     * @hide
-     */
-    public final void setCallSubstate(int callSubstate) {
-        Log.d(this, "setCallSubstate %d", callSubstate);
-        mCallSubstate = callSubstate;
-        for (Listener l : mListeners) {
-            l.onCallSubstateChanged(this, mCallSubstate);
-        }
-    }
-
-    /**
      * Sets state to active (e.g., an ongoing connection where two or more parties can actively
      * communicate).
      */
@@ -1134,7 +1057,6 @@
         }
     }
 
-    /** @hide */
     public final VideoProvider getVideoProvider() {
         return mVideoProvider;
     }
@@ -1202,11 +1124,6 @@
         }
     }
 
-    /** @hide */
-    @SystemApi @Deprecated public final void setCallCapabilities(int connectionCapabilities) {
-        setConnectionCapabilities(connectionCapabilities);
-    }
-
     /**
      * Sets the connection's capabilities as a bit mask of the {@code CAPABILITY_*} constants.
      *
@@ -1596,6 +1513,7 @@
 
     /**
      * Notifies listeners that a conference call has been started.
+     * @hide
      */
     protected void notifyConferenceStarted() {
         for (Listener l : mListeners) {
diff --git a/telecomm/java/android/telecom/ConnectionRequest.java b/telecomm/java/android/telecom/ConnectionRequest.java
index f691c17..71b481b 100644
--- a/telecomm/java/android/telecom/ConnectionRequest.java
+++ b/telecomm/java/android/telecom/ConnectionRequest.java
@@ -16,7 +16,6 @@
 
 package android.telecom;
 
-import android.annotation.SystemApi;
 import android.net.Uri;
 import android.os.Bundle;
 import android.os.Parcel;
@@ -25,9 +24,7 @@
 /**
  * Simple data container encapsulating a request to some entity to
  * create a new {@link Connection}.
- * @hide
  */
-@SystemApi
 public final class ConnectionRequest implements Parcelable {
 
     // TODO: Token to limit recursive invocations
diff --git a/telecomm/java/android/telecom/ConnectionService.java b/telecomm/java/android/telecom/ConnectionService.java
index 0c719cd..73d1644 100644
--- a/telecomm/java/android/telecom/ConnectionService.java
+++ b/telecomm/java/android/telecom/ConnectionService.java
@@ -17,7 +17,6 @@
 package android.telecom;
 
 import android.annotation.SdkConstant;
-import android.annotation.SystemApi;
 import android.app.Service;
 import android.content.ComponentName;
 import android.content.Intent;
@@ -72,9 +71,7 @@
  * receives call-commands such as answer, reject, hold and disconnect.
  * <p>
  * When there are no more live calls, telecom will unbind from the {@code ConnectionService}.
- * @hide
  */
-@SystemApi
 public abstract class ConnectionService extends Service {
     /**
      * The {@link Intent} that must be declared as handled by the service.
@@ -559,13 +556,6 @@
                 mAdapter.setIsConferenced(id, conferenceId);
             }
         }
-
-        @Override
-        public void onCallSubstateChanged(Connection c, int callSubstate) {
-            String id = mIdByConnection.get(c);
-            Log.d(this, "Adapter set call substate %d", callSubstate);
-            mAdapter.setCallSubstate(id, callSubstate);
-        }
     };
 
     /** {@inheritDoc} */
@@ -635,8 +625,7 @@
                         connection.getAudioModeIsVoip(),
                         connection.getStatusHints(),
                         connection.getDisconnectCause(),
-                        createIdList(connection.getConferenceables()),
-                        connection.getCallSubstate()));
+                        createIdList(connection.getConferenceables())));
     }
 
     private void abort(String callId) {
@@ -959,7 +948,7 @@
                     connection.getAudioModeIsVoip(),
                     connection.getStatusHints(),
                     connection.getDisconnectCause(),
-                    emptyList, connection.getCallSubstate());
+                    emptyList);
             mAdapter.addExistingConnection(id, parcelableConnection);
         }
     }
diff --git a/telecomm/java/android/telecom/ConnectionServiceAdapter.java b/telecomm/java/android/telecom/ConnectionServiceAdapter.java
index a410976..d026a28 100644
--- a/telecomm/java/android/telecom/ConnectionServiceAdapter.java
+++ b/telecomm/java/android/telecom/ConnectionServiceAdapter.java
@@ -369,26 +369,4 @@
             }
         }
     }
-
-    /**
-     * Set the call substate for the connection.
-     * Valid values: {@link Connection#CALL_SUBSTATE_NONE},
-     * {@link Connection#CALL_SUBSTATE_AUDIO_CONNECTED_SUSPENDED},
-     * {@link Connection#CALL_SUBSTATE_VIDEO_CONNECTED_SUSPENDED},
-     * {@link Connection#CALL_SUBSTATE_AVP_RETRY},
-     * {@link Connection#CALL_SUBSTATE_MEDIA_PAUSED}.
-     *
-     * @param callId The unique ID of the call to set the substate for.
-     * @param callSubstate The new call substate.
-     * @hide
-     */
-    public final void setCallSubstate(String callId, int callSubstate) {
-        Log.v(this, "setCallSubstate: %d", callSubstate);
-        for (IConnectionServiceAdapter adapter : mAdapters) {
-            try {
-                adapter.setCallSubstate(callId, callSubstate);
-            } catch (RemoteException ignored) {
-            }
-        }
-    }
 }
diff --git a/telecomm/java/android/telecom/ConnectionServiceAdapterServant.java b/telecomm/java/android/telecom/ConnectionServiceAdapterServant.java
index 5f93789..429f296 100644
--- a/telecomm/java/android/telecom/ConnectionServiceAdapterServant.java
+++ b/telecomm/java/android/telecom/ConnectionServiceAdapterServant.java
@@ -59,7 +59,6 @@
     private static final int MSG_SET_CONFERENCEABLE_CONNECTIONS = 20;
     private static final int MSG_ADD_EXISTING_CONNECTION = 21;
     private static final int MSG_ON_POST_DIAL_CHAR = 22;
-    private static final int MSG_SET_CALL_SUBSTATE = 23;
 
     private final IConnectionServiceAdapter mDelegate;
 
@@ -221,10 +220,6 @@
                     }
                     break;
                 }
-                case MSG_SET_CALL_SUBSTATE: {
-                    mDelegate.setCallSubstate((String) msg.obj, msg.arg1);
-                    break;
-                }
             }
         }
     };
@@ -389,12 +384,6 @@
             args.arg2 = connection;
             mHandler.obtainMessage(MSG_ADD_EXISTING_CONNECTION, args).sendToTarget();
         }
-
-        @Override
-        public void setCallSubstate(String connectionId, int callSubstate) {
-            mHandler.obtainMessage(MSG_SET_CALL_SUBSTATE, callSubstate, 0,
-                connectionId).sendToTarget();
-        }
     };
 
     public ConnectionServiceAdapterServant(IConnectionServiceAdapter delegate) {
diff --git a/telecomm/java/android/telecom/DisconnectCause.java b/telecomm/java/android/telecom/DisconnectCause.java
index 130d676..73bcd0c 100644
--- a/telecomm/java/android/telecom/DisconnectCause.java
+++ b/telecomm/java/android/telecom/DisconnectCause.java
@@ -16,7 +16,6 @@
 
 package android.telecom;
 
-import android.annotation.SystemApi;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.media.ToneGenerator;
@@ -30,9 +29,7 @@
  * user. It is the responsibility of the {@link ConnectionService} to provide localized versions of
  * the label and description. It also may contain a reason for the disconnect, which is intended for
  * logging and not for display to the user.
- * @hide
  */
-@SystemApi
 public final class DisconnectCause implements Parcelable {
 
     /** Disconnected because of an unknown or unspecified reason. */
diff --git a/telecomm/java/android/telecom/GatewayInfo.java b/telecomm/java/android/telecom/GatewayInfo.java
index 5b8e4ab..928570e 100644
--- a/telecomm/java/android/telecom/GatewayInfo.java
+++ b/telecomm/java/android/telecom/GatewayInfo.java
@@ -16,7 +16,6 @@
 
 package android.telecom;
 
-import android.annotation.SystemApi;
 import android.net.Uri;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -34,17 +33,13 @@
  * <li> Call the appropriate gateway address.
  * <li> Display information about how the call is being routed to the user.
  * </ol>
- * @hide
  */
-@SystemApi
 public class GatewayInfo implements Parcelable {
 
     private final String mGatewayProviderPackageName;
     private final Uri mGatewayAddress;
     private final Uri mOriginalAddress;
 
-    /** @hide */
-    @SystemApi
     public GatewayInfo(String packageName, Uri gatewayUri, Uri originalAddress) {
         mGatewayProviderPackageName = packageName;
         mGatewayAddress = gatewayUri;
diff --git a/telecomm/java/android/telecom/IConferenceable.java b/telecomm/java/android/telecom/IConferenceable.java
index 095d7cb..a9be20b 100644
--- a/telecomm/java/android/telecom/IConferenceable.java
+++ b/telecomm/java/android/telecom/IConferenceable.java
@@ -16,16 +16,11 @@
 
 package android.telecom;
 
-import android.annotation.SystemApi;
-
 /**
  * Interface used to identify entities with which another entity can participate in a conference
  * call with.  The {@link ConnectionService} implementation will only recognize
  * {@link IConferenceable}s which are {@link Connection}s or {@link Conference}s.
- *
- * @hide
  */
-@SystemApi
 public interface IConferenceable {
 
 }
diff --git a/telecomm/java/android/telecom/InCallService.java b/telecomm/java/android/telecom/InCallService.java
index c0c59fa..7cbc0fc 100644
--- a/telecomm/java/android/telecom/InCallService.java
+++ b/telecomm/java/android/telecom/InCallService.java
@@ -16,8 +16,8 @@
 
 package android.telecom;
 
-import android.annotation.SystemApi;
 import android.annotation.SdkConstant;
+import android.annotation.SystemApi;
 import android.app.Service;
 import android.content.Intent;
 import android.os.Handler;
@@ -31,15 +31,14 @@
 import com.android.internal.telecom.IInCallService;
 
 import java.lang.String;
+import java.util.Collections;
+import java.util.List;
 
 /**
  * This service is implemented by any app that wishes to provide the user-interface for managing
  * phone calls. Telecom binds to this service while there exists a live (active or incoming) call,
  * and uses it to notify the in-call app of any live and and recently disconnected calls.
- *
- * {@hide}
  */
-@SystemApi
 public abstract class InCallService extends Service {
 
     /**
@@ -67,6 +66,7 @@
             switch (msg.what) {
                 case MSG_SET_IN_CALL_ADAPTER:
                     mPhone = new Phone(new InCallAdapter((IInCallAdapter) msg.obj));
+                    mPhone.addListener(mPhoneListener);
                     onPhoneCreated(mPhone);
                     break;
                 case MSG_ADD_CALL:
@@ -148,6 +148,39 @@
         }
     }
 
+    private Phone.Listener mPhoneListener = new Phone.Listener() {
+        /** ${inheritDoc} */
+        @Override
+        public void onAudioStateChanged(Phone phone, AudioState audioState) {
+            InCallService.this.onAudioStateChanged(audioState);
+        }
+
+        /** ${inheritDoc} */
+        @Override
+        public void onBringToForeground(Phone phone, boolean showDialpad) {
+            InCallService.this.onBringToForeground(showDialpad);
+        }
+
+        /** ${inheritDoc} */
+        @Override
+        public void onCallAdded(Phone phone, Call call) {
+            InCallService.this.onCallAdded(call);
+        }
+
+        /** ${inheritDoc} */
+        @Override
+        public void onCallRemoved(Phone phone, Call call) {
+            InCallService.this.onCallRemoved(call);
+        }
+
+        /** ${inheritDoc} */
+        @Override
+        public void onCanAddCallChanged(Phone phone, boolean canAddCall) {
+            InCallService.this.onCanAddCallChanged(canAddCall);
+        }
+
+    };
+
     private Phone mPhone;
 
     public InCallService() {
@@ -165,8 +198,14 @@
             mPhone = null;
 
             oldPhone.destroy();
+            // destroy sets all the calls to disconnected if any live ones still exist. Therefore,
+            // it is important to remove the Listener *after* the call to destroy so that
+            // InCallService.on* callbacks are appropriately called.
+            oldPhone.removeListener(mPhoneListener);
+
             onPhoneDestroyed(oldPhone);
         }
+
         return false;
     }
 
@@ -176,19 +215,79 @@
      * @return The {@code Phone} object associated with this {@code InCallService}, or {@code null}
      *         if the {@code InCallService} is not in a state where it has an associated
      *         {@code Phone}.
+     * @hide
+     * @deprecated Use direct methods on InCallService instead of {@link Phone}.
      */
+    @SystemApi
+    @Deprecated
     public Phone getPhone() {
         return mPhone;
     }
 
     /**
+     * Obtains the current list of {@code Call}s to be displayed by this in-call experience.
+     *
+     * @return A list of the relevant {@code Call}s.
+     */
+    public final List<Call> getCalls() {
+        return mPhone == null ? Collections.<Call>emptyList() : mPhone.getCalls();
+    }
+
+    /**
+     * Returns if the device can support additional calls.
+     *
+     * @return Whether the phone supports adding more calls.
+     */
+    public final boolean canAddCall() {
+        return mPhone == null ? false : mPhone.canAddCall();
+    }
+
+    /**
+     * Obtains the current phone call audio state.
+     *
+     * @return An object encapsulating the audio state. Returns null if the service is not
+     *         fully initialized.
+     */
+    public final AudioState getAudioState() {
+        return mPhone == null ? null : mPhone.getAudioState();
+    }
+
+    /**
+     * Sets the microphone mute state. When this request is honored, there will be change to
+     * the {@link #getAudioState()}.
+     *
+     * @param state {@code true} if the microphone should be muted; {@code false} otherwise.
+     */
+    public final void setMuted(boolean state) {
+        if (mPhone != null) {
+            mPhone.setMuted(state);
+        }
+    }
+
+    /**
+     * Sets the audio route (speaker, bluetooth, etc...).  When this request is honored, there will
+     * be change to the {@link #getAudioState()}.
+     *
+     * @param route The audio route to use.
+     */
+    public final void setAudioRoute(int route) {
+        if (mPhone != null) {
+            mPhone.setAudioRoute(route);
+        }
+    }
+
+    /**
      * Invoked when the {@code Phone} has been created. This is a signal to the in-call experience
      * to start displaying in-call information to the user. Each instance of {@code InCallService}
      * will have only one {@code Phone}, and this method will be called exactly once in the lifetime
      * of the {@code InCallService}.
      *
      * @param phone The {@code Phone} object associated with this {@code InCallService}.
+     * @hide
+     * @deprecated Use direct methods on InCallService instead of {@link Phone}.
      */
+    @SystemApi
+    @Deprecated
     public void onPhoneCreated(Phone phone) {
     }
 
@@ -199,23 +298,76 @@
      * call to {@link #onPhoneCreated(Phone)}.
      *
      * @param phone The {@code Phone} object associated with this {@code InCallService}.
+     * @hide
+     * @deprecated Use direct methods on InCallService instead of {@link Phone}.
      */
+    @SystemApi
+    @Deprecated
     public void onPhoneDestroyed(Phone phone) {
     }
 
     /**
+     * Called when the audio state changes.
+     *
+     * @param audioState The new {@link AudioState}.
+     */
+    public void onAudioStateChanged(AudioState audioState) {
+    }
+
+    /**
+     * Called to bring the in-call screen to the foreground. The in-call experience should
+     * respond immediately by coming to the foreground to inform the user of the state of
+     * ongoing {@code Call}s.
+     *
+     * @param showDialpad If true, put up the dialpad when the screen is shown.
+     */
+    public void onBringToForeground(boolean showDialpad) {
+    }
+
+    /**
+     * Called when a {@code Call} has been added to this in-call session. The in-call user
+     * experience should add necessary state listeners to the specified {@code Call} and
+     * immediately start to show the user information about the existence
+     * and nature of this {@code Call}. Subsequent invocations of {@link #getCalls()} will
+     * include this {@code Call}.
+     *
+     * @param call A newly added {@code Call}.
+     */
+    public void onCallAdded(Call call) {
+    }
+
+    /**
+     * Called when a {@code Call} has been removed from this in-call session. The in-call user
+     * experience should remove any state listeners from the specified {@code Call} and
+     * immediately stop displaying any information about this {@code Call}.
+     * Subsequent invocations of {@link #getCalls()} will no longer include this {@code Call}.
+     *
+     * @param call A newly removed {@code Call}.
+     */
+    public void onCallRemoved(Call call) {
+    }
+
+    /**
+     * Called when the ability to add more calls changes.  If the phone cannot
+     * support more calls then {@code canAddCall} is set to {@code false}.  If it can, then it
+     * is set to {@code true}. This can be used to control the visibility of UI to add more calls.
+     *
+     * @param canAddCall Indicates whether an additional call can be added.
+     */
+    public void onCanAddCallChanged(boolean canAddCall) {
+    }
+
+    /**
      * Class to invoke functionality related to video calls.
-     * @hide
      */
     public static abstract class VideoCall {
 
         /**
-         * Sets a listener to invoke callback methods in the InCallUI after performing video
-         * telephony actions.
+         * Registers a callback to receive commands and state changes for video calls.
          *
-         * @param videoCallListener The call video client.
+         * @param callback The video call callback.
          */
-        public abstract void setVideoCallListener(VideoCall.Listener videoCallListener);
+        public abstract void registerCallback(VideoCall.Callback callback);
 
         /**
          * Sets the camera to be used for video recording in a video call.
@@ -258,7 +410,7 @@
         /**
          * Issues a request to modify the properties of the current session.  The request is sent to
          * the remote device where it it handled by
-         * {@link VideoCall.Listener#onSessionModifyRequestReceived}.
+         * {@link VideoCall.Callback#onSessionModifyRequestReceived}.
          * Some examples of session modification requests: upgrade call from audio to video,
          * downgrade call from video to audio, pause video.
          *
@@ -270,9 +422,9 @@
          * Provides a response to a request to change the current call session video
          * properties.
          * This is in response to a request the InCall UI has received via
-         * {@link VideoCall.Listener#onSessionModifyRequestReceived}.
+         * {@link VideoCall.Callback#onSessionModifyRequestReceived}.
          * The response is handled on the remove device by
-         * {@link VideoCall.Listener#onSessionModifyResponseReceived}.
+         * {@link VideoCall.Callback#onSessionModifyResponseReceived}.
          *
          * @param responseProfile The response call video properties.
          */
@@ -281,14 +433,14 @@
         /**
          * Issues a request to the video provider to retrieve the camera capabilities.
          * Camera capabilities are reported back to the caller via
-         * {@link VideoCall.Listener#onCameraCapabilitiesChanged(CameraCapabilities)}.
+         * {@link VideoCall.Callback#onCameraCapabilitiesChanged(CameraCapabilities)}.
          */
         public abstract void requestCameraCapabilities();
 
         /**
          * Issues a request to the video telephony framework to retrieve the cumulative data usage for
          * the current call.  Data usage is reported back to the caller via
-         * {@link VideoCall.Listener#onCallDataUsageChanged}.
+         * {@link VideoCall.Callback#onCallDataUsageChanged}.
          */
         public abstract void requestCallDataUsage();
 
@@ -301,10 +453,9 @@
         public abstract void setPauseImage(String uri);
 
         /**
-         * Listener class which invokes callbacks after video call actions occur.
-         * @hide
+         * Callback class which invokes callbacks after video call actions occur.
          */
-        public static abstract class Listener {
+        public static abstract class Callback {
             /**
              * Called when a session modification request is received from the remote device.
              * The remote request is sent via
@@ -379,8 +530,7 @@
              *
              * @param cameraCapabilities The changed camera capabilities.
              */
-            public abstract void onCameraCapabilitiesChanged(
-                    CameraCapabilities cameraCapabilities);
+            public abstract void onCameraCapabilitiesChanged(CameraCapabilities cameraCapabilities);
         }
     }
 }
diff --git a/telecomm/java/android/telecom/ParcelableCall.java b/telecomm/java/android/telecom/ParcelableCall.java
index adc648f..c5c3d11 100644
--- a/telecomm/java/android/telecom/ParcelableCall.java
+++ b/telecomm/java/android/telecom/ParcelableCall.java
@@ -54,7 +54,6 @@
     private final int mVideoState;
     private final List<String> mConferenceableCallIds;
     private final Bundle mExtras;
-    private int mCallSubstate;
 
     public ParcelableCall(
             String id,
@@ -76,8 +75,7 @@
             StatusHints statusHints,
             int videoState,
             List<String> conferenceableCallIds,
-            Bundle extras,
-            int callSubstate) {
+            Bundle extras) {
         mId = id;
         mState = state;
         mDisconnectCause = disconnectCause;
@@ -98,7 +96,6 @@
         mVideoState = videoState;
         mConferenceableCallIds = Collections.unmodifiableList(conferenceableCallIds);
         mExtras = extras;
-        mCallSubstate = callSubstate;
     }
 
     /** The unique ID of the call. */
@@ -235,14 +232,6 @@
         return mExtras;
     }
 
-    /**
-     * The call substate.
-     * @return The substate of the call.
-     */
-    public int getCallSubstate() {
-        return mCallSubstate;
-    }
-
     /** Responsible for creating ParcelableCall objects for deserialized Parcels. */
     public static final Parcelable.Creator<ParcelableCall> CREATOR =
             new Parcelable.Creator<ParcelableCall> () {
@@ -273,7 +262,6 @@
             List<String> conferenceableCallIds = new ArrayList<>();
             source.readList(conferenceableCallIds, classLoader);
             Bundle extras = source.readParcelable(classLoader);
-            int callSubstate = source.readInt();
             return new ParcelableCall(
                     id,
                     state,
@@ -294,8 +282,7 @@
                     statusHints,
                     videoState,
                     conferenceableCallIds,
-                    extras,
-                    callSubstate);
+                    extras);
         }
 
         @Override
@@ -334,7 +321,6 @@
         destination.writeInt(mVideoState);
         destination.writeList(mConferenceableCallIds);
         destination.writeParcelable(mExtras, 0);
-        destination.writeInt(mCallSubstate);
     }
 
     @Override
diff --git a/telecomm/java/android/telecom/ParcelableConnection.java b/telecomm/java/android/telecom/ParcelableConnection.java
index b60b99d..552e250 100644
--- a/telecomm/java/android/telecom/ParcelableConnection.java
+++ b/telecomm/java/android/telecom/ParcelableConnection.java
@@ -46,7 +46,6 @@
     private final StatusHints mStatusHints;
     private final DisconnectCause mDisconnectCause;
     private final List<String> mConferenceableConnectionIds;
-    private final int mCallSubstate;
 
     /** @hide */
     public ParcelableConnection(
@@ -63,8 +62,7 @@
             boolean isVoipAudioMode,
             StatusHints statusHints,
             DisconnectCause disconnectCause,
-            List<String> conferenceableConnectionIds,
-            int callSubstate) {
+            List<String> conferenceableConnectionIds) {
         mPhoneAccount = phoneAccount;
         mState = state;
         mConnectionCapabilities = capabilities;
@@ -79,7 +77,6 @@
         mStatusHints = statusHints;
         mDisconnectCause = disconnectCause;
         this.mConferenceableConnectionIds = conferenceableConnectionIds;
-        mCallSubstate = callSubstate;
     }
 
     public PhoneAccountHandle getPhoneAccount() {
@@ -139,10 +136,6 @@
         return mConferenceableConnectionIds;
     }
 
-    public int getCallSubstate() {
-        return mCallSubstate;
-    }
-
     @Override
     public String toString() {
         return new StringBuilder()
@@ -177,7 +170,6 @@
             DisconnectCause disconnectCause = source.readParcelable(classLoader);
             List<String> conferenceableConnectionIds = new ArrayList<>();
             source.readStringList(conferenceableConnectionIds);
-            int callSubstate = source.readInt();
 
             return new ParcelableConnection(
                     phoneAccount,
@@ -193,8 +185,7 @@
                     audioModeIsVoip,
                     statusHints,
                     disconnectCause,
-                    conferenceableConnectionIds,
-                    callSubstate);
+                    conferenceableConnectionIds);
         }
 
         @Override
@@ -227,6 +218,5 @@
         destination.writeParcelable(mStatusHints, 0);
         destination.writeParcelable(mDisconnectCause, 0);
         destination.writeStringList(mConferenceableConnectionIds);
-        destination.writeInt(mCallSubstate);
     }
 }
diff --git a/telecomm/java/android/telecom/Phone.java b/telecomm/java/android/telecom/Phone.java
index cc73109..c1c1129 100644
--- a/telecomm/java/android/telecom/Phone.java
+++ b/telecomm/java/android/telecom/Phone.java
@@ -28,9 +28,11 @@
 /**
  * A unified virtual device providing a means of voice (and other) communication on a device.
  *
- * {@hide}
+ * @hide
+ * @deprecated Use {@link InCallService} directly instead of using this class.
  */
 @SystemApi
+@Deprecated
 public final class Phone {
 
     public abstract static class Listener {
@@ -104,12 +106,10 @@
 
     private boolean mCanAddCall = true;
 
-    /** {@hide} */
     Phone(InCallAdapter adapter) {
         mInCallAdapter = adapter;
     }
 
-    /** {@hide} */
     final void internalAddCall(ParcelableCall parcelableCall) {
         Call call = new Call(this, parcelableCall.getId(), mInCallAdapter);
         mCallByTelecomCallId.put(parcelableCall.getId(), call);
@@ -119,14 +119,12 @@
         fireCallAdded(call);
      }
 
-    /** {@hide} */
     final void internalRemoveCall(Call call) {
         mCallByTelecomCallId.remove(call.internalGetCallId());
         mCalls.remove(call);
         fireCallRemoved(call);
     }
 
-    /** {@hide} */
     final void internalUpdateCall(ParcelableCall parcelableCall) {
          Call call = mCallByTelecomCallId.get(parcelableCall.getId());
          if (call != null) {
@@ -135,7 +133,6 @@
          }
      }
 
-    /** {@hide} */
     final void internalSetPostDialWait(String telecomId, String remaining) {
         Call call = mCallByTelecomCallId.get(telecomId);
         if (call != null) {
@@ -143,7 +140,6 @@
         }
     }
 
-    /** {@hide} */
     final void internalAudioStateChanged(AudioState audioState) {
         if (!Objects.equals(mAudioState, audioState)) {
             mAudioState = audioState;
@@ -151,17 +147,14 @@
         }
     }
 
-    /** {@hide} */
     final Call internalGetCallByTelecomId(String telecomId) {
         return mCallByTelecomCallId.get(telecomId);
     }
 
-    /** {@hide} */
     final void internalBringToForeground(boolean showDialpad) {
         fireBringToForeground(showDialpad);
     }
 
-    /** {@hide} */
     final void internalSetCanAddCall(boolean canAddCall) {
         if (mCanAddCall != canAddCall) {
             mCanAddCall = canAddCall;
@@ -171,7 +164,6 @@
 
     /**
      * Called to destroy the phone and cleanup any lingering calls.
-     * @hide
      */
     final void destroy() {
         for (Call call : mCalls) {
diff --git a/telecomm/java/android/telecom/PhoneAccount.java b/telecomm/java/android/telecom/PhoneAccount.java
index 07f9053..bab460d 100644
--- a/telecomm/java/android/telecom/PhoneAccount.java
+++ b/telecomm/java/android/telecom/PhoneAccount.java
@@ -60,9 +60,7 @@
      * 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;
 
     /**
@@ -74,9 +72,7 @@
      * <p>
      * See {@link #getCapabilities}
      * <p>
-     * {@hide}
      */
-    @SystemApi
     public static final int CAPABILITY_CALL_PROVIDER = 0x2;
 
     /**
@@ -203,13 +199,6 @@
             mSupportedUriSchemes.addAll(phoneAccount.getSupportedUriSchemes());
         }
 
-        /** @hide */
-        @SystemApi
-        public Builder setAccountHandle(PhoneAccountHandle accountHandle) {
-            mAccountHandle = accountHandle;
-            return this;
-        }
-
         /**
          * Sets the address. See {@link PhoneAccount#getAddress}.
          *
@@ -333,9 +322,7 @@
          *
          * @param uriScheme The URI scheme.
          * @return The builder.
-         * @hide
          */
-        @SystemApi
         public Builder addSupportedUriScheme(String uriScheme) {
             if (!TextUtils.isEmpty(uriScheme) && !mSupportedUriSchemes.contains(uriScheme)) {
                 this.mSupportedUriSchemes.add(uriScheme);
@@ -424,9 +411,7 @@
      * Returns a builder initialized with the current {@link PhoneAccount} instance.
      *
      * @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 4600b72..60917b2 100644
--- a/telecomm/java/android/telecom/PhoneAccountHandle.java
+++ b/telecomm/java/android/telecom/PhoneAccountHandle.java
@@ -16,7 +16,6 @@
 
 package android.telecom;
 
-import android.annotation.SystemApi;
 import android.content.ComponentName;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -47,8 +46,6 @@
         this(componentName, id, Process.myUserHandle());
     }
 
-    /** @hide */
-    @SystemApi
     public PhoneAccountHandle(
             ComponentName componentName,
             String id,
@@ -91,9 +88,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/RemoteConference.java b/telecomm/java/android/telecom/RemoteConference.java
index a8879ae..fba3ee3 100644
--- a/telecomm/java/android/telecom/RemoteConference.java
+++ b/telecomm/java/android/telecom/RemoteConference.java
@@ -18,7 +18,6 @@
 
 import com.android.internal.telecom.IConnectionService;
 
-import android.annotation.SystemApi;
 import android.os.RemoteException;
 
 import java.util.ArrayList;
@@ -30,9 +29,7 @@
 
 /**
  * Represents a conference call which can contain any number of {@link Connection} objects.
- * @hide
  */
-@SystemApi
 public final class RemoteConference {
 
     public abstract static class Callback {
@@ -164,11 +161,6 @@
         return mState;
     }
 
-    /** @hide */
-    @Deprecated public final int getCallCapabilities() {
-        return getConnectionCapabilities();
-    }
-
     public final int getConnectionCapabilities() {
         return mConnectionCapabilities;
     }
diff --git a/telecomm/java/android/telecom/RemoteConnection.java b/telecomm/java/android/telecom/RemoteConnection.java
index be7a0a0..4c423f2 100644
--- a/telecomm/java/android/telecom/RemoteConnection.java
+++ b/telecomm/java/android/telecom/RemoteConnection.java
@@ -20,7 +20,6 @@
 import com.android.internal.telecom.IVideoCallback;
 import com.android.internal.telecom.IVideoProvider;
 
-import android.annotation.SystemApi;
 import android.net.Uri;
 import android.os.IBinder;
 import android.os.RemoteException;
@@ -38,9 +37,7 @@
  *
  * @see ConnectionService#createRemoteOutgoingConnection(PhoneAccountHandle, ConnectionRequest)
  * @see ConnectionService#createRemoteIncomingConnection(PhoneAccountHandle, ConnectionRequest)
- * @hide
  */
-@SystemApi
 public final class RemoteConnection {
 
     public static abstract class Callback {
@@ -73,11 +70,6 @@
          */
         public void onRingbackRequested(RemoteConnection connection, boolean ringback) {}
 
-        /** @hide */
-        @Deprecated public void onCallCapabilitiesChanged(
-                RemoteConnection connection,
-                int callCapabilities) {}
-
         /**
          * Indicates that the call capabilities of this {@code RemoteConnection} have changed.
          * See {@link #getConnectionCapabilities()}.
@@ -161,16 +153,6 @@
         public void onVideoStateChanged(RemoteConnection connection, int videoState) {}
 
         /**
-         * Indicates that the call substate of this {@code RemoteConnection} has changed.
-         * See {@link #getCallSubstate()}.
-         *
-         * @param connection The {@code RemoteConnection} invoking this method.
-         * @param callSubstate The new call substate of the {@code RemoteConnection}.
-         * @hide
-         */
-        public void onCallSubstateChanged(RemoteConnection connection, int callSubstate) {}
-
-        /**
          * Indicates that this {@code RemoteConnection} has been destroyed. No further requests
          * should be made to the {@code RemoteConnection}, and references to it should be cleared.
          *
@@ -422,7 +404,6 @@
     private boolean mConnected;
     private int mConnectionCapabilities;
     private int mVideoState;
-    private int mCallSubstate;
     private VideoProvider mVideoProvider;
     private boolean mIsVoipAudioMode;
     private StatusHints mStatusHints;
@@ -603,15 +584,6 @@
     }
 
     /**
-     *
-     * @return The call substate of the {@code RemoteConnection}. See
-     * @hide
-     */
-    public int getCallSubstate() {
-        return mCallSubstate;
-    }
-
-    /**
      * Obtains the video provider of this {@code RemoteConnection}.
      * @return The video provider associated with this {@code RemoteConnection}.
      * @hide
@@ -870,7 +842,6 @@
         mConnectionCapabilities = connectionCapabilities;
         for (Callback c : mCallbacks) {
             c.onConnectionCapabilitiesChanged(this, connectionCapabilities);
-            c.onCallCapabilitiesChanged(this, connectionCapabilities);
         }
     }
 
@@ -925,16 +896,6 @@
     /**
      * @hide
      */
-    void setCallSubstate(int callSubstate) {
-        mCallSubstate = callSubstate;
-        for (Callback c : mCallbacks) {
-            c.onCallSubstateChanged(this, callSubstate);
-        }
-    }
-
-    /**
-     * @hide
-     */
     void setVideoProvider(VideoProvider videoProvider) {
         mVideoProvider = videoProvider;
         for (Callback c : mCallbacks) {
diff --git a/telecomm/java/android/telecom/RemoteConnectionService.java b/telecomm/java/android/telecom/RemoteConnectionService.java
index 7374a3b..a9b725b 100644
--- a/telecomm/java/android/telecom/RemoteConnectionService.java
+++ b/telecomm/java/android/telecom/RemoteConnectionService.java
@@ -84,7 +84,6 @@
                 }
                 connection.setConferenceableConnections(conferenceable);
                 connection.setVideoState(parcel.getVideoState());
-                connection.setCallSubstate(parcel.getCallSubstate());
                 if (connection.getState() == Connection.STATE_DISCONNECTED) {
                     // ... then, if it was created in a disconnected state, that indicates
                     // failure on the providing end, so immediately mark it destroyed
@@ -312,12 +311,6 @@
 
             mOurConnectionServiceImpl.addRemoteExistingConnection(remoteConnction);
         }
-
-        @Override
-        public void setCallSubstate(String callId, int callSubstate) {
-            findConnectionForAction(callId, "callSubstate")
-                    .setCallSubstate(callSubstate);
-        }
     };
 
     private final ConnectionServiceAdapterServant mServant =
diff --git a/telecomm/java/android/telecom/StatusHints.java b/telecomm/java/android/telecom/StatusHints.java
index dd3a639..a32eae7 100644
--- a/telecomm/java/android/telecom/StatusHints.java
+++ b/telecomm/java/android/telecom/StatusHints.java
@@ -16,7 +16,6 @@
 
 package android.telecom;
 
-import android.annotation.SystemApi;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.pm.PackageManager;
@@ -30,9 +29,7 @@
 
 /**
  * Contains status label and icon displayed in the in-call UI.
- * @hide
  */
-@SystemApi
 public final class StatusHints implements Parcelable {
 
     private final ComponentName mPackageName;
diff --git a/telecomm/java/android/telecom/TelecomManager.java b/telecomm/java/android/telecom/TelecomManager.java
index ab7864b..5abbb50 100644
--- a/telecomm/java/android/telecom/TelecomManager.java
+++ b/telecomm/java/android/telecom/TelecomManager.java
@@ -55,8 +55,6 @@
      * Input: get*Extra field {@link #EXTRA_PHONE_ACCOUNT_HANDLE} contains the component name of the
      * {@link android.telecom.ConnectionService} that Telecom should bind to. Telecom will then
      * ask the connection service for more information about the call prior to showing any UI.
-     *
-     * @hide
      */
     public static final String ACTION_INCOMING_CALL = "android.telecom.action.INCOMING_CALL";
 
@@ -97,9 +95,7 @@
     /**
      * The {@link android.content.Intent} action used to show the settings page used to configure
      * {@link PhoneAccount} preferences.
-     * @hide
      */
-    @SystemApi
     public static final String ACTION_CHANGE_PHONE_ACCOUNTS =
             "android.telecom.action.CHANGE_PHONE_ACCOUNTS";
 
@@ -145,10 +141,7 @@
      * Optional extra for {@link #ACTION_INCOMING_CALL} containing a {@link Bundle} which contains
      * metadata about the call. This {@link Bundle} will be returned to the
      * {@link ConnectionService}.
-     *
-     * @hide
      */
-    @SystemApi
     public static final String EXTRA_INCOMING_CALL_EXTRAS =
             "android.telecom.extra.INCOMING_CALL_EXTRAS";
 
@@ -221,9 +214,7 @@
      * {@link ConnectionService}s which interact with {@link RemoteConnection}s should only populate
      * this if the {@link android.telephony.TelephonyManager#getLine1Number()} value, as that is the
      * user's expected caller ID.
-     * @hide
      */
-    @SystemApi
     public static final String EXTRA_CALL_BACK_NUMBER = "android.telecom.extra.CALL_BACK_NUMBER";
 
     /**
@@ -364,9 +355,7 @@
      * @param uriScheme The URI scheme.
      * @return The {@link PhoneAccountHandle} corresponding to the user-chosen default for outgoing
      * phone calls for a specified URI scheme.
-     * @hide
      */
-    @SystemApi
     public PhoneAccountHandle getDefaultOutgoingPhoneAccount(String uriScheme) {
         try {
             if (isServiceConnected()) {
@@ -419,7 +408,6 @@
      * {@code null}, indicating that there currently exists no user-chosen default
      * {@code PhoneAccount}.
      * @return The phone account handle of the current sim call manager.
-     * @hide
      */
     public PhoneAccountHandle getSimCallManager() {
         try {
@@ -532,19 +520,6 @@
     }
 
     /**
-     * Determine whether the device has more than one account registered that can make and receive
-     * phone calls.
-     *
-     * @return {@code true} if the device has more than one account registered and {@code false}
-     * otherwise.
-     * @hide
-     */
-    @SystemApi
-    public boolean hasMultipleCallCapableAccounts() {
-        return getCallCapablePhoneAccounts().size() > 1;
-    }
-
-    /**
      *  Returns a list of all {@link PhoneAccount}s registered for the calling package.
      *
      * @return A list of {@code PhoneAccountHandle} objects.
@@ -647,10 +622,7 @@
      * {@link PhoneAccountHandle#getComponentName()} does not match the package name of the app.
      *
      * @param account The complete {@link PhoneAccount}.
-     *
-     * @hide
      */
-    @SystemApi
     public void registerPhoneAccount(PhoneAccount account) {
         try {
             if (isServiceConnected()) {
@@ -665,9 +637,7 @@
      * Remove a {@link PhoneAccount} registration from the system.
      *
      * @param accountHandle A {@link PhoneAccountHandle} for the {@link PhoneAccount} to unregister.
-     * @hide
      */
-    @SystemApi
     public void unregisterPhoneAccount(PhoneAccountHandle accountHandle) {
         try {
             if (isServiceConnected()) {
@@ -683,6 +653,15 @@
      * @hide
      */
     @SystemApi
+    public void clearPhoneAccounts() {
+        clearAccounts();
+    }
+    /**
+     * Remove all Accounts that belong to the calling package from the system.
+     * @deprecated Use {@link #clearPhoneAccounts()} instead.
+     * @hide
+     */
+    @SystemApi
     public void clearAccounts() {
         try {
             if (isServiceConnected()) {
@@ -728,10 +707,7 @@
      *
      * @param accountHandle The handle for the account to check the voicemail number against
      * @param number The number to look up.
-     *
-     * @hide
      */
-    @SystemApi
     public boolean isVoiceMailNumber(PhoneAccountHandle accountHandle, String number) {
         try {
             if (isServiceConnected()) {
@@ -744,23 +720,21 @@
     }
 
     /**
-     * Return whether a given phone account has a voicemail number configured.
+     * Return the voicemail number for a given phone account.
      *
-     * @param accountHandle The handle for the account to check for a voicemail number.
-     * @return {@code true} If the given phone account has a voicemail number.
-     *
-     * @hide
+     * @param accountHandle The handle for the phone account.
+     * @return The voicemail number for the phone account, and {@code null} if one has not been
+     *         configured.
      */
-    @SystemApi
-    public boolean hasVoiceMailNumber(PhoneAccountHandle accountHandle) {
+    public String getVoiceMailNumber(PhoneAccountHandle accountHandle) {
         try {
             if (isServiceConnected()) {
-                return getTelecomService().hasVoiceMailNumber(accountHandle);
+                return getTelecomService().getVoiceMailNumber(accountHandle);
             }
         } catch (RemoteException e) {
             Log.e(TAG, "RemoteException calling ITelecomService#hasVoiceMailNumber.", e);
         }
-        return false;
+        return null;
     }
 
     /**
@@ -768,10 +742,7 @@
      *
      * @param accountHandle The handle for the account retrieve a number for.
      * @return A string representation of the line 1 phone number.
-     *
-     * @hide
      */
-    @SystemApi
     public String getLine1Number(PhoneAccountHandle accountHandle) {
         try {
             if (isServiceConnected()) {
@@ -881,10 +852,7 @@
 
     /**
      * Silences the ringer if a ringing call exists.
-     *
-     * @hide
      */
-    @SystemApi
     public void silenceRinger() {
         try {
             if (isServiceConnected()) {
@@ -945,9 +913,7 @@
      *            {@link #registerPhoneAccount}.
      * @param extras A bundle that will be passed through to
      *            {@link ConnectionService#onCreateIncomingConnection}.
-     * @hide
      */
-    @SystemApi
     public void addNewIncomingCall(PhoneAccountHandle phoneAccount, Bundle extras) {
         try {
             if (isServiceConnected()) {
@@ -1017,10 +983,8 @@
      * @param accountHandle The handle for the account the MMI code should apply to.
      * @param dialString The digits to dial.
      * @return True if the digits were processed as an MMI code, false otherwise.
-     * @hide
      */
-    @SystemApi
-    public boolean handleMmi(PhoneAccountHandle accountHandle, String dialString) {
+    public boolean handleMmi(String dialString, PhoneAccountHandle accountHandle) {
         ITelecomService service = getTelecomService();
         if (service != null) {
             try {
@@ -1037,9 +1001,7 @@
      * {@code null} to return a URI which will use the default account.
      * @return The URI (with the content:// scheme) specific to the specified {@link PhoneAccount}
      * for the the content retrieve.
-     * @hide
      */
-    @SystemApi
     public Uri getAdnUriForPhoneAccount(PhoneAccountHandle accountHandle) {
         ITelecomService service = getTelecomService();
         if (service != null && accountHandle != null) {
diff --git a/telecomm/java/android/telecom/VideoCallImpl.java b/telecomm/java/android/telecom/VideoCallImpl.java
index 0445448..7bef688 100644
--- a/telecomm/java/android/telecom/VideoCallImpl.java
+++ b/telecomm/java/android/telecom/VideoCallImpl.java
@@ -46,7 +46,7 @@
 
     private final IVideoProvider mVideoProvider;
     private final VideoCallListenerBinder mBinder;
-    private VideoCall.Listener mVideoCallListener;
+    private VideoCall.Callback mCallback;
 
     private IBinder.DeathRecipient mDeathRecipient = new IBinder.DeathRecipient() {
         @Override
@@ -109,14 +109,14 @@
     private final Handler mHandler = new Handler(Looper.getMainLooper()) {
         @Override
         public void handleMessage(Message msg) {
-            if (mVideoCallListener == null) {
+            if (mCallback == null) {
                 return;
             }
 
             SomeArgs args;
             switch (msg.what) {
                 case MSG_RECEIVE_SESSION_MODIFY_REQUEST:
-                    mVideoCallListener.onSessionModifyRequestReceived((VideoProfile) msg.obj);
+                    mCallback.onSessionModifyRequestReceived((VideoProfile) msg.obj);
                     break;
                 case MSG_RECEIVE_SESSION_MODIFY_RESPONSE:
                     args = (SomeArgs) msg.obj;
@@ -125,34 +125,34 @@
                         VideoProfile requestProfile = (VideoProfile) args.arg2;
                         VideoProfile responseProfile = (VideoProfile) args.arg3;
 
-                        mVideoCallListener.onSessionModifyResponseReceived(
+                        mCallback.onSessionModifyResponseReceived(
                                 status, requestProfile, responseProfile);
                     } finally {
                         args.recycle();
                     }
                     break;
                 case MSG_HANDLE_CALL_SESSION_EVENT:
-                    mVideoCallListener.onCallSessionEvent((int) msg.obj);
+                    mCallback.onCallSessionEvent((int) msg.obj);
                     break;
                 case MSG_CHANGE_PEER_DIMENSIONS:
                     args = (SomeArgs) msg.obj;
                     try {
                         int width = (int) args.arg1;
                         int height = (int) args.arg2;
-                        mVideoCallListener.onPeerDimensionsChanged(width, height);
+                        mCallback.onPeerDimensionsChanged(width, height);
                     } finally {
                         args.recycle();
                     }
                     break;
                 case MSG_CHANGE_CALL_DATA_USAGE:
-                    mVideoCallListener.onCallDataUsageChanged((long) msg.obj);
+                    mCallback.onCallDataUsageChanged((long) msg.obj);
                     break;
                 case MSG_CHANGE_CAMERA_CAPABILITIES:
-                    mVideoCallListener.onCameraCapabilitiesChanged(
+                    mCallback.onCameraCapabilitiesChanged(
                             (CameraCapabilities) msg.obj);
                     break;
                 case MSG_CHANGE_VIDEO_QUALITY:
-                    mVideoCallListener.onVideoQualityChanged(msg.arg1);
+                    mCallback.onVideoQualityChanged(msg.arg1);
                     break;
                 default:
                     break;
@@ -170,8 +170,8 @@
     }
 
     /** {@inheritDoc} */
-    public void setVideoCallListener(VideoCall.Listener videoCallListener) {
-        mVideoCallListener = videoCallListener;
+    public void registerCallback(VideoCall.Callback callback) {
+        mCallback = callback;
     }
 
     /** {@inheritDoc} */
diff --git a/telecomm/java/android/telecom/Voicemail.java b/telecomm/java/android/telecom/Voicemail.java
index 186c199..f5b8052 100644
--- a/telecomm/java/android/telecom/Voicemail.java
+++ b/telecomm/java/android/telecom/Voicemail.java
@@ -16,7 +16,6 @@
 
 package android.telecom;
 
-import android.annotation.SystemApi;
 import android.net.Uri;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -26,7 +25,6 @@
  *
  * @hide
  */
-@SystemApi
 public class Voicemail implements Parcelable {
     private final Long mTimestamp;
     private final String mNumber;
@@ -277,4 +275,4 @@
         mIsRead = in.readInt() > 0 ? true : false;
         mHasContent = in.readInt() > 0 ? true : false;
     }
-}
\ No newline at end of file
+}
diff --git a/telecomm/java/com/android/internal/telecom/IConnectionServiceAdapter.aidl b/telecomm/java/com/android/internal/telecom/IConnectionServiceAdapter.aidl
index e6c28f3..7e7e9cc 100644
--- a/telecomm/java/com/android/internal/telecom/IConnectionServiceAdapter.aidl
+++ b/telecomm/java/com/android/internal/telecom/IConnectionServiceAdapter.aidl
@@ -81,6 +81,4 @@
     void setConferenceableConnections(String callId, in List<String> conferenceableCallIds);
 
     void addExistingConnection(String callId, in ParcelableConnection connection);
-
-    void setCallSubstate(String callId, int callSubstate);
 }
diff --git a/telecomm/java/com/android/internal/telecom/ITelecomService.aidl b/telecomm/java/com/android/internal/telecom/ITelecomService.aidl
index d2030f2..35db97f 100644
--- a/telecomm/java/com/android/internal/telecom/ITelecomService.aidl
+++ b/telecomm/java/com/android/internal/telecom/ITelecomService.aidl
@@ -121,9 +121,9 @@
     boolean isVoiceMailNumber(in PhoneAccountHandle accountHandle, String number);
 
     /**
-     * @see TelecomServiceImpl#hasVoiceMailNumber
+     * @see TelecomServiceImpl#getVoiceMailNumber
      */
-    boolean hasVoiceMailNumber(in PhoneAccountHandle accountHandle);
+    String getVoiceMailNumber(in PhoneAccountHandle accountHandle);
 
     /**
      * @see TelecomServiceImpl#getLine1Number
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
new file mode 100644
index 0000000..b1fb3a6
--- /dev/null
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -0,0 +1,219 @@
+/*
+ * 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.telephony;
+
+import com.android.internal.telephony.ICarrierConfigLoader;
+
+import android.annotation.SystemApi;
+import android.content.Context;
+import android.os.Bundle;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+
+/**
+ * Provides access to telephony configuration values that are carrier-specific.
+ * <p>
+ * Users should obtain an instance of this class by calling
+ * {@code mContext.getSystemService(Context.CARRIER_CONFIG_SERVICE);}
+ * </p>
+ *
+ * @see Context#getSystemService
+ * @see Context#CARRIER_CONFIG_SERVICE
+ */
+public class CarrierConfigManager {
+    /**
+     * @hide
+     */
+    public CarrierConfigManager() {
+    }
+
+    /**
+     * This intent is broadcast by the system when carrier config changes.
+     */
+    public static final String
+            ACTION_CARRIER_CONFIG_CHANGED = "android.telephony.action.CARRIER_CONFIG_CHANGED";
+
+    /**
+     * Flag specifying whether VoLTE should be available for carrier, independent of carrier
+     * provisioning. If false: hard disabled. If true: then depends on carrier provisioning,
+     * availability, etc.
+     */
+    public static final String BOOL_CARRIER_VOLTE_AVAILABLE = "bool_carrier_volte_available";
+
+    /**
+     * Flag specifying whether VoLTE availability is based on provisioning.
+     */
+    public static final String BOOL_CARRIER_VOLTE_PROVISIONED = "bool_carrier_volte_provisioned";
+
+    /**
+     * Flag specifying whether VoLTE TTY is supported.
+     */
+    public static final String BOOL_CARRIER_VOLTE_TTY_SUPPORTED
+            = "bool_carrier_volte_tty_supported";
+
+    /**
+     * Show APN Settings for some CDMA carriers.
+     */
+    public static final String BOOL_SHOW_APN_SETTING_CDMA = "bool_show_apn_setting_cdma";
+
+    /**
+     * If Voice Radio Technology is RIL_RADIO_TECHNOLOGY_LTE:14 or RIL_RADIO_TECHNOLOGY_UNKNOWN:0
+     * this is the value that should be used instead. A configuration value of
+     * RIL_RADIO_TECHNOLOGY_UNKNOWN:0 means there is no replacement value and that the default
+     * assumption for phone type (GSM) should be used.
+     */
+    public static final String INT_VOLTE_REPLACEMENT_RAT = "int_volte_replacement_rat";
+
+    /* The following 3 fields are related to carrier visual voicemail. */
+
+    /**
+     *  The carrier number MO sms messages are sent to.
+     *
+     *  @hide
+     */
+    @SystemApi
+    public static final String STRING_VVM_DESTINATION_NUMBER = "string_vvm_destination_number";
+
+    /**
+     * The port through which the MO sms messages are sent through.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final String SHORT_VVM_PORT_NUMBER = "string_vvm_port_number";
+
+    /**
+     * The type of visual voicemail protocol the carrier adheres to (see below).
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final String STRING_VVM_TYPE = "string_vvm_type";
+
+    /* Visual voicemail protocols */
+
+    /**
+     * The OMTP protocol.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final String VVM_TYPE_OMTP = "vvm_type_omtp";
+
+    private final static String TAG = "CarrierConfigManager";
+
+    /** The default value for every variable. */
+    private final static Bundle sDefaults;
+
+    static {
+        sDefaults = new Bundle();
+        sDefaults.putBoolean(BOOL_CARRIER_VOLTE_AVAILABLE, false);
+        sDefaults.putBoolean(BOOL_CARRIER_VOLTE_PROVISIONED, false);
+        sDefaults.putBoolean(BOOL_CARRIER_VOLTE_TTY_SUPPORTED, true);
+        sDefaults.putBoolean(BOOL_SHOW_APN_SETTING_CDMA, false);
+
+        sDefaults.putInt(INT_VOLTE_REPLACEMENT_RAT, 0);
+    }
+
+    /**
+     * Gets the configuration values for a particular subscription, which is associated with a
+     * specific SIM card. If an invalid subId is used, the returned config will contain default
+     * values.
+     *
+     * @param subId the subscription ID, normally obtained from {@link SubscriptionManager}.
+     * @return A {@link Bundle} containing the config for the given subId, or default values for an
+     *         invalid subId.
+     */
+    public Bundle getConfigForSubId(int subId) {
+        try {
+            return getICarrierConfigLoader().getConfigForSubId(subId);
+        } catch (RemoteException ex) {
+            Rlog.e(TAG, "Error getting config for subId " + Integer.toString(subId) + ": "
+                    + ex.toString());
+        } catch (NullPointerException ex) {
+            Rlog.e(TAG, "Error getting config for subId " + Integer.toString(subId) + ": "
+                    + ex.toString());
+        }
+        return null;
+    }
+
+    /**
+     * Gets the configuration values for the default subscription.
+     *
+     * @see #getConfigForSubId
+     */
+    public Bundle getConfig() {
+        return getConfigForSubId(SubscriptionManager.getDefaultSubId());
+    }
+
+    /**
+     * Calling this method triggers telephony services to fetch the current carrier configuration.
+     * <p>
+     * Normally this does not need to be called because the platform reloads config on its own. Call
+     * this method if your app wants to update config at an arbitrary moment.
+     * </p>
+     * <p>
+     * This method returns before the reload has completed, and
+     * {@link android.service.carrier.CarrierConfigService#onLoadConfig} will be called from an
+     * arbitrary thread.
+     * </p>
+     */
+    public void reloadCarrierConfigForSubId(int subId) {
+        try {
+            getICarrierConfigLoader().reloadCarrierConfigForSubId(subId);
+        } catch (RemoteException ex) {
+            Rlog.e(TAG, "Error reloading config for subId=" + subId + ": " + ex.toString());
+        } catch (NullPointerException ex) {
+            Rlog.e(TAG, "Error reloading config for subId=" + subId + ": " + ex.toString());
+        }
+    }
+
+    /**
+     * Request the carrier config loader to update the cofig for phoneId.
+     *
+     * Depending on simState, the config may be cleared or loaded from config app.
+     * This is only used by SubscriptionInfoUpdater.
+     *
+     * @hide
+     */
+    @SystemApi
+    public void updateConfigForPhoneId(int phoneId, String simState) {
+        try {
+            getICarrierConfigLoader().updateConfigForPhoneId(phoneId, simState);
+        } catch (RemoteException ex) {
+            Rlog.e(TAG, "Error updating config for phoneId=" + phoneId + ": " + ex.toString());
+        } catch (NullPointerException ex) {
+            Rlog.e(TAG, "Error updating config for phoneId=" + phoneId + ": " + ex.toString());
+        }
+    }
+
+    /**
+     * Returns a bundle with the default value for every supported configuration variable.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static Bundle getDefaultConfig() {
+        return sDefaults;
+    }
+
+    /** @hide */
+    private ICarrierConfigLoader getICarrierConfigLoader() {
+        return ICarrierConfigLoader.Stub
+                .asInterface(ServiceManager.getService(Context.CARRIER_CONFIG_SERVICE));
+    }
+}
diff --git a/telephony/java/android/telephony/PhoneStateListener.java b/telephony/java/android/telephony/PhoneStateListener.java
index 611dd7bd..d192288 100644
--- a/telephony/java/android/telephony/PhoneStateListener.java
+++ b/telephony/java/android/telephony/PhoneStateListener.java
@@ -219,6 +219,15 @@
      */
     public static final int LISTEN_OEM_HOOK_RAW_EVENT                       = 0x00008000;
 
+    /**
+     * Listen for carrier network changes indicated by a carrier app.
+     *
+     * @see #onCarrierNetworkRequest
+     * @see TelephonyManager#notifyCarrierNetworkChange(boolean)
+     * @hide
+     */
+    public static final int LISTEN_CARRIER_NETWORK_CHANGE                   = 0x00010000;
+
      /*
      * Subscription used to listen to the phone state changes
      * @hide
@@ -321,6 +330,9 @@
                     case LISTEN_OEM_HOOK_RAW_EVENT:
                         PhoneStateListener.this.onOemHookRawEvent((byte[])msg.obj);
                         break;
+                    case LISTEN_CARRIER_NETWORK_CHANGE:
+                        PhoneStateListener.this.onCarrierNetworkChange((boolean)msg.obj);
+                        break;
 
                 }
             }
@@ -500,6 +512,22 @@
     }
 
     /**
+     * Callback invoked when telephony has received notice from a carrier
+     * app that a network action that could result in connectivity loss
+     * has been requested by an app using
+     * {@link android.telephony.TelephonyManager#notifyCarrierNetworkChange(boolean)}
+     *
+     * @param active Whether the carrier network change is or shortly
+     *               will be active. This value is true to indicate
+     *               showing alternative UI and false to stop.
+     *
+     * @hide
+     */
+    public void onCarrierNetworkChange(boolean active) {
+        // default implementation empty
+    }
+
+    /**
      * The callback methods need to be called on the handler thread where
      * this object was created.  If the binder did that for us it'd be nice.
      */
@@ -575,6 +603,10 @@
         public void onOemHookRawEvent(byte[] rawData) {
             Message.obtain(mHandler, LISTEN_OEM_HOOK_RAW_EVENT, 0, 0, rawData).sendToTarget();
         }
+
+        public void onCarrierNetworkChange(boolean active) {
+            Message.obtain(mHandler, LISTEN_CARRIER_NETWORK_CHANGE, 0, 0, active).sendToTarget();
+        }
     };
 
     private void log(String s) {
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index c5573ba..128f6e3 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -160,7 +160,6 @@
      * Returns 1 for Single standby mode (Single SIM functionality)
      * Returns 2 for Dual standby mode.(Dual SIM functionality)
      */
-    /** {@hide} */
     public int getPhoneCount() {
         int phoneCount = 1;
         switch (getMultiSimConfiguration()) {
@@ -682,7 +681,6 @@
      *
      * @param slotId of which deviceID is returned
      */
-    /** {@hide} */
     public String getDeviceId(int slotId) {
         // FIXME this assumes phoneId == slotId
         try {
@@ -2041,6 +2039,35 @@
     }
 
     /**
+     * Informs the system of an intentional upcoming carrier network change by
+     * a carrier app. This call is optional and is only used to allow the
+     * system to provide alternative UI while telephony is performing an action
+     * that may result in intentional, temporary network lack of connectivity.
+     * <p>
+     * Based on the active parameter passed in, this method will either show or
+     * hide the alternative UI. There is no timeout associated with showing
+     * this UX, so a carrier app must be sure to call with active set to false
+     * sometime after calling with it set to true.
+     * <p>
+     * Requires Permission:
+     *   {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}
+     * Or the calling app has carrier privileges.
+     *   @see #hasCarrierPrivileges
+     *
+     * @param active Whether the carrier network change is or shortly will be
+     *               active. Set this value to true to begin showing
+     *               alternative UI and false to stop.
+     */
+    public void notifyCarrierNetworkChange(boolean active) {
+        try {
+            if (sRegistry != null)
+                sRegistry.notifyCarrierNetworkChange(active);
+        } catch (RemoteException ex) {
+        } catch (NullPointerException ex) {
+        }
+    }
+
+    /**
      * Returns the alphabetic identifier associated with the line 1 number.
      * Return null if it is unavailable.
      * <p>
diff --git a/telephony/java/com/android/ims/internal/IImsCallSession.aidl b/telephony/java/com/android/ims/internal/IImsCallSession.aidl
index 9b435dc..b1f2d32 100644
--- a/telephony/java/com/android/ims/internal/IImsCallSession.aidl
+++ b/telephony/java/com/android/ims/internal/IImsCallSession.aidl
@@ -255,11 +255,4 @@
      * @return {@code True} if the session is multiparty.
      */
     boolean isMultiparty();
-
-    /**
-     * Gets the call substate for this session.
-     *
-     * @return the call substate for this session.
-     */
-    int getCallSubstate();
 }
diff --git a/telephony/java/com/android/internal/telephony/ICarrierConfigLoader.aidl b/telephony/java/com/android/internal/telephony/ICarrierConfigLoader.aidl
new file mode 100644
index 0000000..b5cdd9a2
--- /dev/null
+++ b/telephony/java/com/android/internal/telephony/ICarrierConfigLoader.aidl
@@ -0,0 +1,31 @@
+/*
+ * 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.telephony;
+
+import android.os.Bundle;
+
+/**
+ * Interface used to interact with the CarrierConfigLoader
+ */
+interface ICarrierConfigLoader {
+
+    Bundle getConfigForSubId(int subId);
+
+    void reloadCarrierConfigForSubId(int subId);
+
+    void updateConfigForPhoneId(int phoneId, String simState);
+}
diff --git a/telephony/java/com/android/internal/telephony/IPhoneStateListener.aidl b/telephony/java/com/android/internal/telephony/IPhoneStateListener.aidl
index cea62ba..cbedb95 100644
--- a/telephony/java/com/android/internal/telephony/IPhoneStateListener.aidl
+++ b/telephony/java/com/android/internal/telephony/IPhoneStateListener.aidl
@@ -44,5 +44,6 @@
     void onDataConnectionRealTimeInfoChanged(in DataConnectionRealTimeInfo dcRtInfo);
     void onVoLteServiceStateChanged(in VoLteServiceState lteState);
     void onOemHookRawEvent(in byte[] rawData);
+    void onCarrierNetworkChange(in boolean active);
 }
 
diff --git a/telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl b/telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl
index 7d8a8d6..76b69ce 100644
--- a/telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl
@@ -69,4 +69,5 @@
     void notifyVoLteServiceStateChanged(in VoLteServiceState lteState);
     void notifyOemHookRawEventForSubscriber(in int subId, in byte[] rawData);
     void notifySubscriptionInfoChanged();
+    void notifyCarrierNetworkChange(in boolean active);
 }
diff --git a/telephony/java/com/android/internal/telephony/RILConstants.java b/telephony/java/com/android/internal/telephony/RILConstants.java
index 082e8bbb..12541d8 100644
--- a/telephony/java/com/android/internal/telephony/RILConstants.java
+++ b/telephony/java/com/android/internal/telephony/RILConstants.java
@@ -69,6 +69,14 @@
     int SS_MODIFIED_TO_USSD = 25;             /* SS request modified to USSD */
     int SUBSCRIPTION_NOT_SUPPORTED = 26;      /* Subscription not supported */
     int SS_MODIFIED_TO_SS = 27;               /* SS request modified to different SS request */
+    int SIM_ALREADY_POWERED_OFF = 29;         /* SAP: 0x03, Error card aleready powered off */
+    int SIM_ALREADY_POWERED_ON = 30;          /* SAP: 0x05, Error card already powered on */
+    int SIM_DATA_NOT_AVAILABLE = 31;          /* SAP: 0x06, Error data not available */
+    int SIM_SAP_CONNECT_FAILURE = 32;
+    int SIM_SAP_MSG_SIZE_TOO_LARGE = 33;
+    int SIM_SAP_MSG_SIZE_TOO_SMALL = 34;
+    int SIM_SAP_CONNECT_OK_CALL_ONGOING = 35;
+    int LCE_NOT_SUPPORTED = 36;               /* Link Capacity Estimation (LCE) not supported */
 
 
     /* NETWORK_MODE_* See ril.h RIL_REQUEST_SET_PREFERRED_NETWORK_TYPE */
@@ -135,6 +143,11 @@
     int NV_CONFIG_ERASE_RESET = 2;
     int NV_CONFIG_FACTORY_RESET = 3;
 
+    /* LCE service related constants. */
+    int LCE_NOT_AVAILABLE = -1;
+    int LCE_STOPPED = 0;
+    int LCE_ACTIVE = 1;
+
 /*
 cat include/telephony/ril.h | \
    egrep '^#define' | \
@@ -307,6 +320,9 @@
     int RIL_REQUEST_SHUTDOWN = 129;
     int RIL_REQUEST_GET_RADIO_CAPABILITY = 130;
     int RIL_REQUEST_SET_RADIO_CAPABILITY = 131;
+    int RIL_REQUEST_START_LCE = 132;
+    int RIL_REQUEST_STOP_LCE = 133;
+    int RIL_REQUEST_PULL_LCEDATA = 134;
 
     int RIL_UNSOL_RESPONSE_BASE = 1000;
     int RIL_UNSOL_RESPONSE_RADIO_STATE_CHANGED = 1000;
@@ -354,4 +370,5 @@
     int RIL_UNSOL_RADIO_CAPABILITY = 1042;
     int RIL_UNSOL_ON_SS = 1043;
     int RIL_UNSOL_STK_CC_ALPHA_NOTIFY = 1044;
+    int RIL_UNSOL_LCEDATA_RECV = 1045;
 }
diff --git a/test-runner/src/android/test/mock/MockPackageManager.java b/test-runner/src/android/test/mock/MockPackageManager.java
index e78750c..5a8c7ff 100644
--- a/test-runner/src/android/test/mock/MockPackageManager.java
+++ b/test-runner/src/android/test/mock/MockPackageManager.java
@@ -16,6 +16,7 @@
 
 package android.test.mock;
 
+import android.annotation.NonNull;
 import android.app.PackageInstallObserver;
 import android.content.ComponentName;
 import android.content.Intent;
@@ -51,6 +52,7 @@
 import android.graphics.drawable.Drawable;
 import android.net.Uri;
 import android.os.UserHandle;
+import android.os.storage.VolumeInfo;
 
 import java.util.List;
 
@@ -498,6 +500,18 @@
         throw new UnsupportedOperationException();
     }
 
+    /** {@hide} */
+    @Override
+    public @NonNull VolumeInfo getApplicationCurrentVolume(ApplicationInfo app) {
+        throw new UnsupportedOperationException();
+    }
+
+    /** {@hide} */
+    @Override
+    public @NonNull List<VolumeInfo> getApplicationCandidateVolumes(ApplicationInfo app) {
+        throw new UnsupportedOperationException();
+    }
+
     @Override
     public String getInstallerPackageName(String packageName) {
         throw new UnsupportedOperationException();
diff --git a/tests/LockTaskTests/Android.mk b/tests/LockTaskTests/Android.mk
new file mode 100644
index 0000000..ed58643
--- /dev/null
+++ b/tests/LockTaskTests/Android.mk
@@ -0,0 +1,15 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := optional
+LOCAL_MODULE_PATH := $(PRODUCT_OUT)/system/priv-app
+
+LOCAL_PACKAGE_NAME := LockTaskTests
+LOCAL_CERTIFICATE := platform
+
+LOCAL_SRC_FILES := $(call all-Iaidl-files-under, src) $(call all-java-files-under, src)
+
+include $(BUILD_PACKAGE)
+
+# Use the following include to make our test apk.
+include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/tests/LockTaskTests/AndroidManifest.xml b/tests/LockTaskTests/AndroidManifest.xml
new file mode 100644
index 0000000..f88744e
--- /dev/null
+++ b/tests/LockTaskTests/AndroidManifest.xml
@@ -0,0 +1,57 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.google.android.example.locktasktests"
+    android:versionCode="1"
+    android:versionName="1.0" >
+
+    <uses-sdk
+        android:minSdkVersion="22"
+        android:targetSdkVersion="22" />
+    <uses-permission android:name="android.permission.INTERNET"/>
+
+    <application
+        android:icon="@drawable/ic_launcher"
+        android:label="@string/app_name"
+        android:theme="@style/AppTheme"
+        android:allowBackup="true" >
+        <activity
+            android:name="com.google.android.example.locktasktests.MainActivity"
+            android:label="@string/app_name"
+            android:screenOrientation="portrait"
+            android:theme="@style/AppTheme" >
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+        <activity
+            android:name="com.google.android.example.locktasktests.LockDefaultActivity"
+            android:label="@string/title_activity_default"
+            android:taskAffinity=""
+            android:documentLaunchMode="always"
+            android:lockTaskMode="lockTaskModeDefault" >
+        </activity>
+        <activity
+            android:name="com.google.android.example.locktasktests.LockTaskNeverActivity"
+            android:label="@string/title_activity_never"
+            android:taskAffinity=""
+            android:documentLaunchMode="always"
+            android:lockTaskMode="lockTaskModeNever" >
+        </activity>
+        <activity
+            android:name="com.google.android.example.locktasktests.LockWhitelistedActivity"
+            android:label="@string/title_activity_whitelist"
+            android:taskAffinity=""
+            android:documentLaunchMode="always"
+            android:lockTaskMode="lockTaskModeIfWhitelisted" >
+        </activity>
+        <activity
+            android:name="com.google.android.example.locktasktests.LockAtLaunchActivity"
+            android:label="@string/title_activity_always"
+            android:taskAffinity=""
+            android:documentLaunchMode="always"
+            android:lockTaskMode="lockTaskModeAlways" >
+        </activity>
+    </application>
+
+</manifest>
diff --git a/tests/LockTaskTests/res/drawable-hdpi/ic_launcher.png b/tests/LockTaskTests/res/drawable-hdpi/ic_launcher.png
new file mode 100644
index 0000000..288b665
--- /dev/null
+++ b/tests/LockTaskTests/res/drawable-hdpi/ic_launcher.png
Binary files differ
diff --git a/tests/LockTaskTests/res/drawable-mdpi/ic_launcher.png b/tests/LockTaskTests/res/drawable-mdpi/ic_launcher.png
new file mode 100644
index 0000000..6ae570b4
--- /dev/null
+++ b/tests/LockTaskTests/res/drawable-mdpi/ic_launcher.png
Binary files differ
diff --git a/tests/LockTaskTests/res/drawable-xhdpi/ic_launcher.png b/tests/LockTaskTests/res/drawable-xhdpi/ic_launcher.png
new file mode 100644
index 0000000..d4fb7cd
--- /dev/null
+++ b/tests/LockTaskTests/res/drawable-xhdpi/ic_launcher.png
Binary files differ
diff --git a/tests/LockTaskTests/res/drawable-xxhdpi/ic_launcher.png b/tests/LockTaskTests/res/drawable-xxhdpi/ic_launcher.png
new file mode 100644
index 0000000..85a6081
--- /dev/null
+++ b/tests/LockTaskTests/res/drawable-xxhdpi/ic_launcher.png
Binary files differ
diff --git a/tests/LockTaskTests/res/layout/activity_launch.xml b/tests/LockTaskTests/res/layout/activity_launch.xml
new file mode 100644
index 0000000..b619743
--- /dev/null
+++ b/tests/LockTaskTests/res/layout/activity_launch.xml
@@ -0,0 +1,32 @@
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:id="@+id/root_launch"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:orientation="vertical"
+    android:paddingBottom="@dimen/activity_vertical_margin"
+    android:paddingLeft="@dimen/activity_horizontal_margin"
+    android:paddingRight="@dimen/activity_horizontal_margin"
+    android:paddingTop="@dimen/activity_vertical_margin"
+    tools:context="com.google.android.example.locktasktests.LaunchActivity" >
+
+    <Button
+        android:id="@+id/button_try_lock"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:onClick="onTryLock"
+        android:text="@string/try_lock" />
+    <Button
+        android:id="@+id/button_try_unlock"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:onClick="onTryUnlock"
+        android:text="@string/try_unlock" />
+    <Button
+        android:id="@+id/button_launch_main"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:onClick="onLaunchMain"
+        android:text="@string/launch_main" />
+
+</LinearLayout>
diff --git a/tests/LockTaskTests/res/layout/activity_main.xml b/tests/LockTaskTests/res/layout/activity_main.xml
new file mode 100644
index 0000000..c2e93c4
--- /dev/null
+++ b/tests/LockTaskTests/res/layout/activity_main.xml
@@ -0,0 +1,41 @@
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:id="@+id/root_launch"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:orientation="vertical"
+    android:paddingBottom="@dimen/activity_vertical_margin"
+    android:paddingLeft="@dimen/activity_horizontal_margin"
+    android:paddingRight="@dimen/activity_horizontal_margin"
+    android:paddingTop="@dimen/activity_vertical_margin"
+    tools:context="com.google.android.example.locktasktests.MainActivity" >
+    <Button
+        android:id="@+id/button_default"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_marginTop="16dp"
+        android:onClick="onButtonPressed"
+        android:text="@string/launch_default" />
+    <Button
+        android:id="@+id/button_never"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_marginTop="16dp"
+        android:onClick="onButtonPressed"
+        android:text="@string/launch_never" />
+    <Button
+        android:id="@+id/button_whitelist"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_marginTop="16dp"
+        android:onClick="onButtonPressed"
+        android:text="@string/launch_whitelist" />
+    <Button
+        android:id="@+id/button_always"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_marginTop="16dp"
+        android:onClick="onButtonPressed"
+        android:text="@string/launch_always" />
+
+</LinearLayout>
diff --git a/tests/LockTaskTests/res/values-v11/styles.xml b/tests/LockTaskTests/res/values-v11/styles.xml
new file mode 100644
index 0000000..3c02242
--- /dev/null
+++ b/tests/LockTaskTests/res/values-v11/styles.xml
@@ -0,0 +1,11 @@
+<resources>
+
+    <!--
+        Base application theme for API 11+. This theme completely replaces
+        AppBaseTheme from res/values/styles.xml on API 11+ devices.
+    -->
+    <style name="AppBaseTheme" parent="android:Theme.Holo.Light">
+        <!-- API 11 theme customizations can go here. -->
+    </style>
+
+</resources>
diff --git a/tests/LockTaskTests/res/values-v14/styles.xml b/tests/LockTaskTests/res/values-v14/styles.xml
new file mode 100644
index 0000000..a91fd03
--- /dev/null
+++ b/tests/LockTaskTests/res/values-v14/styles.xml
@@ -0,0 +1,12 @@
+<resources>
+
+    <!--
+        Base application theme for API 14+. This theme completely replaces
+        AppBaseTheme from BOTH res/values/styles.xml and
+        res/values-v11/styles.xml on API 14+ devices.
+    -->
+    <style name="AppBaseTheme" parent="android:Theme.Holo.Light.DarkActionBar">
+        <!-- API 14 theme customizations can go here. -->
+    </style>
+
+</resources>
diff --git a/tests/LockTaskTests/res/values-w820dp/dimens.xml b/tests/LockTaskTests/res/values-w820dp/dimens.xml
new file mode 100644
index 0000000..f3e7020
--- /dev/null
+++ b/tests/LockTaskTests/res/values-w820dp/dimens.xml
@@ -0,0 +1,10 @@
+<resources>
+
+    <!--
+         Example customization of dimensions originally defined in res/values/dimens.xml
+         (such as screen margins) for screens with more than 820dp of available width. This
+         would include 7" and 10" devices in landscape (~960dp and ~1280dp respectively).
+    -->
+    <dimen name="activity_horizontal_margin">64dp</dimen>
+
+</resources>
diff --git a/tests/LockTaskTests/res/values/dimens.xml b/tests/LockTaskTests/res/values/dimens.xml
new file mode 100644
index 0000000..55c1e590
--- /dev/null
+++ b/tests/LockTaskTests/res/values/dimens.xml
@@ -0,0 +1,7 @@
+<resources>
+
+    <!-- Default screen margins, per the Android Design guidelines. -->
+    <dimen name="activity_horizontal_margin">16dp</dimen>
+    <dimen name="activity_vertical_margin">16dp</dimen>
+
+</resources>
diff --git a/tests/LockTaskTests/res/values/strings.xml b/tests/LockTaskTests/res/values/strings.xml
new file mode 100644
index 0000000..ae7768e
--- /dev/null
+++ b/tests/LockTaskTests/res/values/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+
+    <string name="app_name">Lock Task Tests</string>
+    <string name="title_activity_default">LockDefaultActivity</string>
+    <string name="title_activity_never">LockTaskNeverActivity</string>
+    <string name="title_activity_whitelist">LockWhitelistedActivity</string>
+    <string name="title_activity_always">LockAtLaunchActivity</string>
+    <string name="launch_default">android:lockTaskMode=\n
+            \"lockTaskModeDefault\"\n
+            Pinnable from Overview.</string>
+    <string name="launch_never">android:lockTaskMode=\n
+            \"lockTaskModeNever\"\n
+            Not Lockable or Pinnable.</string>
+    <string name="launch_whitelist">android:lockTaskMode=\n\"lockTaskModeIfWhitelisted\"\n
+            Lockable if whitelisted, Pinnable.\n
+            Use SampleDeviceOwner app to set whitelist.</string>
+    <string name="launch_always">android:lockTaskMode=\n
+            \"lockTaskModeAlways\"\n
+            Launches into lock mode.</string>
+    <string name="launch_main">launch MainActivity (as activity)"</string>
+    <string name="try_lock">Call startLockMode()</string>
+    <string name="try_unlock">Call stopLockMode()</string>
+
+</resources>
diff --git a/tests/LockTaskTests/res/values/styles.xml b/tests/LockTaskTests/res/values/styles.xml
new file mode 100644
index 0000000..6ce89c7
--- /dev/null
+++ b/tests/LockTaskTests/res/values/styles.xml
@@ -0,0 +1,20 @@
+<resources>
+
+    <!--
+        Base application theme, dependent on API level. This theme is replaced
+        by AppBaseTheme from res/values-vXX/styles.xml on newer devices.
+    -->
+    <style name="AppBaseTheme" parent="android:Theme.Light">
+        <!--
+            Theme customizations available in newer API levels can go in
+            res/values-vXX/styles.xml, while customizations related to
+            backward-compatibility can go here.
+        -->
+    </style>
+
+    <!-- Application theme. -->
+    <style name="AppTheme" parent="AppBaseTheme">
+        <!-- All customizations that are NOT specific to a particular API-level can go here. -->
+    </style>
+
+</resources>
diff --git a/tests/LockTaskTests/src/com/google/android/example/locktasktests/LaunchActivity.java b/tests/LockTaskTests/src/com/google/android/example/locktasktests/LaunchActivity.java
new file mode 100644
index 0000000..1fc53cb
--- /dev/null
+++ b/tests/LockTaskTests/src/com/google/android/example/locktasktests/LaunchActivity.java
@@ -0,0 +1,67 @@
+/*
+ * 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.google.android.example.locktasktests;
+
+import android.app.Activity;
+import android.app.ActivityManager;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+import android.view.View;
+import android.widget.EditText;
+
+public class LaunchActivity extends Activity {
+
+    EditText mEditText;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.activity_launch);
+        setBackgroundOnLockTaskMode();
+    }
+
+    @Override
+    public void onResume() {
+        super.onResume();
+        setBackgroundOnLockTaskMode();
+    }
+
+    public void onTryLock(View view) {
+        startLockTask();
+        setBackgroundOnLockTaskMode();
+    }
+
+    public void onTryUnlock(View view) {
+        stopLockTask();
+        setBackgroundOnLockTaskMode();
+    }
+
+    public void onLaunchMain(View view) {
+        Intent intent = new Intent(this, MainActivity.class);
+        startActivity(intent);
+    }
+
+    private void setBackgroundOnLockTaskMode() {
+        ActivityManager activityManager =
+                (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
+        final int color =
+                activityManager.getLockTaskModeState() != ActivityManager.LOCK_TASK_MODE_NONE ?
+                        0xFFFFC0C0 : 0xFFFFFFFF;
+        findViewById(R.id.root_launch).setBackgroundColor(color);
+    }
+}
diff --git a/tests/LockTaskTests/src/com/google/android/example/locktasktests/LockAtLaunchActivity.java b/tests/LockTaskTests/src/com/google/android/example/locktasktests/LockAtLaunchActivity.java
new file mode 100644
index 0000000..4390c94
--- /dev/null
+++ b/tests/LockTaskTests/src/com/google/android/example/locktasktests/LockAtLaunchActivity.java
@@ -0,0 +1,20 @@
+/*
+ * 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.google.android.example.locktasktests;
+
+public class LockAtLaunchActivity extends LaunchActivity {
+}
diff --git a/tests/LockTaskTests/src/com/google/android/example/locktasktests/LockDefaultActivity.java b/tests/LockTaskTests/src/com/google/android/example/locktasktests/LockDefaultActivity.java
new file mode 100644
index 0000000..7e57ab7
--- /dev/null
+++ b/tests/LockTaskTests/src/com/google/android/example/locktasktests/LockDefaultActivity.java
@@ -0,0 +1,20 @@
+/*
+ * 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.google.android.example.locktasktests;
+
+public class LockDefaultActivity extends LaunchActivity {
+}
diff --git a/tests/LockTaskTests/src/com/google/android/example/locktasktests/LockTaskNeverActivity.java b/tests/LockTaskTests/src/com/google/android/example/locktasktests/LockTaskNeverActivity.java
new file mode 100644
index 0000000..69c2cbc
--- /dev/null
+++ b/tests/LockTaskTests/src/com/google/android/example/locktasktests/LockTaskNeverActivity.java
@@ -0,0 +1,20 @@
+/*
+ * 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.google.android.example.locktasktests;
+
+public class LockTaskNeverActivity extends LaunchActivity {
+}
diff --git a/tests/LockTaskTests/src/com/google/android/example/locktasktests/LockWhitelistedActivity.java b/tests/LockTaskTests/src/com/google/android/example/locktasktests/LockWhitelistedActivity.java
new file mode 100644
index 0000000..387baa2
--- /dev/null
+++ b/tests/LockTaskTests/src/com/google/android/example/locktasktests/LockWhitelistedActivity.java
@@ -0,0 +1,20 @@
+/*
+ * 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.google.android.example.locktasktests;
+
+public class LockWhitelistedActivity extends LaunchActivity {
+}
diff --git a/tests/LockTaskTests/src/com/google/android/example/locktasktests/MainActivity.java b/tests/LockTaskTests/src/com/google/android/example/locktasktests/MainActivity.java
new file mode 100644
index 0000000..82fac03
--- /dev/null
+++ b/tests/LockTaskTests/src/com/google/android/example/locktasktests/MainActivity.java
@@ -0,0 +1,58 @@
+
+package com.google.android.example.locktasktests;
+
+import android.app.Activity;
+import android.app.ActivityManager;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+import android.util.Log;
+import android.view.View;
+
+public class MainActivity extends Activity {
+
+    private final static String TAG = "LockTaskTests";
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.activity_main);
+        setBackgroundOnLockTaskMode();
+    }
+
+    @Override
+    public void onResume() {
+        super.onResume();
+        setBackgroundOnLockTaskMode();
+    }
+
+    public void onButtonPressed(View v) {
+        Class activity = null;
+        switch (v.getId()) {
+            case R.id.button_default:
+                activity = LockDefaultActivity.class;
+                break;
+            case R.id.button_never:
+                activity = LockTaskNeverActivity.class;
+                break;
+            case R.id.button_whitelist:
+                activity = LockWhitelistedActivity.class;
+                break;
+            case R.id.button_always:
+                activity = LockAtLaunchActivity.class;
+                break;
+        }
+        Intent intent = new Intent(this, activity);
+        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+        startActivity(intent);
+    }
+
+    private void setBackgroundOnLockTaskMode() {
+        ActivityManager activityManager =
+                (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
+        final int color =
+                activityManager.getLockTaskModeState() != ActivityManager.LOCK_TASK_MODE_NONE ?
+                        0xFFFFC0C0 : 0xFFFFFFFF;
+        findViewById(R.id.root_launch).setBackgroundColor(color);
+    }
+}
diff --git a/tools/aapt2/Android.mk b/tools/aapt2/Android.mk
index 0622dc6..05034c3 100644
--- a/tools/aapt2/Android.mk
+++ b/tools/aapt2/Android.mk
@@ -27,6 +27,7 @@
 sources := \
 	BigBuffer.cpp \
 	BinaryResourceParser.cpp \
+	BinaryXmlPullParser.cpp \
 	BindingXmlPullParser.cpp \
 	ConfigDescription.cpp \
 	Files.cpp \
@@ -51,7 +52,9 @@
 	ScopedXmlPullParser.cpp \
 	SourceXmlPullParser.cpp \
 	XliffXmlPullParser.cpp \
-	XmlFlattener.cpp
+	XmlFlattener.cpp \
+	ZipEntry.cpp \
+	ZipFile.cpp
 
 testSources := \
 	BigBuffer_test.cpp \
@@ -63,6 +66,7 @@
 	Locale_test.cpp \
 	ManifestParser_test.cpp \
 	Maybe_test.cpp \
+	NameMangler_test.cpp \
 	ResourceParser_test.cpp \
 	Resource_test.cpp \
 	ResourceTable_test.cpp \
diff --git a/tools/aapt2/BinaryResourceParser.cpp b/tools/aapt2/BinaryResourceParser.cpp
index 3eb96bc..71016c1 100644
--- a/tools/aapt2/BinaryResourceParser.cpp
+++ b/tools/aapt2/BinaryResourceParser.cpp
@@ -17,6 +17,7 @@
 #include "BinaryResourceParser.h"
 #include "Logger.h"
 #include "ResChunkPullParser.h"
+#include "Resolver.h"
 #include "ResourceParser.h"
 #include "ResourceTable.h"
 #include "ResourceTypeExtensions.h"
@@ -33,28 +34,14 @@
 
 using namespace android;
 
-template <typename T>
-inline static const T* convertTo(const ResChunk_header* chunk) {
-    if (chunk->headerSize < sizeof(T)) {
-        return nullptr;
-    }
-    return reinterpret_cast<const T*>(chunk);
-}
-
-inline static const uint8_t* getChunkData(const ResChunk_header& chunk) {
-    return reinterpret_cast<const uint8_t*>(&chunk) + chunk.headerSize;
-}
-
-inline static size_t getChunkDataLen(const ResChunk_header& chunk) {
-    return chunk.size - chunk.headerSize;
-}
-
 /*
  * Visitor that converts a reference's resource ID to a resource name,
  * given a mapping from resource ID to resource name.
  */
 struct ReferenceIdToNameVisitor : ValueVisitor {
-    ReferenceIdToNameVisitor(const std::map<ResourceId, ResourceName>& cache) : mCache(cache) {
+    ReferenceIdToNameVisitor(const std::shared_ptr<Resolver>& resolver,
+                             std::map<ResourceId, ResourceName>* cache) :
+            mResolver(resolver), mCache(cache) {
     }
 
     void visit(Reference& reference, ValueVisitorArgs&) override {
@@ -104,24 +91,39 @@
             return;
         }
 
-        auto cacheIter = mCache.find(reference.id);
-        if (cacheIter == std::end(mCache)) {
-            Logger::note() << "failed to find " << reference.id << std::endl;
-        } else {
+        auto cacheIter = mCache->find(reference.id);
+        if (cacheIter != mCache->end()) {
             reference.name = cacheIter->second;
             reference.id = 0;
+        } else {
+            const android::ResTable& table = mResolver->getResTable();
+            android::ResTable::resource_name resourceName;
+            if (table.getResourceName(reference.id.id, false, &resourceName)) {
+                const ResourceType* type = parseResourceType(StringPiece16(resourceName.type,
+                                                                           resourceName.typeLen));
+                assert(type);
+                reference.name.package.assign(resourceName.package, resourceName.packageLen);
+                reference.name.type = *type;
+                reference.name.entry.assign(resourceName.name, resourceName.nameLen);
+                reference.id = 0;
+
+                // Add to cache.
+                mCache->insert({reference.id, reference.name});
+            }
         }
     }
 
-    const std::map<ResourceId, ResourceName>& mCache;
+    std::shared_ptr<Resolver> mResolver;
+    std::map<ResourceId, ResourceName>* mCache;
 };
 
 
-BinaryResourceParser::BinaryResourceParser(std::shared_ptr<ResourceTable> table,
+BinaryResourceParser::BinaryResourceParser(const std::shared_ptr<ResourceTable>& table,
+                                           const std::shared_ptr<Resolver>& resolver,
                                            const Source& source,
                                            const void* data,
                                            size_t len) :
-        mTable(table), mSource(source), mData(data), mDataLen(len) {
+        mTable(table), mResolver(resolver), mSource(source), mData(data), mDataLen(len) {
 }
 
 bool BinaryResourceParser::parse() {
@@ -421,7 +423,7 @@
     // Now go through the table and change resource ID references to
     // symbolic references.
 
-    ReferenceIdToNameVisitor visitor(mIdIndex);
+    ReferenceIdToNameVisitor visitor(mResolver, &mIdIndex);
     for (auto& type : *mTable) {
         for (auto& entry : type->entries) {
             for (auto& configValue : entry->values) {
@@ -676,7 +678,8 @@
 std::unique_ptr<Style> BinaryResourceParser::parseStyle(const ResourceNameRef& name,
                                                         const ConfigDescription& config,
                                                         const ResTable_map_entry* map) {
-    std::unique_ptr<Style> style = util::make_unique<Style>();
+    const bool isWeak = (map->flags & ResTable_entry::FLAG_WEAK) != 0;
+    std::unique_ptr<Style> style = util::make_unique<Style>(isWeak);
     if (map->parent.ident == 0) {
         // The parent is either not set or it is an unresolved symbol.
         // Check to see if it is a symbol.
@@ -759,7 +762,17 @@
                                                                 const ResTable_map_entry* map) {
     std::unique_ptr<Styleable> styleable = util::make_unique<Styleable>();
     for (const ResTable_map& mapEntry : map) {
-        styleable->entries.emplace_back(mapEntry.name.ident);
+        if (mapEntry.name.ident == 0) {
+            // The map entry's key (attribute) is not set. This must be
+            // a symbol reference, so resolve it.
+            ResourceNameRef symbol;
+            bool result = getSymbol(&mapEntry.name.ident, &symbol);
+            assert(result);
+            styleable->entries.emplace_back(symbol);
+        } else {
+            // The map entry's key (attribute) is a regular reference.
+            styleable->entries.emplace_back(mapEntry.name.ident);
+        }
     }
     return styleable;
 }
diff --git a/tools/aapt2/BinaryResourceParser.h b/tools/aapt2/BinaryResourceParser.h
index 9268078..f95a0c8 100644
--- a/tools/aapt2/BinaryResourceParser.h
+++ b/tools/aapt2/BinaryResourceParser.h
@@ -17,6 +17,7 @@
 #ifndef AAPT_BINARY_RESOURCE_PARSER_H
 #define AAPT_BINARY_RESOURCE_PARSER_H
 
+#include "Resolver.h"
 #include "ResourceTable.h"
 #include "ResourceValues.h"
 #include "Source.h"
@@ -41,7 +42,9 @@
      * Creates a parser, which will read `len` bytes from `data`, and
      * add any resources parsed to `table`. `source` is for logging purposes.
      */
-    BinaryResourceParser(std::shared_ptr<ResourceTable> table, const Source& source,
+    BinaryResourceParser(const std::shared_ptr<ResourceTable>& table,
+                         const std::shared_ptr<Resolver>& resolver,
+                         const Source& source,
                          const void* data, size_t len);
 
     BinaryResourceParser(const BinaryResourceParser&) = delete; // No copy.
@@ -89,6 +92,8 @@
 
     std::shared_ptr<ResourceTable> mTable;
 
+    std::shared_ptr<Resolver> mResolver;
+
     const Source mSource;
 
     const void* mData;
diff --git a/tools/aapt2/BinaryXmlPullParser.cpp b/tools/aapt2/BinaryXmlPullParser.cpp
new file mode 100644
index 0000000..7a07c06
--- /dev/null
+++ b/tools/aapt2/BinaryXmlPullParser.cpp
@@ -0,0 +1,204 @@
+/*
+ * 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 "BinaryXmlPullParser.h"
+
+#include <androidfw/ResourceTypes.h>
+#include <memory>
+#include <string>
+#include <vector>
+
+namespace aapt {
+
+static XmlPullParser::Event codeToEvent(android::ResXMLParser::event_code_t code) {
+    switch (code) {
+        case android::ResXMLParser::START_DOCUMENT:
+            return XmlPullParser::Event::kStartDocument;
+        case android::ResXMLParser::END_DOCUMENT:
+            return XmlPullParser::Event::kEndDocument;
+        case android::ResXMLParser::START_NAMESPACE:
+            return XmlPullParser::Event::kStartNamespace;
+        case android::ResXMLParser::END_NAMESPACE:
+            return XmlPullParser::Event::kEndNamespace;
+        case android::ResXMLParser::START_TAG:
+            return XmlPullParser::Event::kStartElement;
+        case android::ResXMLParser::END_TAG:
+            return XmlPullParser::Event::kEndElement;
+        case android::ResXMLParser::TEXT:
+            return XmlPullParser::Event::kText;
+        default:
+            break;
+    }
+    return XmlPullParser::Event::kBadDocument;
+}
+
+BinaryXmlPullParser::BinaryXmlPullParser(const std::shared_ptr<android::ResXMLTree>& parser)
+    : mParser(parser), mEvent(Event::kStartDocument), mHasComment(false), sEmpty(), sEmpty8(),
+      mDepth(0) {
+}
+
+XmlPullParser::Event BinaryXmlPullParser::next() {
+    mStr1.clear();
+    mStr2.clear();
+    mAttributes.clear();
+
+    android::ResXMLParser::event_code_t code;
+    if (mHasComment) {
+        mHasComment = false;
+        code = mParser->getEventType();
+    } else {
+        code = mParser->next();
+        if (code != android::ResXMLParser::BAD_DOCUMENT) {
+            size_t len;
+            const char16_t* comment = mParser->getComment(&len);
+            if (comment) {
+                mHasComment = true;
+                mStr1.assign(comment, len);
+                return XmlPullParser::Event::kComment;
+            }
+        }
+    }
+
+    size_t len;
+    const char16_t* data;
+    mEvent = codeToEvent(code);
+    switch (mEvent) {
+        case Event::kStartNamespace:
+        case Event::kEndNamespace:
+            data = mParser->getNamespacePrefix(&len);
+            mStr1.assign(data, len);
+            data = mParser->getNamespaceUri(&len);
+            mStr2.assign(data, len);
+            break;
+
+        case Event::kStartElement:
+            copyAttributes();
+            // fallthrough
+
+        case Event::kEndElement:
+            data = mParser->getElementNamespace(&len);
+            mStr1.assign(data, len);
+            data = mParser->getElementName(&len);
+            mStr2.assign(data, len);
+            break;
+
+        case Event::kText:
+            data = mParser->getText(&len);
+            mStr1.assign(data, len);
+            break;
+
+        default:
+            break;
+    }
+    return mEvent;
+}
+
+XmlPullParser::Event BinaryXmlPullParser::getEvent() const {
+    if (mHasComment) {
+        return XmlPullParser::Event::kComment;
+    }
+    return mEvent;
+}
+
+const std::string& BinaryXmlPullParser::getLastError() const {
+    return sEmpty8;
+}
+
+const std::u16string& BinaryXmlPullParser::getComment() const {
+    if (mHasComment) {
+        return mStr1;
+    }
+    return sEmpty;
+}
+
+size_t BinaryXmlPullParser::getLineNumber() const {
+    return mParser->getLineNumber();
+}
+
+size_t BinaryXmlPullParser::getDepth() const {
+    return mDepth;
+}
+
+const std::u16string& BinaryXmlPullParser::getText() const {
+    if (!mHasComment && mEvent == XmlPullParser::Event::kText) {
+        return mStr1;
+    }
+    return sEmpty;
+}
+
+const std::u16string& BinaryXmlPullParser::getNamespacePrefix() const {
+    if (!mHasComment && (mEvent == XmlPullParser::Event::kStartNamespace ||
+            mEvent == XmlPullParser::Event::kEndNamespace)) {
+        return mStr1;
+    }
+    return sEmpty;
+}
+
+const std::u16string& BinaryXmlPullParser::getNamespaceUri() const {
+    if (!mHasComment && (mEvent == XmlPullParser::Event::kStartNamespace ||
+            mEvent == XmlPullParser::Event::kEndNamespace)) {
+        return mStr2;
+    }
+    return sEmpty;
+}
+
+const std::u16string& BinaryXmlPullParser::getElementNamespace() const {
+    if (!mHasComment && (mEvent == XmlPullParser::Event::kStartElement ||
+            mEvent == XmlPullParser::Event::kEndElement)) {
+        return mStr1;
+    }
+    return sEmpty;
+}
+
+const std::u16string& BinaryXmlPullParser::getElementName() const {
+    if (!mHasComment && (mEvent == XmlPullParser::Event::kStartElement ||
+            mEvent == XmlPullParser::Event::kEndElement)) {
+        return mStr2;
+    }
+    return sEmpty;
+}
+
+size_t BinaryXmlPullParser::getAttributeCount() const {
+    return mAttributes.size();
+}
+
+XmlPullParser::const_iterator BinaryXmlPullParser::beginAttributes() const {
+    return mAttributes.begin();
+}
+
+XmlPullParser::const_iterator BinaryXmlPullParser::endAttributes() const {
+    return mAttributes.end();
+}
+
+void BinaryXmlPullParser::copyAttributes() {
+    const size_t attrCount = mParser->getAttributeCount();
+    if (attrCount > 0) {
+        mAttributes.reserve(attrCount);
+        for (size_t i = 0; i < attrCount; i++) {
+            XmlPullParser::Attribute attr;
+            size_t len;
+            const char16_t* str = mParser->getAttributeNamespace(i, &len);
+            attr.namespaceUri.assign(str, len);
+            str = mParser->getAttributeName(i, &len);
+            attr.name.assign(str, len);
+            str = mParser->getAttributeStringValue(i, &len);
+            attr.value.assign(str, len);
+            mAttributes.push_back(std::move(attr));
+        }
+    }
+}
+
+} // namespace aapt
diff --git a/tools/aapt2/BinaryXmlPullParser.h b/tools/aapt2/BinaryXmlPullParser.h
new file mode 100644
index 0000000..2d4256a
--- /dev/null
+++ b/tools/aapt2/BinaryXmlPullParser.h
@@ -0,0 +1,73 @@
+/*
+ * 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 AAPT_BINARY_XML_PULL_PARSER_H
+#define AAPT_BINARY_XML_PULL_PARSER_H
+
+#include "XmlPullParser.h"
+
+#include <androidfw/ResourceTypes.h>
+#include <memory>
+#include <string>
+#include <vector>
+
+namespace aapt {
+
+/**
+ * Wraps a ResTable into the canonical XmlPullParser interface.
+ */
+class BinaryXmlPullParser : public XmlPullParser {
+public:
+    BinaryXmlPullParser(const std::shared_ptr<android::ResXMLTree>& parser);
+    BinaryXmlPullParser(const BinaryXmlPullParser& rhs) = delete;
+
+    Event getEvent() const;
+    const std::string& getLastError() const;
+    Event next();
+
+    const std::u16string& getComment() const;
+    size_t getLineNumber() const;
+    size_t getDepth() const;
+
+    const std::u16string& getText() const;
+
+    const std::u16string& getNamespacePrefix() const;
+    const std::u16string& getNamespaceUri() const;
+
+    const std::u16string& getElementNamespace() const;
+    const std::u16string& getElementName() const;
+
+    const_iterator beginAttributes() const;
+    const_iterator endAttributes() const;
+    size_t getAttributeCount() const;
+
+private:
+    void copyAttributes();
+
+    std::shared_ptr<android::ResXMLTree> mParser;
+    std::u16string mStr1;
+    std::u16string mStr2;
+    std::vector<Attribute> mAttributes;
+    Event mEvent;
+    bool mHasComment;
+    const std::u16string sEmpty;
+    const std::string sEmpty8;
+    size_t mDepth;
+};
+
+} // namespace aapt
+
+#endif // AAPT_BINARY_XML_PULL_PARSER_H
diff --git a/tools/aapt2/Flag.cpp b/tools/aapt2/Flag.cpp
index b1ee8e7..3b2ff51 100644
--- a/tools/aapt2/Flag.cpp
+++ b/tools/aapt2/Flag.cpp
@@ -16,6 +16,7 @@
     std::function<void(const StringPiece&)> action;
     bool required;
     bool* flagResult;
+    bool flagValueWhenSet;
     bool parsed;
 };
 
@@ -25,21 +26,22 @@
 void optionalFlag(const StringPiece& name, const StringPiece& description,
                   std::function<void(const StringPiece&)> action) {
     sFlags.push_back(
-            Flag{ name.toString(), description.toString(), action, false, nullptr, false });
+            Flag{ name.toString(), description.toString(), action, false, nullptr, false, false });
 }
 
 void requiredFlag(const StringPiece& name, const StringPiece& description,
                   std::function<void(const StringPiece&)> action) {
     sFlags.push_back(
-            Flag{ name.toString(), description.toString(), action, true, nullptr, false });
+            Flag{ name.toString(), description.toString(), action, true, nullptr, false, false });
 }
 
-void optionalSwitch(const StringPiece& name, const StringPiece& description, bool* result) {
-    sFlags.push_back(
-            Flag{ name.toString(), description.toString(), {}, false, result, false });
+void optionalSwitch(const StringPiece& name, const StringPiece& description, bool resultWhenSet,
+                    bool* result) {
+    sFlags.push_back(Flag{
+            name.toString(), description.toString(), {}, false, result, resultWhenSet, false });
 }
 
-static void usageAndDie(const StringPiece& command) {
+void usageAndDie(const StringPiece& command) {
     std::cerr << command << " [options]";
     for (const Flag& flag : sFlags) {
         if (flag.required) {
@@ -73,7 +75,7 @@
                 match = true;
                 flag.parsed = true;
                 if (flag.flagResult) {
-                    *flag.flagResult = true;
+                    *flag.flagResult = flag.flagValueWhenSet;
                 } else {
                     i++;
                     if (i >= argc) {
diff --git a/tools/aapt2/Flag.h b/tools/aapt2/Flag.h
index 32f5f2c..4745c35 100644
--- a/tools/aapt2/Flag.h
+++ b/tools/aapt2/Flag.h
@@ -16,7 +16,10 @@
 void optionalFlag(const StringPiece& name, const StringPiece& description,
                   std::function<void(const StringPiece&)> action);
 
-void optionalSwitch(const StringPiece& name, const StringPiece& description, bool* result);
+void optionalSwitch(const StringPiece& name, const StringPiece& description, bool resultWhenSet,
+                    bool* result);
+
+void usageAndDie(const StringPiece& command);
 
 void parse(int argc, char** argv, const StringPiece& command);
 
diff --git a/tools/aapt2/JavaClassGenerator.cpp b/tools/aapt2/JavaClassGenerator.cpp
index 779a346..3f92f18 100644
--- a/tools/aapt2/JavaClassGenerator.cpp
+++ b/tools/aapt2/JavaClassGenerator.cpp
@@ -15,6 +15,7 @@
  */
 
 #include "JavaClassGenerator.h"
+#include "NameMangler.h"
 #include "Resource.h"
 #include "ResourceTable.h"
 #include "ResourceValues.h"
@@ -31,7 +32,7 @@
 // The number of attributes to emit per line in a Styleable array.
 constexpr size_t kAttribsPerLine = 4;
 
-JavaClassGenerator::JavaClassGenerator(std::shared_ptr<const ResourceTable> table,
+JavaClassGenerator::JavaClassGenerator(const std::shared_ptr<const ResourceTable>& table,
                                        Options options) :
         mTable(table), mOptions(options) {
 }
@@ -79,42 +80,18 @@
     return output;
 }
 
-bool JavaClassGenerator::generateType(std::ostream& out, const ResourceTableType& type,
-                                      size_t packageId) {
-    const StringPiece finalModifier = mOptions.useFinal ? " final" : "";
-
-    for (const auto& entry : type.entries) {
-        ResourceId id = { packageId, type.typeId, entry->entryId };
-        assert(id.isValid());
-
-        if (!isValidSymbol(entry->name)) {
-            std::stringstream err;
-            err << "invalid symbol name '"
-                << StringPiece16(entry->name)
-                << "'";
-            mError = err.str();
-            return false;
-        }
-
-        out << "        "
-            << "public static" << finalModifier
-            << " int " << transform(entry->name) << " = " << id << ";" << std::endl;
-    }
-    return true;
-}
-
 struct GenArgs : ValueVisitorArgs {
-    GenArgs(std::ostream& o, const ResourceEntry& e) : out(o), entry(e) {
+    GenArgs(std::ostream* o, std::u16string* e) : out(o), entryName(e) {
     }
 
-    std::ostream& out;
-    const ResourceEntry& entry;
+    std::ostream* out;
+    std::u16string* entryName;
 };
 
 void JavaClassGenerator::visit(const Styleable& styleable, ValueVisitorArgs& a) {
     const StringPiece finalModifier = mOptions.useFinal ? " final" : "";
-    std::ostream& out = static_cast<GenArgs&>(a).out;
-    const ResourceEntry& entry = static_cast<GenArgs&>(a).entry;
+    std::ostream* out = static_cast<GenArgs&>(a).out;
+    std::u16string* entryName = static_cast<GenArgs&>(a).entryName;
 
     // This must be sorted by resource ID.
     std::vector<std::pair<ResourceId, StringPiece16>> sortedAttributes;
@@ -127,59 +104,86 @@
     std::sort(sortedAttributes.begin(), sortedAttributes.end());
 
     // First we emit the array containing the IDs of each attribute.
-    out << "        "
-        << "public static final int[] " << transform(entry.name) << " = {";
+    *out << "        "
+         << "public static final int[] " << transform(*entryName) << " = {";
 
     const size_t attrCount = sortedAttributes.size();
     for (size_t i = 0; i < attrCount; i++) {
         if (i % kAttribsPerLine == 0) {
-            out << std::endl << "            ";
+            *out << std::endl << "            ";
         }
 
-        out << sortedAttributes[i].first;
+        *out << sortedAttributes[i].first;
         if (i != attrCount - 1) {
-            out << ", ";
+            *out << ", ";
         }
     }
-    out << std::endl << "        };" << std::endl;
+    *out << std::endl << "        };" << std::endl;
 
     // Now we emit the indices into the array.
     for (size_t i = 0; i < attrCount; i++) {
-        out << "        "
-            << "public static" << finalModifier
-            << " int " << transform(entry.name) << "_" << transform(sortedAttributes[i].second)
-            << " = " << i << ";" << std::endl;
+        *out << "        "
+             << "public static" << finalModifier
+             << " int " << transform(*entryName) << "_" << transform(sortedAttributes[i].second)
+             << " = " << i << ";" << std::endl;
     }
 }
 
-bool JavaClassGenerator::generate(std::ostream& out) {
+bool JavaClassGenerator::generateType(const std::u16string& package, size_t packageId,
+                                      const ResourceTableType& type, std::ostream& out) {
+    const StringPiece finalModifier = mOptions.useFinal ? " final" : "";
+
+    std::u16string unmangledPackage;
+    std::u16string unmangledName;
+    for (const auto& entry : type.entries) {
+        ResourceId id = { packageId, type.typeId, entry->entryId };
+        assert(id.isValid());
+
+        unmangledName = entry->name;
+        if (NameMangler::unmangle(&unmangledName, &unmangledPackage)) {
+            // The entry name was mangled, and we successfully unmangled it.
+            // Check that we want to emit this symbol.
+            if (package != unmangledPackage) {
+                // Skip the entry if it doesn't belong to the package we're writing.
+                continue;
+            }
+        } else {
+            if (package != mTable->getPackage()) {
+                // We are processing a mangled package name,
+                // but this is a non-mangled resource.
+                continue;
+            }
+        }
+
+        if (!isValidSymbol(unmangledName)) {
+            ResourceNameRef resourceName = { package, type.type, unmangledName };
+            std::stringstream err;
+            err << "invalid symbol name '" << resourceName << "'";
+            mError = err.str();
+            return false;
+        }
+
+        if (type.type == ResourceType::kStyleable) {
+            assert(!entry->values.empty());
+            entry->values.front().value->accept(*this, GenArgs{ &out, &unmangledName });
+        } else {
+            out << "        " << "public static" << finalModifier
+                << " int " << transform(unmangledName) << " = " << id << ";" << std::endl;
+        }
+    }
+    return true;
+}
+
+bool JavaClassGenerator::generate(const std::u16string& package, std::ostream& out) {
     const size_t packageId = mTable->getPackageId();
 
-    generateHeader(out, mTable->getPackage());
+    generateHeader(out, package);
 
     out << "public final class R {" << std::endl;
 
     for (const auto& type : *mTable) {
         out << "    public static final class " << type->type << " {" << std::endl;
-        bool result;
-        if (type->type == ResourceType::kStyleable) {
-            for (const auto& entry : type->entries) {
-                assert(!entry->values.empty());
-                if (!isValidSymbol(entry->name)) {
-                    std::stringstream err;
-                    err << "invalid symbol name '"
-                        << StringPiece16(entry->name)
-                        << "'";
-                    mError = err.str();
-                    return false;
-                }
-                entry->values.front().value->accept(*this, GenArgs{ out, *entry });
-            }
-        } else {
-            result = generateType(out, *type, packageId);
-        }
-
-        if (!result) {
+        if (!generateType(package, packageId, *type, out)) {
             return false;
         }
         out << "    }" << std::endl;
diff --git a/tools/aapt2/JavaClassGenerator.h b/tools/aapt2/JavaClassGenerator.h
index 5b8e500..f8b9ee3 100644
--- a/tools/aapt2/JavaClassGenerator.h
+++ b/tools/aapt2/JavaClassGenerator.h
@@ -41,12 +41,16 @@
         bool useFinal = true;
     };
 
-    JavaClassGenerator(std::shared_ptr<const ResourceTable> table, Options options);
+    JavaClassGenerator(const std::shared_ptr<const ResourceTable>& table, Options options);
 
     /*
-     * Writes the R.java file to `out`. Returns true on success.
+     * Writes the R.java file to `out`. Only symbols belonging to `package` are written.
+     * All symbols technically belong to a single package, but linked libraries will
+     * have their names mangled, denoting that they came from a different package.
+     * We need to generate these symbols in a separate file.
+     * Returns true on success.
      */
-    bool generate(std::ostream& out);
+    bool generate(const std::u16string& package, std::ostream& out);
 
     /*
      * ConstValueVisitor implementation.
@@ -56,7 +60,8 @@
     const std::string& getError() const;
 
 private:
-    bool generateType(std::ostream& out, const ResourceTableType& type, size_t packageId);
+    bool generateType(const std::u16string& package, size_t packageId,
+                      const ResourceTableType& type, std::ostream& out);
 
     std::shared_ptr<const ResourceTable> mTable;
     Options mOptions;
diff --git a/tools/aapt2/JavaClassGenerator_test.cpp b/tools/aapt2/JavaClassGenerator_test.cpp
index 32050e3..96bb10b 100644
--- a/tools/aapt2/JavaClassGenerator_test.cpp
+++ b/tools/aapt2/JavaClassGenerator_test.cpp
@@ -15,6 +15,8 @@
  */
 
 #include "JavaClassGenerator.h"
+#include "Linker.h"
+#include "Resolver.h"
 #include "ResourceTable.h"
 #include "ResourceValues.h"
 #include "Util.h"
@@ -47,7 +49,7 @@
     JavaClassGenerator generator(mTable, {});
 
     std::stringstream out;
-    EXPECT_FALSE(generator.generate(out));
+    EXPECT_FALSE(generator.generate(mTable->getPackage(), out));
 }
 
 TEST_F(JavaClassGeneratorTest, TransformInvalidJavaIdentifierCharacter) {
@@ -69,7 +71,7 @@
     JavaClassGenerator generator(mTable, {});
 
     std::stringstream out;
-    EXPECT_TRUE(generator.generate(out));
+    EXPECT_TRUE(generator.generate(mTable->getPackage(), out));
     std::string output = out.str();
 
     EXPECT_NE(std::string::npos,
@@ -82,4 +84,33 @@
               output.find("public static final int hey_dude_cool_attr = 0;"));
 }
 
+TEST_F(JavaClassGeneratorTest, EmitPackageMangledSymbols) {
+    ASSERT_TRUE(addResource(ResourceName{ {}, ResourceType::kId, u"foo" },
+                            ResourceId{ 0x01, 0x02, 0x0000 }));
+    ResourceTable table;
+    table.setPackage(u"com.lib");
+    ASSERT_TRUE(table.addResource(ResourceName{ {}, ResourceType::kId, u"test" }, {},
+                                  SourceLine{ "lib.xml", 33 }, util::make_unique<Id>()));
+    ASSERT_TRUE(mTable->merge(std::move(table)));
+
+    std::shared_ptr<Resolver> resolver = std::make_shared<Resolver>(mTable,
+            std::make_shared<const android::AssetManager>());
+    Linker linker(mTable, resolver);
+    ASSERT_TRUE(linker.linkAndValidate());
+
+    JavaClassGenerator generator(mTable, {});
+
+    std::stringstream out;
+    EXPECT_TRUE(generator.generate(mTable->getPackage(), out));
+    std::string output = out.str();
+    EXPECT_NE(std::string::npos, output.find("int foo ="));
+    EXPECT_EQ(std::string::npos, output.find("int test ="));
+
+    out.str("");
+    EXPECT_TRUE(generator.generate(u"com.lib", out));
+    output = out.str();
+    EXPECT_NE(std::string::npos, output.find("int test ="));
+    EXPECT_EQ(std::string::npos, output.find("int foo ="));
+}
+
 } // namespace aapt
diff --git a/tools/aapt2/Linker.cpp b/tools/aapt2/Linker.cpp
index 1cfb297..4346c8b 100644
--- a/tools/aapt2/Linker.cpp
+++ b/tools/aapt2/Linker.cpp
@@ -128,6 +128,20 @@
 void Linker::visit(Reference& reference, ValueVisitorArgs& a) {
     Args& args = static_cast<Args&>(a);
 
+    if (!reference.name.isValid()) {
+        // We can't have a completely bad reference.
+        assert(reference.id.isValid());
+
+        // This reference has no name but has an ID.
+        // It is a really bad error to have no name and have the same
+        // package ID.
+        assert(reference.id.packageId() != mTable->getPackageId());
+
+        // The reference goes outside this package, let it stay as a
+        // resource ID because it will not change.
+        return;
+    }
+
     Maybe<ResourceId> result = mResolver->findId(reference.name);
     if (!result) {
         addUnresolvedSymbol(reference.name, args.source);
@@ -206,7 +220,7 @@
 void Linker::visit(Style& style, ValueVisitorArgs& a) {
     Args& args = static_cast<Args&>(a);
 
-    if (style.parent.name.isValid()) {
+    if (style.parent.name.isValid() || style.parent.id.isValid()) {
         visit(style.parent, a);
     }
 
diff --git a/tools/aapt2/Linker_test.cpp b/tools/aapt2/Linker_test.cpp
index b1e201b..4d2d360 100644
--- a/tools/aapt2/Linker_test.cpp
+++ b/tools/aapt2/Linker_test.cpp
@@ -30,6 +30,7 @@
     virtual void SetUp() override {
         mTable = std::make_shared<ResourceTable>();
         mTable->setPackage(u"android");
+        mTable->setPackageId(0x01);
         mLinker = std::make_shared<Linker>(mTable, std::make_shared<Resolver>(
                 mTable, std::make_shared<android::AssetManager>()));
 
@@ -75,7 +76,7 @@
 }
 
 TEST_F(LinkerTest, EscapeAndConvertRawString) {
-    std::unique_ptr<Style> style = util::make_unique<Style>();
+    std::unique_ptr<Style> style = util::make_unique<Style>(false);
     style->entries.push_back(Style::Entry{
             ResourceNameRef{ u"android", ResourceType::kAttr, u"integer" },
             util::make_unique<RawString>(mTable->getValueStringPool().makeRef(u"  123"))
@@ -91,7 +92,7 @@
 }
 
 TEST_F(LinkerTest, FailToConvertRawString) {
-    std::unique_ptr<Style> style = util::make_unique<Style>();
+    std::unique_ptr<Style> style = util::make_unique<Style>(false);
     style->entries.push_back(Style::Entry{
             ResourceNameRef{ u"android", ResourceType::kAttr, u"integer" },
             util::make_unique<RawString>(mTable->getValueStringPool().makeRef(u"yo what is up?"))
@@ -103,7 +104,7 @@
 }
 
 TEST_F(LinkerTest, ConvertRawStringToString) {
-    std::unique_ptr<Style> style = util::make_unique<Style>();
+    std::unique_ptr<Style> style = util::make_unique<Style>(false);
     style->entries.push_back(Style::Entry{
             ResourceNameRef{ u"android", ResourceType::kAttr, u"string" },
             util::make_unique<RawString>(
@@ -122,7 +123,7 @@
 }
 
 TEST_F(LinkerTest, ConvertRawStringToFlags) {
-    std::unique_ptr<Style> style = util::make_unique<Style>();
+    std::unique_ptr<Style> style = util::make_unique<Style>(false);
     style->entries.push_back(Style::Entry{
             ResourceNameRef{ u"android", ResourceType::kAttr, u"flags" },
             util::make_unique<RawString>(mTable->getValueStringPool().makeRef(u"banana | apple"))
@@ -140,4 +141,12 @@
     EXPECT_EQ(bin->value.data, 1u | 2u);
 }
 
+TEST_F(LinkerTest, AllowReferenceWithOnlyResourceIdPointingToDifferentPackage) {
+    ASSERT_TRUE(addResource(ResourceName{ u"android", ResourceType::kInteger, u"foo" },
+                util::make_unique<Reference>(ResourceId{ 0x02, 0x01, 0x01 })));
+
+    ASSERT_TRUE(mLinker->linkAndValidate());
+    EXPECT_TRUE(mLinker->getUnresolvedReferences().empty());
+}
+
 } // namespace aapt
diff --git a/tools/aapt2/Main.cpp b/tools/aapt2/Main.cpp
index 87127fd..03b9ba4 100644
--- a/tools/aapt2/Main.cpp
+++ b/tools/aapt2/Main.cpp
@@ -17,6 +17,7 @@
 #include "AppInfo.h"
 #include "BigBuffer.h"
 #include "BinaryResourceParser.h"
+#include "BinaryXmlPullParser.h"
 #include "BindingXmlPullParser.h"
 #include "Files.h"
 #include "Flag.h"
@@ -34,6 +35,7 @@
 #include "TableFlattener.h"
 #include "Util.h"
 #include "XmlFlattener.h"
+#include "ZipFile.h"
 
 #include <algorithm>
 #include <androidfw/AssetManager.h>
@@ -44,8 +46,11 @@
 #include <iostream>
 #include <sstream>
 #include <sys/stat.h>
+#include <unordered_set>
 #include <utils/Errors.h>
 
+constexpr const char* kAaptVersionStr = "2.0-alpha";
+
 using namespace aapt;
 
 void printTable(const ResourceTable& table) {
@@ -96,17 +101,6 @@
     }
 }
 
-std::unique_ptr<FileReference> makeFileReference(StringPool& pool, const StringPiece& filename,
-        ResourceType type, const ConfigDescription& config) {
-    std::stringstream path;
-    path << "res/" << type;
-    if (config != ConfigDescription{}) {
-        path << "-" << config;
-    }
-    path << "/" << filename;
-    return util::make_unique<FileReference>(pool.makeRef(util::utf8ToUtf16(path.str())));
-}
-
 /**
  * Collect files from 'root', filtering out any files that do not
  * match the FileFilter 'filter'.
@@ -148,30 +142,6 @@
     return !error;
 }
 
-bool loadBinaryResourceTable(std::shared_ptr<ResourceTable> table, const Source& source) {
-    std::ifstream ifs(source.path, std::ifstream::in | std::ifstream::binary);
-    if (!ifs) {
-        Logger::error(source) << strerror(errno) << std::endl;
-        return false;
-    }
-
-    std::streampos fsize = ifs.tellg();
-    ifs.seekg(0, std::ios::end);
-    fsize = ifs.tellg() - fsize;
-    ifs.seekg(0, std::ios::beg);
-
-    assert(fsize >= 0);
-    size_t dataSize = static_cast<size_t>(fsize);
-    char* buf = new char[dataSize];
-    ifs.read(buf, dataSize);
-
-    BinaryResourceParser parser(table, source, buf, dataSize);
-    bool result = parser.parse();
-
-    delete [] buf;
-    return result;
-}
-
 bool loadResTable(android::ResTable* table, const Source& source) {
     std::ifstream ifs(source.path, std::ifstream::in | std::ifstream::binary);
     if (!ifs) {
@@ -195,7 +165,7 @@
     return result;
 }
 
-void versionStylesForCompat(std::shared_ptr<ResourceTable> table) {
+void versionStylesForCompat(const std::shared_ptr<ResourceTable>& table) {
     for (auto& type : *table) {
         if (type->type != ResourceType::kStyle) {
             continue;
@@ -251,10 +221,12 @@
                                 {},
 
                                 // Create a copy of the original style.
-                                std::unique_ptr<Value>(configValue.value->clone())
+                                std::unique_ptr<Value>(configValue.value->clone(
+                                            &table->getValueStringPool()))
                         };
 
                         Style& newStyle = static_cast<Style&>(*value.value);
+                        newStyle.weak = true;
 
                         // Move the recorded stripped attributes into this new style.
                         std::move(stripped.begin(), stripped.end(),
@@ -285,59 +257,6 @@
     }
 }
 
-bool collectXml(std::shared_ptr<ResourceTable> table, const Source& source,
-                const ResourceName& name, const ConfigDescription& config) {
-    std::ifstream in(source.path, std::ifstream::binary);
-    if (!in) {
-        Logger::error(source) << strerror(errno) << std::endl;
-        return false;
-    }
-
-    std::set<size_t> sdkLevels;
-
-    SourceXmlPullParser parser(in);
-    while (XmlPullParser::isGoodEvent(parser.next())) {
-        if (parser.getEvent() != XmlPullParser::Event::kStartElement) {
-            continue;
-        }
-
-        const auto endIter = parser.endAttributes();
-        for (auto iter = parser.beginAttributes(); iter != endIter; ++iter) {
-            if (iter->namespaceUri == u"http://schemas.android.com/apk/res/android") {
-                size_t sdkLevel = findAttributeSdkLevel(iter->name);
-                if (sdkLevel > 1) {
-                    sdkLevels.insert(sdkLevel);
-                }
-            }
-
-            ResourceNameRef refName;
-            bool create = false;
-            bool privateRef = false;
-            if (ResourceParser::tryParseReference(iter->value, &refName, &create, &privateRef) &&
-                    create) {
-                table->addResource(refName, {}, source.line(parser.getLineNumber()),
-                                   util::make_unique<Id>());
-            }
-        }
-    }
-
-    for (size_t level : sdkLevels) {
-        Logger::note(source)
-                << "creating v" << level << " versioned file."
-                << std::endl;
-        ConfigDescription newConfig = config;
-        newConfig.sdkVersion = level;
-
-        std::unique_ptr<FileReference> fileResource = makeFileReference(
-                table->getValueStringPool(),
-                util::utf16ToUtf8(name.entry) + ".xml",
-                name.type,
-                newConfig);
-        table->addResource(name, newConfig, source.line(0), std::move(fileResource));
-    }
-    return true;
-}
-
 struct CompileItem {
     Source source;
     ResourceName name;
@@ -345,28 +264,99 @@
     std::string extension;
 };
 
-bool compileXml(std::shared_ptr<Resolver> resolver, const CompileItem& item,
-                const Source& outputSource, std::queue<CompileItem>* queue) {
+struct LinkItem {
+    Source source;
+    std::string apkPath;
+};
+
+std::string buildFileReference(const CompileItem& item) {
+    std::stringstream path;
+    path << "res/" << item.name.type;
+    if (item.config != ConfigDescription{}) {
+        path << "-" << item.config;
+    }
+    path << "/" << util::utf16ToUtf8(item.name.entry) + "." + item.extension;
+    return path.str();
+}
+
+bool addFileReference(const std::shared_ptr<ResourceTable>& table, const CompileItem& item) {
+    StringPool& pool = table->getValueStringPool();
+    StringPool::Ref ref = pool.makeRef(util::utf8ToUtf16(buildFileReference(item)));
+    return table->addResource(item.name, item.config, item.source.line(0),
+                              util::make_unique<FileReference>(ref));
+}
+
+struct AaptOptions {
+    enum class Phase {
+        Link,
+        Compile,
+    };
+
+    // The phase to process.
+    Phase phase;
+
+    // Details about the app.
+    AppInfo appInfo;
+
+    // The location of the manifest file.
+    Source manifest;
+
+    // The APK files to link.
+    std::vector<Source> input;
+
+    // The libraries these files may reference.
+    std::vector<Source> libraries;
+
+    // Output path. This can be a directory or file
+    // depending on the phase.
+    Source output;
+
+    // Directory in which to write binding xml files.
+    Source bindingOutput;
+
+    // Directory to in which to generate R.java.
+    Maybe<Source> generateJavaClass;
+
+    // Whether to output verbose details about
+    // compilation.
+    bool verbose = false;
+
+    // Whether or not to auto-version styles or layouts
+    // referencing attributes defined in a newer SDK
+    // level than the style or layout is defined for.
+    bool versionStylesAndLayouts = true;
+};
+
+
+bool compileXml(const AaptOptions& options, const std::shared_ptr<ResourceTable>& table,
+                const CompileItem& item, std::queue<CompileItem>* outQueue, ZipFile* outApk) {
     std::ifstream in(item.source.path, std::ifstream::binary);
     if (!in) {
         Logger::error(item.source) << strerror(errno) << std::endl;
         return false;
     }
 
-    std::shared_ptr<BindingXmlPullParser> binding;
-    std::shared_ptr<XmlPullParser> xmlParser = std::make_shared<SourceXmlPullParser>(in);
-    if (item.name.type == ResourceType::kLayout) {
-        binding = std::make_shared<BindingXmlPullParser>(xmlParser);
-        xmlParser = binding;
+    BigBuffer outBuffer(1024);
+
+    // No resolver, since we are not compiling attributes here.
+    XmlFlattener flattener(table, {});
+
+    XmlFlattener::Options xmlOptions;
+    if (options.versionStylesAndLayouts) {
+        // We strip attributes that do not belong in this version of the resource.
+        // Non-version qualified resources have an implicit version 1 requirement.
+        xmlOptions.maxSdkAttribute = item.config.sdkVersion ? item.config.sdkVersion : 1;
     }
 
-    BigBuffer outBuffer(1024);
-    XmlFlattener flattener(resolver);
+    std::shared_ptr<BindingXmlPullParser> binding;
+    std::shared_ptr<XmlPullParser> parser = std::make_shared<SourceXmlPullParser>(in);
+    if (item.name.type == ResourceType::kLayout) {
+        // Layouts may have defined bindings, so we need to make sure they get processed.
+        binding = std::make_shared<BindingXmlPullParser>(parser);
+        parser = binding;
+    }
 
-    // We strip attributes that do not belong in this version of the resource.
-    // Non-version qualified resources have an implicit version 1 requirement.
-    XmlFlattener::Options options = { item.config.sdkVersion ? item.config.sdkVersion : 1 };
-    Maybe<size_t> minStrippedSdk = flattener.flatten(item.source, xmlParser, &outBuffer, options);
+    Maybe<size_t> minStrippedSdk = flattener.flatten(item.source, parser, &outBuffer, xmlOptions);
     if (!minStrippedSdk) {
         return false;
     }
@@ -376,24 +366,29 @@
         // with the version of the smallest SDK version stripped.
         CompileItem newWork = item;
         newWork.config.sdkVersion = minStrippedSdk.value();
-        queue->push(newWork);
+        outQueue->push(newWork);
     }
 
-    std::ofstream out(outputSource.path, std::ofstream::binary);
-    if (!out) {
-        Logger::error(outputSource) << strerror(errno) << std::endl;
+    // Write the resulting compiled XML file to the output APK.
+    if (outApk->add(outBuffer, buildFileReference(item).data(), ZipEntry::kCompressStored,
+                nullptr) != android::NO_ERROR) {
+        Logger::error(options.output) << "failed to write compiled '" << item.source << "' to apk."
+                                      << std::endl;
         return false;
     }
 
-    if (!util::writeAll(out, outBuffer)) {
-        Logger::error(outputSource) << strerror(errno) << std::endl;
-        return false;
-    }
+    if (binding && !options.bindingOutput.path.empty()) {
+        // We generated a binding xml file, write it out.
+        Source bindingOutput = options.bindingOutput;
+        appendPath(&bindingOutput.path, buildFileReference(item));
 
-    if (binding) {
-        // We generated a binding xml file, write it out beside the output file.
-        Source bindingOutput = outputSource;
-        bindingOutput.path += ".bind.xml";
+        if (!mkdirs(bindingOutput.path)) {
+            Logger::error(bindingOutput) << strerror(errno) << std::endl;
+            return false;
+        }
+
+        appendPath(&bindingOutput.path, "bind.xml");
+
         std::ofstream bout(bindingOutput.path);
         if (!bout) {
             Logger::error(bindingOutput) << strerror(errno) << std::endl;
@@ -408,100 +403,68 @@
     return true;
 }
 
-bool compilePng(const Source& source, const Source& output) {
-    std::ifstream in(source.path, std::ifstream::binary);
-    if (!in) {
-        Logger::error(source) << strerror(errno) << std::endl;
+bool linkXml(const AaptOptions& options, const std::shared_ptr<Resolver>& resolver,
+             const LinkItem& item, const void* data, size_t dataLen, ZipFile* outApk) {
+    std::shared_ptr<android::ResXMLTree> tree = std::make_shared<android::ResXMLTree>();
+    if (tree->setTo(data, dataLen, false) != android::NO_ERROR) {
         return false;
     }
 
-    std::ofstream out(output.path, std::ofstream::binary);
-    if (!out) {
-        Logger::error(output) << strerror(errno) << std::endl;
+    std::shared_ptr<XmlPullParser> xmlParser = std::make_shared<BinaryXmlPullParser>(tree);
+
+    BigBuffer outBuffer(1024);
+    XmlFlattener flattener({}, resolver);
+    if (!flattener.flatten(item.source, xmlParser, &outBuffer, {})) {
         return false;
     }
 
-    std::string err;
-    Png png;
-    if (!png.process(source, in, out, {}, &err)) {
-        Logger::error(source) << err << std::endl;
+    if (outApk->add(outBuffer, item.apkPath.data(), ZipEntry::kCompressDeflated, nullptr) !=
+            android::NO_ERROR) {
+        Logger::error(options.output) << "failed to write linked file '" << item.source
+                                      << "' to apk." << std::endl;
         return false;
     }
     return true;
 }
 
-bool copyFile(const Source& source, const Source& output) {
-    std::ifstream in(source.path, std::ifstream::binary);
+bool compilePng(const AaptOptions& options, const CompileItem& item, ZipFile* outApk) {
+    std::ifstream in(item.source.path, std::ifstream::binary);
     if (!in) {
-        Logger::error(source) << strerror(errno) << std::endl;
+        Logger::error(item.source) << strerror(errno) << std::endl;
         return false;
     }
 
-    std::ofstream out(output.path, std::ofstream::binary);
-    if (!out) {
-        Logger::error(output) << strerror(errno) << std::endl;
+    BigBuffer outBuffer(4096);
+    std::string err;
+    Png png;
+    if (!png.process(item.source, in, &outBuffer, {}, &err)) {
+        Logger::error(item.source) << err << std::endl;
         return false;
     }
 
-    if (out << in.rdbuf()) {
-        Logger::error(output) << strerror(errno) << std::endl;
-        return true;
+    if (outApk->add(outBuffer, buildFileReference(item).data(), ZipEntry::kCompressStored,
+                nullptr) != android::NO_ERROR) {
+        Logger::error(options.output) << "failed to write compiled '" << item.source
+                                      << "' to apk." << std::endl;
+        return false;
     }
-    return false;
+    return true;
 }
 
-struct AaptOptions {
-    enum class Phase {
-        Full,
-        Collect,
-        Link,
-        Compile,
-        Manifest
-    };
+bool copyFile(const AaptOptions& options, const CompileItem& item, ZipFile* outApk) {
+    if (outApk->add(item.source.path.data(), buildFileReference(item).data(),
+                ZipEntry::kCompressStored, nullptr) != android::NO_ERROR) {
+        Logger::error(options.output) << "failed to copy file '" << item.source << "' to apk."
+                                      << std::endl;
+        return false;
+    }
+    return true;
+}
 
-    // The phase to process.
-    Phase phase;
-
-    // Details about the app.
-    AppInfo appInfo;
-
-    // The location of the manifest file.
-    Source manifest;
-
-    // The source directories to walk and find resource files.
-    std::vector<Source> sourceDirs;
-
-    // The resource files to process and collect.
-    std::vector<Source> collectFiles;
-
-    // The binary table files to link.
-    std::vector<Source> linkFiles;
-
-    // The resource files to compile.
-    std::vector<Source> compileFiles;
-
-    // The libraries these files may reference.
-    std::vector<Source> libraries;
-
-    // Output path. This can be a directory or file
-    // depending on the phase.
-    Source output;
-
-    // Directory to in which to generate R.java.
-    Maybe<Source> generateJavaClass;
-
-    // Whether to output verbose details about
-    // compilation.
-    bool verbose = false;
-};
-
-bool compileAndroidManifest(const std::shared_ptr<Resolver>& resolver,
-                            const AaptOptions& options) {
-    Source outSource = options.output;
-    appendPath(&outSource.path, "AndroidManifest.xml");
-
+bool compileManifest(const AaptOptions& options, const std::shared_ptr<Resolver>& resolver,
+                     ZipFile* outApk) {
     if (options.verbose) {
-        Logger::note(outSource) << "compiling AndroidManifest.xml." << std::endl;
+        Logger::note(options.manifest) << "compiling AndroidManifest.xml." << std::endl;
     }
 
     std::ifstream in(options.manifest.path, std::ifstream::binary);
@@ -512,23 +475,16 @@
 
     BigBuffer outBuffer(1024);
     std::shared_ptr<XmlPullParser> xmlParser = std::make_shared<SourceXmlPullParser>(in);
-    XmlFlattener flattener(resolver);
+    XmlFlattener flattener({}, resolver);
 
-    Maybe<size_t> result = flattener.flatten(options.manifest, xmlParser, &outBuffer,
-                                             XmlFlattener::Options{});
-    if (!result) {
+    if (!flattener.flatten(options.manifest, xmlParser, &outBuffer, {})) {
         return false;
     }
 
-    std::unique_ptr<uint8_t[]> data = std::unique_ptr<uint8_t[]>(new uint8_t[outBuffer.size()]);
-    uint8_t* p = data.get();
-    for (const auto& b : outBuffer) {
-        memcpy(p, b.buffer.get(), b.size);
-        p += b.size;
-    }
+    std::unique_ptr<uint8_t[]> data = util::copy(outBuffer);
 
     android::ResXMLTree tree;
-    if (tree.setTo(data.get(), outBuffer.size()) != android::NO_ERROR) {
+    if (tree.setTo(data.get(), outBuffer.size(), false) != android::NO_ERROR) {
         return false;
     }
 
@@ -537,14 +493,10 @@
         return false;
     }
 
-    std::ofstream out(outSource.path, std::ofstream::binary);
-    if (!out) {
-        Logger::error(outSource) << strerror(errno) << std::endl;
-        return false;
-    }
-
-    if (!util::writeAll(out, outBuffer)) {
-        Logger::error(outSource) << strerror(errno) << std::endl;
+    if (outApk->add(data.get(), outBuffer.size(), "AndroidManifest.xml",
+                ZipEntry::kCompressStored, nullptr) != android::NO_ERROR) {
+        Logger::error(options.output) << "failed to write 'AndroidManifest.xml' to apk."
+                                      << std::endl;
         return false;
     }
     return true;
@@ -562,10 +514,20 @@
     return parser.parse(source, pullParser, outInfo);
 }
 
+static void printCommandsAndDie() {
+    std::cerr << "The following commands are supported:" << std::endl << std::endl;
+    std::cerr << "compile       compiles a subset of resources" << std::endl;
+    std::cerr << "link          links together compiled resources and libraries" << std::endl;
+    std::cerr << std::endl;
+    std::cerr << "run aapt2 with one of the commands and the -h flag for extra details."
+              << std::endl;
+    exit(1);
+}
+
 static AaptOptions prepareArgs(int argc, char** argv) {
     if (argc < 2) {
-        std::cerr << "no command specified." << std::endl;
-        exit(1);
+        std::cerr << "no command specified." << std::endl << std::endl;
+        printCommandsAndDie();
     }
 
     const StringPiece command(argv[1]);
@@ -574,32 +536,32 @@
 
     AaptOptions options;
 
-    StringPiece outputDescription = "place output in file";
-    if (command == "package") {
-        options.phase = AaptOptions::Phase::Full;
-        outputDescription = "place output in directory";
-    } else if (command == "collect") {
-        options.phase = AaptOptions::Phase::Collect;
+    if (command == "--version" || command == "version") {
+        std::cout << kAaptVersionStr << std::endl;
+        exit(0);
     } else if (command == "link") {
         options.phase = AaptOptions::Phase::Link;
     } else if (command == "compile") {
         options.phase = AaptOptions::Phase::Compile;
-        outputDescription = "place output in directory";
-    } else if (command == "manifest") {
-        options.phase = AaptOptions::Phase::Manifest;
-        outputDescription = "place AndroidManifest.xml in directory";
     } else {
-        std::cerr << "invalid command '" << command << "'." << std::endl;
-        exit(1);
+        std::cerr << "invalid command '" << command << "'." << std::endl << std::endl;
+        printCommandsAndDie();
     }
 
-    if (options.phase == AaptOptions::Phase::Full) {
-        flag::requiredFlag("-S", "add a directory in which to find resources",
+    if (options.phase == AaptOptions::Phase::Compile) {
+        flag::requiredFlag("--package", "Android package name",
                 [&options](const StringPiece& arg) {
-                    options.sourceDirs.push_back(Source{ arg.toString() });
+                    options.appInfo.package = util::utf8ToUtf16(arg);
                 });
+        flag::optionalFlag("--binding", "Output directory for binding XML files",
+                [&options](const StringPiece& arg) {
+                    options.bindingOutput = Source{ arg.toString() };
+                });
+        flag::optionalSwitch("--no-version", "Disables automatic style and layout versioning",
+                             false, &options.versionStylesAndLayouts);
 
-        flag::requiredFlag("-M", "path to AndroidManifest.xml",
+    } else if (options.phase == AaptOptions::Phase::Link) {
+        flag::requiredFlag("--manifest", "AndroidManifest.xml of your app",
                 [&options](const StringPiece& arg) {
                     options.manifest = Source{ arg.toString() };
                 });
@@ -613,35 +575,16 @@
                 [&options](const StringPiece& arg) {
                     options.generateJavaClass = Source{ arg.toString() };
                 });
-
-    } else {
-        if (options.phase != AaptOptions::Phase::Manifest) {
-            flag::requiredFlag("--package", "Android package name",
-                    [&options](const StringPiece& arg) {
-                        options.appInfo.package = util::utf8ToUtf16(arg);
-                    });
-        }
-
-        if (options.phase != AaptOptions::Phase::Collect) {
-            flag::optionalFlag("-I", "add an Android APK to link against",
-                    [&options](const StringPiece& arg) {
-                        options.libraries.push_back(Source{ arg.toString() });
-                    });
-        }
-
-        if (options.phase == AaptOptions::Phase::Link) {
-            flag::optionalFlag("--java", "directory in which to generate R.java",
-                    [&options](const StringPiece& arg) {
-                        options.generateJavaClass = Source{ arg.toString() };
-                    });
-        }
     }
 
     // Common flags for all steps.
-    flag::requiredFlag("-o", outputDescription, [&options](const StringPiece& arg) {
+    flag::requiredFlag("-o", "Output path", [&options](const StringPiece& arg) {
         options.output = Source{ arg.toString() };
     });
-    flag::optionalSwitch("-v", "enables verbose logging", &options.verbose);
+
+    bool help = false;
+    flag::optionalSwitch("-v", "enables verbose logging", true, &options.verbose);
+    flag::optionalSwitch("-h", "displays this help menu", true, &help);
 
     // Build the command string for output (eg. "aapt2 compile").
     std::string fullCommand = "aapt2";
@@ -651,28 +594,18 @@
     // Actually read the command line flags.
     flag::parse(argc, argv, fullCommand);
 
+    if (help) {
+        flag::usageAndDie(fullCommand);
+    }
+
     // Copy all the remaining arguments.
-    if (options.phase == AaptOptions::Phase::Collect) {
-        for (const std::string& arg : flag::getArgs()) {
-            options.collectFiles.push_back(Source{ arg });
-        }
-    } else if (options.phase == AaptOptions::Phase::Compile) {
-        for (const std::string& arg : flag::getArgs()) {
-            options.compileFiles.push_back(Source{ arg });
-        }
-    } else if (options.phase == AaptOptions::Phase::Link) {
-        for (const std::string& arg : flag::getArgs()) {
-            options.linkFiles.push_back(Source{ arg });
-        }
-    } else if (options.phase == AaptOptions::Phase::Manifest) {
-        if (!flag::getArgs().empty()) {
-            options.manifest = Source{ flag::getArgs()[0] };
-        }
+    for (const std::string& arg : flag::getArgs()) {
+        options.input.push_back(Source{ arg });
     }
     return options;
 }
 
-static bool collectValues(const std::shared_ptr<ResourceTable>& table, const Source& source,
+static bool compileValues(const std::shared_ptr<ResourceTable>& table, const Source& source,
                           const ConfigDescription& config) {
     std::ifstream in(source.path, std::ifstream::binary);
     if (!in) {
@@ -738,115 +671,91 @@
     };
 }
 
-bool doAll(AaptOptions* options, const std::shared_ptr<ResourceTable>& table,
-           const std::shared_ptr<Resolver>& resolver) {
-    const bool versionStyles = (options->phase == AaptOptions::Phase::Full ||
-            options->phase == AaptOptions::Phase::Link);
-    const bool verifyNoMissingSymbols = (options->phase == AaptOptions::Phase::Full ||
-            options->phase == AaptOptions::Phase::Link);
-    const bool compileFiles = (options->phase == AaptOptions::Phase::Full ||
-            options->phase == AaptOptions::Phase::Compile);
-    const bool flattenTable = (options->phase == AaptOptions::Phase::Full ||
-            options->phase == AaptOptions::Phase::Collect ||
-            options->phase == AaptOptions::Phase::Link);
-    const bool useExtendedChunks = options->phase == AaptOptions::Phase::Collect;
-
-    // Build the output table path.
-    Source outputTable = options->output;
-    if (options->phase == AaptOptions::Phase::Full) {
-        appendPath(&outputTable.path, "resources.arsc");
-    }
-
-    bool error = false;
-    std::queue<CompileItem> compileQueue;
-
-    // If source directories were specified, walk them looking for resource files.
-    if (!options->sourceDirs.empty()) {
-        const char* customIgnore = getenv("ANDROID_AAPT_IGNORE");
-        FileFilter fileFilter;
-        if (customIgnore && customIgnore[0]) {
-            fileFilter.setPattern(customIgnore);
-        } else {
-            fileFilter.setPattern(
-                    "!.svn:!.git:!.ds_store:!*.scc:.*:<dir>_*:!CVS:!thumbs.db:!picasa.ini:!*~");
-        }
-
-        for (const Source& source : options->sourceDirs) {
-            if (!walkTree(source, fileFilter, &options->collectFiles)) {
-                return false;
-            }
-        }
-    }
-
-    // Load all binary resource tables.
-    for (const Source& source : options->linkFiles) {
-        error |= !loadBinaryResourceTable(table, source);
-    }
-
-    if (error) {
-        return false;
-    }
-
-    // Collect all the resource files.
-    // Need to parse the resource type/config/filename.
-    for (const Source& source : options->collectFiles) {
-        Maybe<ResourcePathData> maybePathData = extractResourcePathData(source);
-        if (!maybePathData) {
+bool writeResourceTable(const AaptOptions& options, const std::shared_ptr<ResourceTable>& table,
+                        const TableFlattener::Options& flattenerOptions, ZipFile* outApk) {
+    if (table->begin() != table->end()) {
+        BigBuffer buffer(1024);
+        TableFlattener flattener(flattenerOptions);
+        if (!flattener.flatten(&buffer, *table)) {
+            Logger::error() << "failed to flatten resource table." << std::endl;
             return false;
         }
 
-        const ResourcePathData& pathData = maybePathData.value();
-        if (pathData.resourceDir == u"values") {
-            if (options->verbose) {
-                Logger::note(source) << "collecting values..." << std::endl;
-            }
-
-            error |= !collectValues(table, source, pathData.config);
-            continue;
+        if (options.verbose) {
+            Logger::note() << "Final resource table size=" << util::formatSize(buffer.size())
+                           << std::endl;
         }
 
-        const ResourceType* type = parseResourceType(pathData.resourceDir);
-        if (!type) {
-            Logger::error(source) << "invalid resource type '" << pathData.resourceDir << "'."
-                                  << std::endl;
+        if (outApk->add(buffer, "resources.arsc", ZipEntry::kCompressStored, nullptr) !=
+                android::NO_ERROR) {
+            Logger::note(options.output) << "failed to store resource table." << std::endl;
+            return false;
+        }
+    }
+    return true;
+}
+
+static constexpr int kOpenFlags = ZipFile::kOpenCreate | ZipFile::kOpenTruncate |
+        ZipFile::kOpenReadWrite;
+
+bool link(const AaptOptions& options, const std::shared_ptr<ResourceTable>& outTable,
+          const std::shared_ptr<Resolver>& resolver) {
+    std::map<std::shared_ptr<ResourceTable>, std::unique_ptr<ZipFile>> apkFiles;
+    std::unordered_set<std::u16string> linkedPackages;
+
+    // Populate the linkedPackages with our own.
+    linkedPackages.insert(options.appInfo.package);
+
+    // Load all APK files.
+    for (const Source& source : options.input) {
+        std::unique_ptr<ZipFile> zipFile = util::make_unique<ZipFile>();
+        if (zipFile->open(source.path.data(), ZipFile::kOpenReadOnly) != android::NO_ERROR) {
+            Logger::error(source) << "failed to open: " << strerror(errno) << std::endl;
             return false;
         }
 
-        ResourceName resourceName = { table->getPackage(), *type, pathData.name };
+        std::shared_ptr<ResourceTable> table = std::make_shared<ResourceTable>();
 
-        // Add the file name to the resource table.
-        std::unique_ptr<FileReference> fileReference = makeFileReference(
-                table->getValueStringPool(),
-                util::utf16ToUtf8(pathData.name) + "." + pathData.extension,
-                *type, pathData.config);
-        error |= !table->addResource(resourceName, pathData.config, source.line(0),
-                                     std::move(fileReference));
-
-        if (pathData.extension == "xml") {
-            error |= !collectXml(table, source, resourceName, pathData.config);
+        ZipEntry* entry = zipFile->getEntryByName("resources.arsc");
+        if (!entry) {
+            Logger::error(source) << "missing 'resources.arsc'." << std::endl;
+            return false;
         }
 
-        compileQueue.push(
-                CompileItem{ source, resourceName, pathData.config, pathData.extension });
+        void* uncompressedData = zipFile->uncompress(entry);
+        assert(uncompressedData);
+
+        BinaryResourceParser parser(table, resolver, source, uncompressedData,
+                                    entry->getUncompressedLen());
+        if (!parser.parse()) {
+            free(uncompressedData);
+            return false;
+        }
+        free(uncompressedData);
+
+        // Keep track of where this table came from.
+        apkFiles[table] = std::move(zipFile);
+
+        // Add the package to the set of linked packages.
+        linkedPackages.insert(table->getPackage());
     }
 
-    if (error) {
-        return false;
+    for (auto& p : apkFiles) {
+        const std::shared_ptr<ResourceTable>& inTable = p.first;
+
+        if (!outTable->merge(std::move(*inTable))) {
+            return false;
+        }
     }
 
-    // Version all styles referencing attributes outside of their specified SDK version.
-    if (versionStyles) {
-        versionStylesForCompat(table);
-    }
+    {
+        // Now that everything is merged, let's link it.
+        Linker linker(outTable, resolver);
+        if (!linker.linkAndValidate()) {
+            return false;
+        }
 
-    // Verify that all references are valid.
-    Linker linker(table, resolver);
-    if (!linker.linkAndValidate()) {
-        return false;
-    }
-
-    // Verify that all symbols exist.
-    if (verifyNoMissingSymbols) {
+        // Verify that all symbols exist.
         const auto& unresolvedRefs = linker.getUnresolvedReferences();
         if (!unresolvedRefs.empty()) {
             for (const auto& entry : unresolvedRefs) {
@@ -859,143 +768,192 @@
         }
     }
 
-    // Compile files.
-    if (compileFiles) {
-        // First process any input compile files.
-        for (const Source& source : options->compileFiles) {
-            Maybe<ResourcePathData> maybePathData = extractResourcePathData(source);
-            if (!maybePathData) {
-                return false;
-            }
-
-            const ResourcePathData& pathData = maybePathData.value();
-            const ResourceType* type = parseResourceType(pathData.resourceDir);
-            if (!type) {
-                Logger::error(source) << "invalid resource type '" << pathData.resourceDir
-                                      << "'." << std::endl;
-                return false;
-            }
-
-            ResourceName resourceName = { table->getPackage(), *type, pathData.name };
-            compileQueue.push(
-                    CompileItem{ source, resourceName, pathData.config, pathData.extension });
-        }
-
-        // Now process the actual compile queue.
-        for (; !compileQueue.empty(); compileQueue.pop()) {
-            const CompileItem& item = compileQueue.front();
-
-            // Create the output directory path from the resource type and config.
-            std::stringstream outputPath;
-            outputPath << item.name.type;
-            if (item.config != ConfigDescription{}) {
-                outputPath << "-" << item.config.toString();
-            }
-
-            Source outSource = options->output;
-            appendPath(&outSource.path, "res");
-            appendPath(&outSource.path, outputPath.str());
-
-            // Make the directory.
-            if (!mkdirs(outSource.path)) {
-                Logger::error(outSource) << strerror(errno) << std::endl;
-                return false;
-            }
-
-            // Add the file name to the directory path.
-            appendPath(&outSource.path, util::utf16ToUtf8(item.name.entry) + "." + item.extension);
-
-            if (item.extension == "xml") {
-                if (options->verbose) {
-                    Logger::note(outSource) << "compiling XML file." << std::endl;
-                }
-
-                error |= !compileXml(resolver, item, outSource, &compileQueue);
-            } else if (item.extension == "png" || item.extension == "9.png") {
-                if (options->verbose) {
-                    Logger::note(outSource) << "compiling png file." << std::endl;
-                }
-
-                error |= !compilePng(item.source, outSource);
-            } else {
-                error |= !copyFile(item.source, outSource);
-            }
-        }
-
-        if (error) {
-            return false;
-        }
+    // Open the output APK file for writing.
+    ZipFile outApk;
+    if (outApk.open(options.output.path.data(), kOpenFlags) != android::NO_ERROR) {
+        Logger::error(options.output) << "failed to open: " << strerror(errno) << std::endl;
+        return false;
     }
 
-    // Compile and validate the AndroidManifest.xml.
-    if (!options->manifest.path.empty()) {
-        if (!compileAndroidManifest(resolver, *options)) {
-            return false;
+    if (!compileManifest(options, resolver, &outApk)) {
+        return false;
+    }
+
+    for (auto& p : apkFiles) {
+        std::unique_ptr<ZipFile>& zipFile = p.second;
+
+        // TODO(adamlesinski): Get list of files to read when processing config filter.
+
+        const int numEntries = zipFile->getNumEntries();
+        for (int i = 0; i < numEntries; i++) {
+            ZipEntry* entry = zipFile->getEntryByIndex(i);
+            assert(entry);
+
+            StringPiece filename = entry->getFileName();
+            if (!util::stringStartsWith<char>(filename, "res/")) {
+                continue;
+            }
+
+            if (util::stringEndsWith<char>(filename, ".xml")) {
+                void* uncompressedData = zipFile->uncompress(entry);
+                assert(uncompressedData);
+
+                LinkItem item = { Source{ filename.toString() }, filename.toString() };
+
+                if (!linkXml(options, resolver, item, uncompressedData,
+                            entry->getUncompressedLen(), &outApk)) {
+                    Logger::error(options.output) << "failed to link '" << filename << "'."
+                                                  << std::endl;
+                    return false;
+                }
+            } else {
+                if (outApk.add(zipFile.get(), entry, 0, nullptr) != android::NO_ERROR) {
+                    Logger::error(options.output) << "failed to copy '" << filename << "'."
+                                                  << std::endl;
+                    return false;
+                }
+            }
         }
     }
 
     // Generate the Java class file.
-    if (options->generateJavaClass) {
-        Source outPath = options->generateJavaClass.value();
-        if (options->verbose) {
-            Logger::note() << "writing symbols to " << outPath << "." << std::endl;
-        }
+    if (options.generateJavaClass) {
+        JavaClassGenerator generator(outTable, {});
 
-        // Build the output directory from the package name.
-        // Eg. com.android.app -> com/android/app
-        const std::string packageUtf8 = util::utf16ToUtf8(table->getPackage());
-        for (StringPiece part : util::tokenize<char>(packageUtf8, '.')) {
-            appendPath(&outPath.path, part);
-        }
+        for (const std::u16string& package : linkedPackages) {
+            Source outPath = options.generateJavaClass.value();
 
-        if (!mkdirs(outPath.path)) {
-            Logger::error(outPath) << strerror(errno) << std::endl;
-            return false;
-        }
+            // Build the output directory from the package name.
+            // Eg. com.android.app -> com/android/app
+            const std::string packageUtf8 = util::utf16ToUtf8(package);
+            for (StringPiece part : util::tokenize<char>(packageUtf8, '.')) {
+                appendPath(&outPath.path, part);
+            }
 
-        appendPath(&outPath.path, "R.java");
+            if (!mkdirs(outPath.path)) {
+                Logger::error(outPath) << strerror(errno) << std::endl;
+                return false;
+            }
 
-        std::ofstream fout(outPath.path);
-        if (!fout) {
-            Logger::error(outPath) << strerror(errno) << std::endl;
-            return false;
-        }
+            appendPath(&outPath.path, "R.java");
 
-        JavaClassGenerator generator(table, {});
-        if (!generator.generate(fout)) {
-            Logger::error(outPath) << generator.getError() << "." << std::endl;
-            return false;
+            if (options.verbose) {
+                Logger::note(outPath) << "writing Java symbols." << std::endl;
+            }
+
+            std::ofstream fout(outPath.path);
+            if (!fout) {
+                Logger::error(outPath) << strerror(errno) << std::endl;
+                return false;
+            }
+
+            if (!generator.generate(package, fout)) {
+                Logger::error(outPath) << generator.getError() << "." << std::endl;
+                return false;
+            }
         }
     }
 
     // Flatten the resource table.
-    if (flattenTable && table->begin() != table->end()) {
-        BigBuffer buffer(1024);
-        TableFlattener::Options tableOptions;
-        tableOptions.useExtendedChunks = useExtendedChunks;
-        TableFlattener flattener(tableOptions);
-        if (!flattener.flatten(&buffer, *table)) {
-            Logger::error() << "failed to flatten resource table." << std::endl;
-            return false;
-        }
-
-        if (options->verbose) {
-            Logger::note() << "Final resource table size=" << util::formatSize(buffer.size())
-                           << std::endl;
-        }
-
-        std::ofstream fout(outputTable.path, std::ofstream::binary);
-        if (!fout) {
-            Logger::error(outputTable) << strerror(errno) << "." << std::endl;
-            return false;
-        }
-
-        if (!util::writeAll(fout, buffer)) {
-            Logger::error(outputTable) << strerror(errno) << "." << std::endl;
-            return false;
-        }
-        fout.flush();
+    TableFlattener::Options flattenerOptions;
+    flattenerOptions.useExtendedChunks = false;
+    if (!writeResourceTable(options, outTable, flattenerOptions, &outApk)) {
+        return false;
     }
+
+    outApk.flush();
+    return true;
+}
+
+bool compile(const AaptOptions& options, const std::shared_ptr<ResourceTable>& table,
+             const std::shared_ptr<Resolver>& resolver) {
+    std::queue<CompileItem> compileQueue;
+    bool error = false;
+
+    // Compile all the resource files passed in on the command line.
+    for (const Source& source : options.input) {
+        // Need to parse the resource type/config/filename.
+        Maybe<ResourcePathData> maybePathData = extractResourcePathData(source);
+        if (!maybePathData) {
+            return false;
+        }
+
+        const ResourcePathData& pathData = maybePathData.value();
+        if (pathData.resourceDir == u"values") {
+            // The file is in the values directory, which means its contents will
+            // go into the resource table.
+            if (options.verbose) {
+                Logger::note(source) << "compiling values." << std::endl;
+            }
+
+            error |= !compileValues(table, source, pathData.config);
+        } else {
+            // The file is in a directory like 'layout' or 'drawable'. Find out
+            // the type.
+            const ResourceType* type = parseResourceType(pathData.resourceDir);
+            if (!type) {
+                Logger::error(source) << "invalid resource type '" << pathData.resourceDir << "'."
+                                      << std::endl;
+                return false;
+            }
+
+            compileQueue.push(CompileItem{
+                    source,
+                    ResourceName{ table->getPackage(), *type, pathData.name },
+                    pathData.config,
+                    pathData.extension
+            });
+        }
+    }
+
+    if (error) {
+        return false;
+    }
+
+    // Version all styles referencing attributes outside of their specified SDK version.
+    if (options.versionStylesAndLayouts) {
+        versionStylesForCompat(table);
+    }
+
+    // Open the output APK file for writing.
+    ZipFile outApk;
+    if (outApk.open(options.output.path.data(), kOpenFlags) != android::NO_ERROR) {
+        Logger::error(options.output) << "failed to open: " << strerror(errno) << std::endl;
+        return false;
+    }
+
+    // Compile each file.
+    for (; !compileQueue.empty(); compileQueue.pop()) {
+        const CompileItem& item = compileQueue.front();
+
+        // Add the file name to the resource table.
+        error |= !addFileReference(table, item);
+
+        if (item.extension == "xml") {
+            error |= !compileXml(options, table, item, &compileQueue, &outApk);
+        } else if (item.extension == "png" || item.extension == "9.png") {
+            error |= !compilePng(options, item, &outApk);
+        } else {
+            error |= !copyFile(options, item, &outApk);
+        }
+    }
+
+    if (error) {
+        return false;
+    }
+
+    // Link and assign resource IDs.
+    Linker linker(table, resolver);
+    if (!linker.linkAndValidate()) {
+        return false;
+    }
+
+    // Flatten the resource table.
+    if (!writeResourceTable(options, table, {}, &outApk)) {
+        return false;
+    }
+
+    outApk.flush();
     return true;
 }
 
@@ -1057,10 +1015,16 @@
     // Make the resolver that will cache IDs for us.
     std::shared_ptr<Resolver> resolver = std::make_shared<Resolver>(table, libraries);
 
-    // Do the work.
-    if (!doAll(&options, table, resolver)) {
-        Logger::error() << "aapt exiting with failures." << std::endl;
-        return 1;
+    if (options.phase == AaptOptions::Phase::Compile) {
+        if (!compile(options, table, resolver)) {
+            Logger::error() << "aapt exiting with failures." << std::endl;
+            return 1;
+        }
+    } else if (options.phase == AaptOptions::Phase::Link) {
+        if (!link(options, table, resolver)) {
+            Logger::error() << "aapt exiting with failures." << std::endl;
+            return 1;
+        }
     }
     return 0;
 }
diff --git a/tools/aapt2/NameMangler.h b/tools/aapt2/NameMangler.h
new file mode 100644
index 0000000..1e15e20
--- /dev/null
+++ b/tools/aapt2/NameMangler.h
@@ -0,0 +1,54 @@
+/*
+ * 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 AAPT_NAME_MANGLER_H
+#define AAPT_NAME_MANGLER_H
+
+#include <string>
+
+namespace aapt {
+
+struct NameMangler {
+    /**
+     * Mangles the name in `outName` with the `package` and stores the mangled
+     * result in `outName`. The mangled name should contain symbols that are
+     * illegal to define in XML, so that there will never be name mangling
+     * collisions.
+     */
+    static void mangle(const std::u16string& package, std::u16string* outName) {
+        *outName = package + u"$" + *outName;
+    }
+
+    /**
+     * Unmangles the name in `outName`, storing the correct name back in `outName`
+     * and the package in `outPackage`. Returns true if the name was unmangled or
+     * false if the name was never mangled to begin with.
+     */
+    static bool unmangle(std::u16string* outName, std::u16string* outPackage) {
+        size_t pivot = outName->find(u'$');
+        if (pivot == std::string::npos) {
+            return false;
+        }
+
+        outPackage->assign(outName->data(), pivot);
+        outName->assign(outName->data() + pivot + 1, outName->size() - (pivot + 1));
+        return true;
+    }
+};
+
+} // namespace aapt
+
+#endif // AAPT_NAME_MANGLER_H
diff --git a/tools/aapt2/NameMangler_test.cpp b/tools/aapt2/NameMangler_test.cpp
new file mode 100644
index 0000000..6103655
--- /dev/null
+++ b/tools/aapt2/NameMangler_test.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 "NameMangler.h"
+
+#include <gtest/gtest.h>
+#include <string>
+
+namespace aapt {
+
+TEST(NameManglerTest, MangleName) {
+    std::u16string package = u"android.appcompat";
+    std::u16string name = u"Platform.AppCompat";
+
+    NameMangler::mangle(package, &name);
+    EXPECT_EQ(name, u"android.appcompat$Platform.AppCompat");
+
+    std::u16string newPackage;
+    ASSERT_TRUE(NameMangler::unmangle(&name, &newPackage));
+    EXPECT_EQ(name, u"Platform.AppCompat");
+    EXPECT_EQ(newPackage, u"android.appcompat");
+}
+
+TEST(NameManglerTest, IgnoreUnmangledName) {
+    std::u16string package;
+    std::u16string name = u"foo_bar";
+
+    EXPECT_FALSE(NameMangler::unmangle(&name, &package));
+    EXPECT_EQ(name, u"foo_bar");
+}
+
+} // namespace aapt
diff --git a/tools/aapt2/Png.cpp b/tools/aapt2/Png.cpp
index 76120ac..4e9b68e 100644
--- a/tools/aapt2/Png.cpp
+++ b/tools/aapt2/Png.cpp
@@ -14,6 +14,7 @@
  * limitations under the License.
  */
 
+#include "BigBuffer.h"
 #include "Logger.h"
 #include "Png.h"
 #include "Source.h"
@@ -85,17 +86,12 @@
 }
 
 static void writeDataToStream(png_structp writePtr, png_bytep data, png_size_t length) {
-    std::ostream* output = reinterpret_cast<std::ostream*>(png_get_io_ptr(writePtr));
-    if (!output->write(reinterpret_cast<const char*>(data), length)) {
-        png_error(writePtr, strerror(errno));
-    }
+    BigBuffer* outBuffer = reinterpret_cast<BigBuffer*>(png_get_io_ptr(writePtr));
+    png_bytep buf = outBuffer->nextBlock<png_byte>(length);
+    memcpy(buf, data, length);
 }
 
-static void flushDataToStream(png_structp writePtr) {
-    std::ostream* output = reinterpret_cast<std::ostream*>(png_get_io_ptr(writePtr));
-    if (!output->flush()) {
-        png_error(writePtr, strerror(errno));
-    }
+static void flushDataToStream(png_structp /*writePtr*/) {
 }
 
 static void logWarning(png_structp readPtr, png_const_charp warningMessage) {
@@ -1196,7 +1192,7 @@
 }
 
 
-bool Png::process(const Source& source, std::istream& input, std::ostream& output,
+bool Png::process(const Source& source, std::istream& input, BigBuffer* outBuffer,
                   const Options& options, std::string* outError) {
     png_byte signature[kPngSignatureSize];
 
@@ -1262,7 +1258,7 @@
     png_set_error_fn(writePtr, nullptr, nullptr, logWarning);
 
     // Set the write function to write to std::ostream.
-    png_set_write_fn(writePtr, (png_voidp)&output, writeDataToStream, flushDataToStream);
+    png_set_write_fn(writePtr, (png_voidp)outBuffer, writeDataToStream, flushDataToStream);
 
     if (!writePng(writePtr, writeInfoPtr, &pngInfo, options.grayScaleTolerance, &logger,
                   outError)) {
diff --git a/tools/aapt2/Png.h b/tools/aapt2/Png.h
index bc80754..4577ab8 100644
--- a/tools/aapt2/Png.h
+++ b/tools/aapt2/Png.h
@@ -17,6 +17,7 @@
 #ifndef AAPT_PNG_H
 #define AAPT_PNG_H
 
+#include "BigBuffer.h"
 #include "Source.h"
 
 #include <iostream>
@@ -29,7 +30,7 @@
         int grayScaleTolerance = 0;
     };
 
-    bool process(const Source& source, std::istream& input, std::ostream& output,
+    bool process(const Source& source, std::istream& input, BigBuffer* outBuffer,
                  const Options& options, std::string* outError);
 };
 
diff --git a/tools/aapt2/ResChunkPullParser.h b/tools/aapt2/ResChunkPullParser.h
index 7366c89..1426ed2 100644
--- a/tools/aapt2/ResChunkPullParser.h
+++ b/tools/aapt2/ResChunkPullParser.h
@@ -74,6 +74,22 @@
     std::string mLastError;
 };
 
+template <typename T>
+inline static const T* convertTo(const android::ResChunk_header* chunk) {
+    if (chunk->headerSize < sizeof(T)) {
+        return nullptr;
+    }
+    return reinterpret_cast<const T*>(chunk);
+}
+
+inline static const uint8_t* getChunkData(const android::ResChunk_header& chunk) {
+    return reinterpret_cast<const uint8_t*>(&chunk) + chunk.headerSize;
+}
+
+inline static size_t getChunkDataLen(const android::ResChunk_header& chunk) {
+    return chunk.size - chunk.headerSize;
+}
+
 //
 // Implementation
 //
diff --git a/tools/aapt2/Resolver.cpp b/tools/aapt2/Resolver.cpp
index 93b5e98..ae006ab 100644
--- a/tools/aapt2/Resolver.cpp
+++ b/tools/aapt2/Resolver.cpp
@@ -15,6 +15,7 @@
  */
 
 #include "Maybe.h"
+#include "NameMangler.h"
 #include "Resolver.h"
 #include "Resource.h"
 #include "ResourceTable.h"
@@ -31,6 +32,12 @@
 Resolver::Resolver(std::shared_ptr<const ResourceTable> table,
                    std::shared_ptr<const android::AssetManager> sources) :
         mTable(table), mSources(sources) {
+    const android::ResTable& resTable = mSources->getResources(false);
+    const size_t packageCount = resTable.getBasePackageCount();
+    for (size_t i = 0; i < packageCount; i++) {
+        std::u16string packageName = resTable.getBasePackageName(i).string();
+        mIncludedPackages.insert(std::move(packageName));
+    }
 }
 
 Maybe<ResourceId> Resolver::findId(const ResourceName& name) {
@@ -47,9 +54,31 @@
         return Entry{ cacheIter->second.id, cacheIter->second.attr.get() };
     }
 
+    ResourceName mangledName;
+    const ResourceName* nameToSearch = &name;
+    if (name.package != mTable->getPackage()) {
+        // This may be a reference to an included resource or
+        // to a mangled resource.
+        if (mIncludedPackages.find(name.package) == mIncludedPackages.end()) {
+            // This is not in our included set, so mangle the name and
+            // check for that.
+            mangledName.entry = name.entry;
+            NameMangler::mangle(name.package, &mangledName.entry);
+            mangledName.package = mTable->getPackage();
+            mangledName.type = name.type;
+            nameToSearch = &mangledName;
+        } else {
+            const CacheEntry* cacheEntry = buildCacheEntry(name);
+            if (cacheEntry) {
+                return Entry{ cacheEntry->id, cacheEntry->attr.get() };
+            }
+            return {};
+        }
+    }
+
     const ResourceTableType* type;
     const ResourceEntry* entry;
-    std::tie(type, entry) = mTable->findResource(name);
+    std::tie(type, entry) = mTable->findResource(*nameToSearch);
     if (type && entry) {
         Entry result = {};
         if (mTable->getPackageId() != ResourceTable::kUnsetPackageId &&
@@ -65,11 +94,6 @@
         }
         return result;
     }
-
-    const CacheEntry* cacheEntry = buildCacheEntry(name);
-    if (cacheEntry) {
-        return Entry{ cacheEntry->id, cacheEntry->attr.get() };
-    }
     return {};
 }
 
diff --git a/tools/aapt2/Resolver.h b/tools/aapt2/Resolver.h
index 90a8cd9..cb2234d 100644
--- a/tools/aapt2/Resolver.h
+++ b/tools/aapt2/Resolver.h
@@ -26,6 +26,7 @@
 #include <androidfw/ResourceTypes.h>
 #include <memory>
 #include <vector>
+#include <unordered_set>
 
 namespace aapt {
 
@@ -94,6 +95,7 @@
     std::shared_ptr<const ResourceTable> mTable;
     std::shared_ptr<const android::AssetManager> mSources;
     std::map<ResourceName, CacheEntry> mCache;
+    std::unordered_set<std::u16string> mIncludedPackages;
 };
 
 inline const std::u16string& Resolver::getDefaultPackage() const {
diff --git a/tools/aapt2/Resource.h b/tools/aapt2/Resource.h
index 4d2c64c..f928acd 100644
--- a/tools/aapt2/Resource.h
+++ b/tools/aapt2/Resource.h
@@ -193,8 +193,7 @@
 // ResourceType implementation.
 //
 
-inline ::std::ostream& operator<<(::std::ostream& out,
-        const ResourceType& val) {
+inline ::std::ostream& operator<<(::std::ostream& out, const ResourceType& val) {
     return out << toString(val);
 }
 
@@ -221,6 +220,14 @@
             != std::tie(rhs.package, rhs.type, rhs.entry);
 }
 
+inline ::std::ostream& operator<<(::std::ostream& out, const ResourceName& name) {
+    if (!name.package.empty()) {
+        out << name.package << ":";
+    }
+    return out << name.type << "/" << name.entry;
+}
+
+
 //
 // ResourceNameRef implementation.
 //
@@ -264,8 +271,7 @@
             != std::tie(rhs.package, rhs.type, rhs.entry);
 }
 
-inline ::std::ostream& operator<<(::std::ostream& out,
-        const ResourceNameRef& name) {
+inline ::std::ostream& operator<<(::std::ostream& out, const ResourceNameRef& name) {
     if (!name.package.empty()) {
         out << name.package << ":";
     }
diff --git a/tools/aapt2/ResourceParser.cpp b/tools/aapt2/ResourceParser.cpp
index 4c96187..943892d 100644
--- a/tools/aapt2/ResourceParser.cpp
+++ b/tools/aapt2/ResourceParser.cpp
@@ -22,6 +22,8 @@
 #include "Util.h"
 #include "XliffXmlPullParser.h"
 
+#include <sstream>
+
 namespace aapt {
 
 void ResourceParser::extractResourceName(const StringPiece16& str, StringPiece16* outPackage,
@@ -107,6 +109,71 @@
     return false;
 }
 
+/*
+ * Style parent's are a bit different. We accept the following formats:
+ *
+ * @[package:]style/<entry>
+ * ?[package:]style/<entry>
+ * <package>:[style/]<entry>
+ * [package:style/]<entry>
+ */
+bool ResourceParser::parseStyleParentReference(const StringPiece16& str, Reference* outReference,
+                                               std::string* outError) {
+    if (str.empty()) {
+        return true;
+    }
+
+    StringPiece16 name = str;
+
+    bool hasLeadingIdentifiers = false;
+    bool privateRef = false;
+
+    // Skip over these identifiers. A style's parent is a normal reference.
+    if (name.data()[0] == u'@' || name.data()[0] == u'?') {
+        hasLeadingIdentifiers = true;
+        name = name.substr(1, name.size() - 1);
+        if (name.data()[0] == u'*') {
+            privateRef = true;
+            name = name.substr(1, name.size() - 1);
+        }
+    }
+
+    ResourceNameRef ref;
+    ref.type = ResourceType::kStyle;
+
+    StringPiece16 typeStr;
+    extractResourceName(name, &ref.package, &typeStr, &ref.entry);
+    if (!typeStr.empty()) {
+        // If we have a type, make sure it is a Style.
+        const ResourceType* parsedType = parseResourceType(typeStr);
+        if (!parsedType || *parsedType != ResourceType::kStyle) {
+            std::stringstream err;
+            err << "invalid resource type '" << typeStr << "' for parent of style";
+            *outError = err.str();
+            return false;
+        }
+    } else {
+        // No type was defined, this should not have a leading identifier.
+        if (hasLeadingIdentifiers) {
+            std::stringstream err;
+            err << "invalid parent reference '" << str << "'";
+            *outError = err.str();
+            return false;
+        }
+    }
+
+    if (!hasLeadingIdentifiers && ref.package.empty() && !typeStr.empty()) {
+        std::stringstream err;
+        err << "invalid parent reference '" << str << "'";
+        *outError = err.str();
+        return false;
+    }
+
+    outReference->name = ref.toResourceName();
+    outReference->privateReference = privateRef;
+    return true;
+}
+
 std::unique_ptr<Reference> ResourceParser::tryParseReference(const StringPiece16& str,
                                                              const StringPiece16& defaultPackage,
                                                              bool* outCreate) {
@@ -885,15 +952,16 @@
 
 bool ResourceParser::parseAttr(XmlPullParser* parser, const ResourceNameRef& resourceName) {
     const SourceLine source = mSource.line(parser->getLineNumber());
-    std::unique_ptr<Attribute> attr = parseAttrImpl(parser, resourceName, false);
+    ResourceName actualName = resourceName.toResourceName();
+    std::unique_ptr<Attribute> attr = parseAttrImpl(parser, &actualName, false);
     if (!attr) {
         return false;
     }
-    return mTable->addResource(resourceName, mConfig, source, std::move(attr));
+    return mTable->addResource(actualName, mConfig, source, std::move(attr));
 }
 
 std::unique_ptr<Attribute> ResourceParser::parseAttrImpl(XmlPullParser* parser,
-                                                         const ResourceNameRef& resourceName,
+                                                         ResourceName* resourceName,
                                                          bool weak) {
     uint32_t typeMask = 0;
 
@@ -911,6 +979,18 @@
         }
     }
 
+    // If this is a declaration, the package name may be in the name. Separate these out.
+    // Eg. <attr name="android:text" />
+    // No format attribute is allowed.
+    if (weak && formatAttrIter == endAttrIter) {
+        StringPiece16 package, type, name;
+        extractResourceName(resourceName->entry, &package, &type, &name);
+        if (type.empty() && !package.empty()) {
+            resourceName->package = package.toString();
+            resourceName->entry = name.toString();
+        }
+    }
+
     std::vector<Attribute::Symbol> items;
 
     bool error = false;
@@ -1079,31 +1159,15 @@
 
 bool ResourceParser::parseStyle(XmlPullParser* parser, const ResourceNameRef& resourceName) {
     const SourceLine source = mSource.line(parser->getLineNumber());
-    std::unique_ptr<Style> style = util::make_unique<Style>();
+    std::unique_ptr<Style> style = util::make_unique<Style>(false);
 
     const auto endAttrIter = parser->endAttributes();
     const auto parentAttrIter = parser->findAttribute(u"", u"parent");
     if (parentAttrIter != endAttrIter) {
-        ResourceNameRef ref;
-        bool create = false;
-        bool privateRef = false;
-        if (tryParseReference(parentAttrIter->value, &ref, &create, &privateRef)) {
-            if (create) {
-                mLogger.error(source.line)
-                        << "parent of style can not be an ID."
-                        << std::endl;
-                return false;
-            }
-            style->parent.name = ref.toResourceName();
-            style->parent.privateReference = privateRef;
-        } else if (tryParseAttributeReference(parentAttrIter->value, &ref)) {
-            style->parent.name = ref.toResourceName();
-        } else {
-            // TODO(adamlesinski): Try parsing without the '@' or '?'.
-            // Also, make sure to check the entry name for weird symbols.
-            style->parent.name = ResourceName {
-                {}, ResourceType::kStyle, parentAttrIter->value
-            };
+        std::string errStr;
+        if (!parseStyleParentReference(parentAttrIter->value, &style->parent, &errStr)) {
+            mLogger.error(source.line) << errStr << "." << std::endl;
+            return false;
         }
 
         if (style->parent.name.package.empty()) {
@@ -1277,15 +1341,13 @@
             }
 
             // Copy because our iterator will be invalidated.
-            std::u16string attrName = attrIter->value;
-
-            ResourceNameRef attrResourceName = {
+            ResourceName attrResourceName = {
                     mTable->getPackage(),
                     ResourceType::kAttr,
-                    attrName
+                    attrIter->value
             };
 
-            std::unique_ptr<Attribute> attr = parseAttrImpl(&childParser, attrResourceName, true);
+            std::unique_ptr<Attribute> attr = parseAttrImpl(&childParser, &attrResourceName, true);
             if (!attr) {
                 success = false;
                 continue;
@@ -1293,9 +1355,13 @@
 
             styleable->entries.emplace_back(attrResourceName);
 
-            success &= mTable->addResource(attrResourceName, mConfig,
-                                           mSource.line(childParser.getLineNumber()),
-                                           std::move(attr));
+            // The package may have been corrected to another package. If that is so,
+            // we don't add the declaration.
+            if (attrResourceName.package == mTable->getPackage()) {
+                success &= mTable->addResource(attrResourceName, mConfig,
+                                               mSource.line(childParser.getLineNumber()),
+                                               std::move(attr));
+            }
 
         } else if (elementName != u"eat-comment" && elementName != u"skip") {
             mLogger.error(childParser.getLineNumber())
diff --git a/tools/aapt2/ResourceParser.h b/tools/aapt2/ResourceParser.h
index 96bba4f..52194bd 100644
--- a/tools/aapt2/ResourceParser.h
+++ b/tools/aapt2/ResourceParser.h
@@ -64,6 +64,17 @@
                                            ResourceNameRef* outReference);
 
     /*
+     * Returns true if the string `str` was parsed as a valid reference to a style.
+     * The format for a style parent is slightly more flexible than a normal reference:
+     *
+     * @[package:]style/<entry> or
+     * ?[package:]style/<entry> or
+     * <package>:[style/]<entry>
+     */
+    static bool parseStyleParentReference(const StringPiece16& str, Reference* outReference,
+                                          std::string* outError);
+
+    /*
      * Returns a Reference object if the string was parsed as a resource or attribute reference,
      * ( @[+][package:]type/name | ?[package:]type/name )
      * assigning defaultPackage if the package was not present in the string, and setting
@@ -166,7 +177,7 @@
     bool parsePublic(XmlPullParser* parser, const StringPiece16& name);
     bool parseAttr(XmlPullParser* parser, const ResourceNameRef& resourceName);
     std::unique_ptr<Attribute> parseAttrImpl(XmlPullParser* parser,
-                                             const ResourceNameRef& resourceName,
+                                             ResourceName* resourceName,
                                              bool weak);
     bool parseEnumOrFlagItem(XmlPullParser* parser, const StringPiece16& tag,
                              Attribute::Symbol* outSymbol);
diff --git a/tools/aapt2/ResourceParser_test.cpp b/tools/aapt2/ResourceParser_test.cpp
index 5afbaf4..63352de 100644
--- a/tools/aapt2/ResourceParser_test.cpp
+++ b/tools/aapt2/ResourceParser_test.cpp
@@ -94,6 +94,31 @@
                                                    &privateRef));
 }
 
+TEST(ResourceParserReferenceTest, ParseStyleParentReference) {
+    Reference ref;
+    std::string errStr;
+    EXPECT_TRUE(ResourceParser::parseStyleParentReference(u"@android:style/foo", &ref, &errStr));
+    EXPECT_EQ(ref.name, (ResourceName{ u"android", ResourceType::kStyle, u"foo" }));
+
+    EXPECT_TRUE(ResourceParser::parseStyleParentReference(u"@style/foo", &ref, &errStr));
+    EXPECT_EQ(ref.name, (ResourceName{ {}, ResourceType::kStyle, u"foo" }));
+
+    EXPECT_TRUE(ResourceParser::parseStyleParentReference(u"?android:style/foo", &ref, &errStr));
+    EXPECT_EQ(ref.name, (ResourceName{ u"android", ResourceType::kStyle, u"foo" }));
+
+    EXPECT_TRUE(ResourceParser::parseStyleParentReference(u"?style/foo", &ref, &errStr));
+    EXPECT_EQ(ref.name, (ResourceName{ {}, ResourceType::kStyle, u"foo" }));
+
+    EXPECT_TRUE(ResourceParser::parseStyleParentReference(u"android:style/foo", &ref, &errStr));
+    EXPECT_EQ(ref.name, (ResourceName{ u"android", ResourceType::kStyle, u"foo" }));
+
+    EXPECT_TRUE(ResourceParser::parseStyleParentReference(u"android:foo", &ref, &errStr));
+    EXPECT_EQ(ref.name, (ResourceName{ u"android", ResourceType::kStyle, u"foo" }));
+
+    EXPECT_TRUE(ResourceParser::parseStyleParentReference(u"foo", &ref, &errStr));
+    EXPECT_EQ(ref.name, (ResourceName{ {}, ResourceType::kStyle, u"foo" }));
+}
+
 struct ResourceParserTest : public ::testing::Test {
     virtual void SetUp() override {
         mTable = std::make_shared<ResourceTable>();
@@ -283,7 +308,7 @@
 
 TEST_F(ResourceParserTest, ParseStyle) {
     std::stringstream input;
-    input << "<style name=\"foo\" parent=\"fu\">" << std::endl
+    input << "<style name=\"foo\" parent=\"@style/fu\">" << std::endl
           << "  <item name=\"bar\">#ffffffff</item>" << std::endl
           << "  <item name=\"bat\">@string/hey</item>" << std::endl
           << "  <item name=\"baz\"><b>hey</b></item>" << std::endl
@@ -304,6 +329,17 @@
               (ResourceName{ u"android", ResourceType::kAttr, u"baz" }));
 }
 
+TEST_F(ResourceParserTest, ParseStyleWithShorthandParent) {
+    std::stringstream input;
+    input << "<style name=\"foo\" parent=\"com.app:Theme\"/>" << std::endl;
+    ASSERT_TRUE(testParse(input));
+
+    const Style* style = findResource<Style>(
+            ResourceName{ u"android", ResourceType::kStyle, u"foo" });
+    ASSERT_NE(style, nullptr);
+    EXPECT_EQ(ResourceNameRef(u"com.app", ResourceType::kStyle, u"Theme"), style->parent.name);
+}
+
 TEST_F(ResourceParserTest, ParseAutoGeneratedIdReference) {
     std::stringstream input;
     input << "<string name=\"foo\">@+id/bar</string>" << std::endl;
diff --git a/tools/aapt2/ResourceTable.cpp b/tools/aapt2/ResourceTable.cpp
index 794090d0..02be651 100644
--- a/tools/aapt2/ResourceTable.cpp
+++ b/tools/aapt2/ResourceTable.cpp
@@ -16,6 +16,7 @@
 
 #include "ConfigDescription.h"
 #include "Logger.h"
+#include "NameMangler.h"
 #include "ResourceTable.h"
 #include "ResourceValues.h"
 #include "Util.h"
@@ -311,6 +312,71 @@
     return true;
 }
 
+bool ResourceTable::merge(ResourceTable&& other) {
+    const bool mangleNames = mPackage != other.getPackage();
+    std::u16string mangledName;
+
+    for (auto& otherType : other) {
+        std::unique_ptr<ResourceTableType>& type = findOrCreateType(otherType->type);
+        if (type->publicStatus.isPublic && otherType->publicStatus.isPublic &&
+                type->typeId != otherType->typeId) {
+            Logger::error() << "can not merge type '" << type->type << "': conflicting public IDs "
+                            << "(" << type->typeId << " vs " << otherType->typeId << ")."
+                            << std::endl;
+            return false;
+        }
+
+        for (auto& otherEntry : otherType->entries) {
+            const std::u16string* nameToAdd = &otherEntry->name;
+            if (mangleNames) {
+                mangledName = otherEntry->name;
+                NameMangler::mangle(other.getPackage(), &mangledName);
+                nameToAdd = &mangledName;
+            }
+
+            std::unique_ptr<ResourceEntry>& entry = findOrCreateEntry(type, *nameToAdd);
+            if (entry->publicStatus.isPublic && otherEntry->publicStatus.isPublic &&
+                    entry->entryId != otherEntry->entryId) {
+                Logger::error() << "can not merge entry '" << type->type << "/" << entry->name
+                                << "': conflicting public IDs "
+                                << "(" << entry->entryId << " vs " << entry->entryId << ")."
+                                << std::endl;
+                return false;
+            }
+
+            for (ResourceConfigValue& otherValue : otherEntry->values) {
+                auto iter = std::lower_bound(entry->values.begin(), entry->values.end(),
+                                             otherValue.config, compareConfigs);
+                if (iter != entry->values.end() && iter->config == otherValue.config) {
+                    int collisionResult = defaultCollisionHandler(*iter->value, *otherValue.value);
+                    if (collisionResult > 0) {
+                        // Take the incoming value.
+                        iter->source = std::move(otherValue.source);
+                        iter->comment = std::move(otherValue.comment);
+                        iter->value = std::unique_ptr<Value>(otherValue.value->clone(&mValuePool));
+                    } else if (collisionResult == 0) {
+                        ResourceNameRef resourceName = { mPackage, type->type, entry->name };
+                        Logger::error(otherValue.source)
+                                << "resource '" << resourceName << "' has a conflicting value for "
+                                << "configuration (" << otherValue.config << ")."
+                                << std::endl;
+                        Logger::note(iter->source) << "originally defined here." << std::endl;
+                        return false;
+                    }
+                } else {
+                    entry->values.insert(iter, ResourceConfigValue{
+                            otherValue.config,
+                            std::move(otherValue.source),
+                            std::move(otherValue.comment),
+                            std::unique_ptr<Value>(otherValue.value->clone(&mValuePool)),
+                    });
+                }
+            }
+        }
+    }
+    return true;
+}
+
 std::tuple<const ResourceTableType*, const ResourceEntry*>
 ResourceTable::findResource(const ResourceNameRef& name) const {
     if (name.package != mPackage) {
diff --git a/tools/aapt2/ResourceTable.h b/tools/aapt2/ResourceTable.h
index 57b5213..3591d11 100644
--- a/tools/aapt2/ResourceTable.h
+++ b/tools/aapt2/ResourceTable.h
@@ -148,6 +148,12 @@
 
     bool markPublic(const ResourceNameRef& name, const ResourceId resId, const SourceLine& source);
 
+    /*
+     * Merges the resources from `other` into this table, mangling the names of the resources
+     * if `other` has a different package name.
+     */
+    bool merge(ResourceTable&& other);
+
     /**
      * Returns the string pool used by this ResourceTable.
      * Values that reference strings should use this pool to create
diff --git a/tools/aapt2/ResourceTable_test.cpp b/tools/aapt2/ResourceTable_test.cpp
index 785ea15..06d8699 100644
--- a/tools/aapt2/ResourceTable_test.cpp
+++ b/tools/aapt2/ResourceTable_test.cpp
@@ -31,7 +31,7 @@
     TestValue(StringPiece16 str) : value(str.toString()) {
     }
 
-    TestValue* clone() const override {
+    TestValue* clone(StringPool* /*newPool*/) const override {
         return new TestValue(value);
     }
 
@@ -48,7 +48,7 @@
         return true;
     }
 
-    TestWeakValue* clone() const override {
+    TestWeakValue* clone(StringPool* /*newPool*/) const override {
         return new TestWeakValue();
     }
 
diff --git a/tools/aapt2/ResourceValues.cpp b/tools/aapt2/ResourceValues.cpp
index 60ef1a8..3a6d65d 100644
--- a/tools/aapt2/ResourceValues.cpp
+++ b/tools/aapt2/ResourceValues.cpp
@@ -39,8 +39,8 @@
 RawString::RawString(const StringPool::Ref& ref) : value(ref) {
 }
 
-RawString* RawString::clone() const {
-    return new RawString(value);
+RawString* RawString::clone(StringPool* newPool) const {
+    return new RawString(newPool->makeRef(*value));
 }
 
 bool RawString::flatten(android::Res_value& outValue) const {
@@ -71,7 +71,7 @@
     return true;
 }
 
-Reference* Reference::clone() const {
+Reference* Reference::clone(StringPool* /*newPool*/) const {
     Reference* ref = new Reference();
     ref->referenceType = referenceType;
     ref->name = name;
@@ -106,7 +106,7 @@
     return true;
 }
 
-Id* Id::clone() const {
+Id* Id::clone(StringPool* /*newPool*/) const {
     return new Id();
 }
 
@@ -128,8 +128,8 @@
     return true;
 }
 
-String* String::clone() const {
-    return new String(value);
+String* String::clone(StringPool* newPool) const {
+    return new String(newPool->makeRef(*value));
 }
 
 void String::print(std::ostream& out) const {
@@ -149,8 +149,8 @@
     return true;
 }
 
-StyledString* StyledString::clone() const {
-    return new StyledString(value);
+StyledString* StyledString::clone(StringPool* newPool) const {
+    return new StyledString(newPool->makeRef(value));
 }
 
 void StyledString::print(std::ostream& out) const {
@@ -170,8 +170,8 @@
     return true;
 }
 
-FileReference* FileReference::clone() const {
-    return new FileReference(path);
+FileReference* FileReference::clone(StringPool* newPool) const {
+    return new FileReference(newPool->makeRef(*path));
 }
 
 void FileReference::print(std::ostream& out) const {
@@ -186,7 +186,7 @@
     return true;
 }
 
-BinaryPrimitive* BinaryPrimitive::clone() const {
+BinaryPrimitive* BinaryPrimitive::clone(StringPool* /*newPool*/) const {
     return new BinaryPrimitive(value);
 }
 
@@ -227,7 +227,7 @@
     return true;
 }
 
-Sentinel* Sentinel::clone() const {
+Sentinel* Sentinel::clone(StringPool* /*newPool*/) const {
     return new Sentinel();
 }
 
@@ -243,7 +243,7 @@
     return weak;
 }
 
-Attribute* Attribute::clone() const {
+Attribute* Attribute::clone(StringPool* /*newPool*/) const {
     Attribute* attr = new Attribute(weak);
     attr->typeMask = typeMask;
     std::copy(symbols.begin(), symbols.end(), std::back_inserter(attr->symbols));
@@ -371,13 +371,20 @@
     return out << s.symbol.name.entry << "=" << s.value;
 }
 
-Style* Style::clone() const {
-    Style* style = new Style();
+Style::Style(bool weak) : weak(weak) {
+}
+
+bool Style::isWeak() const {
+    return weak;
+}
+
+Style* Style::clone(StringPool* newPool) const {
+    Style* style = new Style(weak);
     style->parent = parent;
     for (auto& entry : entries) {
         style->entries.push_back(Entry{
                 entry.key,
-                std::unique_ptr<Item>(entry.value->clone())
+                std::unique_ptr<Item>(entry.value->clone(newPool))
         });
     }
     return style;
@@ -399,10 +406,10 @@
     return out;
 }
 
-Array* Array::clone() const {
+Array* Array::clone(StringPool* newPool) const {
     Array* array = new Array();
     for (auto& item : items) {
-        array->items.emplace_back(std::unique_ptr<Item>(item->clone()));
+        array->items.emplace_back(std::unique_ptr<Item>(item->clone(newPool)));
     }
     return array;
 }
@@ -413,12 +420,12 @@
         << "]";
 }
 
-Plural* Plural::clone() const {
+Plural* Plural::clone(StringPool* newPool) const {
     Plural* p = new Plural();
     const size_t count = values.size();
     for (size_t i = 0; i < count; i++) {
         if (values[i]) {
-            p->values[i] = std::unique_ptr<Item>(values[i]->clone());
+            p->values[i] = std::unique_ptr<Item>(values[i]->clone(newPool));
         }
     }
     return p;
@@ -432,7 +439,7 @@
     return out << *item;
 }
 
-Styleable* Styleable::clone() const {
+Styleable* Styleable::clone(StringPool* /*newPool*/) const {
     Styleable* styleable = new Styleable();
     std::copy(entries.begin(), entries.end(), std::back_inserter(styleable->entries));
     return styleable;
diff --git a/tools/aapt2/ResourceValues.h b/tools/aapt2/ResourceValues.h
index f25bcf0..e3352f3 100644
--- a/tools/aapt2/ResourceValues.h
+++ b/tools/aapt2/ResourceValues.h
@@ -63,7 +63,7 @@
     /**
      * Clone the value.
      */
-    virtual Value* clone() const = 0;
+    virtual Value* clone(StringPool* newPool) const = 0;
 
     /**
      * Human readable printout of this value.
@@ -92,7 +92,7 @@
     /**
      * Clone the Item.
      */
-    virtual Item* clone() const override = 0;
+    virtual Item* clone(StringPool* newPool) const override = 0;
 
     /**
      * Fills in an android::Res_value structure with this Item's binary representation.
@@ -132,7 +132,7 @@
     Reference(const ResourceId& i, Type type = Type::kResource);
 
     bool flatten(android::Res_value& outValue) const override;
-    Reference* clone() const override;
+    Reference* clone(StringPool* newPool) const override;
     void print(std::ostream& out) const override;
 };
 
@@ -142,7 +142,7 @@
 struct Id : public BaseItem<Id> {
     bool isWeak() const override;
     bool flatten(android::Res_value& out) const override;
-    Id* clone() const override;
+    Id* clone(StringPool* newPool) const override;
     void print(std::ostream& out) const override;
 };
 
@@ -157,7 +157,7 @@
     RawString(const StringPool::Ref& ref);
 
     bool flatten(android::Res_value& outValue) const override;
-    RawString* clone() const override;
+    RawString* clone(StringPool* newPool) const override;
     void print(std::ostream& out) const override;
 };
 
@@ -167,7 +167,7 @@
     String(const StringPool::Ref& ref);
 
     bool flatten(android::Res_value& outValue) const override;
-    String* clone() const override;
+    String* clone(StringPool* newPool) const override;
     void print(std::ostream& out) const override;
 };
 
@@ -177,7 +177,7 @@
     StyledString(const StringPool::StyleRef& ref);
 
     bool flatten(android::Res_value& outValue) const override;
-    StyledString* clone() const override;
+    StyledString* clone(StringPool* newPool) const override;
     void print(std::ostream& out) const override;
 };
 
@@ -188,7 +188,7 @@
     FileReference(const StringPool::Ref& path);
 
     bool flatten(android::Res_value& outValue) const override;
-    FileReference* clone() const override;
+    FileReference* clone(StringPool* newPool) const override;
     void print(std::ostream& out) const override;
 };
 
@@ -202,8 +202,8 @@
     BinaryPrimitive(const android::Res_value& val);
 
     bool flatten(android::Res_value& outValue) const override;
-    BinaryPrimitive* clone() const override;
-    void print(::std::ostream& out) const override;
+    BinaryPrimitive* clone(StringPool* newPool) const override;
+    void print(std::ostream& out) const override;
 };
 
 /**
@@ -214,8 +214,8 @@
 struct Sentinel : public BaseItem<Sentinel> {
     bool isWeak() const override;
     bool flatten(android::Res_value& outValue) const override;
-    Sentinel* clone() const override;
-    void print(::std::ostream& out) const override;
+    Sentinel* clone(StringPool* newPool) const override;
+    void print(std::ostream& out) const override;
 };
 
 struct Attribute : public BaseValue<Attribute> {
@@ -233,7 +233,7 @@
     Attribute(bool w, uint32_t t = 0u);
 
     bool isWeak() const override;
-    virtual Attribute* clone() const override;
+    virtual Attribute* clone(StringPool* newPool) const override;
     virtual void print(std::ostream& out) const override;
 };
 
@@ -243,17 +243,20 @@
         std::unique_ptr<Item> value;
     };
 
+    bool weak;
     Reference parent;
     std::vector<Entry> entries;
 
-    Style* clone() const override;
+    Style(bool weak);
+    bool isWeak() const override;
+    Style* clone(StringPool* newPool) const override;
     void print(std::ostream& out) const override;
 };
 
 struct Array : public BaseValue<Array> {
     std::vector<std::unique_ptr<Item>> items;
 
-    Array* clone() const override;
+    Array* clone(StringPool* newPool) const override;
     void print(std::ostream& out) const override;
 };
 
@@ -270,14 +273,14 @@
 
     std::array<std::unique_ptr<Item>, Count> values;
 
-    Plural* clone() const override;
+    Plural* clone(StringPool* newPool) const override;
     void print(std::ostream& out) const override;
 };
 
 struct Styleable : public BaseValue<Styleable> {
     std::vector<Reference> entries;
 
-    Styleable* clone() const override;
+    Styleable* clone(StringPool* newPool) const override;
     void print(std::ostream& out) const override;
 };
 
diff --git a/tools/aapt2/StringPool.cpp b/tools/aapt2/StringPool.cpp
index b159a00..b983a53 100644
--- a/tools/aapt2/StringPool.cpp
+++ b/tools/aapt2/StringPool.cpp
@@ -175,6 +175,25 @@
     return StyleRef(styleEntry);
 }
 
+StringPool::StyleRef StringPool::makeRef(const StyleRef& ref) {
+    Entry* entry = new Entry();
+    entry->value = *ref.mEntry->str;
+    entry->context = ref.mEntry->str.mEntry->context;
+    entry->index = mStrings.size();
+    entry->ref = 0;
+    mStrings.emplace_back(entry);
+    mIndexedStrings.insert(std::make_pair(StringPiece16(entry->value), entry));
+
+    StyleEntry* styleEntry = new StyleEntry();
+    styleEntry->str = Ref(entry);
+    for (const Span& span : ref.mEntry->spans) {
+        styleEntry->spans.emplace_back(Span{ makeRef(*span.name), span.firstChar, span.lastChar });
+    }
+    styleEntry->ref = 0;
+    mStyles.emplace_back(styleEntry);
+    return StyleRef(styleEntry);
+}
+
 void StringPool::merge(StringPool&& pool) {
     mIndexedStrings.insert(pool.mIndexedStrings.begin(), pool.mIndexedStrings.end());
     pool.mIndexedStrings.clear();
@@ -266,7 +285,7 @@
     header->stringCount = pool.size();
     header->flags |= android::ResStringPool_header::UTF8_FLAG;
 
-    uint32_t* indices = out->nextBlock<uint32_t>(pool.size());
+    uint32_t* indices = pool.size() != 0 ? out->nextBlock<uint32_t>(pool.size()) : nullptr;
 
     uint32_t* styleIndices = nullptr;
     if (!pool.mStyles.empty()) {
diff --git a/tools/aapt2/StringPool.h b/tools/aapt2/StringPool.h
index 2aa5b65..64772a4 100644
--- a/tools/aapt2/StringPool.h
+++ b/tools/aapt2/StringPool.h
@@ -158,6 +158,12 @@
     StyleRef makeRef(const StyleString& str, const Context& context);
 
     /**
+     * Adds a style from another string pool. Returns a reference to the
+     * style in the string pool.
+     */
+    StyleRef makeRef(const StyleRef& ref);
+
+    /**
      * Moves pool into this one without coalescing strings. When this
      * function returns, pool will be empty.
      */
diff --git a/tools/aapt2/StringPool_test.cpp b/tools/aapt2/StringPool_test.cpp
index 85d101a..9552937 100644
--- a/tools/aapt2/StringPool_test.cpp
+++ b/tools/aapt2/StringPool_test.cpp
@@ -162,6 +162,16 @@
     EXPECT_NE(ref.getIndex(), styleRef.getIndex());
 }
 
+TEST(StringPoolTest, FlattenEmptyStringPoolUtf8) {
+    StringPool pool;
+    BigBuffer buffer(1024);
+    StringPool::flattenUtf8(&buffer, pool);
+
+    std::unique_ptr<uint8_t[]> data = util::copy(buffer);
+    android::ResStringPool test;
+    ASSERT_EQ(test.setTo(data.get(), buffer.size()), android::NO_ERROR);
+}
+
 constexpr const char16_t* sLongString = u"バッテリーを長持ちさせるため、バッテリーセーバーは端末のパフォーマンスを抑え、バイブレーション、位置情報サービス、大半のバックグラウンドデータを制限します。メール、SMSや、同期を使 用するその他のアプリは、起動しても更新されないことがあります。バッテリーセーバーは端末の充電中は自動的にOFFになります。";
 
 TEST(StringPoolTest, FlattenUtf8) {
@@ -183,16 +193,10 @@
     BigBuffer buffer(1024);
     StringPool::flattenUtf8(&buffer, pool);
 
-    uint8_t* data = new uint8_t[buffer.size()];
-    uint8_t* p = data;
-    for (const auto& b : buffer) {
-        memcpy(p, b.buffer.get(), b.size);
-        p += b.size;
-    }
-
+    std::unique_ptr<uint8_t[]> data = util::copy(buffer);
     {
-        ResStringPool test;
-        ASSERT_TRUE(test.setTo(data, buffer.size()) == NO_ERROR);
+        android::ResStringPool test;
+        ASSERT_EQ(test.setTo(data.get(), buffer.size()), android::NO_ERROR);
 
         EXPECT_EQ(util::getString(test, 0), u"hello");
         EXPECT_EQ(util::getString(test, 1), u"goodbye");
@@ -214,7 +218,6 @@
 
         EXPECT_EQ(ResStringPool_span::END, span->name.index);
     }
-    delete[] data;
 }
 
 } // namespace aapt
diff --git a/tools/aapt2/XmlFlattener.cpp b/tools/aapt2/XmlFlattener.cpp
index b6ca6d5..dd6f63a 100644
--- a/tools/aapt2/XmlFlattener.cpp
+++ b/tools/aapt2/XmlFlattener.cpp
@@ -35,6 +35,10 @@
 
 namespace aapt {
 
+constexpr const char16_t* kSchemaAndroid = u"http://schemas.android.com/apk/res/android";
+constexpr const char16_t* kSchemaAuto = u"http://schemas.android.com/apk/res-auto";
+constexpr const char16_t* kSchemaPrefix = u"http://schemas.android.com/apk/res/";
+
 struct AttributeValueFlattener : ValueVisitor {
     struct Args : ValueVisitorArgs {
         Args(std::shared_ptr<Resolver> r, SourceLogger& s, android::Res_value& oV,
@@ -95,7 +99,9 @@
     return a.resourceId < id;
 }
 
-XmlFlattener::XmlFlattener(const std::shared_ptr<Resolver>& resolver) : mResolver(resolver) {
+XmlFlattener::XmlFlattener(const std::shared_ptr<ResourceTable>& table,
+                           const std::shared_ptr<Resolver>& resolver) :
+        mTable(table), mResolver(resolver) {
 }
 
 /**
@@ -190,28 +196,50 @@
                 uint32_t nextAttributeId = 0;
                 const auto endAttrIter = parser->endAttributes();
                 for (auto attrIter = parser->beginAttributes();
-                     attrIter != endAttrIter;
-                     ++attrIter) {
+                        attrIter != endAttrIter;
+                        ++attrIter) {
                     uint32_t id;
                     StringPool::Ref nameRef;
                     const Attribute* attr = nullptr;
-                    if (attrIter->namespaceUri.empty()) {
+
+                    if (options.maxSdkAttribute && attrIter->namespaceUri == kSchemaAndroid) {
+                        size_t sdkVersion = findAttributeSdkLevel(attrIter->name);
+                        if (sdkVersion > options.maxSdkAttribute.value()) {
+                            // We will silently omit this attribute
+                            smallestStrippedAttributeSdk =
+                                    std::min(smallestStrippedAttributeSdk, sdkVersion);
+                            continue;
+                        }
+                    }
+
+                    ResourceNameRef genIdName;
+                    bool create = false;
+                    bool privateRef = false;
+                    if (mTable && ResourceParser::tryParseReference(attrIter->value, &genIdName,
+                            &create, &privateRef) && create) {
+                        mTable->addResource(genIdName, {}, source.line(parser->getLineNumber()),
+                                            util::make_unique<Id>());
+                    }
+
+
+                    StringPiece16 package;
+                    if (util::stringStartsWith<char16_t>(attrIter->namespaceUri, kSchemaPrefix)) {
+                        StringPiece16 schemaPrefix = kSchemaPrefix;
+                        package = attrIter->namespaceUri;
+                        package = package.substr(schemaPrefix.size(),
+                                                 package.size() - schemaPrefix.size());
+                    } else if (attrIter->namespaceUri == kSchemaAuto && mResolver) {
+                        package = mResolver->getDefaultPackage();
+                    }
+
+                    if (package.empty() || !mResolver) {
                         // Attributes that have no resource ID (because they don't belong to a
                         // package) should appear after those that do have resource IDs. Assign
                         // them some/ integer value that will appear after.
                         id = 0x80000000u | nextAttributeId++;
                         nameRef = pool.makeRef(attrIter->name, StringPool::Context{ id });
-                    } else {
-                        StringPiece16 package;
-                        if (attrIter->namespaceUri == u"http://schemas.android.com/apk/res-auto") {
-                            package = mResolver->getDefaultPackage();
-                        } else {
-                            // TODO(adamlesinski): Extract package from namespace.
-                            // The package name appears like so:
-                            // http://schemas.android.com/apk/res/<package name>
-                            package = u"android";
-                        }
 
+                    } else {
                         // Find the Attribute object via our Resolver.
                         ResourceName attrName = {
                                 package.toString(), ResourceType::kAttr, attrIter->name };
@@ -236,16 +264,6 @@
                             continue;
                         }
 
-                        if (options.maxSdkAttribute && package == u"android") {
-                            size_t sdkVersion = findAttributeSdkLevel(attrIter->name);
-                            if (sdkVersion > options.maxSdkAttribute.value()) {
-                                // We will silently omit this attribute
-                                smallestStrippedAttributeSdk =
-                                        std::min(smallestStrippedAttributeSdk, sdkVersion);
-                                continue;
-                            }
-                        }
-
                         id = result.value().id.id;
                         attr = result.value().attr;
 
diff --git a/tools/aapt2/XmlFlattener.h b/tools/aapt2/XmlFlattener.h
index abf64ab..540a5ef 100644
--- a/tools/aapt2/XmlFlattener.h
+++ b/tools/aapt2/XmlFlattener.h
@@ -45,7 +45,8 @@
      * Creates a flattener with a Resolver to resolve references
      * and attributes.
      */
-    XmlFlattener(const std::shared_ptr<Resolver>& resolver);
+    XmlFlattener(const std::shared_ptr<ResourceTable>& table,
+                 const std::shared_ptr<Resolver>& resolver);
 
     XmlFlattener(const XmlFlattener&) = delete; // Not copyable.
 
@@ -60,6 +61,7 @@
                           BigBuffer* outBuffer, Options options);
 
 private:
+    std::shared_ptr<ResourceTable> mTable;
     std::shared_ptr<Resolver> mResolver;
 };
 
diff --git a/tools/aapt2/XmlFlattener_test.cpp b/tools/aapt2/XmlFlattener_test.cpp
index 6e24847..a7d7ac6 100644
--- a/tools/aapt2/XmlFlattener_test.cpp
+++ b/tools/aapt2/XmlFlattener_test.cpp
@@ -47,7 +47,7 @@
         table->addResource(ResourceName{ {}, ResourceType::kId, u"test" },
                            ResourceId{ 0x01020000 }, {}, {}, util::make_unique<Id>());
 
-        mFlattener = std::make_shared<XmlFlattener>(
+        mFlattener = std::make_shared<XmlFlattener>(nullptr,
                 std::make_shared<Resolver>(table, std::make_shared<AssetManager>()));
     }
 
diff --git a/tools/aapt2/ZipEntry.cpp b/tools/aapt2/ZipEntry.cpp
new file mode 100644
index 0000000..ad5d84a
--- /dev/null
+++ b/tools/aapt2/ZipEntry.cpp
@@ -0,0 +1,739 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//
+// Access to entries in a Zip archive.
+//
+
+#define LOG_TAG "zip"
+
+#include "ZipEntry.h"
+#include <utils/Log.h>
+
+#include <stdio.h>
+#include <string.h>
+#include <assert.h>
+
+namespace aapt {
+
+using namespace android;
+
+/*
+ * Initialize a new ZipEntry structure from a FILE* positioned at a
+ * CentralDirectoryEntry.
+ *
+ * On exit, the file pointer will be at the start of the next CDE or
+ * at the EOCD.
+ */
+status_t ZipEntry::initFromCDE(FILE* fp)
+{
+    status_t result;
+    long posn;
+    bool hasDD;
+
+    //ALOGV("initFromCDE ---\n");
+
+    /* read the CDE */
+    result = mCDE.read(fp);
+    if (result != NO_ERROR) {
+        ALOGD("mCDE.read failed\n");
+        return result;
+    }
+
+    //mCDE.dump();
+
+    /* using the info in the CDE, go load up the LFH */
+    posn = ftell(fp);
+    if (fseek(fp, mCDE.mLocalHeaderRelOffset, SEEK_SET) != 0) {
+        ALOGD("local header seek failed (%ld)\n",
+            mCDE.mLocalHeaderRelOffset);
+        return UNKNOWN_ERROR;
+    }
+
+    result = mLFH.read(fp);
+    if (result != NO_ERROR) {
+        ALOGD("mLFH.read failed\n");
+        return result;
+    }
+
+    if (fseek(fp, posn, SEEK_SET) != 0)
+        return UNKNOWN_ERROR;
+
+    //mLFH.dump();
+
+    /*
+     * We *might* need to read the Data Descriptor at this point and
+     * integrate it into the LFH.  If this bit is set, the CRC-32,
+     * compressed size, and uncompressed size will be zero.  In practice
+     * these seem to be rare.
+     */
+    hasDD = (mLFH.mGPBitFlag & kUsesDataDescr) != 0;
+    if (hasDD) {
+        // do something clever
+        //ALOGD("+++ has data descriptor\n");
+    }
+
+    /*
+     * Sanity-check the LFH.  Note that this will fail if the "kUsesDataDescr"
+     * flag is set, because the LFH is incomplete.  (Not a problem, since we
+     * prefer the CDE values.)
+     */
+    if (!hasDD && !compareHeaders()) {
+        ALOGW("warning: header mismatch\n");
+        // keep going?
+    }
+
+    /*
+     * If the mVersionToExtract is greater than 20, we may have an
+     * issue unpacking the record -- could be encrypted, compressed
+     * with something we don't support, or use Zip64 extensions.  We
+     * can defer worrying about that to when we're extracting data.
+     */
+
+    return NO_ERROR;
+}
+
+/*
+ * Initialize a new entry.  Pass in the file name and an optional comment.
+ *
+ * Initializes the CDE and the LFH.
+ */
+void ZipEntry::initNew(const char* fileName, const char* comment)
+{
+    assert(fileName != NULL && *fileName != '\0');  // name required
+
+    /* most fields are properly initialized by constructor */
+    mCDE.mVersionMadeBy = kDefaultMadeBy;
+    mCDE.mVersionToExtract = kDefaultVersion;
+    mCDE.mCompressionMethod = kCompressStored;
+    mCDE.mFileNameLength = strlen(fileName);
+    if (comment != NULL)
+        mCDE.mFileCommentLength = strlen(comment);
+    mCDE.mExternalAttrs = 0x81b60020;   // matches what WinZip does
+
+    if (mCDE.mFileNameLength > 0) {
+        mCDE.mFileName = new unsigned char[mCDE.mFileNameLength+1];
+        strcpy((char*) mCDE.mFileName, fileName);
+    }
+    if (mCDE.mFileCommentLength > 0) {
+        /* TODO: stop assuming null-terminated ASCII here? */
+        mCDE.mFileComment = new unsigned char[mCDE.mFileCommentLength+1];
+        strcpy((char*) mCDE.mFileComment, comment);
+    }
+
+    copyCDEtoLFH();
+}
+
+/*
+ * Initialize a new entry, starting with the ZipEntry from a different
+ * archive.
+ *
+ * Initializes the CDE and the LFH.
+ */
+status_t ZipEntry::initFromExternal(const ZipFile* /* pZipFile */,
+    const ZipEntry* pEntry)
+{
+    mCDE = pEntry->mCDE;
+    // Check whether we got all the memory needed.
+    if ((mCDE.mFileNameLength > 0 && mCDE.mFileName == NULL) ||
+            (mCDE.mFileCommentLength > 0 && mCDE.mFileComment == NULL) ||
+            (mCDE.mExtraFieldLength > 0 && mCDE.mExtraField == NULL)) {
+        return NO_MEMORY;
+    }
+
+    /* construct the LFH from the CDE */
+    copyCDEtoLFH();
+
+    /*
+     * The LFH "extra" field is independent of the CDE "extra", so we
+     * handle it here.
+     */
+    assert(mLFH.mExtraField == NULL);
+    mLFH.mExtraFieldLength = pEntry->mLFH.mExtraFieldLength;
+    if (mLFH.mExtraFieldLength > 0) {
+        mLFH.mExtraField = new unsigned char[mLFH.mExtraFieldLength+1];
+        if (mLFH.mExtraField == NULL)
+            return NO_MEMORY;
+        memcpy(mLFH.mExtraField, pEntry->mLFH.mExtraField,
+            mLFH.mExtraFieldLength+1);
+    }
+
+    return NO_ERROR;
+}
+
+/*
+ * Insert pad bytes in the LFH by tweaking the "extra" field.  This will
+ * potentially confuse something that put "extra" data in here earlier,
+ * but I can't find an actual problem.
+ */
+status_t ZipEntry::addPadding(int padding)
+{
+    if (padding <= 0)
+        return INVALID_OPERATION;
+
+    //ALOGI("HEY: adding %d pad bytes to existing %d in %s\n",
+    //    padding, mLFH.mExtraFieldLength, mCDE.mFileName);
+
+    if (mLFH.mExtraFieldLength > 0) {
+        /* extend existing field */
+        unsigned char* newExtra;
+
+        newExtra = new unsigned char[mLFH.mExtraFieldLength + padding];
+        if (newExtra == NULL)
+            return NO_MEMORY;
+        memset(newExtra + mLFH.mExtraFieldLength, 0, padding);
+        memcpy(newExtra, mLFH.mExtraField, mLFH.mExtraFieldLength);
+
+        delete[] mLFH.mExtraField;
+        mLFH.mExtraField = newExtra;
+        mLFH.mExtraFieldLength += padding;
+    } else {
+        /* create new field */
+        mLFH.mExtraField = new unsigned char[padding];
+        memset(mLFH.mExtraField, 0, padding);
+        mLFH.mExtraFieldLength = padding;
+    }
+
+    return NO_ERROR;
+}
+
+/*
+ * Set the fields in the LFH equal to the corresponding fields in the CDE.
+ *
+ * This does not touch the LFH "extra" field.
+ */
+void ZipEntry::copyCDEtoLFH(void)
+{
+    mLFH.mVersionToExtract  = mCDE.mVersionToExtract;
+    mLFH.mGPBitFlag         = mCDE.mGPBitFlag;
+    mLFH.mCompressionMethod = mCDE.mCompressionMethod;
+    mLFH.mLastModFileTime   = mCDE.mLastModFileTime;
+    mLFH.mLastModFileDate   = mCDE.mLastModFileDate;
+    mLFH.mCRC32             = mCDE.mCRC32;
+    mLFH.mCompressedSize    = mCDE.mCompressedSize;
+    mLFH.mUncompressedSize  = mCDE.mUncompressedSize;
+    mLFH.mFileNameLength    = mCDE.mFileNameLength;
+    // the "extra field" is independent
+
+    delete[] mLFH.mFileName;
+    if (mLFH.mFileNameLength > 0) {
+        mLFH.mFileName = new unsigned char[mLFH.mFileNameLength+1];
+        strcpy((char*) mLFH.mFileName, (const char*) mCDE.mFileName);
+    } else {
+        mLFH.mFileName = NULL;
+    }
+}
+
+/*
+ * Set some information about a file after we add it.
+ */
+void ZipEntry::setDataInfo(long uncompLen, long compLen, unsigned long crc32,
+    int compressionMethod)
+{
+    mCDE.mCompressionMethod = compressionMethod;
+    mCDE.mCRC32 = crc32;
+    mCDE.mCompressedSize = compLen;
+    mCDE.mUncompressedSize = uncompLen;
+    mCDE.mCompressionMethod = compressionMethod;
+    if (compressionMethod == kCompressDeflated) {
+        mCDE.mGPBitFlag |= 0x0002;      // indicates maximum compression used
+    }
+    copyCDEtoLFH();
+}
+
+/*
+ * See if the data in mCDE and mLFH match up.  This is mostly useful for
+ * debugging these classes, but it can be used to identify damaged
+ * archives.
+ *
+ * Returns "false" if they differ.
+ */
+bool ZipEntry::compareHeaders(void) const
+{
+    if (mCDE.mVersionToExtract != mLFH.mVersionToExtract) {
+        ALOGV("cmp: VersionToExtract\n");
+        return false;
+    }
+    if (mCDE.mGPBitFlag != mLFH.mGPBitFlag) {
+        ALOGV("cmp: GPBitFlag\n");
+        return false;
+    }
+    if (mCDE.mCompressionMethod != mLFH.mCompressionMethod) {
+        ALOGV("cmp: CompressionMethod\n");
+        return false;
+    }
+    if (mCDE.mLastModFileTime != mLFH.mLastModFileTime) {
+        ALOGV("cmp: LastModFileTime\n");
+        return false;
+    }
+    if (mCDE.mLastModFileDate != mLFH.mLastModFileDate) {
+        ALOGV("cmp: LastModFileDate\n");
+        return false;
+    }
+    if (mCDE.mCRC32 != mLFH.mCRC32) {
+        ALOGV("cmp: CRC32\n");
+        return false;
+    }
+    if (mCDE.mCompressedSize != mLFH.mCompressedSize) {
+        ALOGV("cmp: CompressedSize\n");
+        return false;
+    }
+    if (mCDE.mUncompressedSize != mLFH.mUncompressedSize) {
+        ALOGV("cmp: UncompressedSize\n");
+        return false;
+    }
+    if (mCDE.mFileNameLength != mLFH.mFileNameLength) {
+        ALOGV("cmp: FileNameLength\n");
+        return false;
+    }
+#if 0       // this seems to be used for padding, not real data
+    if (mCDE.mExtraFieldLength != mLFH.mExtraFieldLength) {
+        ALOGV("cmp: ExtraFieldLength\n");
+        return false;
+    }
+#endif
+    if (mCDE.mFileName != NULL) {
+        if (strcmp((char*) mCDE.mFileName, (char*) mLFH.mFileName) != 0) {
+            ALOGV("cmp: FileName\n");
+            return false;
+        }
+    }
+
+    return true;
+}
+
+
+/*
+ * Convert the DOS date/time stamp into a UNIX time stamp.
+ */
+time_t ZipEntry::getModWhen(void) const
+{
+    struct tm parts;
+
+    parts.tm_sec = (mCDE.mLastModFileTime & 0x001f) << 1;
+    parts.tm_min = (mCDE.mLastModFileTime & 0x07e0) >> 5;
+    parts.tm_hour = (mCDE.mLastModFileTime & 0xf800) >> 11;
+    parts.tm_mday = (mCDE.mLastModFileDate & 0x001f);
+    parts.tm_mon = ((mCDE.mLastModFileDate & 0x01e0) >> 5) -1;
+    parts.tm_year = ((mCDE.mLastModFileDate & 0xfe00) >> 9) + 80;
+    parts.tm_wday = parts.tm_yday = 0;
+    parts.tm_isdst = -1;        // DST info "not available"
+
+    return mktime(&parts);
+}
+
+/*
+ * Set the CDE/LFH timestamp from UNIX time.
+ */
+void ZipEntry::setModWhen(time_t when)
+{
+#if !defined(_WIN32)
+    struct tm tmResult;
+#endif
+    time_t even;
+    unsigned short zdate, ztime;
+
+    struct tm* ptm;
+
+    /* round up to an even number of seconds */
+    even = (time_t)(((unsigned long)(when) + 1) & (~1));
+
+    /* expand */
+#if !defined(_WIN32)
+    ptm = localtime_r(&even, &tmResult);
+#else
+    ptm = localtime(&even);
+#endif
+
+    int year;
+    year = ptm->tm_year;
+    if (year < 80)
+        year = 80;
+
+    zdate = (year - 80) << 9 | (ptm->tm_mon+1) << 5 | ptm->tm_mday;
+    ztime = ptm->tm_hour << 11 | ptm->tm_min << 5 | ptm->tm_sec >> 1;
+
+    mCDE.mLastModFileTime = mLFH.mLastModFileTime = ztime;
+    mCDE.mLastModFileDate = mLFH.mLastModFileDate = zdate;
+}
+
+
+/*
+ * ===========================================================================
+ *      ZipEntry::LocalFileHeader
+ * ===========================================================================
+ */
+
+/*
+ * Read a local file header.
+ *
+ * On entry, "fp" points to the signature at the start of the header.
+ * On exit, "fp" points to the start of data.
+ */
+status_t ZipEntry::LocalFileHeader::read(FILE* fp)
+{
+    status_t result = NO_ERROR;
+    unsigned char buf[kLFHLen];
+
+    assert(mFileName == NULL);
+    assert(mExtraField == NULL);
+
+    if (fread(buf, 1, kLFHLen, fp) != kLFHLen) {
+        result = UNKNOWN_ERROR;
+        goto bail;
+    }
+
+    if (ZipEntry::getLongLE(&buf[0x00]) != kSignature) {
+        ALOGD("whoops: didn't find expected signature\n");
+        result = UNKNOWN_ERROR;
+        goto bail;
+    }
+
+    mVersionToExtract = ZipEntry::getShortLE(&buf[0x04]);
+    mGPBitFlag = ZipEntry::getShortLE(&buf[0x06]);
+    mCompressionMethod = ZipEntry::getShortLE(&buf[0x08]);
+    mLastModFileTime = ZipEntry::getShortLE(&buf[0x0a]);
+    mLastModFileDate = ZipEntry::getShortLE(&buf[0x0c]);
+    mCRC32 = ZipEntry::getLongLE(&buf[0x0e]);
+    mCompressedSize = ZipEntry::getLongLE(&buf[0x12]);
+    mUncompressedSize = ZipEntry::getLongLE(&buf[0x16]);
+    mFileNameLength = ZipEntry::getShortLE(&buf[0x1a]);
+    mExtraFieldLength = ZipEntry::getShortLE(&buf[0x1c]);
+
+    // TODO: validate sizes
+
+    /* grab filename */
+    if (mFileNameLength != 0) {
+        mFileName = new unsigned char[mFileNameLength+1];
+        if (mFileName == NULL) {
+            result = NO_MEMORY;
+            goto bail;
+        }
+        if (fread(mFileName, 1, mFileNameLength, fp) != mFileNameLength) {
+            result = UNKNOWN_ERROR;
+            goto bail;
+        }
+        mFileName[mFileNameLength] = '\0';
+    }
+
+    /* grab extra field */
+    if (mExtraFieldLength != 0) {
+        mExtraField = new unsigned char[mExtraFieldLength+1];
+        if (mExtraField == NULL) {
+            result = NO_MEMORY;
+            goto bail;
+        }
+        if (fread(mExtraField, 1, mExtraFieldLength, fp) != mExtraFieldLength) {
+            result = UNKNOWN_ERROR;
+            goto bail;
+        }
+        mExtraField[mExtraFieldLength] = '\0';
+    }
+
+bail:
+    return result;
+}
+
+/*
+ * Write a local file header.
+ */
+status_t ZipEntry::LocalFileHeader::write(FILE* fp)
+{
+    unsigned char buf[kLFHLen];
+
+    ZipEntry::putLongLE(&buf[0x00], kSignature);
+    ZipEntry::putShortLE(&buf[0x04], mVersionToExtract);
+    ZipEntry::putShortLE(&buf[0x06], mGPBitFlag);
+    ZipEntry::putShortLE(&buf[0x08], mCompressionMethod);
+    ZipEntry::putShortLE(&buf[0x0a], mLastModFileTime);
+    ZipEntry::putShortLE(&buf[0x0c], mLastModFileDate);
+    ZipEntry::putLongLE(&buf[0x0e], mCRC32);
+    ZipEntry::putLongLE(&buf[0x12], mCompressedSize);
+    ZipEntry::putLongLE(&buf[0x16], mUncompressedSize);
+    ZipEntry::putShortLE(&buf[0x1a], mFileNameLength);
+    ZipEntry::putShortLE(&buf[0x1c], mExtraFieldLength);
+
+    if (fwrite(buf, 1, kLFHLen, fp) != kLFHLen)
+        return UNKNOWN_ERROR;
+
+    /* write filename */
+    if (mFileNameLength != 0) {
+        if (fwrite(mFileName, 1, mFileNameLength, fp) != mFileNameLength)
+            return UNKNOWN_ERROR;
+    }
+
+    /* write "extra field" */
+    if (mExtraFieldLength != 0) {
+        if (fwrite(mExtraField, 1, mExtraFieldLength, fp) != mExtraFieldLength)
+            return UNKNOWN_ERROR;
+    }
+
+    return NO_ERROR;
+}
+
+
+/*
+ * Dump the contents of a LocalFileHeader object.
+ */
+void ZipEntry::LocalFileHeader::dump(void) const
+{
+    ALOGD(" LocalFileHeader contents:\n");
+    ALOGD("  versToExt=%u gpBits=0x%04x compression=%u\n",
+        mVersionToExtract, mGPBitFlag, mCompressionMethod);
+    ALOGD("  modTime=0x%04x modDate=0x%04x crc32=0x%08lx\n",
+        mLastModFileTime, mLastModFileDate, mCRC32);
+    ALOGD("  compressedSize=%lu uncompressedSize=%lu\n",
+        mCompressedSize, mUncompressedSize);
+    ALOGD("  filenameLen=%u extraLen=%u\n",
+        mFileNameLength, mExtraFieldLength);
+    if (mFileName != NULL)
+        ALOGD("  filename: '%s'\n", mFileName);
+}
+
+
+/*
+ * ===========================================================================
+ *      ZipEntry::CentralDirEntry
+ * ===========================================================================
+ */
+
+/*
+ * Read the central dir entry that appears next in the file.
+ *
+ * On entry, "fp" should be positioned on the signature bytes for the
+ * entry.  On exit, "fp" will point at the signature word for the next
+ * entry or for the EOCD.
+ */
+status_t ZipEntry::CentralDirEntry::read(FILE* fp)
+{
+    status_t result = NO_ERROR;
+    unsigned char buf[kCDELen];
+
+    /* no re-use */
+    assert(mFileName == NULL);
+    assert(mExtraField == NULL);
+    assert(mFileComment == NULL);
+
+    if (fread(buf, 1, kCDELen, fp) != kCDELen) {
+        result = UNKNOWN_ERROR;
+        goto bail;
+    }
+
+    if (ZipEntry::getLongLE(&buf[0x00]) != kSignature) {
+        ALOGD("Whoops: didn't find expected signature\n");
+        result = UNKNOWN_ERROR;
+        goto bail;
+    }
+
+    mVersionMadeBy = ZipEntry::getShortLE(&buf[0x04]);
+    mVersionToExtract = ZipEntry::getShortLE(&buf[0x06]);
+    mGPBitFlag = ZipEntry::getShortLE(&buf[0x08]);
+    mCompressionMethod = ZipEntry::getShortLE(&buf[0x0a]);
+    mLastModFileTime = ZipEntry::getShortLE(&buf[0x0c]);
+    mLastModFileDate = ZipEntry::getShortLE(&buf[0x0e]);
+    mCRC32 = ZipEntry::getLongLE(&buf[0x10]);
+    mCompressedSize = ZipEntry::getLongLE(&buf[0x14]);
+    mUncompressedSize = ZipEntry::getLongLE(&buf[0x18]);
+    mFileNameLength = ZipEntry::getShortLE(&buf[0x1c]);
+    mExtraFieldLength = ZipEntry::getShortLE(&buf[0x1e]);
+    mFileCommentLength = ZipEntry::getShortLE(&buf[0x20]);
+    mDiskNumberStart = ZipEntry::getShortLE(&buf[0x22]);
+    mInternalAttrs = ZipEntry::getShortLE(&buf[0x24]);
+    mExternalAttrs = ZipEntry::getLongLE(&buf[0x26]);
+    mLocalHeaderRelOffset = ZipEntry::getLongLE(&buf[0x2a]);
+
+    // TODO: validate sizes and offsets
+
+    /* grab filename */
+    if (mFileNameLength != 0) {
+        mFileName = new unsigned char[mFileNameLength+1];
+        if (mFileName == NULL) {
+            result = NO_MEMORY;
+            goto bail;
+        }
+        if (fread(mFileName, 1, mFileNameLength, fp) != mFileNameLength) {
+            result = UNKNOWN_ERROR;
+            goto bail;
+        }
+        mFileName[mFileNameLength] = '\0';
+    }
+
+    /* read "extra field" */
+    if (mExtraFieldLength != 0) {
+        mExtraField = new unsigned char[mExtraFieldLength+1];
+        if (mExtraField == NULL) {
+            result = NO_MEMORY;
+            goto bail;
+        }
+        if (fread(mExtraField, 1, mExtraFieldLength, fp) != mExtraFieldLength) {
+            result = UNKNOWN_ERROR;
+            goto bail;
+        }
+        mExtraField[mExtraFieldLength] = '\0';
+    }
+
+
+    /* grab comment, if any */
+    if (mFileCommentLength != 0) {
+        mFileComment = new unsigned char[mFileCommentLength+1];
+        if (mFileComment == NULL) {
+            result = NO_MEMORY;
+            goto bail;
+        }
+        if (fread(mFileComment, 1, mFileCommentLength, fp) != mFileCommentLength)
+        {
+            result = UNKNOWN_ERROR;
+            goto bail;
+        }
+        mFileComment[mFileCommentLength] = '\0';
+    }
+
+bail:
+    return result;
+}
+
+/*
+ * Write a central dir entry.
+ */
+status_t ZipEntry::CentralDirEntry::write(FILE* fp)
+{
+    unsigned char buf[kCDELen];
+
+    ZipEntry::putLongLE(&buf[0x00], kSignature);
+    ZipEntry::putShortLE(&buf[0x04], mVersionMadeBy);
+    ZipEntry::putShortLE(&buf[0x06], mVersionToExtract);
+    ZipEntry::putShortLE(&buf[0x08], mGPBitFlag);
+    ZipEntry::putShortLE(&buf[0x0a], mCompressionMethod);
+    ZipEntry::putShortLE(&buf[0x0c], mLastModFileTime);
+    ZipEntry::putShortLE(&buf[0x0e], mLastModFileDate);
+    ZipEntry::putLongLE(&buf[0x10], mCRC32);
+    ZipEntry::putLongLE(&buf[0x14], mCompressedSize);
+    ZipEntry::putLongLE(&buf[0x18], mUncompressedSize);
+    ZipEntry::putShortLE(&buf[0x1c], mFileNameLength);
+    ZipEntry::putShortLE(&buf[0x1e], mExtraFieldLength);
+    ZipEntry::putShortLE(&buf[0x20], mFileCommentLength);
+    ZipEntry::putShortLE(&buf[0x22], mDiskNumberStart);
+    ZipEntry::putShortLE(&buf[0x24], mInternalAttrs);
+    ZipEntry::putLongLE(&buf[0x26], mExternalAttrs);
+    ZipEntry::putLongLE(&buf[0x2a], mLocalHeaderRelOffset);
+
+    if (fwrite(buf, 1, kCDELen, fp) != kCDELen)
+        return UNKNOWN_ERROR;
+
+    /* write filename */
+    if (mFileNameLength != 0) {
+        if (fwrite(mFileName, 1, mFileNameLength, fp) != mFileNameLength)
+            return UNKNOWN_ERROR;
+    }
+
+    /* write "extra field" */
+    if (mExtraFieldLength != 0) {
+        if (fwrite(mExtraField, 1, mExtraFieldLength, fp) != mExtraFieldLength)
+            return UNKNOWN_ERROR;
+    }
+
+    /* write comment */
+    if (mFileCommentLength != 0) {
+        if (fwrite(mFileComment, 1, mFileCommentLength, fp) != mFileCommentLength)
+            return UNKNOWN_ERROR;
+    }
+
+    return NO_ERROR;
+}
+
+/*
+ * Dump the contents of a CentralDirEntry object.
+ */
+void ZipEntry::CentralDirEntry::dump(void) const
+{
+    ALOGD(" CentralDirEntry contents:\n");
+    ALOGD("  versMadeBy=%u versToExt=%u gpBits=0x%04x compression=%u\n",
+        mVersionMadeBy, mVersionToExtract, mGPBitFlag, mCompressionMethod);
+    ALOGD("  modTime=0x%04x modDate=0x%04x crc32=0x%08lx\n",
+        mLastModFileTime, mLastModFileDate, mCRC32);
+    ALOGD("  compressedSize=%lu uncompressedSize=%lu\n",
+        mCompressedSize, mUncompressedSize);
+    ALOGD("  filenameLen=%u extraLen=%u commentLen=%u\n",
+        mFileNameLength, mExtraFieldLength, mFileCommentLength);
+    ALOGD("  diskNumStart=%u intAttr=0x%04x extAttr=0x%08lx relOffset=%lu\n",
+        mDiskNumberStart, mInternalAttrs, mExternalAttrs,
+        mLocalHeaderRelOffset);
+
+    if (mFileName != NULL)
+        ALOGD("  filename: '%s'\n", mFileName);
+    if (mFileComment != NULL)
+        ALOGD("  comment: '%s'\n", mFileComment);
+}
+
+/*
+ * Copy-assignment operator for CentralDirEntry.
+ */
+ZipEntry::CentralDirEntry& ZipEntry::CentralDirEntry::operator=(const ZipEntry::CentralDirEntry& src) {
+    if (this == &src) {
+        return *this;
+    }
+
+    // Free up old data.
+    delete[] mFileName;
+    delete[] mExtraField;
+    delete[] mFileComment;
+
+    // Copy scalars.
+    mVersionMadeBy = src.mVersionMadeBy;
+    mVersionToExtract = src.mVersionToExtract;
+    mGPBitFlag = src.mGPBitFlag;
+    mCompressionMethod = src.mCompressionMethod;
+    mLastModFileTime = src.mLastModFileTime;
+    mLastModFileDate = src.mLastModFileDate;
+    mCRC32 = src.mCRC32;
+    mCompressedSize = src.mCompressedSize;
+    mUncompressedSize = src.mUncompressedSize;
+    mFileNameLength = src.mFileNameLength;
+    mExtraFieldLength = src.mExtraFieldLength;
+    mFileCommentLength = src.mFileCommentLength;
+    mDiskNumberStart = src.mDiskNumberStart;
+    mInternalAttrs = src.mInternalAttrs;
+    mExternalAttrs = src.mExternalAttrs;
+    mLocalHeaderRelOffset = src.mLocalHeaderRelOffset;
+
+    // Copy strings, if necessary.
+    if (mFileNameLength > 0) {
+        mFileName = new unsigned char[mFileNameLength + 1];
+        if (mFileName != NULL)
+            strcpy((char*)mFileName, (char*)src.mFileName);
+    } else {
+        mFileName = NULL;
+    }
+    if (mFileCommentLength > 0) {
+        mFileComment = new unsigned char[mFileCommentLength + 1];
+        if (mFileComment != NULL)
+            strcpy((char*)mFileComment, (char*)src.mFileComment);
+    } else {
+        mFileComment = NULL;
+    }
+    if (mExtraFieldLength > 0) {
+        /* we null-terminate this, though it may not be a string */
+        mExtraField = new unsigned char[mExtraFieldLength + 1];
+        if (mExtraField != NULL)
+            memcpy(mExtraField, src.mExtraField, mExtraFieldLength + 1);
+    } else {
+        mExtraField = NULL;
+    }
+
+    return *this;
+}
+
+} // namespace aapt
diff --git a/tools/aapt2/ZipEntry.h b/tools/aapt2/ZipEntry.h
new file mode 100644
index 0000000..d048a3e
--- /dev/null
+++ b/tools/aapt2/ZipEntry.h
@@ -0,0 +1,349 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//
+// Zip archive entries.
+//
+// The ZipEntry class is tightly meshed with the ZipFile class.
+//
+#ifndef __LIBS_ZIPENTRY_H
+#define __LIBS_ZIPENTRY_H
+
+#include <utils/Errors.h>
+
+#include <stdlib.h>
+#include <stdio.h>
+
+namespace aapt {
+
+using android::status_t;
+
+class ZipFile;
+
+/*
+ * ZipEntry objects represent a single entry in a Zip archive.
+ *
+ * You can use one of these to get or set information about an entry, but
+ * there are no functions here for accessing the data itself.  (We could
+ * tuck a pointer to the ZipFile in here for convenience, but that raises
+ * the likelihood of using ZipEntry objects after discarding the ZipFile.)
+ *
+ * File information is stored in two places: next to the file data (the Local
+ * File Header, and possibly a Data Descriptor), and at the end of the file
+ * (the Central Directory Entry).  The two must be kept in sync.
+ */
+class ZipEntry {
+public:
+    friend class ZipFile;
+
+    ZipEntry(void)
+        : mDeleted(false), mMarked(false)
+        {}
+    ~ZipEntry(void) {}
+
+    /*
+     * Returns "true" if the data is compressed.
+     */
+    bool isCompressed(void) const {
+        return mCDE.mCompressionMethod != kCompressStored;
+    }
+    int getCompressionMethod(void) const { return mCDE.mCompressionMethod; }
+
+    /*
+     * Return the uncompressed length.
+     */
+    off_t getUncompressedLen(void) const { return mCDE.mUncompressedSize; }
+
+    /*
+     * Return the compressed length.  For uncompressed data, this returns
+     * the same thing as getUncompresesdLen().
+     */
+    off_t getCompressedLen(void) const { return mCDE.mCompressedSize; }
+
+    /*
+     * Return the offset of the local file header.
+     */
+    off_t getLFHOffset(void) const { return mCDE.mLocalHeaderRelOffset; }
+
+    /*
+     * Return the absolute file offset of the start of the compressed or
+     * uncompressed data.
+     */
+    off_t getFileOffset(void) const {
+        return mCDE.mLocalHeaderRelOffset +
+                LocalFileHeader::kLFHLen +
+                mLFH.mFileNameLength +
+                mLFH.mExtraFieldLength;
+    }
+
+    /*
+     * Return the data CRC.
+     */
+    unsigned long getCRC32(void) const { return mCDE.mCRC32; }
+
+    /*
+     * Return file modification time in UNIX seconds-since-epoch.
+     */
+    time_t getModWhen(void) const;
+
+    /*
+     * Return the archived file name.
+     */
+    const char* getFileName(void) const { return (const char*) mCDE.mFileName; }
+
+    /*
+     * Application-defined "mark".  Can be useful when synchronizing the
+     * contents of an archive with contents on disk.
+     */
+    bool getMarked(void) const { return mMarked; }
+    void setMarked(bool val) { mMarked = val; }
+
+    /*
+     * Some basic functions for raw data manipulation.  "LE" means
+     * Little Endian.
+     */
+    static inline unsigned short getShortLE(const unsigned char* buf) {
+        return buf[0] | (buf[1] << 8);
+    }
+    static inline unsigned long getLongLE(const unsigned char* buf) {
+        return buf[0] | (buf[1] << 8) | (buf[2] << 16) | (buf[3] << 24);
+    }
+    static inline void putShortLE(unsigned char* buf, short val) {
+        buf[0] = (unsigned char) val;
+        buf[1] = (unsigned char) (val >> 8);
+    }
+    static inline void putLongLE(unsigned char* buf, long val) {
+        buf[0] = (unsigned char) val;
+        buf[1] = (unsigned char) (val >> 8);
+        buf[2] = (unsigned char) (val >> 16);
+        buf[3] = (unsigned char) (val >> 24);
+    }
+
+    /* defined for Zip archives */
+    enum {
+        kCompressStored     = 0,        // no compression
+        // shrunk           = 1,
+        // reduced 1        = 2,
+        // reduced 2        = 3,
+        // reduced 3        = 4,
+        // reduced 4        = 5,
+        // imploded         = 6,
+        // tokenized        = 7,
+        kCompressDeflated   = 8,        // standard deflate
+        // Deflate64        = 9,
+        // lib imploded     = 10,
+        // reserved         = 11,
+        // bzip2            = 12,
+    };
+
+    /*
+     * Deletion flag.  If set, the entry will be removed on the next
+     * call to "flush".
+     */
+    bool getDeleted(void) const { return mDeleted; }
+
+protected:
+    /*
+     * Initialize the structure from the file, which is pointing at
+     * our Central Directory entry.
+     */
+    status_t initFromCDE(FILE* fp);
+
+    /*
+     * Initialize the structure for a new file.  We need the filename
+     * and comment so that we can properly size the LFH area.  The
+     * filename is mandatory, the comment is optional.
+     */
+    void initNew(const char* fileName, const char* comment);
+
+    /*
+     * Initialize the structure with the contents of a ZipEntry from
+     * another file.
+     */
+    status_t initFromExternal(const ZipFile* pZipFile, const ZipEntry* pEntry);
+
+    /*
+     * Add some pad bytes to the LFH.  We do this by adding or resizing
+     * the "extra" field.
+     */
+    status_t addPadding(int padding);
+
+    /*
+     * Set information about the data for this entry.
+     */
+    void setDataInfo(long uncompLen, long compLen, unsigned long crc32,
+        int compressionMethod);
+
+    /*
+     * Set the modification date.
+     */
+    void setModWhen(time_t when);
+
+    /*
+     * Set the offset of the local file header, relative to the start of
+     * the current file.
+     */
+    void setLFHOffset(off_t offset) {
+        mCDE.mLocalHeaderRelOffset = (long) offset;
+    }
+
+    /* mark for deletion; used by ZipFile::remove() */
+    void setDeleted(void) { mDeleted = true; }
+
+private:
+    /* these are private and not defined */
+    ZipEntry(const ZipEntry& src);
+    ZipEntry& operator=(const ZipEntry& src);
+
+    /* returns "true" if the CDE and the LFH agree */
+    bool compareHeaders(void) const;
+    void copyCDEtoLFH(void);
+
+    bool        mDeleted;       // set if entry is pending deletion
+    bool        mMarked;        // app-defined marker
+
+    /*
+     * Every entry in the Zip archive starts off with one of these.
+     */
+    class LocalFileHeader {
+    public:
+        LocalFileHeader(void) :
+            mVersionToExtract(0),
+            mGPBitFlag(0),
+            mCompressionMethod(0),
+            mLastModFileTime(0),
+            mLastModFileDate(0),
+            mCRC32(0),
+            mCompressedSize(0),
+            mUncompressedSize(0),
+            mFileNameLength(0),
+            mExtraFieldLength(0),
+            mFileName(NULL),
+            mExtraField(NULL)
+        {}
+        virtual ~LocalFileHeader(void) {
+            delete[] mFileName;
+            delete[] mExtraField;
+        }
+
+        status_t read(FILE* fp);
+        status_t write(FILE* fp);
+
+        // unsigned long mSignature;
+        unsigned short  mVersionToExtract;
+        unsigned short  mGPBitFlag;
+        unsigned short  mCompressionMethod;
+        unsigned short  mLastModFileTime;
+        unsigned short  mLastModFileDate;
+        unsigned long   mCRC32;
+        unsigned long   mCompressedSize;
+        unsigned long   mUncompressedSize;
+        unsigned short  mFileNameLength;
+        unsigned short  mExtraFieldLength;
+        unsigned char*  mFileName;
+        unsigned char*  mExtraField;
+
+        enum {
+            kSignature      = 0x04034b50,
+            kLFHLen         = 30,       // LocalFileHdr len, excl. var fields
+        };
+
+        void dump(void) const;
+    };
+
+    /*
+     * Every entry in the Zip archive has one of these in the "central
+     * directory" at the end of the file.
+     */
+    class CentralDirEntry {
+    public:
+        CentralDirEntry(void) :
+            mVersionMadeBy(0),
+            mVersionToExtract(0),
+            mGPBitFlag(0),
+            mCompressionMethod(0),
+            mLastModFileTime(0),
+            mLastModFileDate(0),
+            mCRC32(0),
+            mCompressedSize(0),
+            mUncompressedSize(0),
+            mFileNameLength(0),
+            mExtraFieldLength(0),
+            mFileCommentLength(0),
+            mDiskNumberStart(0),
+            mInternalAttrs(0),
+            mExternalAttrs(0),
+            mLocalHeaderRelOffset(0),
+            mFileName(NULL),
+            mExtraField(NULL),
+            mFileComment(NULL)
+        {}
+        virtual ~CentralDirEntry(void) {
+            delete[] mFileName;
+            delete[] mExtraField;
+            delete[] mFileComment;
+        }
+
+        status_t read(FILE* fp);
+        status_t write(FILE* fp);
+
+        CentralDirEntry& operator=(const CentralDirEntry& src);
+
+        // unsigned long mSignature;
+        unsigned short  mVersionMadeBy;
+        unsigned short  mVersionToExtract;
+        unsigned short  mGPBitFlag;
+        unsigned short  mCompressionMethod;
+        unsigned short  mLastModFileTime;
+        unsigned short  mLastModFileDate;
+        unsigned long   mCRC32;
+        unsigned long   mCompressedSize;
+        unsigned long   mUncompressedSize;
+        unsigned short  mFileNameLength;
+        unsigned short  mExtraFieldLength;
+        unsigned short  mFileCommentLength;
+        unsigned short  mDiskNumberStart;
+        unsigned short  mInternalAttrs;
+        unsigned long   mExternalAttrs;
+        unsigned long   mLocalHeaderRelOffset;
+        unsigned char*  mFileName;
+        unsigned char*  mExtraField;
+        unsigned char*  mFileComment;
+
+        void dump(void) const;
+
+        enum {
+            kSignature      = 0x02014b50,
+            kCDELen         = 46,       // CentralDirEnt len, excl. var fields
+        };
+    };
+
+    enum {
+        //kDataDescriptorSignature  = 0x08074b50,   // currently unused
+        kDataDescriptorLen  = 16,           // four 32-bit fields
+
+        kDefaultVersion     = 20,           // need deflate, nothing much else
+        kDefaultMadeBy      = 0x0317,       // 03=UNIX, 17=spec v2.3
+        kUsesDataDescr      = 0x0008,       // GPBitFlag bit 3
+    };
+
+    LocalFileHeader     mLFH;
+    CentralDirEntry     mCDE;
+};
+
+}; // namespace aapt
+
+#endif // __LIBS_ZIPENTRY_H
diff --git a/tools/aapt2/ZipFile.cpp b/tools/aapt2/ZipFile.cpp
new file mode 100644
index 0000000..41e59cf
--- /dev/null
+++ b/tools/aapt2/ZipFile.cpp
@@ -0,0 +1,1305 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//
+// Access to Zip archives.
+//
+
+#define LOG_TAG "zip"
+
+#include <androidfw/ZipUtils.h>
+#include <utils/Log.h>
+
+#include "ZipFile.h"
+#include "Util.h"
+
+#include <zlib.h>
+#define DEF_MEM_LEVEL 8                // normally in zutil.h?
+
+#include <memory.h>
+#include <sys/stat.h>
+#include <errno.h>
+#include <assert.h>
+
+namespace aapt {
+
+using namespace android;
+
+/*
+ * Some environments require the "b", some choke on it.
+ */
+#define FILE_OPEN_RO        "rb"
+#define FILE_OPEN_RW        "r+b"
+#define FILE_OPEN_RW_CREATE "w+b"
+
+/* should live somewhere else? */
+static status_t errnoToStatus(int err)
+{
+    if (err == ENOENT)
+        return NAME_NOT_FOUND;
+    else if (err == EACCES)
+        return PERMISSION_DENIED;
+    else
+        return UNKNOWN_ERROR;
+}
+
+/*
+ * Open a file and parse its guts.
+ */
+status_t ZipFile::open(const char* zipFileName, int flags)
+{
+    bool newArchive = false;
+
+    assert(mZipFp == NULL);     // no reopen
+
+    if ((flags & kOpenTruncate))
+        flags |= kOpenCreate;           // trunc implies create
+
+    if ((flags & kOpenReadOnly) && (flags & kOpenReadWrite))
+        return INVALID_OPERATION;       // not both
+    if (!((flags & kOpenReadOnly) || (flags & kOpenReadWrite)))
+        return INVALID_OPERATION;       // not neither
+    if ((flags & kOpenCreate) && !(flags & kOpenReadWrite))
+        return INVALID_OPERATION;       // create requires write
+
+    if (flags & kOpenTruncate) {
+        newArchive = true;
+    } else {
+        newArchive = (access(zipFileName, F_OK) != 0);
+        if (!(flags & kOpenCreate) && newArchive) {
+            /* not creating, must already exist */
+            ALOGD("File %s does not exist", zipFileName);
+            return NAME_NOT_FOUND;
+        }
+    }
+
+    /* open the file */
+    const char* openflags;
+    if (flags & kOpenReadWrite) {
+        if (newArchive)
+            openflags = FILE_OPEN_RW_CREATE;
+        else
+            openflags = FILE_OPEN_RW;
+    } else {
+        openflags = FILE_OPEN_RO;
+    }
+    mZipFp = fopen(zipFileName, openflags);
+    if (mZipFp == NULL) {
+        int err = errno;
+        ALOGD("fopen failed: %d\n", err);
+        return errnoToStatus(err);
+    }
+
+    status_t result;
+    if (!newArchive) {
+        /*
+         * Load the central directory.  If that fails, then this probably
+         * isn't a Zip archive.
+         */
+        result = readCentralDir();
+    } else {
+        /*
+         * Newly-created.  The EndOfCentralDir constructor actually
+         * sets everything to be the way we want it (all zeroes).  We
+         * set mNeedCDRewrite so that we create *something* if the
+         * caller doesn't add any files.  (We could also just unlink
+         * the file if it's brand new and nothing was added, but that's
+         * probably doing more than we really should -- the user might
+         * have a need for empty zip files.)
+         */
+        mNeedCDRewrite = true;
+        result = NO_ERROR;
+    }
+
+    if (flags & kOpenReadOnly)
+        mReadOnly = true;
+    else
+        assert(!mReadOnly);
+
+    return result;
+}
+
+/*
+ * Return the Nth entry in the archive.
+ */
+ZipEntry* ZipFile::getEntryByIndex(int idx) const
+{
+    if (idx < 0 || idx >= (int) mEntries.size())
+        return NULL;
+
+    return mEntries[idx];
+}
+
+/*
+ * Find an entry by name.
+ */
+ZipEntry* ZipFile::getEntryByName(const char* fileName) const
+{
+    /*
+     * Do a stupid linear string-compare search.
+     *
+     * There are various ways to speed this up, especially since it's rare
+     * to intermingle changes to the archive with "get by name" calls.  We
+     * don't want to sort the mEntries vector itself, however, because
+     * it's used to recreate the Central Directory.
+     *
+     * (Hash table works, parallel list of pointers in sorted order is good.)
+     */
+    int idx;
+
+    for (idx = mEntries.size()-1; idx >= 0; idx--) {
+        ZipEntry* pEntry = mEntries[idx];
+        if (!pEntry->getDeleted() &&
+            strcmp(fileName, pEntry->getFileName()) == 0)
+        {
+            return pEntry;
+        }
+    }
+
+    return NULL;
+}
+
+/*
+ * Empty the mEntries vector.
+ */
+void ZipFile::discardEntries(void)
+{
+    int count = mEntries.size();
+
+    while (--count >= 0)
+        delete mEntries[count];
+
+    mEntries.clear();
+}
+
+
+/*
+ * Find the central directory and read the contents.
+ *
+ * The fun thing about ZIP archives is that they may or may not be
+ * readable from start to end.  In some cases, notably for archives
+ * that were written to stdout, the only length information is in the
+ * central directory at the end of the file.
+ *
+ * Of course, the central directory can be followed by a variable-length
+ * comment field, so we have to scan through it backwards.  The comment
+ * is at most 64K, plus we have 18 bytes for the end-of-central-dir stuff
+ * itself, plus apparently sometimes people throw random junk on the end
+ * just for the fun of it.
+ *
+ * This is all a little wobbly.  If the wrong value ends up in the EOCD
+ * area, we're hosed.  This appears to be the way that everbody handles
+ * it though, so we're in pretty good company if this fails.
+ */
+status_t ZipFile::readCentralDir(void)
+{
+    status_t result = NO_ERROR;
+    unsigned char* buf = NULL;
+    off_t fileLength, seekStart;
+    long readAmount;
+    int i;
+
+    fseek(mZipFp, 0, SEEK_END);
+    fileLength = ftell(mZipFp);
+    rewind(mZipFp);
+
+    /* too small to be a ZIP archive? */
+    if (fileLength < EndOfCentralDir::kEOCDLen) {
+        ALOGD("Length is %ld -- too small\n", (long)fileLength);
+        result = INVALID_OPERATION;
+        goto bail;
+    }
+
+    buf = new unsigned char[EndOfCentralDir::kMaxEOCDSearch];
+    if (buf == NULL) {
+        ALOGD("Failure allocating %d bytes for EOCD search",
+             EndOfCentralDir::kMaxEOCDSearch);
+        result = NO_MEMORY;
+        goto bail;
+    }
+
+    if (fileLength > EndOfCentralDir::kMaxEOCDSearch) {
+        seekStart = fileLength - EndOfCentralDir::kMaxEOCDSearch;
+        readAmount = EndOfCentralDir::kMaxEOCDSearch;
+    } else {
+        seekStart = 0;
+        readAmount = (long) fileLength;
+    }
+    if (fseek(mZipFp, seekStart, SEEK_SET) != 0) {
+        ALOGD("Failure seeking to end of zip at %ld", (long) seekStart);
+        result = UNKNOWN_ERROR;
+        goto bail;
+    }
+
+    /* read the last part of the file into the buffer */
+    if (fread(buf, 1, readAmount, mZipFp) != (size_t) readAmount) {
+        ALOGD("short file? wanted %ld\n", readAmount);
+        result = UNKNOWN_ERROR;
+        goto bail;
+    }
+
+    /* find the end-of-central-dir magic */
+    for (i = readAmount - 4; i >= 0; i--) {
+        if (buf[i] == 0x50 &&
+            ZipEntry::getLongLE(&buf[i]) == EndOfCentralDir::kSignature)
+        {
+            ALOGV("+++ Found EOCD at buf+%d\n", i);
+            break;
+        }
+    }
+    if (i < 0) {
+        ALOGD("EOCD not found, not Zip\n");
+        result = INVALID_OPERATION;
+        goto bail;
+    }
+
+    /* extract eocd values */
+    result = mEOCD.readBuf(buf + i, readAmount - i);
+    if (result != NO_ERROR) {
+        ALOGD("Failure reading %ld bytes of EOCD values", readAmount - i);
+        goto bail;
+    }
+    //mEOCD.dump();
+
+    if (mEOCD.mDiskNumber != 0 || mEOCD.mDiskWithCentralDir != 0 ||
+        mEOCD.mNumEntries != mEOCD.mTotalNumEntries)
+    {
+        ALOGD("Archive spanning not supported\n");
+        result = INVALID_OPERATION;
+        goto bail;
+    }
+
+    /*
+     * So far so good.  "mCentralDirSize" is the size in bytes of the
+     * central directory, so we can just seek back that far to find it.
+     * We can also seek forward mCentralDirOffset bytes from the
+     * start of the file.
+     *
+     * We're not guaranteed to have the rest of the central dir in the
+     * buffer, nor are we guaranteed that the central dir will have any
+     * sort of convenient size.  We need to skip to the start of it and
+     * read the header, then the other goodies.
+     *
+     * The only thing we really need right now is the file comment, which
+     * we're hoping to preserve.
+     */
+    if (fseek(mZipFp, mEOCD.mCentralDirOffset, SEEK_SET) != 0) {
+        ALOGD("Failure seeking to central dir offset %ld\n",
+             mEOCD.mCentralDirOffset);
+        result = UNKNOWN_ERROR;
+        goto bail;
+    }
+
+    /*
+     * Loop through and read the central dir entries.
+     */
+    ALOGV("Scanning %d entries...\n", mEOCD.mTotalNumEntries);
+    int entry;
+    for (entry = 0; entry < mEOCD.mTotalNumEntries; entry++) {
+        ZipEntry* pEntry = new ZipEntry;
+
+        result = pEntry->initFromCDE(mZipFp);
+        if (result != NO_ERROR) {
+            ALOGD("initFromCDE failed\n");
+            delete pEntry;
+            goto bail;
+        }
+
+        mEntries.push_back(pEntry);
+    }
+
+
+    /*
+     * If all went well, we should now be back at the EOCD.
+     */
+    {
+        unsigned char checkBuf[4];
+        if (fread(checkBuf, 1, 4, mZipFp) != 4) {
+            ALOGD("EOCD check read failed\n");
+            result = INVALID_OPERATION;
+            goto bail;
+        }
+        if (ZipEntry::getLongLE(checkBuf) != EndOfCentralDir::kSignature) {
+            ALOGD("EOCD read check failed\n");
+            result = UNKNOWN_ERROR;
+            goto bail;
+        }
+        ALOGV("+++ EOCD read check passed\n");
+    }
+
+bail:
+    delete[] buf;
+    return result;
+}
+
+status_t ZipFile::add(const BigBuffer& buffer, const char* storageName, int compressionMethod,
+                      ZipEntry** ppEntry) {
+    std::unique_ptr<uint8_t[]> data = util::copy(buffer);
+    return add(data.get(), buffer.size(), storageName, compressionMethod, ppEntry);
+}
+
+
+/*
+ * Add a new file to the archive.
+ *
+ * This requires creating and populating a ZipEntry structure, and copying
+ * the data into the file at the appropriate position.  The "appropriate
+ * position" is the current location of the central directory, which we
+ * casually overwrite (we can put it back later).
+ *
+ * If we were concerned about safety, we would want to make all changes
+ * in a temp file and then overwrite the original after everything was
+ * safely written.  Not really a concern for us.
+ */
+status_t ZipFile::addCommon(const char* fileName, const void* data, size_t size,
+    const char* storageName, int sourceType, int compressionMethod,
+    ZipEntry** ppEntry)
+{
+    ZipEntry* pEntry = NULL;
+    status_t result = NO_ERROR;
+    long lfhPosn, startPosn, endPosn, uncompressedLen;
+    FILE* inputFp = NULL;
+    unsigned long crc;
+    time_t modWhen;
+
+    if (mReadOnly)
+        return INVALID_OPERATION;
+
+    assert(compressionMethod == ZipEntry::kCompressDeflated ||
+           compressionMethod == ZipEntry::kCompressStored);
+
+    /* make sure we're in a reasonable state */
+    assert(mZipFp != NULL);
+    assert(mEntries.size() == mEOCD.mTotalNumEntries);
+
+    /* make sure it doesn't already exist */
+    if (getEntryByName(storageName) != NULL)
+        return ALREADY_EXISTS;
+
+    if (!data) {
+        inputFp = fopen(fileName, FILE_OPEN_RO);
+        if (inputFp == NULL)
+            return errnoToStatus(errno);
+    }
+
+    if (fseek(mZipFp, mEOCD.mCentralDirOffset, SEEK_SET) != 0) {
+        result = UNKNOWN_ERROR;
+        goto bail;
+    }
+
+    pEntry = new ZipEntry;
+    pEntry->initNew(storageName, NULL);
+
+    /*
+     * From here on out, failures are more interesting.
+     */
+    mNeedCDRewrite = true;
+
+    /*
+     * Write the LFH, even though it's still mostly blank.  We need it
+     * as a place-holder.  In theory the LFH isn't necessary, but in
+     * practice some utilities demand it.
+     */
+    lfhPosn = ftell(mZipFp);
+    pEntry->mLFH.write(mZipFp);
+    startPosn = ftell(mZipFp);
+
+    /*
+     * Copy the data in, possibly compressing it as we go.
+     */
+    if (sourceType == ZipEntry::kCompressStored) {
+        if (compressionMethod == ZipEntry::kCompressDeflated) {
+            bool failed = false;
+            result = compressFpToFp(mZipFp, inputFp, data, size, &crc);
+            if (result != NO_ERROR) {
+                ALOGD("compression failed, storing\n");
+                failed = true;
+            } else {
+                /*
+                 * Make sure it has compressed "enough".  This probably ought
+                 * to be set through an API call, but I don't expect our
+                 * criteria to change over time.
+                 */
+                long src = inputFp ? ftell(inputFp) : size;
+                long dst = ftell(mZipFp) - startPosn;
+                if (dst + (dst / 10) > src) {
+                    ALOGD("insufficient compression (src=%ld dst=%ld), storing\n",
+                        src, dst);
+                    failed = true;
+                }
+            }
+
+            if (failed) {
+                compressionMethod = ZipEntry::kCompressStored;
+                if (inputFp) rewind(inputFp);
+                fseek(mZipFp, startPosn, SEEK_SET);
+                /* fall through to kCompressStored case */
+            }
+        }
+        /* handle "no compression" request, or failed compression from above */
+        if (compressionMethod == ZipEntry::kCompressStored) {
+            if (inputFp) {
+                result = copyFpToFp(mZipFp, inputFp, &crc);
+            } else {
+                result = copyDataToFp(mZipFp, data, size, &crc);
+            }
+            if (result != NO_ERROR) {
+                // don't need to truncate; happens in CDE rewrite
+                ALOGD("failed copying data in\n");
+                goto bail;
+            }
+        }
+
+        // currently seeked to end of file
+        uncompressedLen = inputFp ? ftell(inputFp) : size;
+    } else if (sourceType == ZipEntry::kCompressDeflated) {
+        /* we should support uncompressed-from-compressed, but it's not
+         * important right now */
+        assert(compressionMethod == ZipEntry::kCompressDeflated);
+
+        bool scanResult;
+        int method;
+        long compressedLen;
+
+        scanResult = ZipUtils::examineGzip(inputFp, &method, &uncompressedLen,
+                        &compressedLen, &crc);
+        if (!scanResult || method != ZipEntry::kCompressDeflated) {
+            ALOGD("this isn't a deflated gzip file?");
+            result = UNKNOWN_ERROR;
+            goto bail;
+        }
+
+        result = copyPartialFpToFp(mZipFp, inputFp, compressedLen, NULL);
+        if (result != NO_ERROR) {
+            ALOGD("failed copying gzip data in\n");
+            goto bail;
+        }
+    } else {
+        assert(false);
+        result = UNKNOWN_ERROR;
+        goto bail;
+    }
+
+    /*
+     * We could write the "Data Descriptor", but there doesn't seem to
+     * be any point since we're going to go back and write the LFH.
+     *
+     * Update file offsets.
+     */
+    endPosn = ftell(mZipFp);            // seeked to end of compressed data
+
+    /*
+     * Success!  Fill out new values.
+     */
+    pEntry->setDataInfo(uncompressedLen, endPosn - startPosn, crc,
+        compressionMethod);
+    modWhen = getModTime(inputFp ? fileno(inputFp) : fileno(mZipFp));
+    pEntry->setModWhen(modWhen);
+    pEntry->setLFHOffset(lfhPosn);
+    mEOCD.mNumEntries++;
+    mEOCD.mTotalNumEntries++;
+    mEOCD.mCentralDirSize = 0;      // mark invalid; set by flush()
+    mEOCD.mCentralDirOffset = endPosn;
+
+    /*
+     * Go back and write the LFH.
+     */
+    if (fseek(mZipFp, lfhPosn, SEEK_SET) != 0) {
+        result = UNKNOWN_ERROR;
+        goto bail;
+    }
+    pEntry->mLFH.write(mZipFp);
+
+    /*
+     * Add pEntry to the list.
+     */
+    mEntries.push_back(pEntry);
+    if (ppEntry != NULL)
+        *ppEntry = pEntry;
+    pEntry = NULL;
+
+bail:
+    if (inputFp != NULL)
+        fclose(inputFp);
+    delete pEntry;
+    return result;
+}
+
+/*
+ * Add an entry by copying it from another zip file.  If "padding" is
+ * nonzero, the specified number of bytes will be added to the "extra"
+ * field in the header.
+ *
+ * If "ppEntry" is non-NULL, a pointer to the new entry will be returned.
+ */
+status_t ZipFile::add(const ZipFile* pSourceZip, const ZipEntry* pSourceEntry,
+    int padding, ZipEntry** ppEntry)
+{
+    ZipEntry* pEntry = NULL;
+    status_t result;
+    long lfhPosn, endPosn;
+
+    if (mReadOnly)
+        return INVALID_OPERATION;
+
+    /* make sure we're in a reasonable state */
+    assert(mZipFp != NULL);
+    assert(mEntries.size() == mEOCD.mTotalNumEntries);
+
+    if (fseek(mZipFp, mEOCD.mCentralDirOffset, SEEK_SET) != 0) {
+        result = UNKNOWN_ERROR;
+        goto bail;
+    }
+
+    pEntry = new ZipEntry;
+    if (pEntry == NULL) {
+        result = NO_MEMORY;
+        goto bail;
+    }
+
+    result = pEntry->initFromExternal(pSourceZip, pSourceEntry);
+    if (result != NO_ERROR)
+        goto bail;
+    if (padding != 0) {
+        result = pEntry->addPadding(padding);
+        if (result != NO_ERROR)
+            goto bail;
+    }
+
+    /*
+     * From here on out, failures are more interesting.
+     */
+    mNeedCDRewrite = true;
+
+    /*
+     * Write the LFH.  Since we're not recompressing the data, we already
+     * have all of the fields filled out.
+     */
+    lfhPosn = ftell(mZipFp);
+    pEntry->mLFH.write(mZipFp);
+
+    /*
+     * Copy the data over.
+     *
+     * If the "has data descriptor" flag is set, we want to copy the DD
+     * fields as well.  This is a fixed-size area immediately following
+     * the data.
+     */
+    if (fseek(pSourceZip->mZipFp, pSourceEntry->getFileOffset(), SEEK_SET) != 0)
+    {
+        result = UNKNOWN_ERROR;
+        goto bail;
+    }
+
+    off_t copyLen;
+    copyLen = pSourceEntry->getCompressedLen();
+    if ((pSourceEntry->mLFH.mGPBitFlag & ZipEntry::kUsesDataDescr) != 0)
+        copyLen += ZipEntry::kDataDescriptorLen;
+
+    if (copyPartialFpToFp(mZipFp, pSourceZip->mZipFp, copyLen, NULL)
+        != NO_ERROR)
+    {
+        ALOGW("copy of '%s' failed\n", pEntry->mCDE.mFileName);
+        result = UNKNOWN_ERROR;
+        goto bail;
+    }
+
+    /*
+     * Update file offsets.
+     */
+    endPosn = ftell(mZipFp);
+
+    /*
+     * Success!  Fill out new values.
+     */
+    pEntry->setLFHOffset(lfhPosn);      // sets mCDE.mLocalHeaderRelOffset
+    mEOCD.mNumEntries++;
+    mEOCD.mTotalNumEntries++;
+    mEOCD.mCentralDirSize = 0;      // mark invalid; set by flush()
+    mEOCD.mCentralDirOffset = endPosn;
+
+    /*
+     * Add pEntry to the list.
+     */
+    mEntries.push_back(pEntry);
+    if (ppEntry != NULL)
+        *ppEntry = pEntry;
+    pEntry = NULL;
+
+    result = NO_ERROR;
+
+bail:
+    delete pEntry;
+    return result;
+}
+
+/*
+ * Copy all of the bytes in "src" to "dst".
+ *
+ * On exit, "srcFp" will be seeked to the end of the file, and "dstFp"
+ * will be seeked immediately past the data.
+ */
+status_t ZipFile::copyFpToFp(FILE* dstFp, FILE* srcFp, unsigned long* pCRC32)
+{
+    unsigned char tmpBuf[32768];
+    size_t count;
+
+    *pCRC32 = crc32(0L, Z_NULL, 0);
+
+    while (1) {
+        count = fread(tmpBuf, 1, sizeof(tmpBuf), srcFp);
+        if (ferror(srcFp) || ferror(dstFp))
+            return errnoToStatus(errno);
+        if (count == 0)
+            break;
+
+        *pCRC32 = crc32(*pCRC32, tmpBuf, count);
+
+        if (fwrite(tmpBuf, 1, count, dstFp) != count) {
+            ALOGD("fwrite %d bytes failed\n", (int) count);
+            return UNKNOWN_ERROR;
+        }
+    }
+
+    return NO_ERROR;
+}
+
+/*
+ * Copy all of the bytes in "src" to "dst".
+ *
+ * On exit, "dstFp" will be seeked immediately past the data.
+ */
+status_t ZipFile::copyDataToFp(FILE* dstFp,
+    const void* data, size_t size, unsigned long* pCRC32)
+{
+    *pCRC32 = crc32(0L, Z_NULL, 0);
+    if (size > 0) {
+        *pCRC32 = crc32(*pCRC32, (const unsigned char*)data, size);
+        if (fwrite(data, 1, size, dstFp) != size) {
+            ALOGD("fwrite %d bytes failed\n", (int) size);
+            return UNKNOWN_ERROR;
+        }
+    }
+
+    return NO_ERROR;
+}
+
+/*
+ * Copy some of the bytes in "src" to "dst".
+ *
+ * If "pCRC32" is NULL, the CRC will not be computed.
+ *
+ * On exit, "srcFp" will be seeked to the end of the file, and "dstFp"
+ * will be seeked immediately past the data just written.
+ */
+status_t ZipFile::copyPartialFpToFp(FILE* dstFp, FILE* srcFp, long length,
+    unsigned long* pCRC32)
+{
+    unsigned char tmpBuf[32768];
+    size_t count;
+
+    if (pCRC32 != NULL)
+        *pCRC32 = crc32(0L, Z_NULL, 0);
+
+    while (length) {
+        long readSize;
+
+        readSize = sizeof(tmpBuf);
+        if (readSize > length)
+            readSize = length;
+
+        count = fread(tmpBuf, 1, readSize, srcFp);
+        if ((long) count != readSize) {     // error or unexpected EOF
+            ALOGD("fread %d bytes failed\n", (int) readSize);
+            return UNKNOWN_ERROR;
+        }
+
+        if (pCRC32 != NULL)
+            *pCRC32 = crc32(*pCRC32, tmpBuf, count);
+
+        if (fwrite(tmpBuf, 1, count, dstFp) != count) {
+            ALOGD("fwrite %d bytes failed\n", (int) count);
+            return UNKNOWN_ERROR;
+        }
+
+        length -= readSize;
+    }
+
+    return NO_ERROR;
+}
+
+/*
+ * Compress all of the data in "srcFp" and write it to "dstFp".
+ *
+ * On exit, "srcFp" will be seeked to the end of the file, and "dstFp"
+ * will be seeked immediately past the compressed data.
+ */
+status_t ZipFile::compressFpToFp(FILE* dstFp, FILE* srcFp,
+    const void* data, size_t size, unsigned long* pCRC32)
+{
+    status_t result = NO_ERROR;
+    const size_t kBufSize = 32768;
+    unsigned char* inBuf = NULL;
+    unsigned char* outBuf = NULL;
+    z_stream zstream;
+    bool atEof = false;     // no feof() aviailable yet
+    unsigned long crc;
+    int zerr;
+
+    /*
+     * Create an input buffer and an output buffer.
+     */
+    inBuf = new unsigned char[kBufSize];
+    outBuf = new unsigned char[kBufSize];
+    if (inBuf == NULL || outBuf == NULL) {
+        result = NO_MEMORY;
+        goto bail;
+    }
+
+    /*
+     * Initialize the zlib stream.
+     */
+    memset(&zstream, 0, sizeof(zstream));
+    zstream.zalloc = Z_NULL;
+    zstream.zfree = Z_NULL;
+    zstream.opaque = Z_NULL;
+    zstream.next_in = NULL;
+    zstream.avail_in = 0;
+    zstream.next_out = outBuf;
+    zstream.avail_out = kBufSize;
+    zstream.data_type = Z_UNKNOWN;
+
+    zerr = deflateInit2(&zstream, Z_BEST_COMPRESSION,
+        Z_DEFLATED, -MAX_WBITS, DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY);
+    if (zerr != Z_OK) {
+        result = UNKNOWN_ERROR;
+        if (zerr == Z_VERSION_ERROR) {
+            ALOGE("Installed zlib is not compatible with linked version (%s)\n",
+                ZLIB_VERSION);
+        } else {
+            ALOGD("Call to deflateInit2 failed (zerr=%d)\n", zerr);
+        }
+        goto bail;
+    }
+
+    crc = crc32(0L, Z_NULL, 0);
+
+    /*
+     * Loop while we have data.
+     */
+    do {
+        size_t getSize;
+        int flush;
+
+        /* only read if the input buffer is empty */
+        if (zstream.avail_in == 0 && !atEof) {
+            ALOGV("+++ reading %d bytes\n", (int)kBufSize);
+            if (data) {
+                getSize = size > kBufSize ? kBufSize : size;
+                memcpy(inBuf, data, getSize);
+                data = ((const char*)data) + getSize;
+                size -= getSize;
+            } else {
+                getSize = fread(inBuf, 1, kBufSize, srcFp);
+                if (ferror(srcFp)) {
+                    ALOGD("deflate read failed (errno=%d)\n", errno);
+                    goto z_bail;
+                }
+            }
+            if (getSize < kBufSize) {
+                ALOGV("+++  got %d bytes, EOF reached\n",
+                    (int)getSize);
+                atEof = true;
+            }
+
+            crc = crc32(crc, inBuf, getSize);
+
+            zstream.next_in = inBuf;
+            zstream.avail_in = getSize;
+        }
+
+        if (atEof)
+            flush = Z_FINISH;       /* tell zlib that we're done */
+        else
+            flush = Z_NO_FLUSH;     /* more to come! */
+
+        zerr = deflate(&zstream, flush);
+        if (zerr != Z_OK && zerr != Z_STREAM_END) {
+            ALOGD("zlib deflate call failed (zerr=%d)\n", zerr);
+            result = UNKNOWN_ERROR;
+            goto z_bail;
+        }
+
+        /* write when we're full or when we're done */
+        if (zstream.avail_out == 0 ||
+            (zerr == Z_STREAM_END && zstream.avail_out != (uInt) kBufSize))
+        {
+            ALOGV("+++ writing %d bytes\n", (int) (zstream.next_out - outBuf));
+            if (fwrite(outBuf, 1, zstream.next_out - outBuf, dstFp) !=
+                (size_t)(zstream.next_out - outBuf))
+            {
+                ALOGD("write %d failed in deflate\n",
+                    (int) (zstream.next_out - outBuf));
+                goto z_bail;
+            }
+
+            zstream.next_out = outBuf;
+            zstream.avail_out = kBufSize;
+        }
+    } while (zerr == Z_OK);
+
+    assert(zerr == Z_STREAM_END);       /* other errors should've been caught */
+
+    *pCRC32 = crc;
+
+z_bail:
+    deflateEnd(&zstream);        /* free up any allocated structures */
+
+bail:
+    delete[] inBuf;
+    delete[] outBuf;
+
+    return result;
+}
+
+/*
+ * Mark an entry as deleted.
+ *
+ * We will eventually need to crunch the file down, but if several files
+ * are being removed (perhaps as part of an "update" process) we can make
+ * things considerably faster by deferring the removal to "flush" time.
+ */
+status_t ZipFile::remove(ZipEntry* pEntry)
+{
+    /*
+     * Should verify that pEntry is actually part of this archive, and
+     * not some stray ZipEntry from a different file.
+     */
+
+    /* mark entry as deleted, and mark archive as dirty */
+    pEntry->setDeleted();
+    mNeedCDRewrite = true;
+    return NO_ERROR;
+}
+
+/*
+ * Flush any pending writes.
+ *
+ * In particular, this will crunch out deleted entries, and write the
+ * Central Directory and EOCD if we have stomped on them.
+ */
+status_t ZipFile::flush(void)
+{
+    status_t result = NO_ERROR;
+    long eocdPosn;
+    int i, count;
+
+    if (mReadOnly)
+        return INVALID_OPERATION;
+    if (!mNeedCDRewrite)
+        return NO_ERROR;
+
+    assert(mZipFp != NULL);
+
+    result = crunchArchive();
+    if (result != NO_ERROR)
+        return result;
+
+    if (fseek(mZipFp, mEOCD.mCentralDirOffset, SEEK_SET) != 0)
+        return UNKNOWN_ERROR;
+
+    count = mEntries.size();
+    for (i = 0; i < count; i++) {
+        ZipEntry* pEntry = mEntries[i];
+        pEntry->mCDE.write(mZipFp);
+    }
+
+    eocdPosn = ftell(mZipFp);
+    mEOCD.mCentralDirSize = eocdPosn - mEOCD.mCentralDirOffset;
+
+    mEOCD.write(mZipFp);
+
+    /*
+     * If we had some stuff bloat up during compression and get replaced
+     * with plain files, or if we deleted some entries, there's a lot
+     * of wasted space at the end of the file.  Remove it now.
+     */
+    if (ftruncate(fileno(mZipFp), ftell(mZipFp)) != 0) {
+        ALOGW("ftruncate failed %ld: %s\n", ftell(mZipFp), strerror(errno));
+        // not fatal
+    }
+
+    /* should we clear the "newly added" flag in all entries now? */
+
+    mNeedCDRewrite = false;
+    return NO_ERROR;
+}
+
+/*
+ * Crunch deleted files out of an archive by shifting the later files down.
+ *
+ * Because we're not using a temp file, we do the operation inside the
+ * current file.
+ */
+status_t ZipFile::crunchArchive(void)
+{
+    status_t result = NO_ERROR;
+    int i, count;
+    long delCount, adjust;
+
+#if 0
+    printf("CONTENTS:\n");
+    for (i = 0; i < (int) mEntries.size(); i++) {
+        printf(" %d: lfhOff=%ld del=%d\n",
+            i, mEntries[i]->getLFHOffset(), mEntries[i]->getDeleted());
+    }
+    printf("  END is %ld\n", (long) mEOCD.mCentralDirOffset);
+#endif
+
+    /*
+     * Roll through the set of files, shifting them as appropriate.  We
+     * could probably get a slight performance improvement by sliding
+     * multiple files down at once (because we could use larger reads
+     * when operating on batches of small files), but it's not that useful.
+     */
+    count = mEntries.size();
+    delCount = adjust = 0;
+    for (i = 0; i < count; i++) {
+        ZipEntry* pEntry = mEntries[i];
+        long span;
+
+        if (pEntry->getLFHOffset() != 0) {
+            long nextOffset;
+
+            /* Get the length of this entry by finding the offset
+             * of the next entry.  Directory entries don't have
+             * file offsets, so we need to find the next non-directory
+             * entry.
+             */
+            nextOffset = 0;
+            for (int ii = i+1; nextOffset == 0 && ii < count; ii++)
+                nextOffset = mEntries[ii]->getLFHOffset();
+            if (nextOffset == 0)
+                nextOffset = mEOCD.mCentralDirOffset;
+            span = nextOffset - pEntry->getLFHOffset();
+
+            assert(span >= ZipEntry::LocalFileHeader::kLFHLen);
+        } else {
+            /* This is a directory entry.  It doesn't have
+             * any actual file contents, so there's no need to
+             * move anything.
+             */
+            span = 0;
+        }
+
+        //printf("+++ %d: off=%ld span=%ld del=%d [count=%d]\n",
+        //    i, pEntry->getLFHOffset(), span, pEntry->getDeleted(), count);
+
+        if (pEntry->getDeleted()) {
+            adjust += span;
+            delCount++;
+
+            delete pEntry;
+            mEntries.erase(mEntries.begin() + i);
+
+            /* adjust loop control */
+            count--;
+            i--;
+        } else if (span != 0 && adjust > 0) {
+            /* shuffle this entry back */
+            //printf("+++ Shuffling '%s' back %ld\n",
+            //    pEntry->getFileName(), adjust);
+            result = filemove(mZipFp, pEntry->getLFHOffset() - adjust,
+                        pEntry->getLFHOffset(), span);
+            if (result != NO_ERROR) {
+                /* this is why you use a temp file */
+                ALOGE("error during crunch - archive is toast\n");
+                return result;
+            }
+
+            pEntry->setLFHOffset(pEntry->getLFHOffset() - adjust);
+        }
+    }
+
+    /*
+     * Fix EOCD info.  We have to wait until the end to do some of this
+     * because we use mCentralDirOffset to determine "span" for the
+     * last entry.
+     */
+    mEOCD.mCentralDirOffset -= adjust;
+    mEOCD.mNumEntries -= delCount;
+    mEOCD.mTotalNumEntries -= delCount;
+    mEOCD.mCentralDirSize = 0;  // mark invalid; set by flush()
+
+    assert(mEOCD.mNumEntries == mEOCD.mTotalNumEntries);
+    assert(mEOCD.mNumEntries == count);
+
+    return result;
+}
+
+/*
+ * Works like memmove(), but on pieces of a file.
+ */
+status_t ZipFile::filemove(FILE* fp, off_t dst, off_t src, size_t n)
+{
+    if (dst == src || n <= 0)
+        return NO_ERROR;
+
+    unsigned char readBuf[32768];
+
+    if (dst < src) {
+        /* shift stuff toward start of file; must read from start */
+        while (n != 0) {
+            size_t getSize = sizeof(readBuf);
+            if (getSize > n)
+                getSize = n;
+
+            if (fseek(fp, (long) src, SEEK_SET) != 0) {
+                ALOGD("filemove src seek %ld failed\n", (long) src);
+                return UNKNOWN_ERROR;
+            }
+
+            if (fread(readBuf, 1, getSize, fp) != getSize) {
+                ALOGD("filemove read %ld off=%ld failed\n",
+                    (long) getSize, (long) src);
+                return UNKNOWN_ERROR;
+            }
+
+            if (fseek(fp, (long) dst, SEEK_SET) != 0) {
+                ALOGD("filemove dst seek %ld failed\n", (long) dst);
+                return UNKNOWN_ERROR;
+            }
+
+            if (fwrite(readBuf, 1, getSize, fp) != getSize) {
+                ALOGD("filemove write %ld off=%ld failed\n",
+                    (long) getSize, (long) dst);
+                return UNKNOWN_ERROR;
+            }
+
+            src += getSize;
+            dst += getSize;
+            n -= getSize;
+        }
+    } else {
+        /* shift stuff toward end of file; must read from end */
+        assert(false);      // write this someday, maybe
+        return UNKNOWN_ERROR;
+    }
+
+    return NO_ERROR;
+}
+
+
+/*
+ * Get the modification time from a file descriptor.
+ */
+time_t ZipFile::getModTime(int fd)
+{
+    struct stat sb;
+
+    if (fstat(fd, &sb) < 0) {
+        ALOGD("HEY: fstat on fd %d failed\n", fd);
+        return (time_t) -1;
+    }
+
+    return sb.st_mtime;
+}
+
+
+#if 0       /* this is a bad idea */
+/*
+ * Get a copy of the Zip file descriptor.
+ *
+ * We don't allow this if the file was opened read-write because we tend
+ * to leave the file contents in an uncertain state between calls to
+ * flush().  The duplicated file descriptor should only be valid for reads.
+ */
+int ZipFile::getZipFd(void) const
+{
+    if (!mReadOnly)
+        return INVALID_OPERATION;
+    assert(mZipFp != NULL);
+
+    int fd;
+    fd = dup(fileno(mZipFp));
+    if (fd < 0) {
+        ALOGD("didn't work, errno=%d\n", errno);
+    }
+
+    return fd;
+}
+#endif
+
+
+#if 0
+/*
+ * Expand data.
+ */
+bool ZipFile::uncompress(const ZipEntry* pEntry, void* buf) const
+{
+    return false;
+}
+#endif
+
+// free the memory when you're done
+void* ZipFile::uncompress(const ZipEntry* entry)
+{
+    size_t unlen = entry->getUncompressedLen();
+    size_t clen = entry->getCompressedLen();
+
+    void* buf = malloc(unlen);
+    if (buf == NULL) {
+        return NULL;
+    }
+
+    fseek(mZipFp, 0, SEEK_SET);
+
+    off_t offset = entry->getFileOffset();
+    if (fseek(mZipFp, offset, SEEK_SET) != 0) {
+        goto bail;
+    }
+
+    switch (entry->getCompressionMethod())
+    {
+        case ZipEntry::kCompressStored: {
+            ssize_t amt = fread(buf, 1, unlen, mZipFp);
+            if (amt != (ssize_t)unlen) {
+                goto bail;
+            }
+#if 0
+            printf("data...\n");
+            const unsigned char* p = (unsigned char*)buf;
+            const unsigned char* end = p+unlen;
+            for (int i=0; i<32 && p < end; i++) {
+                printf("0x%08x ", (int)(offset+(i*0x10)));
+                for (int j=0; j<0x10 && p < end; j++) {
+                    printf(" %02x", *p);
+                    p++;
+                }
+                printf("\n");
+            }
+#endif
+
+            }
+            break;
+        case ZipEntry::kCompressDeflated: {
+            if (!ZipUtils::inflateToBuffer(mZipFp, buf, unlen, clen)) {
+                goto bail;
+            }
+            }
+            break;
+        default:
+            goto bail;
+    }
+    return buf;
+
+bail:
+    free(buf);
+    return NULL;
+}
+
+
+/*
+ * ===========================================================================
+ *      ZipFile::EndOfCentralDir
+ * ===========================================================================
+ */
+
+/*
+ * Read the end-of-central-dir fields.
+ *
+ * "buf" should be positioned at the EOCD signature, and should contain
+ * the entire EOCD area including the comment.
+ */
+status_t ZipFile::EndOfCentralDir::readBuf(const unsigned char* buf, int len)
+{
+    /* don't allow re-use */
+    assert(mComment == NULL);
+
+    if (len < kEOCDLen) {
+        /* looks like ZIP file got truncated */
+        ALOGD(" Zip EOCD: expected >= %d bytes, found %d\n",
+            kEOCDLen, len);
+        return INVALID_OPERATION;
+    }
+
+    /* this should probably be an assert() */
+    if (ZipEntry::getLongLE(&buf[0x00]) != kSignature)
+        return UNKNOWN_ERROR;
+
+    mDiskNumber = ZipEntry::getShortLE(&buf[0x04]);
+    mDiskWithCentralDir = ZipEntry::getShortLE(&buf[0x06]);
+    mNumEntries = ZipEntry::getShortLE(&buf[0x08]);
+    mTotalNumEntries = ZipEntry::getShortLE(&buf[0x0a]);
+    mCentralDirSize = ZipEntry::getLongLE(&buf[0x0c]);
+    mCentralDirOffset = ZipEntry::getLongLE(&buf[0x10]);
+    mCommentLen = ZipEntry::getShortLE(&buf[0x14]);
+
+    // TODO: validate mCentralDirOffset
+
+    if (mCommentLen > 0) {
+        if (kEOCDLen + mCommentLen > len) {
+            ALOGD("EOCD(%d) + comment(%d) exceeds len (%d)\n",
+                kEOCDLen, mCommentLen, len);
+            return UNKNOWN_ERROR;
+        }
+        mComment = new unsigned char[mCommentLen];
+        memcpy(mComment, buf + kEOCDLen, mCommentLen);
+    }
+
+    return NO_ERROR;
+}
+
+/*
+ * Write an end-of-central-directory section.
+ */
+status_t ZipFile::EndOfCentralDir::write(FILE* fp)
+{
+    unsigned char buf[kEOCDLen];
+
+    ZipEntry::putLongLE(&buf[0x00], kSignature);
+    ZipEntry::putShortLE(&buf[0x04], mDiskNumber);
+    ZipEntry::putShortLE(&buf[0x06], mDiskWithCentralDir);
+    ZipEntry::putShortLE(&buf[0x08], mNumEntries);
+    ZipEntry::putShortLE(&buf[0x0a], mTotalNumEntries);
+    ZipEntry::putLongLE(&buf[0x0c], mCentralDirSize);
+    ZipEntry::putLongLE(&buf[0x10], mCentralDirOffset);
+    ZipEntry::putShortLE(&buf[0x14], mCommentLen);
+
+    if (fwrite(buf, 1, kEOCDLen, fp) != kEOCDLen)
+        return UNKNOWN_ERROR;
+    if (mCommentLen > 0) {
+        assert(mComment != NULL);
+        if (fwrite(mComment, mCommentLen, 1, fp) != mCommentLen)
+            return UNKNOWN_ERROR;
+    }
+
+    return NO_ERROR;
+}
+
+/*
+ * Dump the contents of an EndOfCentralDir object.
+ */
+void ZipFile::EndOfCentralDir::dump(void) const
+{
+    ALOGD(" EndOfCentralDir contents:\n");
+    ALOGD("  diskNum=%u diskWCD=%u numEnt=%u totalNumEnt=%u\n",
+        mDiskNumber, mDiskWithCentralDir, mNumEntries, mTotalNumEntries);
+    ALOGD("  centDirSize=%lu centDirOff=%lu commentLen=%u\n",
+        mCentralDirSize, mCentralDirOffset, mCommentLen);
+}
+
+} // namespace aapt
diff --git a/tools/aapt2/ZipFile.h b/tools/aapt2/ZipFile.h
new file mode 100644
index 0000000..9cbd1fa
--- /dev/null
+++ b/tools/aapt2/ZipFile.h
@@ -0,0 +1,276 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//
+// General-purpose Zip archive access.  This class allows both reading and
+// writing to Zip archives, including deletion of existing entries.
+//
+#ifndef __LIBS_ZIPFILE_H
+#define __LIBS_ZIPFILE_H
+
+#include "BigBuffer.h"
+#include "ZipEntry.h"
+
+#include <stdio.h>
+#include <utils/Errors.h>
+#include <vector>
+
+namespace aapt {
+
+using android::status_t;
+
+/*
+ * Manipulate a Zip archive.
+ *
+ * Some changes will not be visible in the until until "flush" is called.
+ *
+ * The correct way to update a file archive is to make all changes to a
+ * copy of the archive in a temporary file, and then unlink/rename over
+ * the original after everything completes.  Because we're only interested
+ * in using this for packaging, we don't worry about such things.  Crashing
+ * after making changes and before flush() completes could leave us with
+ * an unusable Zip archive.
+ */
+class ZipFile {
+public:
+    ZipFile(void)
+      : mZipFp(NULL), mReadOnly(false), mNeedCDRewrite(false)
+      {}
+    ~ZipFile(void) {
+        if (!mReadOnly)
+            flush();
+        if (mZipFp != NULL)
+            fclose(mZipFp);
+        discardEntries();
+    }
+
+    /*
+     * Open a new or existing archive.
+     */
+    enum {
+        kOpenReadOnly   = 0x01,
+        kOpenReadWrite  = 0x02,
+        kOpenCreate     = 0x04,     // create if it doesn't exist
+        kOpenTruncate   = 0x08,     // if it exists, empty it
+    };
+    status_t open(const char* zipFileName, int flags);
+
+    /*
+     * Add a file to the end of the archive.  Specify whether you want the
+     * library to try to store it compressed.
+     *
+     * If "storageName" is specified, the archive will use that instead
+     * of "fileName".
+     *
+     * If there is already an entry with the same name, the call fails.
+     * Existing entries with the same name must be removed first.
+     *
+     * If "ppEntry" is non-NULL, a pointer to the new entry will be returned.
+     */
+    status_t add(const char* fileName, int compressionMethod,
+        ZipEntry** ppEntry)
+    {
+        return add(fileName, fileName, compressionMethod, ppEntry);
+    }
+    status_t add(const char* fileName, const char* storageName,
+        int compressionMethod, ZipEntry** ppEntry)
+    {
+        return addCommon(fileName, NULL, 0, storageName,
+                         ZipEntry::kCompressStored,
+                         compressionMethod, ppEntry);
+    }
+
+    /*
+     * Add a file that is already compressed with gzip.
+     *
+     * If "ppEntry" is non-NULL, a pointer to the new entry will be returned.
+     */
+    status_t addGzip(const char* fileName, const char* storageName,
+        ZipEntry** ppEntry)
+    {
+        return addCommon(fileName, NULL, 0, storageName,
+                         ZipEntry::kCompressDeflated,
+                         ZipEntry::kCompressDeflated, ppEntry);
+    }
+
+    /*
+     * Add a file from an in-memory data buffer.
+     *
+     * If "ppEntry" is non-NULL, a pointer to the new entry will be returned.
+     */
+    status_t add(const void* data, size_t size, const char* storageName,
+        int compressionMethod, ZipEntry** ppEntry)
+    {
+        return addCommon(NULL, data, size, storageName,
+                         ZipEntry::kCompressStored,
+                         compressionMethod, ppEntry);
+    }
+
+    status_t add(const BigBuffer& data, const char* storageName,
+        int compressionMethod, ZipEntry** ppEntry);
+
+    /*
+     * Add an entry by copying it from another zip file.  If "padding" is
+     * nonzero, the specified number of bytes will be added to the "extra"
+     * field in the header.
+     *
+     * If "ppEntry" is non-NULL, a pointer to the new entry will be returned.
+     */
+    status_t add(const ZipFile* pSourceZip, const ZipEntry* pSourceEntry,
+        int padding, ZipEntry** ppEntry);
+
+    /*
+     * Mark an entry as having been removed.  It is not actually deleted
+     * from the archive or our internal data structures until flush() is
+     * called.
+     */
+    status_t remove(ZipEntry* pEntry);
+
+    /*
+     * Flush changes.  If mNeedCDRewrite is set, this writes the central dir.
+     */
+    status_t flush(void);
+
+    /*
+     * Expand the data into the buffer provided.  The buffer must hold
+     * at least <uncompressed len> bytes.  Variation expands directly
+     * to a file.
+     *
+     * Returns "false" if an error was encountered in the compressed data.
+     */
+    //bool uncompress(const ZipEntry* pEntry, void* buf) const;
+    //bool uncompress(const ZipEntry* pEntry, FILE* fp) const;
+    void* uncompress(const ZipEntry* pEntry);
+
+    /*
+     * Get an entry, by name.  Returns NULL if not found.
+     *
+     * Does not return entries pending deletion.
+     */
+    ZipEntry* getEntryByName(const char* fileName) const;
+
+    /*
+     * Get the Nth entry in the archive.
+     *
+     * This will return an entry that is pending deletion.
+     */
+    int getNumEntries(void) const { return mEntries.size(); }
+    ZipEntry* getEntryByIndex(int idx) const;
+
+private:
+    /* these are private and not defined */
+    ZipFile(const ZipFile& src);
+    ZipFile& operator=(const ZipFile& src);
+
+    class EndOfCentralDir {
+    public:
+        EndOfCentralDir(void) :
+            mDiskNumber(0),
+            mDiskWithCentralDir(0),
+            mNumEntries(0),
+            mTotalNumEntries(0),
+            mCentralDirSize(0),
+            mCentralDirOffset(0),
+            mCommentLen(0),
+            mComment(NULL)
+            {}
+        virtual ~EndOfCentralDir(void) {
+            delete[] mComment;
+        }
+
+        status_t readBuf(const unsigned char* buf, int len);
+        status_t write(FILE* fp);
+
+        //unsigned long   mSignature;
+        unsigned short  mDiskNumber;
+        unsigned short  mDiskWithCentralDir;
+        unsigned short  mNumEntries;
+        unsigned short  mTotalNumEntries;
+        unsigned long   mCentralDirSize;
+        unsigned long   mCentralDirOffset;      // offset from first disk
+        unsigned short  mCommentLen;
+        unsigned char*  mComment;
+
+        enum {
+            kSignature      = 0x06054b50,
+            kEOCDLen        = 22,       // EndOfCentralDir len, excl. comment
+
+            kMaxCommentLen  = 65535,    // longest possible in ushort
+            kMaxEOCDSearch  = kMaxCommentLen + EndOfCentralDir::kEOCDLen,
+
+        };
+
+        void dump(void) const;
+    };
+
+
+    /* read all entries in the central dir */
+    status_t readCentralDir(void);
+
+    /* crunch deleted entries out */
+    status_t crunchArchive(void);
+
+    /* clean up mEntries */
+    void discardEntries(void);
+
+    /* common handler for all "add" functions */
+    status_t addCommon(const char* fileName, const void* data, size_t size,
+        const char* storageName, int sourceType, int compressionMethod,
+        ZipEntry** ppEntry);
+
+    /* copy all of "srcFp" into "dstFp" */
+    status_t copyFpToFp(FILE* dstFp, FILE* srcFp, unsigned long* pCRC32);
+    /* copy all of "data" into "dstFp" */
+    status_t copyDataToFp(FILE* dstFp,
+        const void* data, size_t size, unsigned long* pCRC32);
+    /* copy some of "srcFp" into "dstFp" */
+    status_t copyPartialFpToFp(FILE* dstFp, FILE* srcFp, long length,
+        unsigned long* pCRC32);
+    /* like memmove(), but on parts of a single file */
+    status_t filemove(FILE* fp, off_t dest, off_t src, size_t n);
+    /* compress all of "srcFp" into "dstFp", using Deflate */
+    status_t compressFpToFp(FILE* dstFp, FILE* srcFp,
+        const void* data, size_t size, unsigned long* pCRC32);
+
+    /* get modification date from a file descriptor */
+    time_t getModTime(int fd);
+
+    /*
+     * We use stdio FILE*, which gives us buffering but makes dealing
+     * with files >2GB awkward.  Until we support Zip64, we're fine.
+     */
+    FILE*           mZipFp;             // Zip file pointer
+
+    /* one of these per file */
+    EndOfCentralDir mEOCD;
+
+    /* did we open this read-only? */
+    bool            mReadOnly;
+
+    /* set this when we trash the central dir */
+    bool            mNeedCDRewrite;
+
+    /*
+     * One ZipEntry per entry in the zip file.  I'm using pointers instead
+     * of objects because it's easier than making operator= work for the
+     * classes and sub-classes.
+     */
+    std::vector<ZipEntry*>   mEntries;
+};
+
+}; // namespace aapt
+
+#endif // __LIBS_ZIPFILE_H
diff --git a/tools/aapt2/data/Makefile b/tools/aapt2/data/Makefile
index f296dc1..5a2a1d1 100644
--- a/tools/aapt2/data/Makefile
+++ b/tools/aapt2/data/Makefile
@@ -14,6 +14,7 @@
 
 LOCAL_PACKAGE := com.android.app
 LOCAL_RESOURCE_DIR := res
+LOCAL_LIBS := lib/out/package.apk
 LOCAL_OUT := out
 LOCAL_GEN := out/gen
 
@@ -21,13 +22,12 @@
 # AAPT2 custom rules.
 ##
 
-PRIVATE_ARSC := $(LOCAL_OUT)/resources.arsc
 PRIVATE_APK_UNALIGNED := $(LOCAL_OUT)/package-unaligned.apk
 PRIVATE_APK_ALIGNED := $(LOCAL_OUT)/package.apk
 
 # Eg: framework.apk, etc.
-PRIVATE_LIBS := $(FRAMEWORK)
-$(info PRIVATE_LIBS = $(PRIVATE_LIBS))
+PRIVATE_INCLUDES := $(FRAMEWORK)
+$(info PRIVATE_INCLUDES = $(PRIVATE_INCLUDES))
 
 # Eg: gen/com/android/app/R.java
 PRIVATE_R_JAVA := $(LOCAL_GEN)/$(subst .,/,$(LOCAL_PACKAGE))/R.java
@@ -42,56 +42,32 @@
 	$(patsubst $(LOCAL_RESOURCE_DIR)/%/,%,$(sort $(dir $(PRIVATE_RESOURCES))))
 $(info PRIVATE_RESOURCE_TYPES = $(PRIVATE_RESOURCE_TYPES))
 
-# Eg: drawable, drawable-xhdpi, layout
-PRIVATE_NON_VALUE_RESOURCE_TYPES := $(filter-out values%,$(PRIVATE_RESOURCE_TYPES))
-$(info PRIVATE_NON_VALUE_RESOURCE_TYPES = $(PRIVATE_NON_VALUE_RESOURCE_TYPES))
-
-# Eg: out/values-v4.table, out/drawable-xhdpi.table
-PRIVATE_INTERMEDIATE_TABLES := $(patsubst %,$(LOCAL_OUT)/%.table,$(PRIVATE_RESOURCE_TYPES))
+# Eg: out/values-v4.apk, out/drawable-xhdpi.apk
+PRIVATE_INTERMEDIATE_TABLES := $(patsubst %,$(LOCAL_OUT)/%.apk,$(PRIVATE_RESOURCE_TYPES))
 $(info PRIVATE_INTERMEDIATE_TABLES = $(PRIVATE_INTERMEDIATE_TABLES))
 
-# Eg: out/res/layout/main.xml, out/res/drawable/icon.png
-PRIVATE_INTERMEDIATE_FILES := $(patsubst $(LOCAL_RESOURCE_DIR)/%,$(LOCAL_OUT)/res/%,$(filter-out $(LOCAL_RESOURCE_DIR)/values%,$(PRIVATE_RESOURCES)))
-$(info PRIVATE_INTERMEDIATE_FILES = $(PRIVATE_INTERMEDIATE_FILES))
-
 # Generates rules for collect phase.
 # $1: Resource type (values-v4)
-# returns: out/values-v4.table: res/values-v4/styles.xml res/values-v4/colors.xml
+# returns: out/values-v4.apk: res/values-v4/styles.xml res/values-v4/colors.xml
 define make-collect-rule
-$(LOCAL_OUT)/$1.table: $(filter $(LOCAL_RESOURCE_DIR)/$1/%,$(PRIVATE_RESOURCES))
-	$(AAPT) collect --package $(LOCAL_PACKAGE) -o $$@ $$^
+$(LOCAL_OUT)/$1.apk: $(filter $(LOCAL_RESOURCE_DIR)/$1/%,$(PRIVATE_RESOURCES))
+	$(AAPT) compile --package $(LOCAL_PACKAGE) --binding $(LOCAL_GEN) -o $$@ $$^
 endef
 
-# Collect: out/values-v4.table <- res/values-v4/styles.xml res/values-v4/colors.xml
+# Collect: out/values-v4.apk <- res/values-v4/styles.xml res/values-v4/colors.xml
 $(foreach d,$(PRIVATE_RESOURCE_TYPES),$(eval $(call make-collect-rule,$d)))
 
-# Link: out/resources.arsc <- out/values-v4.table out/drawable-v4.table
-$(PRIVATE_ARSC): $(PRIVATE_INTERMEDIATE_TABLES) $(PRIVATE_LIBS)
-	$(AAPT) link --package $(LOCAL_PACKAGE) $(addprefix -I ,$(PRIVATE_LIBS)) --java $(LOCAL_GEN) -o $@ $(PRIVATE_INTERMEDIATE_TABLES)
-
-# Compile Manifest: out/AndroidManifest.xml <- AndroidManifest.xml out/resources.arsc
-$(LOCAL_OUT)/AndroidManifest.xml: AndroidManifest.xml $(PRIVATE_ARSC) $(PRIVATE_LIBS)
-	$(AAPT) manifest -I $(PRIVATE_ARSC) $(addprefix -I ,$(PRIVATE_LIBS)) -o $(LOCAL_OUT) AndroidManifest.xml
-
-# Generates rules for compile phase.
-# $1: resource file (res/drawable/icon.png)
-# returns: out/res/drawable/icon.png: res/drawable/icon.png out/resources.arsc
-define make-compile-rule
-$1: $(patsubst $(LOCAL_OUT)/res/%,$(LOCAL_RESOURCE_DIR)/%,$1) $(PRIVATE_ARSC) $(PRIVATE_LIBS)
-	$(AAPT) compile --package $(LOCAL_PACKAGE) -I $(PRIVATE_ARSC) $(addprefix -I ,$(PRIVATE_LIBS)) -o $(LOCAL_OUT) $$<
-endef
-
-# Compile: out/res/drawable-xhdpi/icon.png <- res/drawable-xhdpi/icon.png
-$(foreach f,$(PRIVATE_INTERMEDIATE_FILES),$(eval $(call make-compile-rule,$f)))
+# Link: out/package-unaligned.apk <- out/values-v4.apk out/drawable-v4.apk
+$(PRIVATE_APK_UNALIGNED): $(PRIVATE_INTERMEDIATE_TABLES) $(PRIVATE_INCLUDES) $(LOCAL_LIBS) AndroidManifest.xml
+	$(AAPT) link --manifest AndroidManifest.xml $(addprefix -I ,$(PRIVATE_INCLUDES)) --java $(LOCAL_GEN) -o $@ $(PRIVATE_INTERMEDIATE_TABLES) $(LOCAL_LIBS)
 
 # R.java: gen/com/android/app/R.java <- out/resources.arsc
 # No action since R.java is generated when out/resources.arsc is.
-$(PRIVATE_R_JAVA): $(PRIVATE_ARSC)
+$(PRIVATE_R_JAVA): $(PRIVATE_APK_UNALIGNED)
 
 # Assemble: zip out/resources.arsc AndroidManifest.xml and res/**/*
-$(PRIVATE_APK_ALIGNED): $(PRIVATE_ARSC) $(PRIVATE_INTERMEDIATE_FILES) $(LOCAL_OUT)/AndroidManifest.xml
-	cd $(LOCAL_OUT); $(ZIP) $(patsubst $(LOCAL_OUT)/%,%,$(PRIVATE_APK_UNALIGNED)) $(patsubst $(LOCAL_OUT)/%,%,$^)
-	$(ZIPALIGN) $(PRIVATE_APK_UNALIGNED) $@
+$(PRIVATE_APK_ALIGNED): $(PRIVATE_APK_UNALIGNED)
+	$(ZIPALIGN) $< $@
 
 # Create the out directory if needed.
 dummy := $(shell test -d $(LOCAL_OUT) || mkdir -p $(LOCAL_OUT))
@@ -100,7 +76,7 @@
 java: $(PRIVATE_R_JAVA)
 
 .PHONY: assemble
-assemble: $(LOCAL_OUT)/package.apk
+assemble: $(PRIVATE_APK_ALIGNED)
 
 .PHONY: all
 all: assemble java
diff --git a/tools/aapt2/data/lib/AndroidManifest.xml b/tools/aapt2/data/lib/AndroidManifest.xml
new file mode 100644
index 0000000..c1612e5
--- /dev/null
+++ b/tools/aapt2/data/lib/AndroidManifest.xml
@@ -0,0 +1,3 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="android.appcompat"/>
diff --git a/tools/aapt2/data/lib/Makefile b/tools/aapt2/data/lib/Makefile
new file mode 100644
index 0000000..2897ff1
--- /dev/null
+++ b/tools/aapt2/data/lib/Makefile
@@ -0,0 +1,81 @@
+##
+# Environment dependent variables
+##
+
+AAPT := aapt2
+ZIPALIGN := zipalign 4
+FRAMEWORK := ../../../../../../out/target/common/obj/APPS/framework-res_intermediates/package-export.apk
+
+##
+# Project depenedent variables
+##
+
+LOCAL_PACKAGE := android.appcompat
+LOCAL_RESOURCE_DIR := res
+LOCAL_OUT := out
+LOCAL_GEN := out/gen
+
+##
+# AAPT2 custom rules.
+##
+
+PRIVATE_APK_UNALIGNED := $(LOCAL_OUT)/package-unaligned.apk
+PRIVATE_APK_ALIGNED := $(LOCAL_OUT)/package.apk
+
+# Eg: framework.apk, etc.
+PRIVATE_LIBS := $(FRAMEWORK)
+$(info PRIVATE_LIBS = $(PRIVATE_LIBS))
+
+# Eg: gen/com/android/app/R.java
+PRIVATE_R_JAVA := $(LOCAL_GEN)/$(subst .,/,$(LOCAL_PACKAGE))/R.java
+$(info PRIVATE_R_JAVA = $(PRIVATE_R_JAVA))
+
+# Eg: res/drawable/icon.png, res/values/styles.xml
+PRIVATE_RESOURCES := $(shell find $(LOCAL_RESOURCE_DIR) -mindepth 1 -maxdepth 2 -type f)
+$(info PRIVATE_RESOURCES = $(PRIVATE_RESOURCES))
+
+# Eg: drawable, values, layouts
+PRIVATE_RESOURCE_TYPES := \
+	$(patsubst $(LOCAL_RESOURCE_DIR)/%/,%,$(sort $(dir $(PRIVATE_RESOURCES))))
+$(info PRIVATE_RESOURCE_TYPES = $(PRIVATE_RESOURCE_TYPES))
+
+# Eg: out/values-v4.apk, out/drawable-xhdpi.apk
+PRIVATE_INTERMEDIATE_TABLES := $(patsubst %,$(LOCAL_OUT)/%.apk,$(PRIVATE_RESOURCE_TYPES))
+$(info PRIVATE_INTERMEDIATE_TABLES = $(PRIVATE_INTERMEDIATE_TABLES))
+
+# Generates rules for collect phase.
+# $1: Resource type (values-v4)
+# returns: out/values-v4.apk: res/values-v4/styles.xml res/values-v4/colors.xml
+define make-collect-rule
+$(LOCAL_OUT)/$1.apk: $(filter $(LOCAL_RESOURCE_DIR)/$1/%,$(PRIVATE_RESOURCES))
+	$(AAPT) compile --package $(LOCAL_PACKAGE) -o $$@ $$^
+endef
+
+# Collect: out/values-v4.apk <- res/values-v4/styles.xml res/values-v4/colors.xml
+$(foreach d,$(PRIVATE_RESOURCE_TYPES),$(eval $(call make-collect-rule,$d)))
+
+# Link: out/package-unaligned.apk <- out/values-v4.apk out/drawable-v4.apk
+$(PRIVATE_APK_UNALIGNED): $(PRIVATE_INTERMEDIATE_TABLES) $(PRIVATE_LIBS) AndroidManifest.xml
+	$(AAPT) link --manifest AndroidManifest.xml $(addprefix -I ,$(PRIVATE_LIBS)) --java $(LOCAL_GEN) -o $@ $(PRIVATE_INTERMEDIATE_TABLES)
+
+# R.java: gen/com/android/app/R.java <- out/resources.arsc
+# No action since R.java is generated when out/resources.arsc is.
+$(PRIVATE_R_JAVA): $(PRIVATE_APK_UNALIGNED)
+
+# Assemble: zip out/resources.arsc AndroidManifest.xml and res/**/*
+$(PRIVATE_APK_ALIGNED): $(PRIVATE_APK_UNALIGNED)
+	$(ZIPALIGN) $< $@
+
+# Create the out directory if needed.
+dummy := $(shell test -d $(LOCAL_OUT) || mkdir -p $(LOCAL_OUT))
+
+.PHONY: java
+java: $(PRIVATE_R_JAVA)
+
+.PHONY: assemble
+assemble: $(PRIVATE_APK_ALIGNED)
+
+.PHONY: all
+all: assemble java
+
+.DEFAULT_GOAL := all
diff --git a/tools/aapt2/data/lib/res/values/styles.xml b/tools/aapt2/data/lib/res/values/styles.xml
new file mode 100644
index 0000000..adb5c4f
--- /dev/null
+++ b/tools/aapt2/data/lib/res/values/styles.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+    <style name="Platform.AppCompat" parent="@android:style/Theme">
+        <item name="android:windowNoTitle">true</item>
+    </style>
+</resources>
diff --git a/tools/aapt2/data/res/values/styles.xml b/tools/aapt2/data/res/values/styles.xml
index 71ce388..c5dd276 100644
--- a/tools/aapt2/data/res/values/styles.xml
+++ b/tools/aapt2/data/res/values/styles.xml
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
 <resources>
-    <style name="App" parent="android:Theme.Material">
+    <style name="App" parent="android.appcompat:Platform.AppCompat">
         <item name="android:background">@color/primary</item>
         <item name="android:colorPrimary">@color/primary</item>
         <item name="android:colorPrimaryDark">@color/primary_dark</item>
diff --git a/tools/aapt2/process.dot b/tools/aapt2/process.dot
index a92405d..4741952 100644
--- a/tools/aapt2/process.dot
+++ b/tools/aapt2/process.dot
@@ -19,6 +19,13 @@
     res_layout_fr_main_xml [label="res/layout-fr/main.xml"];
     res_values_fr_strings_xml [label="res/values-fr/strings.xml"];
 
+    lib_apk_resources_arsc [label="lib.apk:resources.arsc",color=green];
+    lib_apk_res_layout_main_xml [label="lib.apk:res/layout/main.xml",color=green];
+    lib_apk_res_drawable_icon_png [label="lib.apk:res/drawable/icon.png",color=green];
+    lib_apk_fr_res_layout_main_xml [label="lib.apk:res/layout-fr/main.xml",color=green];
+    lib_apk_fr_res_drawable_icon_png [label="lib.apk:res/drawable-fr/icon.png",color=green];
+    out_res_layout_lib_main_xml [label="out/res/layout/lib-main.xml"];
+
     out_package -> package_default;
     out_fr_package -> package_fr;
 
@@ -26,6 +33,7 @@
     package_default -> out_table_aligned;
     package_default -> out_res_layout_main_xml;
     package_default -> out_res_layout_v21_main_xml [color=red];
+    package_default -> out_res_layout_lib_main_xml;
 
     package_fr [shape=box,label="Assemble",color=blue];
     package_fr -> out_table_fr_aligned;
@@ -44,6 +52,7 @@
     link_tables [shape=box,label="Link",color=blue];
     link_tables -> out_values_table;
     link_tables -> out_layout_table;
+    link_tables -> lib_apk_resources_arsc;
 
     out_values_table -> compile_values;
 
@@ -61,10 +70,11 @@
     link_fr_tables [shape=box,label="Link",color=blue];
     link_fr_tables -> out_values_fr_table;
     link_fr_tables -> out_layout_fr_table;
+    link_fr_tables -> lib_apk_resources_arsc;
 
     out_values_fr_table -> compile_values_fr;
 
-    compile_values_fr [shape=box,label="Compile",color=blue];
+    compile_values_fr [shape=box,label="Collect",color=blue];
     compile_values_fr -> res_values_fr_strings_xml;
 
     out_layout_fr_table -> collect_xml_fr;
@@ -89,4 +99,10 @@
 
     compile_res_layout_fr_main_xml -> res_layout_fr_main_xml;
     compile_res_layout_fr_main_xml -> out_table_fr_aligned;
+
+    out_res_layout_lib_main_xml -> compile_res_layout_lib_main_xml;
+
+    compile_res_layout_lib_main_xml [shape=box,label="Compile",color=blue];
+    compile_res_layout_lib_main_xml -> out_table_aligned;
+    compile_res_layout_lib_main_xml -> lib_apk_res_layout_main_xml;
 }
diff --git a/tools/layoutlib/bridge/src/android/app/Fragment_Delegate.java b/tools/layoutlib/bridge/src/android/app/Fragment_Delegate.java
index aabd3f1..f7654ce 100644
--- a/tools/layoutlib/bridge/src/android/app/Fragment_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/app/Fragment_Delegate.java
@@ -16,7 +16,7 @@
 
 package android.app;
 
-import com.android.ide.common.rendering.api.IProjectCallback;
+import com.android.ide.common.rendering.api.LayoutlibCallback;
 import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
 
 import android.content.Context;
@@ -30,19 +30,19 @@
  *
  * The methods being re-implemented are the ones responsible for instantiating Fragment objects.
  * Because the classes of these objects are found in the project, these methods need access to
- * {@link IProjectCallback} object. They are however static methods, so the callback is set
- * before the inflation through {@link #setProjectCallback(IProjectCallback)}.
+ * {@link LayoutlibCallback} object. They are however static methods, so the callback is set
+ * before the inflation through {@link #setLayoutlibCallback(LayoutlibCallback)}.
  */
 public class Fragment_Delegate {
 
-    private static IProjectCallback sProjectCallback;
+    private static LayoutlibCallback sLayoutlibCallback;
 
     /**
-     * Sets the current {@link IProjectCallback} to be used to instantiate classes coming
+     * Sets the current {@link LayoutlibCallback} to be used to instantiate classes coming
      * from the project being rendered.
      */
-    public static void setProjectCallback(IProjectCallback projectCallback) {
-        sProjectCallback = projectCallback;
+    public static void setLayoutlibCallback(LayoutlibCallback layoutlibCallback) {
+        sLayoutlibCallback = layoutlibCallback;
     }
 
     /**
@@ -62,17 +62,17 @@
      * This is currently just used to get its ClassLoader.
      * @param fname The class name of the fragment to instantiate.
      * @param args Bundle of arguments to supply to the fragment, which it
-     * can retrieve with {@link #getArguments()}.  May be null.
+     * can retrieve with {@link Fragment#getArguments()}.  May be null.
      * @return Returns a new fragment instance.
-     * @throws InstantiationException If there is a failure in instantiating
+     * @throws Fragment.InstantiationException If there is a failure in instantiating
      * the given fragment class.  This is a runtime exception; it is not
      * normally expected to happen.
      */
     @LayoutlibDelegate
     /*package*/ static Fragment instantiate(Context context, String fname, Bundle args) {
         try {
-            if (sProjectCallback != null) {
-                Fragment f = (Fragment) sProjectCallback.loadView(fname,
+            if (sLayoutlibCallback != null) {
+                Fragment f = (Fragment) sLayoutlibCallback.loadView(fname,
                         new Class[0], new Object[0]);
 
                 if (args != null) {
diff --git a/tools/layoutlib/bridge/src/android/content/res/BridgeResources.java b/tools/layoutlib/bridge/src/android/content/res/BridgeResources.java
index 96ca250..2c2c672 100644
--- a/tools/layoutlib/bridge/src/android/content/res/BridgeResources.java
+++ b/tools/layoutlib/bridge/src/android/content/res/BridgeResources.java
@@ -16,8 +16,8 @@
 
 package android.content.res;
 
-import com.android.ide.common.rendering.api.IProjectCallback;
 import com.android.ide.common.rendering.api.LayoutLog;
+import com.android.ide.common.rendering.api.LayoutlibCallback;
 import com.android.ide.common.rendering.api.ResourceValue;
 import com.android.layoutlib.bridge.Bridge;
 import com.android.layoutlib.bridge.BridgeConstants;
@@ -49,7 +49,7 @@
 public final class BridgeResources extends Resources {
 
     private BridgeContext mContext;
-    private IProjectCallback mProjectCallback;
+    private LayoutlibCallback mLayoutlibCallback;
     private boolean[] mPlatformResourceFlag = new boolean[1];
     private TypedValue mTmpValue = new TypedValue();
 
@@ -94,12 +94,12 @@
             AssetManager assets,
             DisplayMetrics metrics,
             Configuration config,
-            IProjectCallback projectCallback) {
+            LayoutlibCallback layoutlibCallback) {
         return Resources.mSystem = new BridgeResources(context,
                 assets,
                 metrics,
                 config,
-                projectCallback);
+                layoutlibCallback);
     }
 
     /**
@@ -109,16 +109,16 @@
     public static void disposeSystem() {
         if (Resources.mSystem instanceof BridgeResources) {
             ((BridgeResources)(Resources.mSystem)).mContext = null;
-            ((BridgeResources)(Resources.mSystem)).mProjectCallback = null;
+            ((BridgeResources)(Resources.mSystem)).mLayoutlibCallback = null;
         }
         Resources.mSystem = null;
     }
 
     private BridgeResources(BridgeContext context, AssetManager assets, DisplayMetrics metrics,
-            Configuration config, IProjectCallback projectCallback) {
+            Configuration config, LayoutlibCallback layoutlibCallback) {
         super(assets, metrics, config);
         mContext = context;
-        mProjectCallback = projectCallback;
+        mLayoutlibCallback = layoutlibCallback;
     }
 
     public BridgeTypedArray newTypeArray(int numEntries, boolean platformFile) {
@@ -138,8 +138,8 @@
         }
 
         // didn't find a match in the framework? look in the project.
-        if (mProjectCallback != null) {
-            resourceInfo = mProjectCallback.resolveResourceId(id);
+        if (mLayoutlibCallback != null) {
+            resourceInfo = mLayoutlibCallback.resolveResourceId(id);
 
             if (resourceInfo != null) {
                 platformResFlag_out[0] = false;
@@ -154,11 +154,6 @@
     }
 
     @Override
-    public Drawable getDrawable(int id) throws NotFoundException {
-        return getDrawable(id, null);
-    }
-
-    @Override
     public Drawable getDrawable(int id, Theme theme) {
         Pair<String, ResourceValue> value = getResourceValue(id, mPlatformResourceFlag);
 
@@ -257,7 +252,7 @@
             try {
                 // check if the current parser can provide us with a custom parser.
                 if (mPlatformResourceFlag[0] == false) {
-                    parser = mProjectCallback.getParser(value);
+                    parser = mLayoutlibCallback.getParser(value);
                 }
 
                 // create a new one manually if needed.
@@ -692,8 +687,8 @@
         Pair<ResourceType, String> resourceInfo = Bridge.resolveResourceId(id);
 
         // if the name is unknown in the framework, get it from the custom view loader.
-        if (resourceInfo == null && mProjectCallback != null) {
-            resourceInfo = mProjectCallback.resolveResourceId(id);
+        if (resourceInfo == null && mLayoutlibCallback != null) {
+            resourceInfo = mLayoutlibCallback.resolveResourceId(id);
         }
 
         String message = null;
diff --git a/tools/layoutlib/bridge/src/android/content/res/BridgeTypedArray.java b/tools/layoutlib/bridge/src/android/content/res/BridgeTypedArray.java
index 18036927..7d4271b 100644
--- a/tools/layoutlib/bridge/src/android/content/res/BridgeTypedArray.java
+++ b/tools/layoutlib/bridge/src/android/content/res/BridgeTypedArray.java
@@ -16,6 +16,7 @@
 
 package android.content.res;
 
+import com.android.annotations.Nullable;
 import com.android.ide.common.rendering.api.AttrResourceValue;
 import com.android.ide.common.rendering.api.LayoutLog;
 import com.android.ide.common.rendering.api.RenderResources;
@@ -40,9 +41,12 @@
 import android.view.ViewGroup.LayoutParams;
 
 import java.io.File;
+import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Map;
 
+import static com.android.ide.common.rendering.api.RenderResources.*;
+
 /**
  * Custom implementation of TypedArray to handle non compiled resources.
  */
@@ -56,6 +60,11 @@
     private final String[] mNames;
     private final boolean[] mIsFramework;
 
+    // Contains ids that are @empty. We still store null in mResourceData for that index, since we
+    // want to save on the check against empty, each time a resource value is requested.
+    @Nullable
+    private int[] mEmptyIds;
+
     public BridgeTypedArray(BridgeResources resources, BridgeContext context, int len,
             boolean platformFile) {
         super(resources, null, null, 0);
@@ -90,19 +99,32 @@
         // fills TypedArray.mIndices which is used to implement getIndexCount/getIndexAt
         // first count the array size
         int count = 0;
+        ArrayList<Integer> emptyIds = null;
         for (int i = 0; i < mResourceData.length; i++) {
             ResourceValue data = mResourceData[i];
             if (data != null) {
-                if (RenderResources.REFERENCE_NULL.equals(data.getValue())) {
-                    // No need to store this resource value. This saves needless checking for
-                    // "@null" every time  an attribute is requested.
+                String dataValue = data.getValue();
+                if (REFERENCE_NULL.equals(dataValue) || REFERENCE_UNDEFINED.equals(dataValue)) {
                     mResourceData[i] = null;
+                } else if (REFERENCE_EMPTY.equals(dataValue)) {
+                    mResourceData[i] = null;
+                    if (emptyIds == null) {
+                        emptyIds = new ArrayList<Integer>(4);
+                    }
+                    emptyIds.add(i);
                 } else {
                     count++;
                 }
             }
         }
 
+        if (emptyIds != null) {
+            mEmptyIds = new int[emptyIds.size()];
+            for (int i = 0; i < emptyIds.size(); i++) {
+                mEmptyIds[i] = emptyIds.get(i);
+            }
+        }
+
         // allocate the table with an extra to store the size
         mIndices = new int[count+1];
         mIndices[0] = count;
@@ -624,7 +646,7 @@
                 if (isFrameworkId) {
                     idValue = Bridge.getResourceId(ResourceType.ID, idName);
                 } else {
-                    idValue = mContext.getProjectCallback().getResourceId(ResourceType.ID, idName);
+                    idValue = mContext.getLayoutlibCallback().getResourceId(ResourceType.ID, idName);
                 }
                 return idValue == null ? defValue : idValue;
             }
@@ -644,7 +666,7 @@
             idValue = Bridge.getResourceId(resValue.getResourceType(),
                     resValue.getName());
         } else {
-            idValue = mContext.getProjectCallback().getResourceId(
+            idValue = mContext.getLayoutlibCallback().getResourceId(
                     resValue.getResourceType(), resValue.getName());
         }
 
@@ -748,6 +770,12 @@
         return index >= 0 && index < mResourceData.length && mResourceData[index] != null;
     }
 
+    @Override
+    public boolean hasValueOrEmpty(int index) {
+        return hasValue(index) || index >= 0 && index < mResourceData.length &&
+                mEmptyIds != null && Arrays.binarySearch(mEmptyIds, index) >= 0;
+    }
+
     /**
      * Retrieve the raw TypedValue for the attribute at <var>index</var>
      * and return a temporary object holding its data.  This object is only
diff --git a/tools/layoutlib/bridge/src/android/util/BridgeXmlPullAttributes.java b/tools/layoutlib/bridge/src/android/util/BridgeXmlPullAttributes.java
index 691339e..138b2d5 100644
--- a/tools/layoutlib/bridge/src/android/util/BridgeXmlPullAttributes.java
+++ b/tools/layoutlib/bridge/src/android/util/BridgeXmlPullAttributes.java
@@ -69,8 +69,8 @@
 
         // this is not an attribute in the android namespace, we query the customviewloader, if
         // the namespaces match.
-        if (mContext.getProjectCallback().getNamespace().equals(ns)) {
-            Integer v = mContext.getProjectCallback().getResourceId(ResourceType.ATTR, name);
+        if (mContext.getLayoutlibCallback().getNamespace().equals(ns)) {
+            Integer v = mContext.getLayoutlibCallback().getResourceId(ResourceType.ATTR, name);
             if (v != null) {
                 return v.intValue();
             }
@@ -273,7 +273,7 @@
             if (mPlatformFile || resource.isFramework()) {
                 id = Bridge.getResourceId(resource.getResourceType(), resource.getName());
             } else {
-                id = mContext.getProjectCallback().getResourceId(
+                id = mContext.getLayoutlibCallback().getResourceId(
                         resource.getResourceType(), resource.getName());
             }
 
diff --git a/tools/layoutlib/bridge/src/android/view/BridgeInflater.java b/tools/layoutlib/bridge/src/android/view/BridgeInflater.java
index 80036e5..9eea663 100644
--- a/tools/layoutlib/bridge/src/android/view/BridgeInflater.java
+++ b/tools/layoutlib/bridge/src/android/view/BridgeInflater.java
@@ -16,7 +16,7 @@
 
 package android.view;
 
-import com.android.ide.common.rendering.api.IProjectCallback;
+import com.android.ide.common.rendering.api.LayoutlibCallback;
 import com.android.ide.common.rendering.api.LayoutLog;
 import com.android.ide.common.rendering.api.MergeCookie;
 import com.android.ide.common.rendering.api.ResourceReference;
@@ -46,7 +46,7 @@
  */
 public final class BridgeInflater extends LayoutInflater {
 
-    private final IProjectCallback mProjectCallback;
+    private final LayoutlibCallback mLayoutlibCallback;
     private boolean mIsInMerge = false;
     private ResourceReference mResourceReference;
 
@@ -64,21 +64,21 @@
         super(original, newContext);
         newContext = getBaseContext(newContext);
         if (newContext instanceof BridgeContext) {
-            mProjectCallback = ((BridgeContext) newContext).getProjectCallback();
+            mLayoutlibCallback = ((BridgeContext) newContext).getLayoutlibCallback();
         } else {
-            mProjectCallback = null;
+            mLayoutlibCallback = null;
         }
     }
 
     /**
-     * Instantiate a new BridgeInflater with an {@link IProjectCallback} object.
+     * Instantiate a new BridgeInflater with an {@link LayoutlibCallback} object.
      *
      * @param context The Android application context.
-     * @param projectCallback the {@link IProjectCallback} object.
+     * @param layoutlibCallback the {@link LayoutlibCallback} object.
      */
-    public BridgeInflater(Context context, IProjectCallback projectCallback) {
+    public BridgeInflater(Context context, LayoutlibCallback layoutlibCallback) {
         super(context);
-        mProjectCallback = projectCallback;
+        mLayoutlibCallback = layoutlibCallback;
         mConstructorArgs[0] = context;
     }
 
@@ -167,12 +167,13 @@
 
             ResourceValue value = null;
 
+            @SuppressWarnings("deprecation")
             Pair<ResourceType, String> layoutInfo = Bridge.resolveResourceId(resource);
             if (layoutInfo != null) {
                 value = bridgeContext.getRenderResources().getFrameworkResource(
                         ResourceType.LAYOUT, layoutInfo.getSecond());
             } else {
-                layoutInfo = mProjectCallback.resolveResourceId(resource);
+                layoutInfo = mLayoutlibCallback.resolveResourceId(resource);
 
                 if (layoutInfo != null) {
                     value = bridgeContext.getRenderResources().getProjectResource(
@@ -203,7 +204,7 @@
     }
 
     private View loadCustomView(String name, AttributeSet attrs) throws Exception {
-        if (mProjectCallback != null) {
+        if (mLayoutlibCallback != null) {
             // first get the classname in case it's not the node name
             if (name.equals("view")) {
                 name = attrs.getAttributeValue(null, "class");
@@ -211,7 +212,7 @@
 
             mConstructorArgs[1] = attrs;
 
-            Object customView = mProjectCallback.loadView(name, mConstructorSignature,
+            Object customView = mLayoutlibCallback.loadView(name, mConstructorSignature,
                     mConstructorArgs);
 
             if (customView instanceof View) {
diff --git a/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java b/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java
index 818940d..82012c1 100644
--- a/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java
+++ b/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java
@@ -376,6 +376,10 @@
     }
 
     @Override
+    public void setForcedDisplayScalingMode(int displayId, int mode) {
+    }
+
+    @Override
     public void setInTouchMode(boolean arg0) 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 8e74ce1..1da8bb4 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
@@ -19,8 +19,8 @@
 import com.android.annotations.Nullable;
 import com.android.ide.common.rendering.api.AssetRepository;
 import com.android.ide.common.rendering.api.ILayoutPullParser;
-import com.android.ide.common.rendering.api.IProjectCallback;
 import com.android.ide.common.rendering.api.LayoutLog;
+import com.android.ide.common.rendering.api.LayoutlibCallback;
 import com.android.ide.common.rendering.api.RenderResources;
 import com.android.ide.common.rendering.api.ResourceReference;
 import com.android.ide.common.rendering.api.ResourceValue;
@@ -114,7 +114,7 @@
     private final RenderResources mRenderResources;
     private final Configuration mConfig;
     private final ApplicationInfo mApplicationInfo;
-    private final IProjectCallback mProjectCallback;
+    private final LayoutlibCallback mLayoutlibCallback;
     private final WindowManager mWindowManager;
     private final DisplayManager mDisplayManager;
 
@@ -148,13 +148,13 @@
     public BridgeContext(Object projectKey, DisplayMetrics metrics,
             RenderResources renderResources,
             AssetRepository assets,
-            IProjectCallback projectCallback,
+            LayoutlibCallback layoutlibCallback,
             Configuration config,
             int targetSdkVersion,
             boolean hasRtlSupport) {
         mProjectKey = projectKey;
         mMetrics = metrics;
-        mProjectCallback = projectCallback;
+        mLayoutlibCallback = layoutlibCallback;
 
         mRenderResources = renderResources;
         mConfig = config;
@@ -173,7 +173,7 @@
 
     /**
      * Initializes the {@link Resources} singleton to be linked to this {@link Context}, its
-     * {@link DisplayMetrics}, {@link Configuration}, and {@link IProjectCallback}.
+     * {@link DisplayMetrics}, {@link Configuration}, and {@link LayoutlibCallback}.
      *
      * @see #disposeResources()
      */
@@ -185,7 +185,7 @@
                 assetManager,
                 mMetrics,
                 mConfig,
-                mProjectCallback);
+                mLayoutlibCallback);
         mTheme = mSystemResources.newTheme();
     }
 
@@ -224,8 +224,8 @@
         return mMetrics;
     }
 
-    public IProjectCallback getProjectCallback() {
-        return mProjectCallback;
+    public LayoutlibCallback getLayoutlibCallback() {
+        return mLayoutlibCallback;
     }
 
     public RenderResources getRenderResources() {
@@ -284,7 +284,7 @@
         Pair<ResourceType, String> resourceInfo = Bridge.resolveResourceId(resid);
         boolean isFrameworkRes = true;
         if (resourceInfo == null) {
-            resourceInfo = mProjectCallback.resolveResourceId(resid);
+            resourceInfo = mLayoutlibCallback.resolveResourceId(resid);
             isFrameworkRes = false;
         }
 
@@ -340,8 +340,8 @@
         }
 
         // didn't find a match in the framework? look in the project.
-        if (mProjectCallback != null) {
-            resourceInfo = mProjectCallback.resolveResourceId(id);
+        if (mLayoutlibCallback != null) {
+            resourceInfo = mLayoutlibCallback.resolveResourceId(id);
 
             if (resourceInfo != null) {
                 return new ResourceReference(resourceInfo.getSecond(), false);
@@ -439,9 +439,9 @@
     private ILayoutPullParser getParser(ResourceReference resource) {
         ILayoutPullParser parser;
         if (resource instanceof ResourceValue) {
-            parser = mProjectCallback.getParser((ResourceValue) resource);
+            parser = mLayoutlibCallback.getParser((ResourceValue) resource);
         } else {
-            parser = mProjectCallback.getParser(resource.getName());
+            parser = mLayoutlibCallback.getParser(resource.getName());
         }
         return parser;
     }
@@ -694,7 +694,7 @@
             boolean isFrameworkRes = true;
             Pair<ResourceType, String> value = Bridge.resolveResourceId(defStyleRes);
             if (value == null) {
-                value = mProjectCallback.resolveResourceId(defStyleRes);
+                value = mLayoutlibCallback.resolveResourceId(defStyleRes);
                 isFrameworkRes = false;
             }
 
@@ -732,7 +732,7 @@
             }
         }
 
-        String appNamespace = mProjectCallback.getNamespace();
+        String appNamespace = mLayoutlibCallback.getNamespace();
 
         if (attributeList != null) {
             for (int index = 0 ; index < attributeList.size() ; index++) {
@@ -875,7 +875,7 @@
             if (resolvedResource != null) {
                 isFramework = true;
             } else {
-                resolvedResource = mProjectCallback.resolveResourceId(attr);
+                resolvedResource = mLayoutlibCallback.resolveResourceId(attr);
             }
 
             if (resolvedResource != null) {
@@ -901,7 +901,7 @@
             return Pair.of(info.getSecond(), Boolean.TRUE);
         }
 
-        info = mProjectCallback.resolveResourceId(attr);
+        info = mLayoutlibCallback.resolveResourceId(attr);
         if (info != null) {
             return Pair.of(info.getSecond(), Boolean.FALSE);
         }
@@ -953,8 +953,8 @@
         // getResourceId creates a new resource id if an existing resource id isn't found. So, we
         // check for the existence of the resource before calling it.
         if (getRenderResources().getProjectResource(resType, resName) != null) {
-            if (mProjectCallback != null) {
-                Integer value = mProjectCallback.getResourceId(resType, resName);
+            if (mLayoutlibCallback != null) {
+                Integer value = mLayoutlibCallback.getResourceId(resType, resName);
                 if (value != null) {
                     return value;
                 }
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/support/RecyclerViewUtil.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/support/RecyclerViewUtil.java
index b72329a..9273ac2 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/support/RecyclerViewUtil.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/support/RecyclerViewUtil.java
@@ -18,8 +18,8 @@
 
 import com.android.annotations.NonNull;
 import com.android.annotations.Nullable;
-import com.android.ide.common.rendering.api.IProjectCallback;
 import com.android.ide.common.rendering.api.LayoutLog;
+import com.android.ide.common.rendering.api.LayoutlibCallback;
 import com.android.ide.common.rendering.api.SessionParams;
 import com.android.layoutlib.bridge.Bridge;
 import com.android.layoutlib.bridge.android.BridgeContext;
@@ -30,7 +30,6 @@
 import android.widget.LinearLayout;
 
 import java.lang.reflect.Method;
-import java.util.HashMap;
 
 import static com.android.layoutlib.bridge.util.ReflectionUtils.*;
 
@@ -61,7 +60,7 @@
     public static void setAdapter(@NonNull View recyclerView, @NonNull BridgeContext context,
             @NonNull SessionParams params) {
         try {
-            setLayoutManager(recyclerView, context, params.getProjectCallback());
+            setLayoutManager(recyclerView, context, params.getLayoutlibCallback());
             Object adapter = createAdapter(params);
             setProperty(recyclerView, CN_ADAPTER, adapter, "setAdapter");
         } catch (ReflectionException e) {
@@ -71,7 +70,7 @@
     }
 
     private static void setLayoutManager(@NonNull View recyclerView, @NonNull BridgeContext context,
-            @NonNull IProjectCallback callback) throws ReflectionException {
+            @NonNull LayoutlibCallback callback) throws ReflectionException {
         Object cookie = context.getCookie(recyclerView);
         assert cookie == null || cookie instanceof LayoutManagerType || cookie instanceof String;
         if (!(cookie instanceof LayoutManagerType)) {
@@ -90,7 +89,7 @@
 
     @Nullable
     private static Object createLayoutManager(@Nullable LayoutManagerType type,
-            @NonNull Context context, @NonNull IProjectCallback callback)
+            @NonNull Context context, @NonNull LayoutlibCallback callback)
             throws ReflectionException {
         if (type == null) {
             type = LayoutManagerType.getDefault();
@@ -109,7 +108,7 @@
             return null;
         }
         try {
-            return params.getProjectCallback().loadView(CN_ADAPTER, new Class[0], new Object[0]);
+            return params.getLayoutlibCallback().loadView(CN_ADAPTER, new Class[0], new Object[0]);
         } catch (Exception e) {
             throw new ReflectionException(e);
         }
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
index ee57067..dd1f661 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/AppCompatActionBar.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/AppCompatActionBar.java
@@ -70,7 +70,7 @@
         try {
             Class[] constructorParams = {View.class};
             Object[] constructorArgs = {getDecorContent()};
-            mWindowDecorActionBar = params.getProjectCallback().loadView(WINDOW_ACTION_BAR_CLASS,
+            mWindowDecorActionBar = params.getLayoutlibCallback().loadView(WINDOW_ACTION_BAR_CLASS,
                     constructorParams, constructorArgs);
 
             mWindowActionBarClass = mWindowDecorActionBar == null ? 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
index 2a83ea1..3d1a9b9 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/BridgeActionBar.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/BridgeActionBar.java
@@ -51,7 +51,7 @@
             @NonNull ViewGroup parentView) {
         mBridgeContext = context;
         mParams = params;
-        mCallback = params.getProjectCallback().getActionBarCallback();
+        mCallback = params.getLayoutlibCallback().getActionBarCallback();
         ResourceValue layoutName = getLayoutResource(context);
         if (layoutName == null) {
             throw new RuntimeException("Unable to find the layout for Action Bar.");
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderAction.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderAction.java
index 127cb72..c708316 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderAction.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderAction.java
@@ -16,10 +16,6 @@
 
 package com.android.layoutlib.bridge.impl;
 
-import static com.android.ide.common.rendering.api.Result.Status.ERROR_LOCK_INTERRUPTED;
-import static com.android.ide.common.rendering.api.Result.Status.ERROR_TIMEOUT;
-import static com.android.ide.common.rendering.api.Result.Status.SUCCESS;
-
 import com.android.ide.common.rendering.api.HardwareConfig;
 import com.android.ide.common.rendering.api.LayoutLog;
 import com.android.ide.common.rendering.api.RenderParams;
@@ -43,6 +39,10 @@
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.locks.ReentrantLock;
 
+import static com.android.ide.common.rendering.api.Result.Status.ERROR_LOCK_INTERRUPTED;
+import static com.android.ide.common.rendering.api.Result.Status.ERROR_TIMEOUT;
+import static com.android.ide.common.rendering.api.Result.Status.SUCCESS;
+
 /**
  * Base class for rendering action.
  *
@@ -120,7 +120,7 @@
 
         // build the context
         mContext = new BridgeContext(mParams.getProjectKey(), metrics, resources,
-                mParams.getAssets(), mParams.getProjectCallback(), getConfiguration(),
+                mParams.getAssets(), mParams.getLayoutlibCallback(), getConfiguration(),
                 mParams.getTargetSdkVersion(), mParams.isRtlSupported());
 
         setUp();
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 7c11284d..d9572591 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
@@ -16,20 +16,13 @@
 
 package com.android.layoutlib.bridge.impl;
 
-import static com.android.ide.common.rendering.api.Result.Status.ERROR_ANIM_NOT_FOUND;
-import static com.android.ide.common.rendering.api.Result.Status.ERROR_INFLATION;
-import static com.android.ide.common.rendering.api.Result.Status.ERROR_NOT_INFLATED;
-import static com.android.ide.common.rendering.api.Result.Status.ERROR_UNKNOWN;
-import static com.android.ide.common.rendering.api.Result.Status.ERROR_VIEWGROUP_NO_CHILDREN;
-import static com.android.ide.common.rendering.api.Result.Status.SUCCESS;
-
 import com.android.annotations.NonNull;
 import com.android.annotations.Nullable;
 import com.android.ide.common.rendering.api.AdapterBinding;
 import com.android.ide.common.rendering.api.HardwareConfig;
 import com.android.ide.common.rendering.api.IAnimationListener;
 import com.android.ide.common.rendering.api.ILayoutPullParser;
-import com.android.ide.common.rendering.api.IProjectCallback;
+import com.android.ide.common.rendering.api.LayoutlibCallback;
 import com.android.ide.common.rendering.api.RenderResources;
 import com.android.ide.common.rendering.api.RenderSession;
 import com.android.ide.common.rendering.api.ResourceReference;
@@ -54,13 +47,13 @@
 import com.android.layoutlib.bridge.android.BridgeXmlBlockParser;
 import com.android.layoutlib.bridge.android.RenderParamsFlags;
 import com.android.layoutlib.bridge.android.support.RecyclerViewUtil;
-import com.android.layoutlib.bridge.bars.BridgeActionBar;
 import com.android.layoutlib.bridge.bars.AppCompatActionBar;
+import com.android.layoutlib.bridge.bars.BridgeActionBar;
 import com.android.layoutlib.bridge.bars.Config;
+import com.android.layoutlib.bridge.bars.FrameworkActionBar;
 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.FrameworkActionBar;
 import com.android.layoutlib.bridge.impl.binding.FakeAdapter;
 import com.android.layoutlib.bridge.impl.binding.FakeExpandableAdapter;
 import com.android.resources.Density;
@@ -116,6 +109,13 @@
 import java.util.List;
 import java.util.Map;
 
+import static com.android.ide.common.rendering.api.Result.Status.ERROR_ANIM_NOT_FOUND;
+import static com.android.ide.common.rendering.api.Result.Status.ERROR_INFLATION;
+import static com.android.ide.common.rendering.api.Result.Status.ERROR_NOT_INFLATED;
+import static com.android.ide.common.rendering.api.Result.Status.ERROR_UNKNOWN;
+import static com.android.ide.common.rendering.api.Result.Status.ERROR_VIEWGROUP_NO_CHILDREN;
+import static com.android.ide.common.rendering.api.Result.Status.SUCCESS;
+
 /**
  * Class implementing the render session.
  * <p/>
@@ -219,7 +219,7 @@
         WindowManagerGlobal_Delegate.setWindowManagerService(iwm);
 
         // build the inflater and parser.
-        mInflater = new BridgeInflater(context, params.getProjectCallback());
+        mInflater = new BridgeInflater(context, params.getLayoutlibCallback());
         context.setBridgeInflater(mInflater);
 
         mBlockParser = new BridgeXmlBlockParser(
@@ -401,7 +401,7 @@
 
             // Sets the project callback (custom view loader) to the fragment delegate so that
             // it can instantiate the custom Fragment.
-            Fragment_Delegate.setProjectCallback(params.getProjectCallback());
+            Fragment_Delegate.setLayoutlibCallback(params.getLayoutlibCallback());
 
             String rootTag = params.getFlag(RenderParamsFlags.FLAG_KEY_ROOT_TAG);
             boolean isPreference = "PreferenceScreen".equals(rootTag);
@@ -416,13 +416,13 @@
             // done with the parser, pop it.
             context.popParser();
 
-            Fragment_Delegate.setProjectCallback(null);
+            Fragment_Delegate.setLayoutlibCallback(null);
 
             // set the AttachInfo on the root view.
             AttachInfo_Accessor.setAttachInfo(mViewRoot);
 
             // post-inflate process. For now this supports TabHost/TabWidget
-            postInflateProcess(view, params.getProjectCallback(), isPreference ? view : null);
+            postInflateProcess(view, params.getLayoutlibCallback(), isPreference ? view : null);
 
             // get the background drawable
             if (mWindowBackground != null) {
@@ -686,7 +686,7 @@
             animationResource = context.getRenderResources().getProjectResource(
                     ResourceType.ANIMATOR, animationName);
             if (animationResource != null) {
-                animationId = context.getProjectCallback().getResourceId(
+                animationId = context.getLayoutlibCallback().getResourceId(
                         ResourceType.ANIMATOR, animationName);
             }
         }
@@ -1257,17 +1257,17 @@
      * {@link TabWidget}, and the corresponding {@link FrameLayout} and make new tabs automatically
      * based on the content of the {@link FrameLayout}.
      * @param view the root view to process.
-     * @param projectCallback callback to the project.
+     * @param layoutlibCallback callback to the project.
      * @param skip the view and it's children are not processed.
      */
     @SuppressWarnings("deprecation")  // For the use of Pair
-    private void postInflateProcess(View view, IProjectCallback projectCallback, View skip)
+    private void postInflateProcess(View view, LayoutlibCallback layoutlibCallback, View skip)
             throws PostInflateException {
         if (view == skip) {
             return;
         }
         if (view instanceof TabHost) {
-            setupTabHost((TabHost) view, projectCallback);
+            setupTabHost((TabHost) view, layoutlibCallback);
         } else if (view instanceof QuickContactBadge) {
             QuickContactBadge badge = (QuickContactBadge) view;
             badge.setImageToDefault();
@@ -1286,8 +1286,8 @@
 
                 // if there was no adapter binding, trying to get it from the call back.
                 if (binding == null) {
-                    binding = params.getProjectCallback().getAdapterBinding(listRef,
-                            context.getViewKey(view), view);
+                    binding = layoutlibCallback.getAdapterBinding(
+                            listRef, context.getViewKey(view), view);
                 }
 
                 if (binding != null) {
@@ -1303,7 +1303,7 @@
                             for (int i = 0; i < count; i++) {
                                 Pair<View, Boolean> pair = context.inflateView(
                                         binding.getHeaderAt(i),
-                                        list, false /*attachToRoot*/, skipCallbackParser);
+                                        list, false, skipCallbackParser);
                                 if (pair.getFirst() != null) {
                                     list.addHeaderView(pair.getFirst());
                                 }
@@ -1315,7 +1315,7 @@
                             for (int i = 0; i < count; i++) {
                                 Pair<View, Boolean> pair = context.inflateView(
                                         binding.getFooterAt(i),
-                                        list, false /*attachToRoot*/, skipCallbackParser);
+                                        list, false, skipCallbackParser);
                                 if (pair.getFirst() != null) {
                                     list.addFooterView(pair.getFirst());
                                 }
@@ -1326,17 +1326,14 @@
 
                         if (view instanceof ExpandableListView) {
                             ((ExpandableListView) view).setAdapter(
-                                    new FakeExpandableAdapter(
-                                            listRef, binding, params.getProjectCallback()));
+                                    new FakeExpandableAdapter(listRef, binding, layoutlibCallback));
                         } else {
                             ((AbsListView) view).setAdapter(
-                                    new FakeAdapter(
-                                            listRef, binding, params.getProjectCallback()));
+                                    new FakeAdapter(listRef, binding, layoutlibCallback));
                         }
                     } else if (view instanceof AbsSpinner) {
                         ((AbsSpinner) view).setAdapter(
-                                new FakeAdapter(
-                                        listRef, binding, params.getProjectCallback()));
+                                new FakeAdapter(listRef, binding, layoutlibCallback));
                     }
                 }
             }
@@ -1347,7 +1344,7 @@
             final int count = group.getChildCount();
             for (int c = 0; c < count; c++) {
                 View child = group.getChildAt(c);
-                postInflateProcess(child, projectCallback, skip);
+                postInflateProcess(child, layoutlibCallback, skip);
             }
         }
     }
@@ -1371,10 +1368,10 @@
     /**
      * Sets up a {@link TabHost} object.
      * @param tabHost the TabHost to setup.
-     * @param projectCallback The project callback object to access the project R class.
+     * @param layoutlibCallback The project callback object to access the project R class.
      * @throws PostInflateException
      */
-    private void setupTabHost(TabHost tabHost, IProjectCallback projectCallback)
+    private void setupTabHost(TabHost tabHost, LayoutlibCallback layoutlibCallback)
             throws PostInflateException {
         // look for the TabWidget, and the FrameLayout. They have their own specific names
         View v = tabHost.findViewById(android.R.id.tabs);
@@ -1417,8 +1414,9 @@
 
         if (count == 0) {
             // Create a dummy child to get a single tab
-            TabSpec spec = tabHost.newTabSpec("tag").setIndicator("Tab Label",
-                    tabHost.getResources().getDrawable(android.R.drawable.ic_menu_info_details))
+            TabSpec spec = tabHost.newTabSpec("tag")
+                    .setIndicator("Tab Label", tabHost.getResources()
+                            .getDrawable(android.R.drawable.ic_menu_info_details, null))
                     .setContent(new TabHost.TabContentFactory() {
                         @Override
                         public View createTabContent(String tag) {
@@ -1434,7 +1432,7 @@
                 @SuppressWarnings("ConstantConditions")  // child cannot be null.
                 int id = child.getId();
                 @SuppressWarnings("deprecation")
-                Pair<ResourceType, String> resource = projectCallback.resolveResourceId(id);
+                Pair<ResourceType, String> resource = layoutlibCallback.resolveResourceId(id);
                 String name;
                 if (resource != null) {
                     name = resource.getSecond();
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/binding/AdapterHelper.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/binding/AdapterHelper.java
index 6c998af..9aab340 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/binding/AdapterHelper.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/binding/AdapterHelper.java
@@ -17,7 +17,7 @@
 package com.android.layoutlib.bridge.impl.binding;
 
 import com.android.ide.common.rendering.api.DataBindingItem;
-import com.android.ide.common.rendering.api.IProjectCallback;
+import com.android.ide.common.rendering.api.LayoutlibCallback;
 import com.android.ide.common.rendering.api.LayoutLog;
 import com.android.ide.common.rendering.api.ResourceReference;
 import com.android.ide.common.rendering.api.IProjectCallback.ViewAttribute;
@@ -40,7 +40,7 @@
 public class AdapterHelper {
 
     static Pair<View, Boolean> getView(AdapterItem item, AdapterItem parentItem, ViewGroup parent,
-            IProjectCallback callback, ResourceReference adapterRef, boolean skipCallbackParser) {
+            LayoutlibCallback callback, ResourceReference adapterRef, boolean skipCallbackParser) {
         // we don't care about recycling here because we never scroll.
         DataBindingItem dataBindingItem = item.getDataBindingItem();
 
@@ -65,7 +65,7 @@
     }
 
     private static void fillView(BridgeContext context, View view, AdapterItem item,
-            AdapterItem parentItem, IProjectCallback callback, ResourceReference adapterRef) {
+            AdapterItem parentItem, LayoutlibCallback callback, ResourceReference adapterRef) {
         if (view instanceof ViewGroup) {
             ViewGroup group = (ViewGroup) view;
             final int count = group.getChildCount();
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/binding/FakeAdapter.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/binding/FakeAdapter.java
index 9a13f5a..142eac1 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/binding/FakeAdapter.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/binding/FakeAdapter.java
@@ -18,7 +18,7 @@
 
 import com.android.ide.common.rendering.api.AdapterBinding;
 import com.android.ide.common.rendering.api.DataBindingItem;
-import com.android.ide.common.rendering.api.IProjectCallback;
+import com.android.ide.common.rendering.api.LayoutlibCallback;
 import com.android.ide.common.rendering.api.ResourceReference;
 import com.android.util.Pair;
 
@@ -37,18 +37,17 @@
  * and {@link SpinnerAdapter}.
  *
  */
-@SuppressWarnings("deprecation")
 public class FakeAdapter extends BaseAdapter {
 
     // don't use a set because the order is important.
     private final List<ResourceReference> mTypes = new ArrayList<ResourceReference>();
-    private final IProjectCallback mCallback;
+    private final LayoutlibCallback mCallback;
     private final ResourceReference mAdapterRef;
     private final List<AdapterItem> mItems = new ArrayList<AdapterItem>();
     private boolean mSkipCallbackParser = false;
 
     public FakeAdapter(ResourceReference adapterRef, AdapterBinding binding,
-            IProjectCallback callback) {
+            LayoutlibCallback callback) {
         mAdapterRef = adapterRef;
         mCallback = callback;
 
@@ -111,11 +110,11 @@
     public View getView(int position, View convertView, ViewGroup parent) {
         // we don't care about recycling here because we never scroll.
         AdapterItem item = mItems.get(position);
-        Pair<View, Boolean> pair = AdapterHelper.getView(item, null /*parentGroup*/, parent,
-                mCallback, mAdapterRef, mSkipCallbackParser);
+        @SuppressWarnings("deprecation")
+        Pair<View, Boolean> pair = AdapterHelper.getView(item, null, parent, mCallback,
+                mAdapterRef, mSkipCallbackParser);
         mSkipCallbackParser = pair.getSecond();
         return pair.getFirst();
-
     }
 
     @Override
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/binding/FakeExpandableAdapter.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/binding/FakeExpandableAdapter.java
index e539579..344b17e 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/binding/FakeExpandableAdapter.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/binding/FakeExpandableAdapter.java
@@ -18,7 +18,7 @@
 
 import com.android.ide.common.rendering.api.AdapterBinding;
 import com.android.ide.common.rendering.api.DataBindingItem;
-import com.android.ide.common.rendering.api.IProjectCallback;
+import com.android.ide.common.rendering.api.LayoutlibCallback;
 import com.android.ide.common.rendering.api.ResourceReference;
 import com.android.util.Pair;
 
@@ -34,7 +34,7 @@
 @SuppressWarnings("deprecation")
 public class FakeExpandableAdapter implements ExpandableListAdapter, HeterogeneousExpandableList {
 
-    private final IProjectCallback mCallback;
+    private final LayoutlibCallback mCallback;
     private final ResourceReference mAdapterRef;
     private boolean mSkipCallbackParser = false;
 
@@ -45,7 +45,7 @@
     private final List<ResourceReference> mChildrenTypes = new ArrayList<ResourceReference>();
 
     public FakeExpandableAdapter(ResourceReference adapterRef, AdapterBinding binding,
-            IProjectCallback callback) {
+            LayoutlibCallback callback) {
         mAdapterRef = adapterRef;
         mCallback = callback;
 
diff --git a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/golden/activity.png b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/golden/activity.png
index e38f437..9bf302a 100644
--- a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/golden/activity.png
+++ b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/golden/activity.png
Binary files differ
diff --git a/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/intensive/setup/LayoutLibTestCallback.java b/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/intensive/setup/LayoutLibTestCallback.java
index 565e881..0a5e798 100644
--- a/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/intensive/setup/LayoutLibTestCallback.java
+++ b/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/intensive/setup/LayoutLibTestCallback.java
@@ -20,7 +20,7 @@
 import com.android.ide.common.rendering.api.ActionBarCallback;
 import com.android.ide.common.rendering.api.AdapterBinding;
 import com.android.ide.common.rendering.api.ILayoutPullParser;
-import com.android.ide.common.rendering.api.IProjectCallback;
+import com.android.ide.common.rendering.api.LayoutlibCallback;
 import com.android.ide.common.rendering.api.ResourceReference;
 import com.android.ide.common.rendering.api.ResourceValue;
 import com.android.resources.ResourceType;
@@ -28,10 +28,7 @@
 import com.android.util.Pair;
 import com.android.utils.ILogger;
 
-import java.io.ByteArrayOutputStream;
 import java.io.File;
-import java.io.IOException;
-import java.io.InputStream;
 import java.lang.reflect.Constructor;
 import java.lang.reflect.Field;
 import java.lang.reflect.Modifier;
@@ -40,7 +37,7 @@
 import com.google.android.collect.Maps;
 
 @SuppressWarnings("deprecation") // For Pair
-public class LayoutLibTestCallback extends ClassLoader implements IProjectCallback {
+public class LayoutLibTestCallback extends LayoutlibCallback {
 
     private static final String PROJECT_CLASSES_LOCATION = "/testApp/MyApplication/build/intermediates/classes/debug/";
     private static final String PACKAGE_NAME = "com.android.layoutlib.test.myapplication";
@@ -48,16 +45,16 @@
     private final Map<Integer, Pair<ResourceType, String>> mProjectResources = Maps.newHashMap();
     private final Map<IntArrayWrapper, String> mStyleableValueToNameMap = Maps.newHashMap();
     private final Map<ResourceType, Map<String, Integer>> mResources = Maps.newHashMap();
-    private final Map<String, Class<?>> mClasses = Maps.newHashMap();
     private final ILogger mLog;
     private final ActionBarCallback mActionBarCallback = new ActionBarCallback();
+    private final ClassLoader mModuleClassLoader = new ModuleClassLoader(PROJECT_CLASSES_LOCATION);
 
     public LayoutLibTestCallback(ILogger logger) {
         mLog = logger;
     }
 
     public void initResources() throws ClassNotFoundException {
-        Class<?> rClass = loadClass(PACKAGE_NAME + ".R");
+        Class<?> rClass = mModuleClassLoader.loadClass(PACKAGE_NAME + ".R");
         Class<?>[] nestedClasses = rClass.getDeclaredClasses();
         for (Class<?> resClass : nestedClasses) {
             final ResourceType resType = ResourceType.getEnum(resClass.getSimpleName());
@@ -91,40 +88,11 @@
         }
     }
 
-    @Override
-    protected Class<?> findClass(String name) throws ClassNotFoundException {
-        Class<?> aClass = mClasses.get(name);
-        if (aClass != null) {
-            return aClass;
-        }
-        String pathName = PROJECT_CLASSES_LOCATION.concat(name.replace('.', '/')).concat(".class");
-        InputStream classInputStream = getClass().getResourceAsStream(pathName);
-        if (classInputStream == null) {
-            throw new ClassNotFoundException("Unable to find class " + name + " at " + pathName);
-        }
-        byte[] data;
-        try {
-            ByteArrayOutputStream buffer = new ByteArrayOutputStream();
-            int nRead;
-            data = new byte[16384];
-            while ((nRead = classInputStream.read(data, 0, data.length)) != -1) {
-                buffer.write(data, 0, nRead);
-            }
-            buffer.flush();
-            data = buffer.toByteArray();
-        } catch (IOException e) {
-            // Wrap the exception with ClassNotFoundException so that caller can deal with it.
-            throw new ClassNotFoundException("Unable to load class " + name, e);
-        }
-        aClass = defineClass(name, data, 0, data.length);
-        mClasses.put(name, aClass);
-        return aClass;
-    }
 
     @Override
     public Object loadView(String name, Class[] constructorSignature, Object[] constructorArgs)
             throws Exception {
-        Class<?> viewClass = findClass(name);
+        Class<?> viewClass = mModuleClassLoader.loadClass(name);
         Constructor<?> viewConstructor = viewClass.getConstructor(constructorSignature);
         viewConstructor.setAccessible(true);
         return viewConstructor.newInstance(constructorArgs);
@@ -180,4 +148,9 @@
     public ActionBarCallback getActionBarCallback() {
         return mActionBarCallback;
     }
+
+    @Override
+    public boolean supports(int ideFeature) {
+        return false;
+    }
 }
diff --git a/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/intensive/setup/ModuleClassLoader.java b/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/intensive/setup/ModuleClassLoader.java
new file mode 100644
index 0000000..110f4c8
--- /dev/null
+++ b/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/intensive/setup/ModuleClassLoader.java
@@ -0,0 +1,67 @@
+/*
+ * 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.intensive.setup;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Map;
+
+import com.google.android.collect.Maps;
+
+/**
+ * The ClassLoader to load the project's classes.
+ */
+public class ModuleClassLoader extends ClassLoader {
+
+    private final Map<String, Class<?>> mClasses = Maps.newHashMap();
+    private final String mClassLocation;
+
+    public ModuleClassLoader(String classLocation) {
+        mClassLocation = classLocation;
+    }
+
+    @Override
+    protected Class<?> findClass(String name) throws ClassNotFoundException {
+        Class<?> aClass = mClasses.get(name);
+        if (aClass != null) {
+            return aClass;
+        }
+        String pathName = mClassLocation.concat(name.replace('.', '/')).concat(".class");
+        InputStream classInputStream = getClass().getResourceAsStream(pathName);
+        if (classInputStream == null) {
+            throw new ClassNotFoundException("Unable to find class " + name + " at " + pathName);
+        }
+        byte[] data;
+        try {
+            ByteArrayOutputStream buffer = new ByteArrayOutputStream();
+            int nRead;
+            data = new byte[16384];  // 16k
+            while ((nRead = classInputStream.read(data, 0, data.length)) != -1) {
+                buffer.write(data, 0, nRead);
+            }
+            buffer.flush();
+            data = buffer.toByteArray();
+        } catch (IOException e) {
+            // Wrap the exception with ClassNotFoundException so that caller can deal with it.
+            throw new ClassNotFoundException("Unable to load class " + name, e);
+        }
+        aClass = defineClass(name, data, 0, data.length);
+        mClasses.put(name, aClass);
+        return aClass;
+    }
+}
diff --git a/wifi/java/android/net/wifi/ScanResult.java b/wifi/java/android/net/wifi/ScanResult.java
index e8a51e3..5dc70bd 100644
--- a/wifi/java/android/net/wifi/ScanResult.java
+++ b/wifi/java/android/net/wifi/ScanResult.java
@@ -261,6 +261,12 @@
         return freq > 4900 && freq < 5900;
     }
 
+    /**
+     *  @hide
+     * storing the raw bytes of full result IEs
+     **/
+    public byte[] bytes;
+
     /** information element from beacon
      * @hide
      */
diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java
index 4e4f9b2..18f90d8 100644
--- a/wifi/java/android/net/wifi/WifiManager.java
+++ b/wifi/java/android/net/wifi/WifiManager.java
@@ -20,10 +20,16 @@
 import android.annotation.SdkConstant.SdkConstantType;
 import android.annotation.SystemApi;
 import android.content.Context;
+import android.net.ConnectivityManager;
+import android.net.ConnectivityManager.NetworkCallback;
 import android.net.DhcpInfo;
+import android.net.Network;
+import android.net.NetworkCapabilities;
+import android.net.NetworkRequest;
 import android.net.wifi.ScanSettings;
 import android.net.wifi.WifiChannel;
 import android.os.Binder;
+import android.os.Build;
 import android.os.IBinder;
 import android.os.Handler;
 import android.os.HandlerThread;
@@ -38,6 +44,7 @@
 import java.net.InetAddress;
 import java.util.concurrent.CountDownLatch;
 
+import com.android.internal.annotations.GuardedBy;
 import com.android.internal.util.AsyncChannel;
 import com.android.internal.util.Protocol;
 
@@ -568,6 +575,7 @@
 
     private Context mContext;
     IWifiManager mService;
+    private final int mTargetSdkVersion;
 
     private static final int INVALID_KEY = 0;
     private static int sListenerKey = 1;
@@ -576,11 +584,17 @@
 
     private static AsyncChannel sAsyncChannel;
     private static CountDownLatch sConnected;
+    private static ConnectivityManager sCM;
 
     private static final Object sThreadRefLock = new Object();
     private static int sThreadRefCount;
     private static HandlerThread sHandlerThread;
 
+    @GuardedBy("sCM")
+    // TODO: Introduce refcounting and make this a per-process static callback, instead of a
+    // per-WifiManager callback.
+    private PinningNetworkCallback mNetworkCallback;
+
     /**
      * Create a new WifiManager instance.
      * Applications will almost always want to use
@@ -594,6 +608,7 @@
     public WifiManager(Context context, IWifiManager service) {
         mContext = context;
         mService = service;
+        mTargetSdkVersion = context.getApplicationInfo().targetSdkVersion;
         init();
     }
 
@@ -740,6 +755,20 @@
      * networks are disabled, and an attempt to connect to the selected
      * network is initiated. This may result in the asynchronous delivery
      * of state change events.
+     * <p>
+     * <b>Note:</b> If an application's target SDK version is
+     * {@link android.os.Build.VERSION_CODES#MNC} or newer, network
+     * communication may not use Wi-Fi even if Wi-Fi is connected; traffic may
+     * instead be sent through another network, such as cellular data,
+     * Bluetooth tethering, or Ethernet. For example, traffic will never use a
+     * Wi-Fi network that does not provide Internet access (e.g. a wireless
+     * printer), if another network that does offer Internet access (e.g.
+     * cellular data) is available. Applications that need to ensure that their
+     * network traffic uses Wi-Fi should use APIs such as
+     * {@link Network#bindSocket(java.net.Socket)},
+     * {@link Network#openConnection(java.net.URL)}, or
+     * {@link ConnectivityManager#bindProcessToNetwork} to do so.
+     *
      * @param netId the ID of the network in the list of configured networks
      * @param disableOthers if true, disable all other networks. The way to
      * select a particular network to connect to is specify {@code true}
@@ -747,11 +776,23 @@
      * @return {@code true} if the operation succeeded
      */
     public boolean enableNetwork(int netId, boolean disableOthers) {
-        try {
-            return mService.enableNetwork(netId, disableOthers);
-        } catch (RemoteException e) {
-            return false;
+        final boolean pin = disableOthers && mTargetSdkVersion < Build.VERSION_CODES.MNC;
+        if (pin) {
+            registerPinningNetworkCallback();
         }
+
+        boolean success;
+        try {
+            success = mService.enableNetwork(netId, disableOthers);
+        } catch (RemoteException e) {
+            success = false;
+        }
+
+        if (pin && !success) {
+            unregisterPinningNetworkCallback();
+        }
+
+        return success;
     }
 
     /**
@@ -1951,6 +1992,92 @@
                 "No permission to access and change wifi or a bad initialization");
     }
 
+    private void initConnectivityManager() {
+        // TODO: what happens if an app calls a WifiManager API before ConnectivityManager is
+        // registered? Can we fix this by starting ConnectivityService before WifiService?
+        if (sCM == null) {
+            sCM = (ConnectivityManager) mContext.getSystemService(Context.CONNECTIVITY_SERVICE);
+            if (sCM == null) {
+                throw new IllegalStateException("Bad luck, ConnectivityService not started.");
+            }
+        }
+    }
+
+    /**
+     * A NetworkCallback that pins the process to the first wifi network to connect.
+     *
+     * We use this to maintain compatibility with pre-M apps that call WifiManager.enableNetwork()
+     * to connect to a Wi-Fi network that has no Internet access, and then assume that they will be
+     * able to use that network because it's the system default.
+     *
+     * In order to maintain compatibility with apps that call setProcessDefaultNetwork themselves,
+     * we try not to set the default network unless they have already done so, and we try not to
+     * clear the default network unless we set it ourselves.
+     *
+     * This should maintain behaviour that's compatible with L, which would pin the whole system to
+     * any wifi network that was created via enableNetwork(..., true) until that network
+     * disconnected.
+     *
+     * Note that while this hack allows network traffic to flow, it is quite limited. For example:
+     *
+     * 1. setProcessDefaultNetwork only affects this process, so:
+     *    - Any subprocesses spawned by this process will not be pinned to Wi-Fi.
+     *    - If this app relies on any other apps on the device also being on Wi-Fi, that won't work
+     *      either, because other apps on the device will not be pinned.
+     * 2. The behaviour of other APIs is not modified. For example:
+     *    - getActiveNetworkInfo will return the system default network, not Wi-Fi.
+     *    - There will be no CONNECTIVITY_ACTION broadcasts about TYPE_WIFI.
+     *    - getProcessDefaultNetwork will not return null, so if any apps are relying on that, they
+     *      will be surprised as well.
+     */
+    private class PinningNetworkCallback extends NetworkCallback {
+        private Network mPinnedNetwork;
+
+        @Override
+        public void onPreCheck(Network network) {
+            if (sCM.getProcessDefaultNetwork() == null && mPinnedNetwork == null) {
+                sCM.setProcessDefaultNetwork(network);
+                mPinnedNetwork = network;
+                Log.d(TAG, "Wifi alternate reality enabled on network " + network);
+            }
+        }
+
+        @Override
+        public void onLost(Network network) {
+            if (network.equals(mPinnedNetwork) && network.equals(sCM.getProcessDefaultNetwork())) {
+                sCM.setProcessDefaultNetwork(null);
+                Log.d(TAG, "Wifi alternate reality disabled on network " + network);
+                mPinnedNetwork = null;
+                unregisterPinningNetworkCallback();
+            }
+        }
+    }
+
+    private void registerPinningNetworkCallback() {
+        initConnectivityManager();
+        synchronized (sCM) {
+            if (mNetworkCallback == null) {
+                // TODO: clear all capabilities.
+                NetworkRequest request = new NetworkRequest.Builder()
+                        .addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
+                        .removeCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
+                        .build();
+                mNetworkCallback = new PinningNetworkCallback();
+                sCM.registerNetworkCallback(request, mNetworkCallback);
+            }
+        }
+    }
+
+    private void unregisterPinningNetworkCallback() {
+        initConnectivityManager();
+        synchronized (sCM) {
+            if (mNetworkCallback != null) {
+                sCM.unregisterNetworkCallback(mNetworkCallback);
+                mNetworkCallback = null;
+            }
+        }
+    }
+
     /**
      * Connect to a network with the given configuration. The network also
      * gets added to the supplicant configuration.