Merge "Optionally support accessibility with UiAutomator"
diff --git a/Android.mk b/Android.mk
index c947b0e..ad164e20 100644
--- a/Android.mk
+++ b/Android.mk
@@ -187,6 +187,8 @@
 	core/java/android/hardware/location/IGeofenceHardware.aidl \
 	core/java/android/hardware/location/IGeofenceHardwareCallback.aidl \
 	core/java/android/hardware/location/IGeofenceHardwareMonitorCallback.aidl \
+	core/java/android/hardware/location/IContextHubCallback.aidl \
+	core/java/android/hardware/location/IContextHubService.aidl \
 	core/java/android/hardware/soundtrigger/IRecognitionStatusCallback.aidl \
 	core/java/android/hardware/usb/IUsbManager.aidl \
 	core/java/android/net/ICaptivePortal.aidl \
@@ -331,10 +333,10 @@
 	location/java/android/location/IFusedProvider.aidl \
 	location/java/android/location/IGeocodeProvider.aidl \
 	location/java/android/location/IGeofenceProvider.aidl \
+	location/java/android/location/IGnssStatusListener.aidl \
+	location/java/android/location/IGnssStatusProvider.aidl \
 	location/java/android/location/IGpsMeasurementsListener.aidl \
 	location/java/android/location/IGpsNavigationMessageListener.aidl \
-	location/java/android/location/IGpsStatusListener.aidl \
-	location/java/android/location/IGpsStatusProvider.aidl \
 	location/java/android/location/ILocationListener.aidl \
 	location/java/android/location/ILocationManager.aidl \
 	location/java/android/location/IFusedGeofenceHardware.aidl \
@@ -417,6 +419,7 @@
 	telephony/java/com/android/internal/telephony/ISub.aidl \
 	telephony/java/com/android/internal/telephony/ITelephony.aidl \
 	telephony/java/com/android/internal/telephony/ITelephonyDebug.aidl \
+	telephony/java/com/android/internal/telephony/ITelephonyDebugSubscriber.aidl \
 	telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl \
 	telephony/java/com/android/internal/telephony/IWapPushManager.aidl \
 	wifi/java/android/net/wifi/IWifiManager.aidl \
diff --git a/api/current.txt b/api/current.txt
index 5073d6b..cf2ea6d 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -506,6 +506,8 @@
     field public static final int encryptionAware = 16844038; // 0x1010506
     field public static final int end = 16843996; // 0x10104dc
     field public static final int endColor = 16843166; // 0x101019e
+    field public static final int endX = 16844051; // 0x1010513
+    field public static final int endY = 16844052; // 0x1010514
     field public static final deprecated int endYear = 16843133; // 0x101017d
     field public static final int enterFadeDuration = 16843532; // 0x101030c
     field public static final int entries = 16842930; // 0x10100b2
@@ -881,6 +883,7 @@
     field public static final int numbersTextColor = 16843937; // 0x10104a1
     field public static final deprecated int numeric = 16843109; // 0x1010165
     field public static final int numericShortcut = 16843236; // 0x10101e4
+    field public static final int offset = 16844053; // 0x1010515
     field public static final int onClick = 16843375; // 0x101026f
     field public static final int oneshot = 16843159; // 0x1010197
     field public static final int opacity = 16843550; // 0x101031e
@@ -1128,6 +1131,8 @@
     field public static final int startColor = 16843165; // 0x101019d
     field public static final int startDelay = 16843746; // 0x10103e2
     field public static final int startOffset = 16843198; // 0x10101be
+    field public static final int startX = 16844049; // 0x1010511
+    field public static final int startY = 16844050; // 0x1010512
     field public static final deprecated int startYear = 16843132; // 0x101017c
     field public static final int stateListAnimator = 16843848; // 0x1010448
     field public static final int stateNotNeeded = 16842774; // 0x1010016
@@ -6514,8 +6519,9 @@
     field public static final int TAG_ADB_SHELL_CMD = 210002; // 0x33452
     field public static final int TAG_ADB_SHELL_INTERACTIVE = 210001; // 0x33451
     field public static final int TAG_APP_PROCESS_START = 210005; // 0x33455
-    field public static final int TAG_DEVICE_LOCKED = 210007; // 0x33457
-    field public static final int TAG_DEVICE_UNLOCK_ATTEMPT = 210006; // 0x33456
+    field public static final int TAG_KEYGUARD_DISMISSED = 210006; // 0x33456
+    field public static final int TAG_KEYGUARD_DISMISS_AUTH_ATTEMPT = 210007; // 0x33457
+    field public static final int TAG_KEYGUARD_SECURED = 210008; // 0x33458
     field public static final int TAG_SYNC_RECV_FILE = 210003; // 0x33453
     field public static final int TAG_SYNC_SEND_FILE = 210004; // 0x33454
   }
@@ -9952,7 +9958,7 @@
     method public final long skip(long) throws java.io.IOException;
   }
 
-  public class ColorStateList implements android.os.Parcelable {
+  public class ColorStateList extends android.content.res.ComplexColor implements android.os.Parcelable {
     ctor public ColorStateList(int[][], int[]);
     method public static deprecated android.content.res.ColorStateList createFromXml(android.content.res.Resources, org.xmlpull.v1.XmlPullParser) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
     method public static android.content.res.ColorStateList createFromXml(android.content.res.Resources, org.xmlpull.v1.XmlPullParser, android.content.res.Resources.Theme) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
@@ -9961,13 +9967,18 @@
     method public int getColorForState(int[], int);
     method public int getDefaultColor();
     method public boolean isOpaque();
-    method public boolean isStateful();
     method public static android.content.res.ColorStateList valueOf(int);
     method public android.content.res.ColorStateList withAlpha(int);
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.content.res.ColorStateList> CREATOR;
   }
 
+  public abstract class ComplexColor {
+    ctor public ComplexColor();
+    method public abstract int getDefaultColor();
+    method public boolean isStateful();
+  }
+
   public final class Configuration implements java.lang.Comparable android.os.Parcelable {
     ctor public Configuration();
     ctor public Configuration(android.content.res.Configuration);
@@ -10071,6 +10082,11 @@
     field public int uiMode;
   }
 
+  public class GradientColor extends android.content.res.ComplexColor {
+    method public static android.content.res.GradientColor createFromXml(android.content.res.Resources, android.content.res.XmlResourceParser, android.content.res.Resources.Theme) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
+    method public int getDefaultColor();
+  }
+
   public class ObbInfo implements android.os.Parcelable {
     method public int describeContents();
     method public void writeToParcel(android.os.Parcel, int);
@@ -10130,6 +10146,7 @@
     method public void getValue(java.lang.String, android.util.TypedValue, boolean) throws android.content.res.Resources.NotFoundException;
     method public void getValueForDensity(int, int, android.util.TypedValue, boolean) throws android.content.res.Resources.NotFoundException;
     method public android.content.res.XmlResourceParser getXml(int) throws android.content.res.Resources.NotFoundException;
+    method public android.content.res.ComplexColor loadComplexColor(android.util.TypedValue, int, android.content.res.Resources.Theme);
     method public final android.content.res.Resources.Theme newTheme();
     method public android.content.res.TypedArray obtainAttributes(android.util.AttributeSet, int[]);
     method public android.content.res.TypedArray obtainTypedArray(int) throws android.content.res.Resources.NotFoundException;
@@ -10165,6 +10182,7 @@
     method public int getChangingConfigurations();
     method public int getColor(int, int);
     method public android.content.res.ColorStateList getColorStateList(int);
+    method public android.content.res.ComplexColor getComplexColor(int);
     method public float getDimension(int, float);
     method public int getDimensionPixelOffset(int, int);
     method public int getDimensionPixelSize(int, int);
@@ -13361,6 +13379,7 @@
     method public float getResolution();
     method public java.lang.String getStringType();
     method public int getType();
+    method public java.util.UUID getUuid();
     method public java.lang.String getVendor();
     method public int getVersion();
     method public boolean isWakeUpSensor();
@@ -13421,6 +13440,21 @@
     field public static final deprecated int TYPE_TEMPERATURE = 7; // 0x7
   }
 
+  public class SensorAdditionalInfo {
+    field public static final int TYPE_FRAME_BEGIN = 0; // 0x0
+    field public static final int TYPE_FRAME_END = 1; // 0x1
+    field public static final int TYPE_INTERNAL_TEMPERATURE = 65537; // 0x10001
+    field public static final int TYPE_SAMPLING = 65540; // 0x10004
+    field public static final int TYPE_SENSOR_PLACEMENT = 65539; // 0x10003
+    field public static final int TYPE_UNTRACKED_DELAY = 65536; // 0x10000
+    field public static final int TYPE_VEC3_CALIBRATION = 65538; // 0x10002
+    field public final float[] floatValues;
+    field public final int[] intValues;
+    field public final android.hardware.Sensor sensor;
+    field public final int serial;
+    field public final int type;
+  }
+
   public class SensorEvent {
     field public int accuracy;
     field public android.hardware.Sensor sensor;
@@ -13428,6 +13462,14 @@
     field public final float[] values;
   }
 
+  public abstract class SensorEventCallback implements android.hardware.SensorEventListener2 {
+    ctor public SensorEventCallback();
+    method public void onAccuracyChanged(android.hardware.Sensor, int);
+    method public void onFlushCompleted(android.hardware.Sensor);
+    method public void onSensorAdditionalInfo(android.hardware.SensorAdditionalInfo);
+    method public void onSensorChanged(android.hardware.SensorEvent);
+  }
+
   public abstract interface SensorEventListener {
     method public abstract void onAccuracyChanged(android.hardware.Sensor, int);
     method public abstract void onSensorChanged(android.hardware.SensorEvent);
@@ -13449,6 +13491,7 @@
     method public static void getAngleChange(float[], float[], float[]);
     method public android.hardware.Sensor getDefaultSensor(int);
     method public android.hardware.Sensor getDefaultSensor(int, boolean);
+    method public java.util.List<android.hardware.Sensor> getDynamicSensorList(int);
     method public static float getInclination(float[]);
     method public static float[] getOrientation(float[], float[]);
     method public static void getQuaternionFromVector(float[], float[]);
@@ -13456,6 +13499,8 @@
     method public static void getRotationMatrixFromVector(float[], float[]);
     method public java.util.List<android.hardware.Sensor> getSensorList(int);
     method public deprecated int getSensors();
+    method public void registerDynamicSensorCallback(android.hardware.SensorManager.DynamicSensorConnectionCallback);
+    method public void registerDynamicSensorCallback(android.hardware.SensorManager.DynamicSensorConnectionCallback, android.os.Handler);
     method public deprecated boolean registerListener(android.hardware.SensorListener, int);
     method public deprecated boolean registerListener(android.hardware.SensorListener, int, int);
     method public boolean registerListener(android.hardware.SensorEventListener, android.hardware.Sensor, int);
@@ -13464,6 +13509,7 @@
     method public boolean registerListener(android.hardware.SensorEventListener, android.hardware.Sensor, int, int, android.os.Handler);
     method public static boolean remapCoordinateSystem(float[], int, int, float[]);
     method public boolean requestTriggerSensor(android.hardware.TriggerEventListener, android.hardware.Sensor);
+    method public void unregisterDynamicSensorCallback(android.hardware.SensorManager.DynamicSensorConnectionCallback);
     method public deprecated void unregisterListener(android.hardware.SensorListener);
     method public deprecated void unregisterListener(android.hardware.SensorListener, int);
     method public void unregisterListener(android.hardware.SensorEventListener, android.hardware.Sensor);
@@ -13528,6 +13574,12 @@
     field public static final float STANDARD_GRAVITY = 9.80665f;
   }
 
+  public static abstract class SensorManager.DynamicSensorConnectionCallback {
+    ctor public SensorManager.DynamicSensorConnectionCallback();
+    method public void onDynamicSensorConnected(android.hardware.Sensor);
+    method public void onDynamicSensorDisconnected(android.hardware.Sensor);
+  }
+
   public final class TriggerEvent {
     field public android.hardware.Sensor sensor;
     field public long timestamp;
@@ -19028,6 +19080,288 @@
     method public static boolean isPresent();
   }
 
+  public abstract interface GnssNmeaListener {
+    method public abstract void onNmeaReceived(long, java.lang.String);
+  }
+
+  public final class GnssStatus {
+    method public float getAzimuth(int);
+    method public int getConstellationType(int);
+    method public float getElevation(int);
+    method public int getNumSatellites();
+    method public int getPrn(int);
+    method public float getSnr(int);
+    method public boolean hasAlmanac(int);
+    method public boolean hasEphemeris(int);
+    method public boolean usedInFix(int);
+    field public static final int CONSTELLATION_BEIDOU = 5; // 0x5
+    field public static final int CONSTELLATION_GALILEO = 6; // 0x6
+    field public static final int CONSTELLATION_GLONASS = 3; // 0x3
+    field public static final int CONSTELLATION_GPS = 1; // 0x1
+    field public static final int CONSTELLATION_QZSS = 4; // 0x4
+    field public static final int CONSTELLATION_SBAS = 2; // 0x2
+    field public static final int CONSTELLATION_UNKNOWN = 0; // 0x0
+  }
+
+  public abstract class GnssStatusCallback {
+    ctor public GnssStatusCallback();
+    method public void onFirstFix(int);
+    method public void onSatelliteStatusChanged(android.location.GnssStatus);
+    method public void onStarted();
+    method public void onStopped();
+  }
+
+  public final class GpsClock implements android.os.Parcelable {
+    method public int describeContents();
+    method public double getBiasInNs();
+    method public double getBiasUncertaintyInNs();
+    method public double getDriftInNsPerSec();
+    method public double getDriftUncertaintyInNsPerSec();
+    method public long getFullBiasInNs();
+    method public short getLeapSecond();
+    method public long getTimeInNs();
+    method public long getTimeOfLastHwClockDiscontinuityInNs();
+    method public double getTimeUncertaintyInNs();
+    method public byte getType();
+    method public boolean hasBiasInNs();
+    method public boolean hasBiasUncertaintyInNs();
+    method public boolean hasDriftInNsPerSec();
+    method public boolean hasDriftUncertaintyInNsPerSec();
+    method public boolean hasFullBiasInNs();
+    method public boolean hasLeapSecond();
+    method public boolean hasTimeUncertaintyInNs();
+    method public void reset();
+    method public void resetBiasInNs();
+    method public void resetBiasUncertaintyInNs();
+    method public void resetDriftInNsPerSec();
+    method public void resetDriftUncertaintyInNsPerSec();
+    method public void resetFullBiasInNs();
+    method public void resetLeapSecond();
+    method public void resetTimeUncertaintyInNs();
+    method public void set(android.location.GpsClock);
+    method public void setBiasInNs(double);
+    method public void setBiasUncertaintyInNs(double);
+    method public void setDriftInNsPerSec(double);
+    method public void setDriftUncertaintyInNsPerSec(double);
+    method public void setFullBiasInNs(long);
+    method public void setLeapSecond(short);
+    method public void setTimeInNs(long);
+    method public void setTimeOfLastHwClockDiscontinuityInNs(long);
+    method public void setTimeUncertaintyInNs(double);
+    method public void setType(byte);
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final byte CLOCK_TYPE_GPS_TIME = 2; // 0x2
+    field public static final byte CLOCK_TYPE_LOCAL_HW_TIME = 1; // 0x1
+    field public static final byte CLOCK_TYPE_UNKNOWN = 0; // 0x0
+    field public static final android.os.Parcelable.Creator<android.location.GpsClock> CREATOR;
+  }
+
+  public static abstract class GpsClock.GpsClockType implements java.lang.annotation.Annotation {
+  }
+
+  public final class GpsMeasurement implements android.os.Parcelable {
+    method public int describeContents();
+    method public double getAccumulatedDeltaRangeInMeters();
+    method public short getAccumulatedDeltaRangeState();
+    method public double getAccumulatedDeltaRangeUncertaintyInMeters();
+    method public double getAzimuthInDeg();
+    method public double getAzimuthUncertaintyInDeg();
+    method public int getBitNumber();
+    method public long getCarrierCycles();
+    method public float getCarrierFrequencyInHz();
+    method public double getCarrierPhase();
+    method public double getCarrierPhaseUncertainty();
+    method public double getCn0InDbHz();
+    method public double getCodePhaseInChips();
+    method public double getCodePhaseUncertaintyInChips();
+    method public double getDopplerShiftInHz();
+    method public double getDopplerShiftUncertaintyInHz();
+    method public double getElevationInDeg();
+    method public double getElevationUncertaintyInDeg();
+    method public byte getLossOfLock();
+    method public byte getMultipathIndicator();
+    method public byte getPrn();
+    method public double getPseudorangeInMeters();
+    method public double getPseudorangeRateCarrierInMetersPerSec();
+    method public double getPseudorangeRateCarrierUncertaintyInMetersPerSec();
+    method public double getPseudorangeRateInMetersPerSec();
+    method public double getPseudorangeRateUncertaintyInMetersPerSec();
+    method public double getPseudorangeUncertaintyInMeters();
+    method public long getReceivedGpsTowInNs();
+    method public long getReceivedGpsTowUncertaintyInNs();
+    method public double getSnrInDb();
+    method public short getState();
+    method public short getTimeFromLastBitInMs();
+    method public double getTimeOffsetInNs();
+    method public boolean hasAzimuthInDeg();
+    method public boolean hasAzimuthUncertaintyInDeg();
+    method public boolean hasBitNumber();
+    method public boolean hasCarrierCycles();
+    method public boolean hasCarrierFrequencyInHz();
+    method public boolean hasCarrierPhase();
+    method public boolean hasCarrierPhaseUncertainty();
+    method public boolean hasCodePhaseInChips();
+    method public boolean hasCodePhaseUncertaintyInChips();
+    method public boolean hasDopplerShiftInHz();
+    method public boolean hasDopplerShiftUncertaintyInHz();
+    method public boolean hasElevationInDeg();
+    method public boolean hasElevationUncertaintyInDeg();
+    method public boolean hasPseudorangeInMeters();
+    method public boolean hasPseudorangeUncertaintyInMeters();
+    method public boolean hasSnrInDb();
+    method public boolean hasTimeFromLastBitInMs();
+    method public boolean isPseudorangeRateCorrected();
+    method public boolean isUsedInFix();
+    method public void reset();
+    method public void resetAzimuthInDeg();
+    method public void resetAzimuthUncertaintyInDeg();
+    method public void resetBitNumber();
+    method public void resetCarrierCycles();
+    method public void resetCarrierFrequencyInHz();
+    method public void resetCarrierPhase();
+    method public void resetCarrierPhaseUncertainty();
+    method public void resetCodePhaseInChips();
+    method public void resetCodePhaseUncertaintyInChips();
+    method public void resetDopplerShiftInHz();
+    method public void resetDopplerShiftUncertaintyInHz();
+    method public void resetElevationInDeg();
+    method public void resetElevationUncertaintyInDeg();
+    method public void resetPseudorangeInMeters();
+    method public void resetPseudorangeUncertaintyInMeters();
+    method public void resetSnrInDb();
+    method public void resetTimeFromLastBitInMs();
+    method public void set(android.location.GpsMeasurement);
+    method public void setAccumulatedDeltaRangeInMeters(double);
+    method public void setAccumulatedDeltaRangeState(short);
+    method public void setAccumulatedDeltaRangeUncertaintyInMeters(double);
+    method public void setAzimuthInDeg(double);
+    method public void setAzimuthUncertaintyInDeg(double);
+    method public void setBitNumber(int);
+    method public void setCarrierCycles(long);
+    method public void setCarrierFrequencyInHz(float);
+    method public void setCarrierPhase(double);
+    method public void setCarrierPhaseUncertainty(double);
+    method public void setCn0InDbHz(double);
+    method public void setCodePhaseInChips(double);
+    method public void setCodePhaseUncertaintyInChips(double);
+    method public void setDopplerShiftInHz(double);
+    method public void setDopplerShiftUncertaintyInHz(double);
+    method public void setElevationInDeg(double);
+    method public void setElevationUncertaintyInDeg(double);
+    method public void setLossOfLock(byte);
+    method public void setMultipathIndicator(byte);
+    method public void setPrn(byte);
+    method public void setPseudorangeInMeters(double);
+    method public void setPseudorangeRateCarrierInMetersPerSec(double);
+    method public void setPseudorangeRateCarrierUncertaintyInMetersPerSec(double);
+    method public void setPseudorangeRateInMetersPerSec(double);
+    method public void setPseudorangeRateUncertaintyInMetersPerSec(double);
+    method public void setPseudorangeUncertaintyInMeters(double);
+    method public void setReceivedGpsTowInNs(long);
+    method public void setReceivedGpsTowUncertaintyInNs(long);
+    method public void setSnrInDb(double);
+    method public void setState(short);
+    method public void setTimeFromLastBitInMs(short);
+    method public void setTimeOffsetInNs(double);
+    method public void setUsedInFix(boolean);
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final short ADR_STATE_CYCLE_SLIP = 4; // 0x4
+    field public static final short ADR_STATE_RESET = 2; // 0x2
+    field public static final short ADR_STATE_UNKNOWN = 0; // 0x0
+    field public static final short ADR_STATE_VALID = 1; // 0x1
+    field public static final android.os.Parcelable.Creator<android.location.GpsMeasurement> CREATOR;
+    field public static final byte LOSS_OF_LOCK_CYCLE_SLIP = 2; // 0x2
+    field public static final byte LOSS_OF_LOCK_OK = 1; // 0x1
+    field public static final byte LOSS_OF_LOCK_UNKNOWN = 0; // 0x0
+    field public static final byte MULTIPATH_INDICATOR_DETECTED = 1; // 0x1
+    field public static final byte MULTIPATH_INDICATOR_NOT_USED = 2; // 0x2
+    field public static final byte MULTIPATH_INDICATOR_UNKNOWN = 0; // 0x0
+    field public static final short STATE_BIT_SYNC = 2; // 0x2
+    field public static final short STATE_CODE_LOCK = 1; // 0x1
+    field public static final short STATE_MSEC_AMBIGUOUS = 16; // 0x10
+    field public static final short STATE_SUBFRAME_SYNC = 4; // 0x4
+    field public static final short STATE_TOW_DECODED = 8; // 0x8
+    field public static final short STATE_UNKNOWN = 0; // 0x0
+  }
+
+  public static abstract class GpsMeasurement.LossOfLockStatus implements java.lang.annotation.Annotation {
+  }
+
+  public static abstract class GpsMeasurement.MultipathIndicator implements java.lang.annotation.Annotation {
+  }
+
+  public final class GpsMeasurementsEvent implements android.os.Parcelable {
+    ctor public GpsMeasurementsEvent(android.location.GpsClock, android.location.GpsMeasurement[]);
+    method public int describeContents();
+    method public android.location.GpsClock getClock();
+    method public java.util.Collection<android.location.GpsMeasurement> getMeasurements();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.location.GpsMeasurementsEvent> CREATOR;
+    field public static final int STATUS_GPS_LOCATION_DISABLED = 2; // 0x2
+    field public static final int STATUS_NOT_SUPPORTED = 0; // 0x0
+    field public static final int STATUS_READY = 1; // 0x1
+  }
+
+  public static abstract class GpsMeasurementsEvent.Callback {
+    ctor public GpsMeasurementsEvent.Callback();
+    method public void onGpsMeasurementsReceived(android.location.GpsMeasurementsEvent);
+    method public void onStatusChanged(int);
+  }
+
+  public static abstract class GpsMeasurementsEvent.GpsMeasurementsStatus implements java.lang.annotation.Annotation {
+  }
+
+  public final class GpsNavigationMessage implements android.os.Parcelable {
+    method public int describeContents();
+    method public byte[] getData();
+    method public short getMessageId();
+    method public byte getPrn();
+    method public short getStatus();
+    method public short getSubmessageId();
+    method public byte getType();
+    method public void reset();
+    method public void set(android.location.GpsNavigationMessage);
+    method public void setData(byte[]);
+    method public void setMessageId(short);
+    method public void setPrn(byte);
+    method public void setStatus(short);
+    method public void setSubmessageId(short);
+    method public void setType(byte);
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.location.GpsNavigationMessage> CREATOR;
+    field public static final byte MESSAGE_TYPE_CNAV2 = 4; // 0x4
+    field public static final byte MESSAGE_TYPE_L1CA = 1; // 0x1
+    field public static final byte MESSAGE_TYPE_L2CNAV = 2; // 0x2
+    field public static final byte MESSAGE_TYPE_L5CNAV = 3; // 0x3
+    field public static final byte MESSAGE_TYPE_UNKNOWN = 0; // 0x0
+    field public static final short STATUS_PARITY_PASSED = 1; // 0x1
+    field public static final short STATUS_PARITY_REBUILT = 2; // 0x2
+    field public static final short STATUS_UNKNOWN = 0; // 0x0
+  }
+
+  public static abstract class GpsNavigationMessage.GpsNavigationMessageType implements java.lang.annotation.Annotation {
+  }
+
+  public final class GpsNavigationMessageEvent implements android.os.Parcelable {
+    ctor public GpsNavigationMessageEvent(android.location.GpsNavigationMessage);
+    method public int describeContents();
+    method public android.location.GpsNavigationMessage getNavigationMessage();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.location.GpsNavigationMessageEvent> CREATOR;
+    field public static final int STATUS_GPS_LOCATION_DISABLED = 2; // 0x2
+    field public static final int STATUS_NOT_SUPPORTED = 0; // 0x0
+    field public static final int STATUS_READY = 1; // 0x1
+  }
+
+  public static abstract class GpsNavigationMessageEvent.Callback {
+    ctor public GpsNavigationMessageEvent.Callback();
+    method public void onGpsNavigationMessageReceived(android.location.GpsNavigationMessageEvent);
+    method public void onStatusChanged(int);
+  }
+
+  public static abstract class GpsNavigationMessageEvent.GpsNavigationMessageStatus implements java.lang.annotation.Annotation {
+  }
+
   public final class GpsSatellite {
     method public float getAzimuth();
     method public float getElevation();
@@ -19112,8 +19446,10 @@
   }
 
   public class LocationManager {
-    method public boolean addGpsStatusListener(android.location.GpsStatus.Listener);
-    method public boolean addNmeaListener(android.location.GpsStatus.NmeaListener);
+    method public deprecated boolean addGpsStatusListener(android.location.GpsStatus.Listener);
+    method public deprecated boolean addNmeaListener(android.location.GpsStatus.NmeaListener);
+    method public boolean addNmeaListener(android.location.GnssNmeaListener);
+    method public boolean addNmeaListener(android.location.GnssNmeaListener, android.os.Handler);
     method public void addProximityAlert(double, double, float, long, android.app.PendingIntent);
     method public void addTestProvider(java.lang.String, boolean, boolean, boolean, boolean, boolean, boolean, boolean, int, int);
     method public void clearTestProviderEnabled(java.lang.String);
@@ -19121,14 +19457,21 @@
     method public void clearTestProviderStatus(java.lang.String);
     method public java.util.List<java.lang.String> getAllProviders();
     method public java.lang.String getBestProvider(android.location.Criteria, boolean);
-    method public android.location.GpsStatus getGpsStatus(android.location.GpsStatus);
+    method public deprecated android.location.GpsStatus getGpsStatus(android.location.GpsStatus);
     method public android.location.Location getLastKnownLocation(java.lang.String);
     method public android.location.LocationProvider getProvider(java.lang.String);
     method public java.util.List<java.lang.String> getProviders(boolean);
     method public java.util.List<java.lang.String> getProviders(android.location.Criteria, boolean);
     method public boolean isProviderEnabled(java.lang.String);
-    method public void removeGpsStatusListener(android.location.GpsStatus.Listener);
-    method public void removeNmeaListener(android.location.GpsStatus.NmeaListener);
+    method public boolean registerGnssStatusCallback(android.location.GnssStatusCallback);
+    method public boolean registerGnssStatusCallback(android.location.GnssStatusCallback, android.os.Handler);
+    method public boolean registerGpsMeasurementCallback(android.location.GpsMeasurementsEvent.Callback);
+    method public boolean registerGpsMeasurementCallback(android.location.GpsMeasurementsEvent.Callback, android.os.Handler);
+    method public boolean registerGpsNavigationMessageCallback(android.location.GpsNavigationMessageEvent.Callback);
+    method public boolean registerGpsNavigationMessageCallback(android.location.GpsNavigationMessageEvent.Callback, android.os.Handler);
+    method public deprecated void removeGpsStatusListener(android.location.GpsStatus.Listener);
+    method public deprecated void removeNmeaListener(android.location.GpsStatus.NmeaListener);
+    method public void removeNmeaListener(android.location.GnssNmeaListener);
     method public void removeProximityAlert(android.app.PendingIntent);
     method public void removeTestProvider(java.lang.String);
     method public void removeUpdates(android.location.LocationListener);
@@ -19146,6 +19489,9 @@
     method public void setTestProviderEnabled(java.lang.String, boolean);
     method public void setTestProviderLocation(java.lang.String, android.location.Location);
     method public void setTestProviderStatus(java.lang.String, int, android.os.Bundle, long);
+    method public void unregisterGnssStatusCallback(android.location.GnssStatusCallback);
+    method public void unregisterGpsMeasurementCallback(android.location.GpsMeasurementsEvent.Callback);
+    method public void unregisterGpsNavigationMessageCallback(android.location.GpsNavigationMessageEvent.Callback);
     field public static final java.lang.String GPS_PROVIDER = "gps";
     field public static final java.lang.String KEY_LOCATION_CHANGED = "location";
     field public static final java.lang.String KEY_PROVIDER_ENABLED = "providerEnabled";
@@ -19577,8 +19923,11 @@
 
   public class AudioRecordConfiguration implements android.os.Parcelable {
     method public int describeContents();
-    method public int getAudioSessionId();
+    method public android.media.AudioDeviceInfo getAudioDevice();
+    method public int getClientAudioSessionId();
     method public int getClientAudioSource();
+    method public android.media.AudioFormat getClientFormat();
+    method public android.media.AudioFormat getFormat();
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.media.AudioRecordConfiguration> CREATOR;
   }
@@ -19752,6 +20101,16 @@
     ctor public DeniedByServerException(java.lang.String);
   }
 
+  public abstract class DrmInitData {
+    ctor public DrmInitData();
+    method public abstract android.media.DrmInitData.SchemeInitData get(java.util.UUID);
+  }
+
+  public static final class DrmInitData.SchemeInitData {
+    field public final byte[] data;
+    field public final java.lang.String mimeType;
+  }
+
   public class ExifInterface {
     ctor public ExifInterface(java.lang.String) throws java.io.IOException;
     method public double getAltitude(double);
@@ -20442,6 +20801,7 @@
     ctor public MediaExtractor();
     method public boolean advance();
     method public long getCachedDuration();
+    method public android.media.DrmInitData getDrmInitData();
     method public java.util.Map<java.util.UUID, byte[]> getPsshInfo();
     method public boolean getSampleCryptoInfo(android.media.MediaCodec.CryptoInfo);
     method public int getSampleFlags();
@@ -20486,6 +20846,16 @@
     method public final void setInteger(java.lang.String, int);
     method public final void setLong(java.lang.String, long);
     method public final void setString(java.lang.String, java.lang.String);
+    field public static final int COLOR_RANGE_FULL = 1; // 0x1
+    field public static final int COLOR_RANGE_LIMITED = 2; // 0x2
+    field public static final int COLOR_STANDARD_BT2020 = 6; // 0x6
+    field public static final int COLOR_STANDARD_BT601_NTSC = 4; // 0x4
+    field public static final int COLOR_STANDARD_BT601_PAL = 2; // 0x2
+    field public static final int COLOR_STANDARD_BT709 = 1; // 0x1
+    field public static final int COLOR_TRANSFER_HLG = 7; // 0x7
+    field public static final int COLOR_TRANSFER_LINEAR = 1; // 0x1
+    field public static final int COLOR_TRANSFER_SDR_VIDEO = 3; // 0x3
+    field public static final int COLOR_TRANSFER_ST2084 = 6; // 0x6
     field public static final java.lang.String KEY_AAC_DRC_ATTENUATION_FACTOR = "aac-drc-cut-level";
     field public static final java.lang.String KEY_AAC_DRC_BOOST_FACTOR = "aac-drc-boost-level";
     field public static final java.lang.String KEY_AAC_DRC_HEAVY_COMPRESSION = "aac-drc-heavy-compression";
@@ -20501,6 +20871,9 @@
     field public static final java.lang.String KEY_CHANNEL_COUNT = "channel-count";
     field public static final java.lang.String KEY_CHANNEL_MASK = "channel-mask";
     field public static final java.lang.String KEY_COLOR_FORMAT = "color-format";
+    field public static final java.lang.String KEY_COLOR_RANGE = "color-range";
+    field public static final java.lang.String KEY_COLOR_STANDARD = "color-standard";
+    field public static final java.lang.String KEY_COLOR_TRANSFER = "color-transfer";
     field public static final java.lang.String KEY_COMPLEXITY = "complexity";
     field public static final java.lang.String KEY_DURATION = "durationUs";
     field public static final java.lang.String KEY_FLAC_COMPRESSION_LEVEL = "flac-compression-level";
@@ -22126,6 +22499,7 @@
     method public void setRatingType(int);
     method public void setSessionActivity(android.app.PendingIntent);
     field public static final int FLAG_HANDLES_MEDIA_BUTTONS = 1; // 0x1
+    field public static final int FLAG_HANDLES_PREPARE_ONLY = 4; // 0x4
     field public static final int FLAG_HANDLES_TRANSPORT_CONTROLS = 2; // 0x2
   }
 
@@ -22204,6 +22578,7 @@
     field public static final long ACTION_SKIP_TO_QUEUE_ITEM = 4096L; // 0x1000L
     field public static final long ACTION_STOP = 1L; // 0x1L
     field public static final android.os.Parcelable.Creator<android.media.session.PlaybackState> CREATOR;
+    field public static final java.lang.String EXTRA_PREPARE_ONLY = "android.media.session.extra.PREPARE_ONLY";
     field public static final long PLAYBACK_POSITION_UNKNOWN = -1L; // 0xffffffffffffffffL
     field public static final int STATE_BUFFERING = 6; // 0x6
     field public static final int STATE_CONNECTING = 8; // 0x8
@@ -22520,7 +22895,7 @@
     method public void onInputAdded(java.lang.String);
     method public void onInputRemoved(java.lang.String);
     method public void onInputStateChanged(java.lang.String, int);
-    method public void onTvInputInfoChanged(java.lang.String, android.media.tv.TvInputInfo);
+    method public void onTvInputInfoChanged(android.media.tv.TvInputInfo);
   }
 
   public abstract class TvInputService extends android.app.Service {
@@ -22528,7 +22903,7 @@
     method public final android.os.IBinder onBind(android.content.Intent);
     method public android.media.tv.TvInputService.RecordingSession onCreateRecordingSession(java.lang.String);
     method public abstract android.media.tv.TvInputService.Session onCreateSession(java.lang.String);
-    method public final void setTvInputInfo(java.lang.String, android.media.tv.TvInputInfo);
+    method public static final void setTvInputInfo(android.content.Context, android.media.tv.TvInputInfo);
     field public static final java.lang.String SERVICE_INTERFACE = "android.media.tv.TvInputService";
     field public static final java.lang.String SERVICE_META_DATA = "android.media.tv.input";
   }
@@ -22791,6 +23166,7 @@
     field public static final int OPERATION_GET_OBJECT_PROP_VALUE = 38915; // 0x9803
     field public static final int OPERATION_GET_OBJECT_REFERENCES = 38928; // 0x9810
     field public static final int OPERATION_GET_PARTIAL_OBJECT = 4123; // 0x101b
+    field public static final int OPERATION_GET_PARTIAL_OBJECT_64 = 38337; // 0x95c1
     field public static final int OPERATION_GET_STORAGE_INFO = 4101; // 0x1005
     field public static final int OPERATION_GET_STORAGE_I_DS = 4100; // 0x1004
     field public static final int OPERATION_GET_THUMB = 4106; // 0x100a
@@ -22828,6 +23204,7 @@
     method public android.mtp.MtpObjectInfo getObjectInfo(int);
     method public long getParent(int);
     method public long getPartialObject(int, long, long, byte[]) throws java.io.IOException;
+    method public long getPartialObject64(int, long, long, byte[]) throws java.io.IOException;
     method public long getStorageId(int);
     method public int[] getStorageIds();
     method public android.mtp.MtpStorageInfo getStorageInfo(int);
@@ -22836,7 +23213,7 @@
     method public boolean importFile(int, android.os.ParcelFileDescriptor);
     method public boolean open(android.hardware.usb.UsbDeviceConnection);
     method public android.mtp.MtpEvent readEvent(android.os.CancellationSignal);
-    method public boolean sendObject(int, int, android.os.ParcelFileDescriptor);
+    method public boolean sendObject(int, long, android.os.ParcelFileDescriptor);
     method public android.mtp.MtpObjectInfo sendObjectInfo(android.mtp.MtpObjectInfo);
   }
 
@@ -22867,23 +23244,31 @@
     method public final int getAssociationDesc();
     method public final int getAssociationType();
     method public final int getCompressedSize();
+    method public final long getCompressedSizeLong();
     method public final long getDateCreated();
     method public final long getDateModified();
     method public final int getFormat();
     method public final int getImagePixDepth();
+    method public final long getImagePixDepthLong();
     method public final int getImagePixHeight();
+    method public final long getImagePixHeightLong();
     method public final int getImagePixWidth();
+    method public final long getImagePixWidthLong();
     method public final java.lang.String getKeywords();
     method public final java.lang.String getName();
     method public final int getObjectHandle();
     method public final int getParent();
     method public final int getProtectionStatus();
     method public final int getSequenceNumber();
+    method public final long getSequenceNumberLong();
     method public final int getStorageId();
     method public final int getThumbCompressedSize();
+    method public final long getThumbCompressedSizeLong();
     method public final int getThumbFormat();
     method public final int getThumbPixHeight();
+    method public final long getThumbPixHeightLong();
     method public final int getThumbPixWidth();
+    method public final long getThumbPixWidthLong();
   }
 
   public static class MtpObjectInfo.Builder {
@@ -22892,24 +23277,24 @@
     method public android.mtp.MtpObjectInfo build();
     method public android.mtp.MtpObjectInfo.Builder setAssociationDesc(int);
     method public android.mtp.MtpObjectInfo.Builder setAssociationType(int);
-    method public android.mtp.MtpObjectInfo.Builder setCompressedSize(int);
+    method public android.mtp.MtpObjectInfo.Builder setCompressedSize(long);
     method public android.mtp.MtpObjectInfo.Builder setDateCreated(long);
     method public android.mtp.MtpObjectInfo.Builder setDateModified(long);
     method public android.mtp.MtpObjectInfo.Builder setFormat(int);
-    method public android.mtp.MtpObjectInfo.Builder setImagePixDepth(int);
-    method public android.mtp.MtpObjectInfo.Builder setImagePixHeight(int);
-    method public android.mtp.MtpObjectInfo.Builder setImagePixWidth(int);
+    method public android.mtp.MtpObjectInfo.Builder setImagePixDepth(long);
+    method public android.mtp.MtpObjectInfo.Builder setImagePixHeight(long);
+    method public android.mtp.MtpObjectInfo.Builder setImagePixWidth(long);
     method public android.mtp.MtpObjectInfo.Builder setKeywords(java.lang.String);
     method public android.mtp.MtpObjectInfo.Builder setName(java.lang.String);
     method public android.mtp.MtpObjectInfo.Builder setObjectHandle(int);
     method public android.mtp.MtpObjectInfo.Builder setParent(int);
     method public android.mtp.MtpObjectInfo.Builder setProtectionStatus(int);
-    method public android.mtp.MtpObjectInfo.Builder setSequenceNumber(int);
+    method public android.mtp.MtpObjectInfo.Builder setSequenceNumber(long);
     method public android.mtp.MtpObjectInfo.Builder setStorageId(int);
-    method public android.mtp.MtpObjectInfo.Builder setThumbCompressedSize(int);
+    method public android.mtp.MtpObjectInfo.Builder setThumbCompressedSize(long);
     method public android.mtp.MtpObjectInfo.Builder setThumbFormat(int);
-    method public android.mtp.MtpObjectInfo.Builder setThumbPixHeight(int);
-    method public android.mtp.MtpObjectInfo.Builder setThumbPixWidth(int);
+    method public android.mtp.MtpObjectInfo.Builder setThumbPixHeight(long);
+    method public android.mtp.MtpObjectInfo.Builder setThumbPixWidth(long);
   }
 
   public final class MtpStorageInfo {
@@ -28156,6 +28541,10 @@
     field public static final int OPEN = 32; // 0x20
   }
 
+  public class FileUriExposedException extends java.lang.RuntimeException {
+    ctor public FileUriExposedException(java.lang.String);
+  }
+
   public class Handler {
     ctor public Handler();
     ctor public Handler(android.os.Handler.Callback);
@@ -28747,6 +29136,7 @@
     method public android.os.StrictMode.VmPolicy.Builder detectLeakedSqlLiteObjects();
     method public android.os.StrictMode.VmPolicy.Builder penaltyDeath();
     method public android.os.StrictMode.VmPolicy.Builder penaltyDeathOnCleartextNetwork();
+    method public android.os.StrictMode.VmPolicy.Builder penaltyDeathOnFileUriExposure();
     method public android.os.StrictMode.VmPolicy.Builder penaltyDropBox();
     method public android.os.StrictMode.VmPolicy.Builder penaltyLog();
     method public android.os.StrictMode.VmPolicy.Builder setClassInstanceLimit(java.lang.Class, int);
@@ -28853,6 +29243,8 @@
     field public static final java.lang.String DISALLOW_USB_FILE_TRANSFER = "no_usb_file_transfer";
     field public static final java.lang.String ENSURE_VERIFY_APPS = "ensure_verify_apps";
     field public static final java.lang.String KEY_RESTRICTIONS_PENDING = "restrictions_pending";
+    field public static final int USER_CREATION_FAILED_NOT_PERMITTED = 1; // 0x1
+    field public static final int USER_CREATION_FAILED_NO_MORE_USERS = 2; // 0x2
   }
 
   public abstract class Vibrator {
@@ -29210,6 +29602,8 @@
     method public static void setDefaultValues(android.content.Context, java.lang.String, int, int, boolean);
     method public void setSharedPreferencesMode(int);
     method public void setSharedPreferencesName(java.lang.String);
+    method public void setStorageDefault();
+    method public void setStorageDeviceEncrypted();
     field public static final java.lang.String KEY_HAS_SET_DEFAULT_VALUES = "_has_set_default_values";
     field public static final java.lang.String METADATA_KEY_PREFERENCES = "android.preference";
   }
@@ -31204,7 +31598,9 @@
     method public static java.lang.String getSearchDocumentsQuery(android.net.Uri);
     method public static java.lang.String getTreeDocumentId(android.net.Uri);
     method public static boolean isDocumentUri(android.content.Context, android.net.Uri);
+    method public static boolean isTreeUri(android.net.Uri);
     method public static android.net.Uri moveDocument(android.content.ContentResolver, android.net.Uri, android.net.Uri, android.net.Uri);
+    method public static boolean removeDocument(android.content.ContentResolver, android.net.Uri, android.net.Uri);
     method public static android.net.Uri renameDocument(android.content.ContentResolver, android.net.Uri, java.lang.String);
     field public static final java.lang.String EXTRA_ERROR = "error";
     field public static final java.lang.String EXTRA_EXCLUDE_SELF = "android.provider.extra.EXCLUDE_SELF";
@@ -31230,6 +31626,7 @@
     field public static final int FLAG_SUPPORTS_COPY = 128; // 0x80
     field public static final int FLAG_SUPPORTS_DELETE = 4; // 0x4
     field public static final int FLAG_SUPPORTS_MOVE = 256; // 0x100
+    field public static final int FLAG_SUPPORTS_REMOVE = 2048; // 0x800
     field public static final int FLAG_SUPPORTS_RENAME = 64; // 0x40
     field public static final int FLAG_SUPPORTS_THUMBNAIL = 1; // 0x1
     field public static final int FLAG_SUPPORTS_WRITE = 2; // 0x2
@@ -31277,6 +31674,7 @@
     method public android.database.Cursor queryRecentDocuments(java.lang.String, java.lang.String[]) throws java.io.FileNotFoundException;
     method public abstract android.database.Cursor queryRoots(java.lang.String[]) throws java.io.FileNotFoundException;
     method public android.database.Cursor querySearchDocuments(java.lang.String, java.lang.String, java.lang.String[]) throws java.io.FileNotFoundException;
+    method public boolean removeDocument(java.lang.String, java.lang.String) throws java.io.FileNotFoundException;
     method public java.lang.String renameDocument(java.lang.String, java.lang.String) throws java.io.FileNotFoundException;
     method public final void revokeDocumentPermission(java.lang.String);
     method public final int update(android.net.Uri, android.content.ContentValues, java.lang.String, java.lang.String[]);
@@ -31656,6 +32054,7 @@
     field public static final java.lang.String ACTION_INPUT_METHOD_SETTINGS = "android.settings.INPUT_METHOD_SETTINGS";
     field public static final java.lang.String ACTION_INPUT_METHOD_SUBTYPE_SETTINGS = "android.settings.INPUT_METHOD_SUBTYPE_SETTINGS";
     field public static final java.lang.String ACTION_INTERNAL_STORAGE_SETTINGS = "android.settings.INTERNAL_STORAGE_SETTINGS";
+    field public static final java.lang.String ACTION_KEYBOARD_LAYOUT_SETTINGS = "android.settings.KEYBOARD_LAYOUT_SETTINGS";
     field public static final java.lang.String ACTION_LOCALE_SETTINGS = "android.settings.LOCALE_SETTINGS";
     field public static final java.lang.String ACTION_LOCATION_SOURCE_SETTINGS = "android.settings.LOCATION_SOURCE_SETTINGS";
     field public static final java.lang.String ACTION_MANAGE_ALL_APPLICATIONS_SETTINGS = "android.settings.MANAGE_ALL_APPLICATIONS_SETTINGS";
@@ -35406,14 +35805,14 @@
     field public static final java.lang.String SERVICE_INTERFACE = "android.telecom.CallScreeningService";
   }
 
-  public class CallScreeningService.CallResponse {
+  public static class CallScreeningService.CallResponse {
     method public boolean getDisallowCall();
     method public boolean getRejectCall();
     method public boolean getSkipCallLog();
     method public boolean getSkipNotification();
   }
 
-  public class CallScreeningService.CallResponse.Builder {
+  public static class CallScreeningService.CallResponse.Builder {
     ctor public CallScreeningService.CallResponse.Builder();
     method public android.telecom.CallScreeningService.CallResponse build();
     method public android.telecom.CallScreeningService.CallResponse.Builder setDisallowCall(boolean);
@@ -36800,7 +37199,7 @@
     method public void testActivityTestCaseSetUpProperly() throws java.lang.Exception;
   }
 
-  public abstract class ActivityInstrumentationTestCase2 extends android.test.ActivityTestCase {
+  public abstract deprecated class ActivityInstrumentationTestCase2 extends android.test.ActivityTestCase {
     ctor public deprecated ActivityInstrumentationTestCase2(java.lang.String, java.lang.Class<T>);
     ctor public ActivityInstrumentationTestCase2(java.lang.Class<T>);
     method public T getActivity();
@@ -36808,14 +37207,14 @@
     method public void setActivityIntent(android.content.Intent);
   }
 
-  public abstract class ActivityTestCase extends android.test.InstrumentationTestCase {
+  public abstract deprecated class ActivityTestCase extends android.test.InstrumentationTestCase {
     ctor public ActivityTestCase();
     method protected android.app.Activity getActivity();
     method protected void scrubClass(java.lang.Class<?>) throws java.lang.IllegalAccessException;
     method protected void setActivity(android.app.Activity);
   }
 
-  public abstract class ActivityUnitTestCase extends android.test.ActivityTestCase {
+  public abstract deprecated class ActivityUnitTestCase extends android.test.ActivityTestCase {
     ctor public ActivityUnitTestCase(java.lang.Class<T>);
     method public T getActivity();
     method public int getFinishedActivityRequest();
@@ -36828,7 +37227,7 @@
     method protected T startActivity(android.content.Intent, android.os.Bundle, java.lang.Object);
   }
 
-  public class AndroidTestCase extends junit.framework.TestCase {
+  public deprecated class AndroidTestCase extends junit.framework.TestCase {
     ctor public AndroidTestCase();
     method public void assertActivityRequiresPermission(java.lang.String, java.lang.String, java.lang.String);
     method public void assertReadingContentUriRequiresPermission(android.net.Uri, java.lang.String);
@@ -36840,7 +37239,7 @@
     field protected android.content.Context mContext;
   }
 
-  public class AndroidTestRunner extends junit.runner.BaseTestRunner {
+  public deprecated class AndroidTestRunner extends junit.runner.BaseTestRunner {
     ctor public AndroidTestRunner();
     method public void addTestListener(junit.framework.TestListener);
     method public void clearTestListeners();
@@ -36861,7 +37260,7 @@
     method public void testStarted(java.lang.String);
   }
 
-  public abstract class ApplicationTestCase extends android.test.AndroidTestCase {
+  public abstract deprecated class ApplicationTestCase extends android.test.AndroidTestCase {
     ctor public ApplicationTestCase(java.lang.Class<T>);
     method protected final void createApplication();
     method public T getApplication();
@@ -36879,10 +37278,10 @@
     ctor public ComparisonFailure(java.lang.String, java.lang.String, java.lang.String);
   }
 
-  public abstract class FlakyTest implements java.lang.annotation.Annotation {
+  public abstract deprecated class FlakyTest implements java.lang.annotation.Annotation {
   }
 
-  public class InstrumentationTestCase extends junit.framework.TestCase {
+  public deprecated class InstrumentationTestCase extends junit.framework.TestCase {
     ctor public InstrumentationTestCase();
     method public android.app.Instrumentation getInstrumentation();
     method public deprecated void injectInsrumentation(android.app.Instrumentation);
@@ -36895,7 +37294,7 @@
     method public void sendRepeatedKeys(int...);
   }
 
-  public class InstrumentationTestRunner extends android.app.Instrumentation implements android.test.TestSuiteProvider {
+  public deprecated class InstrumentationTestRunner extends android.app.Instrumentation implements android.test.TestSuiteProvider {
     ctor public InstrumentationTestRunner();
     method public junit.framework.TestSuite getAllTests();
     method protected android.test.AndroidTestRunner getAndroidTestRunner();
@@ -36914,14 +37313,14 @@
     field public static final int REPORT_VALUE_RESULT_START = 1; // 0x1
   }
 
-  public class InstrumentationTestSuite extends junit.framework.TestSuite {
+  public deprecated class InstrumentationTestSuite extends junit.framework.TestSuite {
     ctor public InstrumentationTestSuite(android.app.Instrumentation);
     ctor public InstrumentationTestSuite(java.lang.String, android.app.Instrumentation);
     ctor public InstrumentationTestSuite(java.lang.Class, android.app.Instrumentation);
     method public void addTestSuite(java.lang.Class);
   }
 
-  public class IsolatedContext extends android.content.ContextWrapper {
+  public deprecated class IsolatedContext extends android.content.ContextWrapper {
     ctor public IsolatedContext(android.content.ContentResolver, android.content.Context);
     method public java.util.List<android.content.Intent> getAndClearBroadcastIntents();
   }
@@ -36931,7 +37330,7 @@
     method public T getLoaderResultSynchronously(android.content.Loader<T>);
   }
 
-  public final class MoreAsserts {
+  public final deprecated class MoreAsserts {
     method public static void assertAssignableFrom(java.lang.Class<?>, java.lang.Object);
     method public static void assertAssignableFrom(java.lang.Class<?>, java.lang.Class<?>);
     method public static java.util.regex.MatchResult assertContainsRegex(java.lang.String, java.lang.String, java.lang.String);
@@ -36970,7 +37369,7 @@
     method public static void checkEqualsAndHashCodeMethods(java.lang.Object, java.lang.Object, boolean);
   }
 
-  public abstract interface PerformanceTestCase {
+  public abstract deprecated interface PerformanceTestCase {
     method public abstract boolean isPerformanceOnly();
     method public abstract int startPerformance(android.test.PerformanceTestCase.Intermediates);
   }
@@ -36999,7 +37398,7 @@
     method public static android.content.ContentResolver newResolverWithContentProviderFromSql(android.content.Context, java.lang.String, java.lang.Class<T>, java.lang.String, java.lang.String, int, java.lang.String) throws java.lang.IllegalAccessException, java.lang.InstantiationException;
   }
 
-  public class RenamingDelegatingContext extends android.content.ContextWrapper {
+  public deprecated class RenamingDelegatingContext extends android.content.ContextWrapper {
     ctor public RenamingDelegatingContext(android.content.Context, java.lang.String);
     ctor public RenamingDelegatingContext(android.content.Context, android.content.Context, java.lang.String);
     method public java.lang.String getDatabasePrefix();
@@ -37008,7 +37407,7 @@
     method public static T providerWithRenamedContext(java.lang.Class<T>, android.content.Context, java.lang.String, boolean) throws java.lang.IllegalAccessException, java.lang.InstantiationException;
   }
 
-  public abstract class ServiceTestCase extends android.test.AndroidTestCase {
+  public abstract deprecated class ServiceTestCase extends android.test.AndroidTestCase {
     ctor public ServiceTestCase(java.lang.Class<T>);
     method protected android.os.IBinder bindService(android.content.Intent);
     method public android.app.Application getApplication();
@@ -37021,23 +37420,23 @@
     method public void testServiceTestCaseSetUpProperly() throws java.lang.Exception;
   }
 
-  public abstract class SingleLaunchActivityTestCase extends android.test.InstrumentationTestCase {
+  public abstract deprecated class SingleLaunchActivityTestCase extends android.test.InstrumentationTestCase {
     ctor public SingleLaunchActivityTestCase(java.lang.String, java.lang.Class<T>);
     method public T getActivity();
     method public void testActivityTestCaseSetUpProperly() throws java.lang.Exception;
   }
 
-  public class SyncBaseInstrumentation extends android.test.InstrumentationTestCase {
+  public deprecated class SyncBaseInstrumentation extends android.test.InstrumentationTestCase {
     ctor public SyncBaseInstrumentation();
     method protected void cancelSyncsandDisableAutoSync();
     method protected void syncProvider(android.net.Uri, java.lang.String, java.lang.String) throws java.lang.Exception;
   }
 
-  public abstract interface TestSuiteProvider {
+  public abstract deprecated interface TestSuiteProvider {
     method public abstract junit.framework.TestSuite getTestSuite();
   }
 
-  public class TouchUtils {
+  public deprecated class TouchUtils {
     ctor public TouchUtils();
     method public static void clickView(android.test.InstrumentationTestCase, android.view.View);
     method public static deprecated void drag(android.test.ActivityInstrumentationTestCase, float, float, float, float, int);
@@ -37072,10 +37471,10 @@
     method public static void touchAndCancelView(android.test.InstrumentationTestCase, android.view.View);
   }
 
-  public abstract class UiThreadTest implements java.lang.annotation.Annotation {
+  public abstract deprecated class UiThreadTest implements java.lang.annotation.Annotation {
   }
 
-  public class ViewAsserts {
+  public deprecated class ViewAsserts {
     method public static void assertBaselineAligned(android.view.View, android.view.View);
     method public static void assertBottomAligned(android.view.View, android.view.View);
     method public static void assertBottomAligned(android.view.View, android.view.View, int);
@@ -37100,11 +37499,11 @@
 
 package android.test.mock {
 
-  public class MockApplication extends android.app.Application {
+  public deprecated class MockApplication extends android.app.Application {
     ctor public MockApplication();
   }
 
-  public class MockContentProvider extends android.content.ContentProvider {
+  public deprecated class MockContentProvider extends android.content.ContentProvider {
     ctor protected MockContentProvider();
     ctor public MockContentProvider(android.content.Context);
     ctor public MockContentProvider(android.content.Context, java.lang.String, java.lang.String, android.content.pm.PathPermission[]);
@@ -37116,13 +37515,13 @@
     method public int update(android.net.Uri, android.content.ContentValues, java.lang.String, java.lang.String[]);
   }
 
-  public class MockContentResolver extends android.content.ContentResolver {
+  public deprecated class MockContentResolver extends android.content.ContentResolver {
     ctor public MockContentResolver();
     ctor public MockContentResolver(android.content.Context);
     method public void addProvider(java.lang.String, android.content.ContentProvider);
   }
 
-  public class MockContext extends android.content.Context {
+  public deprecated class MockContext extends android.content.Context {
     ctor public MockContext();
     method public boolean bindService(android.content.Intent, android.content.ServiceConnection, int);
     method public int checkCallingOrSelfPermission(java.lang.String);
@@ -37222,7 +37621,7 @@
     method public void unregisterReceiver(android.content.BroadcastReceiver);
   }
 
-  public class MockCursor implements android.database.Cursor {
+  public deprecated class MockCursor implements android.database.Cursor {
     ctor public MockCursor();
     method public void close();
     method public void copyStringToBuffer(int, android.database.CharArrayBuffer);
@@ -37267,13 +37666,13 @@
     method public void unregisterDataSetObserver(android.database.DataSetObserver);
   }
 
-  public class MockDialogInterface implements android.content.DialogInterface {
+  public deprecated class MockDialogInterface implements android.content.DialogInterface {
     ctor public MockDialogInterface();
     method public void cancel();
     method public void dismiss();
   }
 
-  public class MockPackageManager extends android.content.pm.PackageManager {
+  public deprecated class MockPackageManager extends android.content.pm.PackageManager {
     ctor public MockPackageManager();
     method public void addPackageToPreferred(java.lang.String);
     method public boolean addPermission(android.content.pm.PermissionInfo);
@@ -37360,7 +37759,7 @@
     method public void verifyPendingInstall(int, int);
   }
 
-  public class MockResources extends android.content.res.Resources {
+  public deprecated class MockResources extends android.content.res.Resources {
     ctor public MockResources();
   }
 
@@ -37401,19 +37800,19 @@
 
 package android.test.suitebuilder.annotation {
 
-  public abstract class LargeTest implements java.lang.annotation.Annotation {
+  public abstract deprecated class LargeTest implements java.lang.annotation.Annotation {
   }
 
-  public abstract class MediumTest implements java.lang.annotation.Annotation {
+  public abstract deprecated class MediumTest implements java.lang.annotation.Annotation {
   }
 
-  public abstract class SmallTest implements java.lang.annotation.Annotation {
+  public abstract deprecated class SmallTest implements java.lang.annotation.Annotation {
   }
 
-  public abstract class Smoke implements java.lang.annotation.Annotation {
+  public abstract deprecated class Smoke implements java.lang.annotation.Annotation {
   }
 
-  public abstract class Suppress implements java.lang.annotation.Annotation {
+  public abstract deprecated class Suppress implements java.lang.annotation.Annotation {
   }
 
 }
diff --git a/api/removed.txt b/api/removed.txt
index 6b7961e..2c6729d 100644
--- a/api/removed.txt
+++ b/api/removed.txt
@@ -201,7 +201,7 @@
 
 package android.test.mock {
 
-  public class MockPackageManager extends android.content.pm.PackageManager {
+  public deprecated class MockPackageManager extends android.content.pm.PackageManager {
     method public deprecated java.lang.String getDefaultBrowserPackageName(int);
     method public deprecated boolean setDefaultBrowserPackageName(java.lang.String, int);
   }
diff --git a/api/system-current.txt b/api/system-current.txt
index 53353fb..6a420e2 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -601,6 +601,8 @@
     field public static final int encryptionAware = 16844038; // 0x1010506
     field public static final int end = 16843996; // 0x10104dc
     field public static final int endColor = 16843166; // 0x101019e
+    field public static final int endX = 16844051; // 0x1010513
+    field public static final int endY = 16844052; // 0x1010514
     field public static final deprecated int endYear = 16843133; // 0x101017d
     field public static final int enterFadeDuration = 16843532; // 0x101030c
     field public static final int entries = 16842930; // 0x10100b2
@@ -976,6 +978,7 @@
     field public static final int numbersTextColor = 16843937; // 0x10104a1
     field public static final deprecated int numeric = 16843109; // 0x1010165
     field public static final int numericShortcut = 16843236; // 0x10101e4
+    field public static final int offset = 16844053; // 0x1010515
     field public static final int onClick = 16843375; // 0x101026f
     field public static final int oneshot = 16843159; // 0x1010197
     field public static final int opacity = 16843550; // 0x101031e
@@ -1227,6 +1230,8 @@
     field public static final int startColor = 16843165; // 0x101019d
     field public static final int startDelay = 16843746; // 0x10103e2
     field public static final int startOffset = 16843198; // 0x10101be
+    field public static final int startX = 16844049; // 0x1010511
+    field public static final int startY = 16844050; // 0x1010512
     field public static final deprecated int startYear = 16843132; // 0x101017c
     field public static final int stateListAnimator = 16843848; // 0x1010448
     field public static final int stateNotNeeded = 16842774; // 0x1010016
@@ -6772,8 +6777,9 @@
     field public static final int TAG_ADB_SHELL_CMD = 210002; // 0x33452
     field public static final int TAG_ADB_SHELL_INTERACTIVE = 210001; // 0x33451
     field public static final int TAG_APP_PROCESS_START = 210005; // 0x33455
-    field public static final int TAG_DEVICE_LOCKED = 210007; // 0x33457
-    field public static final int TAG_DEVICE_UNLOCK_ATTEMPT = 210006; // 0x33456
+    field public static final int TAG_KEYGUARD_DISMISSED = 210006; // 0x33456
+    field public static final int TAG_KEYGUARD_DISMISS_AUTH_ATTEMPT = 210007; // 0x33457
+    field public static final int TAG_KEYGUARD_SECURED = 210008; // 0x33458
     field public static final int TAG_SYNC_RECV_FILE = 210003; // 0x33453
     field public static final int TAG_SYNC_SEND_FILE = 210004; // 0x33454
   }
@@ -9953,6 +9959,7 @@
     method public abstract boolean isPermissionRevokedByPolicy(java.lang.String, java.lang.String);
     method public abstract boolean isSafeMode();
     method public abstract java.util.List<android.content.pm.ResolveInfo> queryBroadcastReceivers(android.content.Intent, int);
+    method public java.util.List<android.content.pm.ResolveInfo> queryBroadcastReceiversAsUser(android.content.Intent, int, android.os.UserHandle);
     method public abstract java.util.List<android.content.pm.ProviderInfo> queryContentProviders(java.lang.String, int, int);
     method public abstract java.util.List<android.content.pm.InstrumentationInfo> queryInstrumentation(java.lang.String, int);
     method public abstract java.util.List<android.content.pm.ResolveInfo> queryIntentActivities(android.content.Intent, int);
@@ -10352,7 +10359,7 @@
     method public final long skip(long) throws java.io.IOException;
   }
 
-  public class ColorStateList implements android.os.Parcelable {
+  public class ColorStateList extends android.content.res.ComplexColor implements android.os.Parcelable {
     ctor public ColorStateList(int[][], int[]);
     method public static deprecated android.content.res.ColorStateList createFromXml(android.content.res.Resources, org.xmlpull.v1.XmlPullParser) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
     method public static android.content.res.ColorStateList createFromXml(android.content.res.Resources, org.xmlpull.v1.XmlPullParser, android.content.res.Resources.Theme) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
@@ -10361,13 +10368,18 @@
     method public int getColorForState(int[], int);
     method public int getDefaultColor();
     method public boolean isOpaque();
-    method public boolean isStateful();
     method public static android.content.res.ColorStateList valueOf(int);
     method public android.content.res.ColorStateList withAlpha(int);
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.content.res.ColorStateList> CREATOR;
   }
 
+  public abstract class ComplexColor {
+    ctor public ComplexColor();
+    method public abstract int getDefaultColor();
+    method public boolean isStateful();
+  }
+
   public final class Configuration implements java.lang.Comparable android.os.Parcelable {
     ctor public Configuration();
     ctor public Configuration(android.content.res.Configuration);
@@ -10471,6 +10483,11 @@
     field public int uiMode;
   }
 
+  public class GradientColor extends android.content.res.ComplexColor {
+    method public static android.content.res.GradientColor createFromXml(android.content.res.Resources, android.content.res.XmlResourceParser, android.content.res.Resources.Theme) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
+    method public int getDefaultColor();
+  }
+
   public class ObbInfo implements android.os.Parcelable {
     method public int describeContents();
     method public void writeToParcel(android.os.Parcel, int);
@@ -10530,6 +10547,7 @@
     method public void getValue(java.lang.String, android.util.TypedValue, boolean) throws android.content.res.Resources.NotFoundException;
     method public void getValueForDensity(int, int, android.util.TypedValue, boolean) throws android.content.res.Resources.NotFoundException;
     method public android.content.res.XmlResourceParser getXml(int) throws android.content.res.Resources.NotFoundException;
+    method public android.content.res.ComplexColor loadComplexColor(android.util.TypedValue, int, android.content.res.Resources.Theme);
     method public final android.content.res.Resources.Theme newTheme();
     method public android.content.res.TypedArray obtainAttributes(android.util.AttributeSet, int[]);
     method public android.content.res.TypedArray obtainTypedArray(int) throws android.content.res.Resources.NotFoundException;
@@ -10565,6 +10583,7 @@
     method public int getChangingConfigurations();
     method public int getColor(int, int);
     method public android.content.res.ColorStateList getColorStateList(int);
+    method public android.content.res.ComplexColor getComplexColor(int);
     method public float getDimension(int, float);
     method public int getDimensionPixelOffset(int, int);
     method public int getDimensionPixelSize(int, int);
@@ -13761,6 +13780,7 @@
     method public float getResolution();
     method public java.lang.String getStringType();
     method public int getType();
+    method public java.util.UUID getUuid();
     method public java.lang.String getVendor();
     method public int getVersion();
     method public boolean isDataInjectionSupported();
@@ -13771,6 +13791,7 @@
     field public static final int REPORTING_MODE_SPECIAL_TRIGGER = 3; // 0x3
     field public static final java.lang.String STRING_TYPE_ACCELEROMETER = "android.sensor.accelerometer";
     field public static final java.lang.String STRING_TYPE_AMBIENT_TEMPERATURE = "android.sensor.ambient_temperature";
+    field public static final java.lang.String STRING_TYPE_DYNAMIC_SENSOR_META = "android.sensor.dynamic_sensor_meta";
     field public static final java.lang.String STRING_TYPE_GAME_ROTATION_VECTOR = "android.sensor.game_rotation_vector";
     field public static final java.lang.String STRING_TYPE_GEOMAGNETIC_ROTATION_VECTOR = "android.sensor.geomagnetic_rotation_vector";
     field public static final java.lang.String STRING_TYPE_GRAVITY = "android.sensor.gravity";
@@ -13798,6 +13819,7 @@
     field public static final int TYPE_ACCELEROMETER = 1; // 0x1
     field public static final int TYPE_ALL = -1; // 0xffffffff
     field public static final int TYPE_AMBIENT_TEMPERATURE = 13; // 0xd
+    field public static final int TYPE_DYNAMIC_SENSOR_META = 32; // 0x20
     field public static final int TYPE_GAME_ROTATION_VECTOR = 15; // 0xf
     field public static final int TYPE_GEOMAGNETIC_ROTATION_VECTOR = 20; // 0x14
     field public static final int TYPE_GRAVITY = 9; // 0x9
@@ -13824,6 +13846,21 @@
     field public static final int TYPE_WRIST_TILT_GESTURE = 26; // 0x1a
   }
 
+  public class SensorAdditionalInfo {
+    field public static final int TYPE_FRAME_BEGIN = 0; // 0x0
+    field public static final int TYPE_FRAME_END = 1; // 0x1
+    field public static final int TYPE_INTERNAL_TEMPERATURE = 65537; // 0x10001
+    field public static final int TYPE_SAMPLING = 65540; // 0x10004
+    field public static final int TYPE_SENSOR_PLACEMENT = 65539; // 0x10003
+    field public static final int TYPE_UNTRACKED_DELAY = 65536; // 0x10000
+    field public static final int TYPE_VEC3_CALIBRATION = 65538; // 0x10002
+    field public final float[] floatValues;
+    field public final int[] intValues;
+    field public final android.hardware.Sensor sensor;
+    field public final int serial;
+    field public final int type;
+  }
+
   public class SensorEvent {
     field public int accuracy;
     field public android.hardware.Sensor sensor;
@@ -13831,6 +13868,14 @@
     field public final float[] values;
   }
 
+  public abstract class SensorEventCallback implements android.hardware.SensorEventListener2 {
+    ctor public SensorEventCallback();
+    method public void onAccuracyChanged(android.hardware.Sensor, int);
+    method public void onFlushCompleted(android.hardware.Sensor);
+    method public void onSensorAdditionalInfo(android.hardware.SensorAdditionalInfo);
+    method public void onSensorChanged(android.hardware.SensorEvent);
+  }
+
   public abstract interface SensorEventListener {
     method public abstract void onAccuracyChanged(android.hardware.Sensor, int);
     method public abstract void onSensorChanged(android.hardware.SensorEvent);
@@ -13852,6 +13897,7 @@
     method public static void getAngleChange(float[], float[], float[]);
     method public android.hardware.Sensor getDefaultSensor(int);
     method public android.hardware.Sensor getDefaultSensor(int, boolean);
+    method public java.util.List<android.hardware.Sensor> getDynamicSensorList(int);
     method public static float getInclination(float[]);
     method public static float[] getOrientation(float[], float[]);
     method public static void getQuaternionFromVector(float[], float[]);
@@ -13861,6 +13907,8 @@
     method public deprecated int getSensors();
     method public boolean initDataInjection(boolean);
     method public boolean injectSensorData(android.hardware.Sensor, float[], int, long);
+    method public void registerDynamicSensorCallback(android.hardware.SensorManager.DynamicSensorConnectionCallback);
+    method public void registerDynamicSensorCallback(android.hardware.SensorManager.DynamicSensorConnectionCallback, android.os.Handler);
     method public deprecated boolean registerListener(android.hardware.SensorListener, int);
     method public deprecated boolean registerListener(android.hardware.SensorListener, int, int);
     method public boolean registerListener(android.hardware.SensorEventListener, android.hardware.Sensor, int);
@@ -13869,6 +13917,7 @@
     method public boolean registerListener(android.hardware.SensorEventListener, android.hardware.Sensor, int, int, android.os.Handler);
     method public static boolean remapCoordinateSystem(float[], int, int, float[]);
     method public boolean requestTriggerSensor(android.hardware.TriggerEventListener, android.hardware.Sensor);
+    method public void unregisterDynamicSensorCallback(android.hardware.SensorManager.DynamicSensorConnectionCallback);
     method public deprecated void unregisterListener(android.hardware.SensorListener);
     method public deprecated void unregisterListener(android.hardware.SensorListener, int);
     method public void unregisterListener(android.hardware.SensorEventListener, android.hardware.Sensor);
@@ -13933,6 +13982,12 @@
     field public static final float STANDARD_GRAVITY = 9.80665f;
   }
 
+  public static abstract class SensorManager.DynamicSensorConnectionCallback {
+    ctor public SensorManager.DynamicSensorConnectionCallback();
+    method public void onDynamicSensorConnected(android.hardware.Sensor);
+    method public void onDynamicSensorDisconnected(android.hardware.Sensor);
+  }
+
   public final class TriggerEvent {
     field public android.hardware.Sensor sensor;
     field public long timestamp;
@@ -15072,6 +15127,66 @@
 
 package android.hardware.location {
 
+  public class ContextHubInfo {
+    ctor public ContextHubInfo();
+    method public int describeContents();
+    method public int getId();
+    method public android.hardware.location.MemoryRegion[] getMemoryRegions();
+    method public java.lang.String getName();
+    method public float getPeakMips();
+    method public float getPeakPowerDrawMw();
+    method public int getPlatformVersion();
+    method public float getSleepPowerDrawMw();
+    method public int getStaticSwVersion();
+    method public float getStoppedPowerDrawMw();
+    method public int[] getSupportedSensors();
+    method public java.lang.String getToolchain();
+    method public int getToolchainVersion();
+    method public java.lang.String getVendor();
+    method public void setId(int);
+    method public void setMemoryRegions(android.hardware.location.MemoryRegion[]);
+    method public void setName(java.lang.String);
+    method public void setPeakMips(float);
+    method public void setPeakPowerDrawMw(float);
+    method public void setPlatformVersion(int);
+    method public void setSleepPowerDrawMw(float);
+    method public void setStaticSwVersion(int);
+    method public void setStoppedPowerDrawMw(float);
+    method public void setSupportedSensors(int[]);
+    method public void setToolchain(java.lang.String);
+    method public void setToolchainVersion(int);
+    method public void setVendor(java.lang.String);
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.hardware.location.ContextHubInfo> CREATOR;
+  }
+
+  public final class ContextHubManager {
+    method public java.lang.Integer[] findNanoAppOnHub(int, android.hardware.location.NanoAppFilter);
+    method public int[] getContexthubHandles();
+    method public android.hardware.location.ContextHubInfo getContexthubInfo(int);
+    method public android.hardware.location.NanoAppInstanceInfo getNanoAppInstanceInfo(int);
+    method public int loadNanoApp(int, android.hardware.location.NanoApp);
+    method public int sendMessage(int, int, android.hardware.location.ContextHubMessage);
+    method public int unloadNanoApp(int);
+    field public static final int ANY_HUB = -1; // 0xffffffff
+    field public static final int MSG_DATA_SEND = 3; // 0x3
+    field public static final int MSG_LOAD_NANO_APP = 1; // 0x1
+    field public static final int MSG_UNLOAD_NANO_APP = 2; // 0x2
+  }
+
+  public class ContextHubMessage {
+    ctor public ContextHubMessage(int, int, byte[]);
+    method public int describeContents();
+    method public byte[] getData();
+    method public int getMsgType();
+    method public int getVersion();
+    method public void setMsgData(byte[]);
+    method public void setMsgType(int);
+    method public void setVersion(int);
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.hardware.location.ContextHubMessage> CREATOR;
+  }
+
   public final class GeofenceHardware {
     ctor public GeofenceHardware(android.hardware.location.IGeofenceHardware);
     method public boolean addGeofence(int, int, android.hardware.location.GeofenceHardwareRequest, android.hardware.location.GeofenceHardwareCallback);
@@ -15188,6 +15303,89 @@
     method public abstract void onMonitoringSystemChange(android.hardware.location.GeofenceHardwareMonitorEvent) throws android.os.RemoteException;
   }
 
+  public class MemoryRegion implements android.os.Parcelable {
+    ctor public MemoryRegion(android.os.Parcel);
+    method public int describeContents();
+    method public int getCapacityBytes();
+    method public int getFreeCapacityBytes();
+    method public boolean isExecutable();
+    method public boolean isReadable();
+    method public boolean isWritable();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.hardware.location.MemoryRegion> CREATOR;
+  }
+
+  public class NanoApp {
+    ctor public NanoApp();
+    method public int describeContents();
+    method public byte[] getAppBinary();
+    method public int getAppId();
+    method public int getAppVersion();
+    method public java.lang.String getName();
+    method public int getNeededExecMemBytes();
+    method public int getNeededReadMemBytes();
+    method public int[] getNeededSensors();
+    method public int getNeededWriteMemBytes();
+    method public int[] getOutputEvents();
+    method public java.lang.String getPublisher();
+    method public void setAppBinary(byte[]);
+    method public void setAppId(int);
+    method public void setAppVersion(int);
+    method public void setName(java.lang.String);
+    method public void setNeededExecMemBytes(int);
+    method public void setNeededReadMemBytes(int);
+    method public void setNeededSensors(int[]);
+    method public void setNeededWriteMemBytes(int);
+    method public void setOutputEvents(int[]);
+    method public void setPublisher(java.lang.String);
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.hardware.location.NanoApp> CREATOR;
+  }
+
+  public class NanoAppFilter {
+    ctor public NanoAppFilter(long, int, int, long);
+    method public int describeContents();
+    method public boolean testMatch(android.hardware.location.NanoAppInstanceInfo);
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final int APP_ANY = -1; // 0xffffffff
+    field public static final android.os.Parcelable.Creator<android.hardware.location.NanoAppFilter> CREATOR;
+    field public static final int FLAGS_VERSION_ANY = -1; // 0xffffffff
+    field public static final int FLAGS_VERSION_GREAT_THAN = 2; // 0x2
+    field public static final int FLAGS_VERSION_LESS_THAN = 4; // 0x4
+    field public static final int FLAGS_VERSION_STRICTLY_EQUAL = 8; // 0x8
+    field public static final int HUB_ANY = -1; // 0xffffffff
+    field public static final int VENDOR_ANY = -1; // 0xffffffff
+  }
+
+  public class NanoAppInstanceInfo {
+    ctor public NanoAppInstanceInfo();
+    method public int describeContents();
+    method public int getAppId();
+    method public int getAppVersion();
+    method public int getContexthubId();
+    method public int getHandle();
+    method public java.lang.String getName();
+    method public int getNeededExecMemBytes();
+    method public int getNeededReadMemBytes();
+    method public int[] getNeededSensors();
+    method public int getNeededWriteMemBytes();
+    method public int[] getOutputEvents();
+    method public java.lang.String getPublisher();
+    method public void setAppId(int);
+    method public void setAppVersion(int);
+    method public void setContexthubId(int);
+    method public void setHandle(int);
+    method public void setName(java.lang.String);
+    method public void setNeededExecMemBytes(int);
+    method public void setNeededReadMemBytes(int);
+    method public void setNeededSensors(int[]);
+    method public void setNeededWriteMemBytes(int);
+    method public void setOutputEvents(int[]);
+    method public void setPublisher(java.lang.String);
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.hardware.location.NanoAppInstanceInfo> CREATOR;
+  }
+
 }
 
 package android.hardware.radio {
@@ -20062,7 +20260,38 @@
     method public static boolean isPresent();
   }
 
-  public class GpsClock implements android.os.Parcelable {
+  public abstract interface GnssNmeaListener {
+    method public abstract void onNmeaReceived(long, java.lang.String);
+  }
+
+  public final class GnssStatus {
+    method public float getAzimuth(int);
+    method public int getConstellationType(int);
+    method public float getElevation(int);
+    method public int getNumSatellites();
+    method public int getPrn(int);
+    method public float getSnr(int);
+    method public boolean hasAlmanac(int);
+    method public boolean hasEphemeris(int);
+    method public boolean usedInFix(int);
+    field public static final int CONSTELLATION_BEIDOU = 5; // 0x5
+    field public static final int CONSTELLATION_GALILEO = 6; // 0x6
+    field public static final int CONSTELLATION_GLONASS = 3; // 0x3
+    field public static final int CONSTELLATION_GPS = 1; // 0x1
+    field public static final int CONSTELLATION_QZSS = 4; // 0x4
+    field public static final int CONSTELLATION_SBAS = 2; // 0x2
+    field public static final int CONSTELLATION_UNKNOWN = 0; // 0x0
+  }
+
+  public abstract class GnssStatusCallback {
+    ctor public GnssStatusCallback();
+    method public void onFirstFix(int);
+    method public void onSatelliteStatusChanged(android.location.GnssStatus);
+    method public void onStarted();
+    method public void onStopped();
+  }
+
+  public final class GpsClock implements android.os.Parcelable {
     method public int describeContents();
     method public double getBiasInNs();
     method public double getBiasUncertaintyInNs();
@@ -20071,6 +20300,7 @@
     method public long getFullBiasInNs();
     method public short getLeapSecond();
     method public long getTimeInNs();
+    method public long getTimeOfLastHwClockDiscontinuityInNs();
     method public double getTimeUncertaintyInNs();
     method public byte getType();
     method public boolean hasBiasInNs();
@@ -20096,16 +20326,20 @@
     method public void setFullBiasInNs(long);
     method public void setLeapSecond(short);
     method public void setTimeInNs(long);
+    method public void setTimeOfLastHwClockDiscontinuityInNs(long);
     method public void setTimeUncertaintyInNs(double);
     method public void setType(byte);
     method public void writeToParcel(android.os.Parcel, int);
+    field public static final byte CLOCK_TYPE_GPS_TIME = 2; // 0x2
+    field public static final byte CLOCK_TYPE_LOCAL_HW_TIME = 1; // 0x1
+    field public static final byte CLOCK_TYPE_UNKNOWN = 0; // 0x0
     field public static final android.os.Parcelable.Creator<android.location.GpsClock> CREATOR;
-    field public static final byte TYPE_GPS_TIME = 2; // 0x2
-    field public static final byte TYPE_LOCAL_HW_TIME = 1; // 0x1
-    field public static final byte TYPE_UNKNOWN = 0; // 0x0
   }
 
-  public class GpsMeasurement implements android.os.Parcelable {
+  public static abstract class GpsClock.GpsClockType implements java.lang.annotation.Annotation {
+  }
+
+  public final class GpsMeasurement implements android.os.Parcelable {
     method public int describeContents();
     method public double getAccumulatedDeltaRangeInMeters();
     method public short getAccumulatedDeltaRangeState();
@@ -20128,6 +20362,8 @@
     method public byte getMultipathIndicator();
     method public byte getPrn();
     method public double getPseudorangeInMeters();
+    method public double getPseudorangeRateCarrierInMetersPerSec();
+    method public double getPseudorangeRateCarrierUncertaintyInMetersPerSec();
     method public double getPseudorangeRateInMetersPerSec();
     method public double getPseudorangeRateUncertaintyInMetersPerSec();
     method public double getPseudorangeUncertaintyInMeters();
@@ -20196,6 +20432,8 @@
     method public void setMultipathIndicator(byte);
     method public void setPrn(byte);
     method public void setPseudorangeInMeters(double);
+    method public void setPseudorangeRateCarrierInMetersPerSec(double);
+    method public void setPseudorangeRateCarrierUncertaintyInMetersPerSec(double);
     method public void setPseudorangeRateInMetersPerSec(double);
     method public void setPseudorangeRateUncertaintyInMetersPerSec(double);
     method public void setPseudorangeUncertaintyInMeters(double);
@@ -20226,7 +20464,13 @@
     field public static final short STATE_UNKNOWN = 0; // 0x0
   }
 
-  public class GpsMeasurementsEvent implements android.os.Parcelable {
+  public static abstract class GpsMeasurement.LossOfLockStatus implements java.lang.annotation.Annotation {
+  }
+
+  public static abstract class GpsMeasurement.MultipathIndicator implements java.lang.annotation.Annotation {
+  }
+
+  public final class GpsMeasurementsEvent implements android.os.Parcelable {
     ctor public GpsMeasurementsEvent(android.location.GpsClock, android.location.GpsMeasurement[]);
     method public int describeContents();
     method public android.location.GpsClock getClock();
@@ -20238,12 +20482,16 @@
     field public static final int STATUS_READY = 1; // 0x1
   }
 
-  public static abstract interface GpsMeasurementsEvent.Listener {
-    method public abstract void onGpsMeasurementsReceived(android.location.GpsMeasurementsEvent);
-    method public abstract void onStatusChanged(int);
+  public static abstract class GpsMeasurementsEvent.Callback {
+    ctor public GpsMeasurementsEvent.Callback();
+    method public void onGpsMeasurementsReceived(android.location.GpsMeasurementsEvent);
+    method public void onStatusChanged(int);
   }
 
-  public class GpsNavigationMessage implements android.os.Parcelable {
+  public static abstract class GpsMeasurementsEvent.GpsMeasurementsStatus implements java.lang.annotation.Annotation {
+  }
+
+  public final class GpsNavigationMessage implements android.os.Parcelable {
     method public int describeContents();
     method public byte[] getData();
     method public short getMessageId();
@@ -20261,30 +20509,37 @@
     method public void setType(byte);
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.location.GpsNavigationMessage> CREATOR;
+    field public static final byte MESSAGE_TYPE_CNAV2 = 4; // 0x4
+    field public static final byte MESSAGE_TYPE_L1CA = 1; // 0x1
+    field public static final byte MESSAGE_TYPE_L2CNAV = 2; // 0x2
+    field public static final byte MESSAGE_TYPE_L5CNAV = 3; // 0x3
+    field public static final byte MESSAGE_TYPE_UNKNOWN = 0; // 0x0
     field public static final short STATUS_PARITY_PASSED = 1; // 0x1
     field public static final short STATUS_PARITY_REBUILT = 2; // 0x2
     field public static final short STATUS_UNKNOWN = 0; // 0x0
-    field public static final byte TYPE_CNAV2 = 4; // 0x4
-    field public static final byte TYPE_L1CA = 1; // 0x1
-    field public static final byte TYPE_L2CNAV = 2; // 0x2
-    field public static final byte TYPE_L5CNAV = 3; // 0x3
-    field public static final byte TYPE_UNKNOWN = 0; // 0x0
   }
 
-  public class GpsNavigationMessageEvent implements android.os.Parcelable {
+  public static abstract class GpsNavigationMessage.GpsNavigationMessageType implements java.lang.annotation.Annotation {
+  }
+
+  public final class GpsNavigationMessageEvent implements android.os.Parcelable {
     ctor public GpsNavigationMessageEvent(android.location.GpsNavigationMessage);
     method public int describeContents();
     method public android.location.GpsNavigationMessage getNavigationMessage();
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.location.GpsNavigationMessageEvent> CREATOR;
-    field public static int STATUS_GPS_LOCATION_DISABLED;
-    field public static int STATUS_NOT_SUPPORTED;
-    field public static int STATUS_READY;
+    field public static final int STATUS_GPS_LOCATION_DISABLED = 2; // 0x2
+    field public static final int STATUS_NOT_SUPPORTED = 0; // 0x0
+    field public static final int STATUS_READY = 1; // 0x1
   }
 
-  public static abstract interface GpsNavigationMessageEvent.Listener {
-    method public abstract void onGpsNavigationMessageReceived(android.location.GpsNavigationMessageEvent);
-    method public abstract void onStatusChanged(int);
+  public static abstract class GpsNavigationMessageEvent.Callback {
+    ctor public GpsNavigationMessageEvent.Callback();
+    method public void onGpsNavigationMessageReceived(android.location.GpsNavigationMessageEvent);
+    method public void onStatusChanged(int);
+  }
+
+  public static abstract class GpsNavigationMessageEvent.GpsNavigationMessageStatus implements java.lang.annotation.Annotation {
   }
 
   public final class GpsSatellite {
@@ -20391,10 +20646,10 @@
   }
 
   public class LocationManager {
-    method public boolean addGpsMeasurementListener(android.location.GpsMeasurementsEvent.Listener);
-    method public boolean addGpsNavigationMessageListener(android.location.GpsNavigationMessageEvent.Listener);
-    method public boolean addGpsStatusListener(android.location.GpsStatus.Listener);
-    method public boolean addNmeaListener(android.location.GpsStatus.NmeaListener);
+    method public deprecated boolean addGpsStatusListener(android.location.GpsStatus.Listener);
+    method public deprecated boolean addNmeaListener(android.location.GpsStatus.NmeaListener);
+    method public boolean addNmeaListener(android.location.GnssNmeaListener);
+    method public boolean addNmeaListener(android.location.GnssNmeaListener, android.os.Handler);
     method public void addProximityAlert(double, double, float, long, android.app.PendingIntent);
     method public void addTestProvider(java.lang.String, boolean, boolean, boolean, boolean, boolean, boolean, boolean, int, int);
     method public void clearTestProviderEnabled(java.lang.String);
@@ -20402,16 +20657,21 @@
     method public void clearTestProviderStatus(java.lang.String);
     method public java.util.List<java.lang.String> getAllProviders();
     method public java.lang.String getBestProvider(android.location.Criteria, boolean);
-    method public android.location.GpsStatus getGpsStatus(android.location.GpsStatus);
+    method public deprecated android.location.GpsStatus getGpsStatus(android.location.GpsStatus);
     method public android.location.Location getLastKnownLocation(java.lang.String);
     method public android.location.LocationProvider getProvider(java.lang.String);
     method public java.util.List<java.lang.String> getProviders(boolean);
     method public java.util.List<java.lang.String> getProviders(android.location.Criteria, boolean);
     method public boolean isProviderEnabled(java.lang.String);
-    method public void removeGpsMeasurementListener(android.location.GpsMeasurementsEvent.Listener);
-    method public void removeGpsNavigationMessageListener(android.location.GpsNavigationMessageEvent.Listener);
-    method public void removeGpsStatusListener(android.location.GpsStatus.Listener);
-    method public void removeNmeaListener(android.location.GpsStatus.NmeaListener);
+    method public boolean registerGnssStatusCallback(android.location.GnssStatusCallback);
+    method public boolean registerGnssStatusCallback(android.location.GnssStatusCallback, android.os.Handler);
+    method public boolean registerGpsMeasurementCallback(android.location.GpsMeasurementsEvent.Callback);
+    method public boolean registerGpsMeasurementCallback(android.location.GpsMeasurementsEvent.Callback, android.os.Handler);
+    method public boolean registerGpsNavigationMessageCallback(android.location.GpsNavigationMessageEvent.Callback);
+    method public boolean registerGpsNavigationMessageCallback(android.location.GpsNavigationMessageEvent.Callback, android.os.Handler);
+    method public deprecated void removeGpsStatusListener(android.location.GpsStatus.Listener);
+    method public deprecated void removeNmeaListener(android.location.GpsStatus.NmeaListener);
+    method public void removeNmeaListener(android.location.GnssNmeaListener);
     method public void removeProximityAlert(android.app.PendingIntent);
     method public void removeTestProvider(java.lang.String);
     method public void removeUpdates(android.location.LocationListener);
@@ -20431,6 +20691,9 @@
     method public void setTestProviderEnabled(java.lang.String, boolean);
     method public void setTestProviderLocation(java.lang.String, android.location.Location);
     method public void setTestProviderStatus(java.lang.String, int, android.os.Bundle, long);
+    method public void unregisterGnssStatusCallback(android.location.GnssStatusCallback);
+    method public void unregisterGpsMeasurementCallback(android.location.GpsMeasurementsEvent.Callback);
+    method public void unregisterGpsNavigationMessageCallback(android.location.GpsNavigationMessageEvent.Callback);
     field public static final java.lang.String GPS_PROVIDER = "gps";
     field public static final java.lang.String KEY_LOCATION_CHANGED = "location";
     field public static final java.lang.String KEY_PROVIDER_ENABLED = "providerEnabled";
@@ -20928,8 +21191,11 @@
 
   public class AudioRecordConfiguration implements android.os.Parcelable {
     method public int describeContents();
-    method public int getAudioSessionId();
+    method public android.media.AudioDeviceInfo getAudioDevice();
+    method public int getClientAudioSessionId();
     method public int getClientAudioSource();
+    method public android.media.AudioFormat getClientFormat();
+    method public android.media.AudioFormat getFormat();
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.media.AudioRecordConfiguration> CREATOR;
   }
@@ -21103,6 +21369,16 @@
     ctor public DeniedByServerException(java.lang.String);
   }
 
+  public abstract class DrmInitData {
+    ctor public DrmInitData();
+    method public abstract android.media.DrmInitData.SchemeInitData get(java.util.UUID);
+  }
+
+  public static final class DrmInitData.SchemeInitData {
+    field public final byte[] data;
+    field public final java.lang.String mimeType;
+  }
+
   public class ExifInterface {
     ctor public ExifInterface(java.lang.String) throws java.io.IOException;
     method public double getAltitude(double);
@@ -21793,6 +22069,7 @@
     ctor public MediaExtractor();
     method public boolean advance();
     method public long getCachedDuration();
+    method public android.media.DrmInitData getDrmInitData();
     method public java.util.Map<java.util.UUID, byte[]> getPsshInfo();
     method public boolean getSampleCryptoInfo(android.media.MediaCodec.CryptoInfo);
     method public int getSampleFlags();
@@ -21837,6 +22114,16 @@
     method public final void setInteger(java.lang.String, int);
     method public final void setLong(java.lang.String, long);
     method public final void setString(java.lang.String, java.lang.String);
+    field public static final int COLOR_RANGE_FULL = 1; // 0x1
+    field public static final int COLOR_RANGE_LIMITED = 2; // 0x2
+    field public static final int COLOR_STANDARD_BT2020 = 6; // 0x6
+    field public static final int COLOR_STANDARD_BT601_NTSC = 4; // 0x4
+    field public static final int COLOR_STANDARD_BT601_PAL = 2; // 0x2
+    field public static final int COLOR_STANDARD_BT709 = 1; // 0x1
+    field public static final int COLOR_TRANSFER_HLG = 7; // 0x7
+    field public static final int COLOR_TRANSFER_LINEAR = 1; // 0x1
+    field public static final int COLOR_TRANSFER_SDR_VIDEO = 3; // 0x3
+    field public static final int COLOR_TRANSFER_ST2084 = 6; // 0x6
     field public static final java.lang.String KEY_AAC_DRC_ATTENUATION_FACTOR = "aac-drc-cut-level";
     field public static final java.lang.String KEY_AAC_DRC_BOOST_FACTOR = "aac-drc-boost-level";
     field public static final java.lang.String KEY_AAC_DRC_HEAVY_COMPRESSION = "aac-drc-heavy-compression";
@@ -21852,6 +22139,9 @@
     field public static final java.lang.String KEY_CHANNEL_COUNT = "channel-count";
     field public static final java.lang.String KEY_CHANNEL_MASK = "channel-mask";
     field public static final java.lang.String KEY_COLOR_FORMAT = "color-format";
+    field public static final java.lang.String KEY_COLOR_RANGE = "color-range";
+    field public static final java.lang.String KEY_COLOR_STANDARD = "color-standard";
+    field public static final java.lang.String KEY_COLOR_TRANSFER = "color-transfer";
     field public static final java.lang.String KEY_COMPLEXITY = "complexity";
     field public static final java.lang.String KEY_DURATION = "durationUs";
     field public static final java.lang.String KEY_FLAC_COMPRESSION_LEVEL = "flac-compression-level";
@@ -23162,12 +23452,15 @@
   public class AudioMixingRule {
     field public static final int RULE_MATCH_ATTRIBUTE_CAPTURE_PRESET = 2; // 0x2
     field public static final int RULE_MATCH_ATTRIBUTE_USAGE = 1; // 0x1
+    field public static final int RULE_MATCH_UID = 4; // 0x4
   }
 
   public static class AudioMixingRule.Builder {
     ctor public AudioMixingRule.Builder();
+    method public android.media.audiopolicy.AudioMixingRule.Builder addMixRule(int, java.lang.Object) throws java.lang.IllegalArgumentException;
     method public android.media.audiopolicy.AudioMixingRule.Builder addRule(android.media.AudioAttributes, int) throws java.lang.IllegalArgumentException;
     method public android.media.audiopolicy.AudioMixingRule build();
+    method public android.media.audiopolicy.AudioMixingRule.Builder excludeMixRule(int, java.lang.Object) throws java.lang.IllegalArgumentException;
     method public android.media.audiopolicy.AudioMixingRule.Builder excludeRule(android.media.AudioAttributes, int) throws java.lang.IllegalArgumentException;
   }
 
@@ -23547,6 +23840,7 @@
     method public void setRatingType(int);
     method public void setSessionActivity(android.app.PendingIntent);
     field public static final int FLAG_HANDLES_MEDIA_BUTTONS = 1; // 0x1
+    field public static final int FLAG_HANDLES_PREPARE_ONLY = 4; // 0x4
     field public static final int FLAG_HANDLES_TRANSPORT_CONTROLS = 2; // 0x2
   }
 
@@ -23625,6 +23919,7 @@
     field public static final long ACTION_SKIP_TO_QUEUE_ITEM = 4096L; // 0x1000L
     field public static final long ACTION_STOP = 1L; // 0x1L
     field public static final android.os.Parcelable.Creator<android.media.session.PlaybackState> CREATOR;
+    field public static final java.lang.String EXTRA_PREPARE_ONLY = "android.media.session.extra.PREPARE_ONLY";
     field public static final long PLAYBACK_POSITION_UNKNOWN = -1L; // 0xffffffffffffffffL
     field public static final int STATE_BUFFERING = 6; // 0x6
     field public static final int STATE_CONNECTING = 8; // 0x8
@@ -23673,6 +23968,38 @@
 
 }
 
+package android.media.soundtrigger {
+
+  public final class SoundTriggerDetector {
+    method public boolean startRecognition();
+    method public boolean stopRecognition();
+  }
+
+  public abstract class SoundTriggerDetector.Callback {
+    ctor public SoundTriggerDetector.Callback();
+    method public abstract void onAvailabilityChanged(int);
+    method public abstract void onDetected();
+    method public abstract void onError();
+    method public abstract void onRecognitionPaused();
+    method public abstract void onRecognitionResumed();
+  }
+
+  public final class SoundTriggerManager {
+    method public android.media.soundtrigger.SoundTriggerDetector createSoundTriggerDetector(java.util.UUID, android.media.soundtrigger.SoundTriggerDetector.Callback, android.os.Handler);
+    method public void deleteModel(java.util.UUID);
+    method public android.media.soundtrigger.SoundTriggerManager.Model getModel(java.util.UUID);
+    method public void updateModel(android.media.soundtrigger.SoundTriggerManager.Model);
+  }
+
+  public static class SoundTriggerManager.Model {
+    method public static android.media.soundtrigger.SoundTriggerManager.Model create(java.util.UUID, java.util.UUID, byte[]);
+    method public byte[] getModelData();
+    method public java.util.UUID getModelUuid();
+    method public java.util.UUID getVendorUuid();
+  }
+
+}
+
 package android.media.tv {
 
   public final class TvContentRating {
@@ -24083,7 +24410,7 @@
     method public void onInputRemoved(java.lang.String);
     method public void onInputStateChanged(java.lang.String, int);
     method public void onInputUpdated(java.lang.String);
-    method public void onTvInputInfoChanged(java.lang.String, android.media.tv.TvInputInfo);
+    method public void onTvInputInfoChanged(android.media.tv.TvInputInfo);
   }
 
   public abstract class TvInputService extends android.app.Service {
@@ -24095,7 +24422,7 @@
     method public java.lang.String onHardwareRemoved(android.media.tv.TvInputHardwareInfo);
     method public android.media.tv.TvInputInfo onHdmiDeviceAdded(android.hardware.hdmi.HdmiDeviceInfo);
     method public java.lang.String onHdmiDeviceRemoved(android.hardware.hdmi.HdmiDeviceInfo);
-    method public final void setTvInputInfo(java.lang.String, android.media.tv.TvInputInfo);
+    method public static final void setTvInputInfo(android.content.Context, android.media.tv.TvInputInfo);
     field public static final java.lang.String SERVICE_INTERFACE = "android.media.tv.TvInputService";
     field public static final java.lang.String SERVICE_META_DATA = "android.media.tv.input";
   }
@@ -24402,6 +24729,7 @@
     field public static final int OPERATION_GET_OBJECT_PROP_VALUE = 38915; // 0x9803
     field public static final int OPERATION_GET_OBJECT_REFERENCES = 38928; // 0x9810
     field public static final int OPERATION_GET_PARTIAL_OBJECT = 4123; // 0x101b
+    field public static final int OPERATION_GET_PARTIAL_OBJECT_64 = 38337; // 0x95c1
     field public static final int OPERATION_GET_STORAGE_INFO = 4101; // 0x1005
     field public static final int OPERATION_GET_STORAGE_I_DS = 4100; // 0x1004
     field public static final int OPERATION_GET_THUMB = 4106; // 0x100a
@@ -24439,6 +24767,7 @@
     method public android.mtp.MtpObjectInfo getObjectInfo(int);
     method public long getParent(int);
     method public long getPartialObject(int, long, long, byte[]) throws java.io.IOException;
+    method public long getPartialObject64(int, long, long, byte[]) throws java.io.IOException;
     method public long getStorageId(int);
     method public int[] getStorageIds();
     method public android.mtp.MtpStorageInfo getStorageInfo(int);
@@ -24447,7 +24776,7 @@
     method public boolean importFile(int, android.os.ParcelFileDescriptor);
     method public boolean open(android.hardware.usb.UsbDeviceConnection);
     method public android.mtp.MtpEvent readEvent(android.os.CancellationSignal);
-    method public boolean sendObject(int, int, android.os.ParcelFileDescriptor);
+    method public boolean sendObject(int, long, android.os.ParcelFileDescriptor);
     method public android.mtp.MtpObjectInfo sendObjectInfo(android.mtp.MtpObjectInfo);
   }
 
@@ -24478,23 +24807,31 @@
     method public final int getAssociationDesc();
     method public final int getAssociationType();
     method public final int getCompressedSize();
+    method public final long getCompressedSizeLong();
     method public final long getDateCreated();
     method public final long getDateModified();
     method public final int getFormat();
     method public final int getImagePixDepth();
+    method public final long getImagePixDepthLong();
     method public final int getImagePixHeight();
+    method public final long getImagePixHeightLong();
     method public final int getImagePixWidth();
+    method public final long getImagePixWidthLong();
     method public final java.lang.String getKeywords();
     method public final java.lang.String getName();
     method public final int getObjectHandle();
     method public final int getParent();
     method public final int getProtectionStatus();
     method public final int getSequenceNumber();
+    method public final long getSequenceNumberLong();
     method public final int getStorageId();
     method public final int getThumbCompressedSize();
+    method public final long getThumbCompressedSizeLong();
     method public final int getThumbFormat();
     method public final int getThumbPixHeight();
+    method public final long getThumbPixHeightLong();
     method public final int getThumbPixWidth();
+    method public final long getThumbPixWidthLong();
   }
 
   public static class MtpObjectInfo.Builder {
@@ -24503,24 +24840,24 @@
     method public android.mtp.MtpObjectInfo build();
     method public android.mtp.MtpObjectInfo.Builder setAssociationDesc(int);
     method public android.mtp.MtpObjectInfo.Builder setAssociationType(int);
-    method public android.mtp.MtpObjectInfo.Builder setCompressedSize(int);
+    method public android.mtp.MtpObjectInfo.Builder setCompressedSize(long);
     method public android.mtp.MtpObjectInfo.Builder setDateCreated(long);
     method public android.mtp.MtpObjectInfo.Builder setDateModified(long);
     method public android.mtp.MtpObjectInfo.Builder setFormat(int);
-    method public android.mtp.MtpObjectInfo.Builder setImagePixDepth(int);
-    method public android.mtp.MtpObjectInfo.Builder setImagePixHeight(int);
-    method public android.mtp.MtpObjectInfo.Builder setImagePixWidth(int);
+    method public android.mtp.MtpObjectInfo.Builder setImagePixDepth(long);
+    method public android.mtp.MtpObjectInfo.Builder setImagePixHeight(long);
+    method public android.mtp.MtpObjectInfo.Builder setImagePixWidth(long);
     method public android.mtp.MtpObjectInfo.Builder setKeywords(java.lang.String);
     method public android.mtp.MtpObjectInfo.Builder setName(java.lang.String);
     method public android.mtp.MtpObjectInfo.Builder setObjectHandle(int);
     method public android.mtp.MtpObjectInfo.Builder setParent(int);
     method public android.mtp.MtpObjectInfo.Builder setProtectionStatus(int);
-    method public android.mtp.MtpObjectInfo.Builder setSequenceNumber(int);
+    method public android.mtp.MtpObjectInfo.Builder setSequenceNumber(long);
     method public android.mtp.MtpObjectInfo.Builder setStorageId(int);
-    method public android.mtp.MtpObjectInfo.Builder setThumbCompressedSize(int);
+    method public android.mtp.MtpObjectInfo.Builder setThumbCompressedSize(long);
     method public android.mtp.MtpObjectInfo.Builder setThumbFormat(int);
-    method public android.mtp.MtpObjectInfo.Builder setThumbPixHeight(int);
-    method public android.mtp.MtpObjectInfo.Builder setThumbPixWidth(int);
+    method public android.mtp.MtpObjectInfo.Builder setThumbPixHeight(long);
+    method public android.mtp.MtpObjectInfo.Builder setThumbPixWidth(long);
   }
 
   public final class MtpStorageInfo {
@@ -30232,6 +30569,10 @@
     field public static final int OPEN = 32; // 0x20
   }
 
+  public class FileUriExposedException extends java.lang.RuntimeException {
+    ctor public FileUriExposedException(java.lang.String);
+  }
+
   public class Handler {
     ctor public Handler();
     ctor public Handler(android.os.Handler.Callback);
@@ -30831,6 +31172,7 @@
     method public android.os.StrictMode.VmPolicy.Builder detectLeakedSqlLiteObjects();
     method public android.os.StrictMode.VmPolicy.Builder penaltyDeath();
     method public android.os.StrictMode.VmPolicy.Builder penaltyDeathOnCleartextNetwork();
+    method public android.os.StrictMode.VmPolicy.Builder penaltyDeathOnFileUriExposure();
     method public android.os.StrictMode.VmPolicy.Builder penaltyDropBox();
     method public android.os.StrictMode.VmPolicy.Builder penaltyLog();
     method public android.os.StrictMode.VmPolicy.Builder setClassInstanceLimit(java.lang.Class, int);
@@ -30874,6 +31216,7 @@
     method public deprecated boolean isOwner();
     method public boolean isSystem();
     method public static int myUserId();
+    method public static android.os.UserHandle of(int);
     method public static android.os.UserHandle readFromParcel(android.os.Parcel);
     method public void writeToParcel(android.os.Parcel, int);
     method public static void writeToParcel(android.os.UserHandle, android.os.Parcel);
@@ -30888,6 +31231,7 @@
     method public android.os.PersistableBundle getSeedAccountOptions();
     method public java.lang.String getSeedAccountType();
     method public long getSerialNumberForUser(android.os.UserHandle);
+    method public long[] getSerialNumbersOfUsers(boolean);
     method public int getUserCount();
     method public long getUserCreationTime(android.os.UserHandle);
     method public android.os.UserHandle getUserForSerialNumber(long);
@@ -30946,6 +31290,8 @@
     field public static final java.lang.String DISALLOW_USB_FILE_TRANSFER = "no_usb_file_transfer";
     field public static final java.lang.String ENSURE_VERIFY_APPS = "ensure_verify_apps";
     field public static final java.lang.String KEY_RESTRICTIONS_PENDING = "restrictions_pending";
+    field public static final int USER_CREATION_FAILED_NOT_PERMITTED = 1; // 0x1
+    field public static final int USER_CREATION_FAILED_NO_MORE_USERS = 2; // 0x2
   }
 
   public abstract class Vibrator {
@@ -31303,6 +31649,9 @@
     method public static void setDefaultValues(android.content.Context, java.lang.String, int, int, boolean);
     method public void setSharedPreferencesMode(int);
     method public void setSharedPreferencesName(java.lang.String);
+    method public void setStorageCredentialEncrypted();
+    method public void setStorageDefault();
+    method public void setStorageDeviceEncrypted();
     field public static final java.lang.String KEY_HAS_SET_DEFAULT_VALUES = "_has_set_default_values";
     field public static final java.lang.String METADATA_KEY_PREFERENCES = "android.preference";
   }
@@ -33327,7 +33676,9 @@
     method public static java.lang.String getSearchDocumentsQuery(android.net.Uri);
     method public static java.lang.String getTreeDocumentId(android.net.Uri);
     method public static boolean isDocumentUri(android.content.Context, android.net.Uri);
+    method public static boolean isTreeUri(android.net.Uri);
     method public static android.net.Uri moveDocument(android.content.ContentResolver, android.net.Uri, android.net.Uri, android.net.Uri);
+    method public static boolean removeDocument(android.content.ContentResolver, android.net.Uri, android.net.Uri);
     method public static android.net.Uri renameDocument(android.content.ContentResolver, android.net.Uri, java.lang.String);
     field public static final java.lang.String EXTRA_ERROR = "error";
     field public static final java.lang.String EXTRA_EXCLUDE_SELF = "android.provider.extra.EXCLUDE_SELF";
@@ -33353,6 +33704,7 @@
     field public static final int FLAG_SUPPORTS_COPY = 128; // 0x80
     field public static final int FLAG_SUPPORTS_DELETE = 4; // 0x4
     field public static final int FLAG_SUPPORTS_MOVE = 256; // 0x100
+    field public static final int FLAG_SUPPORTS_REMOVE = 2048; // 0x800
     field public static final int FLAG_SUPPORTS_RENAME = 64; // 0x40
     field public static final int FLAG_SUPPORTS_THUMBNAIL = 1; // 0x1
     field public static final int FLAG_SUPPORTS_WRITE = 2; // 0x2
@@ -33400,6 +33752,7 @@
     method public android.database.Cursor queryRecentDocuments(java.lang.String, java.lang.String[]) throws java.io.FileNotFoundException;
     method public abstract android.database.Cursor queryRoots(java.lang.String[]) throws java.io.FileNotFoundException;
     method public android.database.Cursor querySearchDocuments(java.lang.String, java.lang.String, java.lang.String[]) throws java.io.FileNotFoundException;
+    method public boolean removeDocument(java.lang.String, java.lang.String) throws java.io.FileNotFoundException;
     method public java.lang.String renameDocument(java.lang.String, java.lang.String) throws java.io.FileNotFoundException;
     method public final void revokeDocumentPermission(java.lang.String);
     method public final int update(android.net.Uri, android.content.ContentValues, java.lang.String, java.lang.String[]);
@@ -33881,6 +34234,7 @@
     field public static final java.lang.String ACTION_INPUT_METHOD_SETTINGS = "android.settings.INPUT_METHOD_SETTINGS";
     field public static final java.lang.String ACTION_INPUT_METHOD_SUBTYPE_SETTINGS = "android.settings.INPUT_METHOD_SUBTYPE_SETTINGS";
     field public static final java.lang.String ACTION_INTERNAL_STORAGE_SETTINGS = "android.settings.INTERNAL_STORAGE_SETTINGS";
+    field public static final java.lang.String ACTION_KEYBOARD_LAYOUT_SETTINGS = "android.settings.KEYBOARD_LAYOUT_SETTINGS";
     field public static final java.lang.String ACTION_LOCALE_SETTINGS = "android.settings.LOCALE_SETTINGS";
     field public static final java.lang.String ACTION_LOCATION_SOURCE_SETTINGS = "android.settings.LOCATION_SOURCE_SETTINGS";
     field public static final java.lang.String ACTION_MANAGE_ALL_APPLICATIONS_SETTINGS = "android.settings.MANAGE_ALL_APPLICATIONS_SETTINGS";
@@ -37712,14 +38066,14 @@
     field public static final java.lang.String SERVICE_INTERFACE = "android.telecom.CallScreeningService";
   }
 
-  public class CallScreeningService.CallResponse {
+  public static class CallScreeningService.CallResponse {
     method public boolean getDisallowCall();
     method public boolean getRejectCall();
     method public boolean getSkipCallLog();
     method public boolean getSkipNotification();
   }
 
-  public class CallScreeningService.CallResponse.Builder {
+  public static class CallScreeningService.CallResponse.Builder {
     ctor public CallScreeningService.CallResponse.Builder();
     method public android.telecom.CallScreeningService.CallResponse build();
     method public android.telecom.CallScreeningService.CallResponse.Builder setDisallowCall(boolean);
@@ -39252,7 +39606,7 @@
     method public void testActivityTestCaseSetUpProperly() throws java.lang.Exception;
   }
 
-  public abstract class ActivityInstrumentationTestCase2 extends android.test.ActivityTestCase {
+  public abstract deprecated class ActivityInstrumentationTestCase2 extends android.test.ActivityTestCase {
     ctor public deprecated ActivityInstrumentationTestCase2(java.lang.String, java.lang.Class<T>);
     ctor public ActivityInstrumentationTestCase2(java.lang.Class<T>);
     method public T getActivity();
@@ -39260,14 +39614,14 @@
     method public void setActivityIntent(android.content.Intent);
   }
 
-  public abstract class ActivityTestCase extends android.test.InstrumentationTestCase {
+  public abstract deprecated class ActivityTestCase extends android.test.InstrumentationTestCase {
     ctor public ActivityTestCase();
     method protected android.app.Activity getActivity();
     method protected void scrubClass(java.lang.Class<?>) throws java.lang.IllegalAccessException;
     method protected void setActivity(android.app.Activity);
   }
 
-  public abstract class ActivityUnitTestCase extends android.test.ActivityTestCase {
+  public abstract deprecated class ActivityUnitTestCase extends android.test.ActivityTestCase {
     ctor public ActivityUnitTestCase(java.lang.Class<T>);
     method public T getActivity();
     method public int getFinishedActivityRequest();
@@ -39280,7 +39634,7 @@
     method protected T startActivity(android.content.Intent, android.os.Bundle, java.lang.Object);
   }
 
-  public class AndroidTestCase extends junit.framework.TestCase {
+  public deprecated class AndroidTestCase extends junit.framework.TestCase {
     ctor public AndroidTestCase();
     method public void assertActivityRequiresPermission(java.lang.String, java.lang.String, java.lang.String);
     method public void assertReadingContentUriRequiresPermission(android.net.Uri, java.lang.String);
@@ -39292,7 +39646,7 @@
     field protected android.content.Context mContext;
   }
 
-  public class AndroidTestRunner extends junit.runner.BaseTestRunner {
+  public deprecated class AndroidTestRunner extends junit.runner.BaseTestRunner {
     ctor public AndroidTestRunner();
     method public void addTestListener(junit.framework.TestListener);
     method public void clearTestListeners();
@@ -39313,7 +39667,7 @@
     method public void testStarted(java.lang.String);
   }
 
-  public abstract class ApplicationTestCase extends android.test.AndroidTestCase {
+  public abstract deprecated class ApplicationTestCase extends android.test.AndroidTestCase {
     ctor public ApplicationTestCase(java.lang.Class<T>);
     method protected final void createApplication();
     method public T getApplication();
@@ -39331,10 +39685,10 @@
     ctor public ComparisonFailure(java.lang.String, java.lang.String, java.lang.String);
   }
 
-  public abstract class FlakyTest implements java.lang.annotation.Annotation {
+  public abstract deprecated class FlakyTest implements java.lang.annotation.Annotation {
   }
 
-  public class InstrumentationTestCase extends junit.framework.TestCase {
+  public deprecated class InstrumentationTestCase extends junit.framework.TestCase {
     ctor public InstrumentationTestCase();
     method public android.app.Instrumentation getInstrumentation();
     method public deprecated void injectInsrumentation(android.app.Instrumentation);
@@ -39347,7 +39701,7 @@
     method public void sendRepeatedKeys(int...);
   }
 
-  public class InstrumentationTestRunner extends android.app.Instrumentation implements android.test.TestSuiteProvider {
+  public deprecated class InstrumentationTestRunner extends android.app.Instrumentation implements android.test.TestSuiteProvider {
     ctor public InstrumentationTestRunner();
     method public junit.framework.TestSuite getAllTests();
     method protected android.test.AndroidTestRunner getAndroidTestRunner();
@@ -39366,14 +39720,14 @@
     field public static final int REPORT_VALUE_RESULT_START = 1; // 0x1
   }
 
-  public class InstrumentationTestSuite extends junit.framework.TestSuite {
+  public deprecated class InstrumentationTestSuite extends junit.framework.TestSuite {
     ctor public InstrumentationTestSuite(android.app.Instrumentation);
     ctor public InstrumentationTestSuite(java.lang.String, android.app.Instrumentation);
     ctor public InstrumentationTestSuite(java.lang.Class, android.app.Instrumentation);
     method public void addTestSuite(java.lang.Class);
   }
 
-  public class IsolatedContext extends android.content.ContextWrapper {
+  public deprecated class IsolatedContext extends android.content.ContextWrapper {
     ctor public IsolatedContext(android.content.ContentResolver, android.content.Context);
     method public java.util.List<android.content.Intent> getAndClearBroadcastIntents();
   }
@@ -39383,7 +39737,7 @@
     method public T getLoaderResultSynchronously(android.content.Loader<T>);
   }
 
-  public final class MoreAsserts {
+  public final deprecated class MoreAsserts {
     method public static void assertAssignableFrom(java.lang.Class<?>, java.lang.Object);
     method public static void assertAssignableFrom(java.lang.Class<?>, java.lang.Class<?>);
     method public static java.util.regex.MatchResult assertContainsRegex(java.lang.String, java.lang.String, java.lang.String);
@@ -39422,7 +39776,7 @@
     method public static void checkEqualsAndHashCodeMethods(java.lang.Object, java.lang.Object, boolean);
   }
 
-  public abstract interface PerformanceTestCase {
+  public abstract deprecated interface PerformanceTestCase {
     method public abstract boolean isPerformanceOnly();
     method public abstract int startPerformance(android.test.PerformanceTestCase.Intermediates);
   }
@@ -39451,7 +39805,7 @@
     method public static android.content.ContentResolver newResolverWithContentProviderFromSql(android.content.Context, java.lang.String, java.lang.Class<T>, java.lang.String, java.lang.String, int, java.lang.String) throws java.lang.IllegalAccessException, java.lang.InstantiationException;
   }
 
-  public class RenamingDelegatingContext extends android.content.ContextWrapper {
+  public deprecated class RenamingDelegatingContext extends android.content.ContextWrapper {
     ctor public RenamingDelegatingContext(android.content.Context, java.lang.String);
     ctor public RenamingDelegatingContext(android.content.Context, android.content.Context, java.lang.String);
     method public java.lang.String getDatabasePrefix();
@@ -39460,7 +39814,7 @@
     method public static T providerWithRenamedContext(java.lang.Class<T>, android.content.Context, java.lang.String, boolean) throws java.lang.IllegalAccessException, java.lang.InstantiationException;
   }
 
-  public abstract class ServiceTestCase extends android.test.AndroidTestCase {
+  public abstract deprecated class ServiceTestCase extends android.test.AndroidTestCase {
     ctor public ServiceTestCase(java.lang.Class<T>);
     method protected android.os.IBinder bindService(android.content.Intent);
     method public android.app.Application getApplication();
@@ -39473,23 +39827,23 @@
     method public void testServiceTestCaseSetUpProperly() throws java.lang.Exception;
   }
 
-  public abstract class SingleLaunchActivityTestCase extends android.test.InstrumentationTestCase {
+  public abstract deprecated class SingleLaunchActivityTestCase extends android.test.InstrumentationTestCase {
     ctor public SingleLaunchActivityTestCase(java.lang.String, java.lang.Class<T>);
     method public T getActivity();
     method public void testActivityTestCaseSetUpProperly() throws java.lang.Exception;
   }
 
-  public class SyncBaseInstrumentation extends android.test.InstrumentationTestCase {
+  public deprecated class SyncBaseInstrumentation extends android.test.InstrumentationTestCase {
     ctor public SyncBaseInstrumentation();
     method protected void cancelSyncsandDisableAutoSync();
     method protected void syncProvider(android.net.Uri, java.lang.String, java.lang.String) throws java.lang.Exception;
   }
 
-  public abstract interface TestSuiteProvider {
+  public abstract deprecated interface TestSuiteProvider {
     method public abstract junit.framework.TestSuite getTestSuite();
   }
 
-  public class TouchUtils {
+  public deprecated class TouchUtils {
     ctor public TouchUtils();
     method public static void clickView(android.test.InstrumentationTestCase, android.view.View);
     method public static deprecated void drag(android.test.ActivityInstrumentationTestCase, float, float, float, float, int);
@@ -39524,10 +39878,10 @@
     method public static void touchAndCancelView(android.test.InstrumentationTestCase, android.view.View);
   }
 
-  public abstract class UiThreadTest implements java.lang.annotation.Annotation {
+  public abstract deprecated class UiThreadTest implements java.lang.annotation.Annotation {
   }
 
-  public class ViewAsserts {
+  public deprecated class ViewAsserts {
     method public static void assertBaselineAligned(android.view.View, android.view.View);
     method public static void assertBottomAligned(android.view.View, android.view.View);
     method public static void assertBottomAligned(android.view.View, android.view.View, int);
@@ -39552,11 +39906,11 @@
 
 package android.test.mock {
 
-  public class MockApplication extends android.app.Application {
+  public deprecated class MockApplication extends android.app.Application {
     ctor public MockApplication();
   }
 
-  public class MockContentProvider extends android.content.ContentProvider {
+  public deprecated class MockContentProvider extends android.content.ContentProvider {
     ctor protected MockContentProvider();
     ctor public MockContentProvider(android.content.Context);
     ctor public MockContentProvider(android.content.Context, java.lang.String, java.lang.String, android.content.pm.PathPermission[]);
@@ -39568,13 +39922,13 @@
     method public int update(android.net.Uri, android.content.ContentValues, java.lang.String, java.lang.String[]);
   }
 
-  public class MockContentResolver extends android.content.ContentResolver {
+  public deprecated class MockContentResolver extends android.content.ContentResolver {
     ctor public MockContentResolver();
     ctor public MockContentResolver(android.content.Context);
     method public void addProvider(java.lang.String, android.content.ContentProvider);
   }
 
-  public class MockContext extends android.content.Context {
+  public deprecated class MockContext extends android.content.Context {
     ctor public MockContext();
     method public boolean bindService(android.content.Intent, android.content.ServiceConnection, int);
     method public int checkCallingOrSelfPermission(java.lang.String);
@@ -39678,7 +40032,7 @@
     method public void unregisterReceiver(android.content.BroadcastReceiver);
   }
 
-  public class MockCursor implements android.database.Cursor {
+  public deprecated class MockCursor implements android.database.Cursor {
     ctor public MockCursor();
     method public void close();
     method public void copyStringToBuffer(int, android.database.CharArrayBuffer);
@@ -39723,13 +40077,13 @@
     method public void unregisterDataSetObserver(android.database.DataSetObserver);
   }
 
-  public class MockDialogInterface implements android.content.DialogInterface {
+  public deprecated class MockDialogInterface implements android.content.DialogInterface {
     ctor public MockDialogInterface();
     method public void cancel();
     method public void dismiss();
   }
 
-  public class MockPackageManager extends android.content.pm.PackageManager {
+  public deprecated class MockPackageManager extends android.content.pm.PackageManager {
     ctor public MockPackageManager();
     method public void addOnPermissionsChangeListener(android.content.pm.PackageManager.OnPermissionsChangedListener);
     method public void addPackageToPreferred(java.lang.String);
@@ -39823,7 +40177,7 @@
     method public void verifyPendingInstall(int, int);
   }
 
-  public class MockResources extends android.content.res.Resources {
+  public deprecated class MockResources extends android.content.res.Resources {
     ctor public MockResources();
   }
 
@@ -39864,19 +40218,19 @@
 
 package android.test.suitebuilder.annotation {
 
-  public abstract class LargeTest implements java.lang.annotation.Annotation {
+  public abstract deprecated class LargeTest implements java.lang.annotation.Annotation {
   }
 
-  public abstract class MediumTest implements java.lang.annotation.Annotation {
+  public abstract deprecated class MediumTest implements java.lang.annotation.Annotation {
   }
 
-  public abstract class SmallTest implements java.lang.annotation.Annotation {
+  public abstract deprecated class SmallTest implements java.lang.annotation.Annotation {
   }
 
-  public abstract class Smoke implements java.lang.annotation.Annotation {
+  public abstract deprecated class Smoke implements java.lang.annotation.Annotation {
   }
 
-  public abstract class Suppress implements java.lang.annotation.Annotation {
+  public abstract deprecated class Suppress implements java.lang.annotation.Annotation {
   }
 
 }
diff --git a/api/system-removed.txt b/api/system-removed.txt
index 90a5dc7..27de913 100644
--- a/api/system-removed.txt
+++ b/api/system-removed.txt
@@ -192,7 +192,7 @@
 
 package android.test.mock {
 
-  public class MockPackageManager extends android.content.pm.PackageManager {
+  public deprecated class MockPackageManager extends android.content.pm.PackageManager {
     method public deprecated java.lang.String getDefaultBrowserPackageName(int);
     method public deprecated boolean setDefaultBrowserPackageName(java.lang.String, int);
   }
diff --git a/api/test-current.txt b/api/test-current.txt
index e049566..22eeef4 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -506,6 +506,8 @@
     field public static final int encryptionAware = 16844038; // 0x1010506
     field public static final int end = 16843996; // 0x10104dc
     field public static final int endColor = 16843166; // 0x101019e
+    field public static final int endX = 16844051; // 0x1010513
+    field public static final int endY = 16844052; // 0x1010514
     field public static final deprecated int endYear = 16843133; // 0x101017d
     field public static final int enterFadeDuration = 16843532; // 0x101030c
     field public static final int entries = 16842930; // 0x10100b2
@@ -881,6 +883,7 @@
     field public static final int numbersTextColor = 16843937; // 0x10104a1
     field public static final deprecated int numeric = 16843109; // 0x1010165
     field public static final int numericShortcut = 16843236; // 0x10101e4
+    field public static final int offset = 16844053; // 0x1010515
     field public static final int onClick = 16843375; // 0x101026f
     field public static final int oneshot = 16843159; // 0x1010197
     field public static final int opacity = 16843550; // 0x101031e
@@ -1128,6 +1131,8 @@
     field public static final int startColor = 16843165; // 0x101019d
     field public static final int startDelay = 16843746; // 0x10103e2
     field public static final int startOffset = 16843198; // 0x10101be
+    field public static final int startX = 16844049; // 0x1010511
+    field public static final int startY = 16844050; // 0x1010512
     field public static final deprecated int startYear = 16843132; // 0x101017c
     field public static final int stateListAnimator = 16843848; // 0x1010448
     field public static final int stateNotNeeded = 16842774; // 0x1010016
@@ -6516,8 +6521,9 @@
     field public static final int TAG_ADB_SHELL_CMD = 210002; // 0x33452
     field public static final int TAG_ADB_SHELL_INTERACTIVE = 210001; // 0x33451
     field public static final int TAG_APP_PROCESS_START = 210005; // 0x33455
-    field public static final int TAG_DEVICE_LOCKED = 210007; // 0x33457
-    field public static final int TAG_DEVICE_UNLOCK_ATTEMPT = 210006; // 0x33456
+    field public static final int TAG_KEYGUARD_DISMISSED = 210006; // 0x33456
+    field public static final int TAG_KEYGUARD_DISMISS_AUTH_ATTEMPT = 210007; // 0x33457
+    field public static final int TAG_KEYGUARD_SECURED = 210008; // 0x33458
     field public static final int TAG_SYNC_RECV_FILE = 210003; // 0x33453
     field public static final int TAG_SYNC_SEND_FILE = 210004; // 0x33454
   }
@@ -9960,7 +9966,7 @@
     method public final long skip(long) throws java.io.IOException;
   }
 
-  public class ColorStateList implements android.os.Parcelable {
+  public class ColorStateList extends android.content.res.ComplexColor implements android.os.Parcelable {
     ctor public ColorStateList(int[][], int[]);
     method public static deprecated android.content.res.ColorStateList createFromXml(android.content.res.Resources, org.xmlpull.v1.XmlPullParser) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
     method public static android.content.res.ColorStateList createFromXml(android.content.res.Resources, org.xmlpull.v1.XmlPullParser, android.content.res.Resources.Theme) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
@@ -9969,13 +9975,18 @@
     method public int getColorForState(int[], int);
     method public int getDefaultColor();
     method public boolean isOpaque();
-    method public boolean isStateful();
     method public static android.content.res.ColorStateList valueOf(int);
     method public android.content.res.ColorStateList withAlpha(int);
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.content.res.ColorStateList> CREATOR;
   }
 
+  public abstract class ComplexColor {
+    ctor public ComplexColor();
+    method public abstract int getDefaultColor();
+    method public boolean isStateful();
+  }
+
   public final class Configuration implements java.lang.Comparable android.os.Parcelable {
     ctor public Configuration();
     ctor public Configuration(android.content.res.Configuration);
@@ -10079,6 +10090,11 @@
     field public int uiMode;
   }
 
+  public class GradientColor extends android.content.res.ComplexColor {
+    method public static android.content.res.GradientColor createFromXml(android.content.res.Resources, android.content.res.XmlResourceParser, android.content.res.Resources.Theme) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
+    method public int getDefaultColor();
+  }
+
   public class ObbInfo implements android.os.Parcelable {
     method public int describeContents();
     method public void writeToParcel(android.os.Parcel, int);
@@ -10138,6 +10154,7 @@
     method public void getValue(java.lang.String, android.util.TypedValue, boolean) throws android.content.res.Resources.NotFoundException;
     method public void getValueForDensity(int, int, android.util.TypedValue, boolean) throws android.content.res.Resources.NotFoundException;
     method public android.content.res.XmlResourceParser getXml(int) throws android.content.res.Resources.NotFoundException;
+    method public android.content.res.ComplexColor loadComplexColor(android.util.TypedValue, int, android.content.res.Resources.Theme);
     method public final android.content.res.Resources.Theme newTheme();
     method public android.content.res.TypedArray obtainAttributes(android.util.AttributeSet, int[]);
     method public android.content.res.TypedArray obtainTypedArray(int) throws android.content.res.Resources.NotFoundException;
@@ -10173,6 +10190,7 @@
     method public int getChangingConfigurations();
     method public int getColor(int, int);
     method public android.content.res.ColorStateList getColorStateList(int);
+    method public android.content.res.ComplexColor getComplexColor(int);
     method public float getDimension(int, float);
     method public int getDimensionPixelOffset(int, int);
     method public int getDimensionPixelSize(int, int);
@@ -13369,6 +13387,7 @@
     method public float getResolution();
     method public java.lang.String getStringType();
     method public int getType();
+    method public java.util.UUID getUuid();
     method public java.lang.String getVendor();
     method public int getVersion();
     method public boolean isWakeUpSensor();
@@ -13429,6 +13448,21 @@
     field public static final deprecated int TYPE_TEMPERATURE = 7; // 0x7
   }
 
+  public class SensorAdditionalInfo {
+    field public static final int TYPE_FRAME_BEGIN = 0; // 0x0
+    field public static final int TYPE_FRAME_END = 1; // 0x1
+    field public static final int TYPE_INTERNAL_TEMPERATURE = 65537; // 0x10001
+    field public static final int TYPE_SAMPLING = 65540; // 0x10004
+    field public static final int TYPE_SENSOR_PLACEMENT = 65539; // 0x10003
+    field public static final int TYPE_UNTRACKED_DELAY = 65536; // 0x10000
+    field public static final int TYPE_VEC3_CALIBRATION = 65538; // 0x10002
+    field public final float[] floatValues;
+    field public final int[] intValues;
+    field public final android.hardware.Sensor sensor;
+    field public final int serial;
+    field public final int type;
+  }
+
   public class SensorEvent {
     field public int accuracy;
     field public android.hardware.Sensor sensor;
@@ -13436,6 +13470,14 @@
     field public final float[] values;
   }
 
+  public abstract class SensorEventCallback implements android.hardware.SensorEventListener2 {
+    ctor public SensorEventCallback();
+    method public void onAccuracyChanged(android.hardware.Sensor, int);
+    method public void onFlushCompleted(android.hardware.Sensor);
+    method public void onSensorAdditionalInfo(android.hardware.SensorAdditionalInfo);
+    method public void onSensorChanged(android.hardware.SensorEvent);
+  }
+
   public abstract interface SensorEventListener {
     method public abstract void onAccuracyChanged(android.hardware.Sensor, int);
     method public abstract void onSensorChanged(android.hardware.SensorEvent);
@@ -13457,6 +13499,7 @@
     method public static void getAngleChange(float[], float[], float[]);
     method public android.hardware.Sensor getDefaultSensor(int);
     method public android.hardware.Sensor getDefaultSensor(int, boolean);
+    method public java.util.List<android.hardware.Sensor> getDynamicSensorList(int);
     method public static float getInclination(float[]);
     method public static float[] getOrientation(float[], float[]);
     method public static void getQuaternionFromVector(float[], float[]);
@@ -13464,6 +13507,8 @@
     method public static void getRotationMatrixFromVector(float[], float[]);
     method public java.util.List<android.hardware.Sensor> getSensorList(int);
     method public deprecated int getSensors();
+    method public void registerDynamicSensorCallback(android.hardware.SensorManager.DynamicSensorConnectionCallback);
+    method public void registerDynamicSensorCallback(android.hardware.SensorManager.DynamicSensorConnectionCallback, android.os.Handler);
     method public deprecated boolean registerListener(android.hardware.SensorListener, int);
     method public deprecated boolean registerListener(android.hardware.SensorListener, int, int);
     method public boolean registerListener(android.hardware.SensorEventListener, android.hardware.Sensor, int);
@@ -13472,6 +13517,7 @@
     method public boolean registerListener(android.hardware.SensorEventListener, android.hardware.Sensor, int, int, android.os.Handler);
     method public static boolean remapCoordinateSystem(float[], int, int, float[]);
     method public boolean requestTriggerSensor(android.hardware.TriggerEventListener, android.hardware.Sensor);
+    method public void unregisterDynamicSensorCallback(android.hardware.SensorManager.DynamicSensorConnectionCallback);
     method public deprecated void unregisterListener(android.hardware.SensorListener);
     method public deprecated void unregisterListener(android.hardware.SensorListener, int);
     method public void unregisterListener(android.hardware.SensorEventListener, android.hardware.Sensor);
@@ -13536,6 +13582,12 @@
     field public static final float STANDARD_GRAVITY = 9.80665f;
   }
 
+  public static abstract class SensorManager.DynamicSensorConnectionCallback {
+    ctor public SensorManager.DynamicSensorConnectionCallback();
+    method public void onDynamicSensorConnected(android.hardware.Sensor);
+    method public void onDynamicSensorDisconnected(android.hardware.Sensor);
+  }
+
   public final class TriggerEvent {
     field public android.hardware.Sensor sensor;
     field public long timestamp;
@@ -19036,6 +19088,288 @@
     method public static boolean isPresent();
   }
 
+  public abstract interface GnssNmeaListener {
+    method public abstract void onNmeaReceived(long, java.lang.String);
+  }
+
+  public final class GnssStatus {
+    method public float getAzimuth(int);
+    method public int getConstellationType(int);
+    method public float getElevation(int);
+    method public int getNumSatellites();
+    method public int getPrn(int);
+    method public float getSnr(int);
+    method public boolean hasAlmanac(int);
+    method public boolean hasEphemeris(int);
+    method public boolean usedInFix(int);
+    field public static final int CONSTELLATION_BEIDOU = 5; // 0x5
+    field public static final int CONSTELLATION_GALILEO = 6; // 0x6
+    field public static final int CONSTELLATION_GLONASS = 3; // 0x3
+    field public static final int CONSTELLATION_GPS = 1; // 0x1
+    field public static final int CONSTELLATION_QZSS = 4; // 0x4
+    field public static final int CONSTELLATION_SBAS = 2; // 0x2
+    field public static final int CONSTELLATION_UNKNOWN = 0; // 0x0
+  }
+
+  public abstract class GnssStatusCallback {
+    ctor public GnssStatusCallback();
+    method public void onFirstFix(int);
+    method public void onSatelliteStatusChanged(android.location.GnssStatus);
+    method public void onStarted();
+    method public void onStopped();
+  }
+
+  public final class GpsClock implements android.os.Parcelable {
+    method public int describeContents();
+    method public double getBiasInNs();
+    method public double getBiasUncertaintyInNs();
+    method public double getDriftInNsPerSec();
+    method public double getDriftUncertaintyInNsPerSec();
+    method public long getFullBiasInNs();
+    method public short getLeapSecond();
+    method public long getTimeInNs();
+    method public long getTimeOfLastHwClockDiscontinuityInNs();
+    method public double getTimeUncertaintyInNs();
+    method public byte getType();
+    method public boolean hasBiasInNs();
+    method public boolean hasBiasUncertaintyInNs();
+    method public boolean hasDriftInNsPerSec();
+    method public boolean hasDriftUncertaintyInNsPerSec();
+    method public boolean hasFullBiasInNs();
+    method public boolean hasLeapSecond();
+    method public boolean hasTimeUncertaintyInNs();
+    method public void reset();
+    method public void resetBiasInNs();
+    method public void resetBiasUncertaintyInNs();
+    method public void resetDriftInNsPerSec();
+    method public void resetDriftUncertaintyInNsPerSec();
+    method public void resetFullBiasInNs();
+    method public void resetLeapSecond();
+    method public void resetTimeUncertaintyInNs();
+    method public void set(android.location.GpsClock);
+    method public void setBiasInNs(double);
+    method public void setBiasUncertaintyInNs(double);
+    method public void setDriftInNsPerSec(double);
+    method public void setDriftUncertaintyInNsPerSec(double);
+    method public void setFullBiasInNs(long);
+    method public void setLeapSecond(short);
+    method public void setTimeInNs(long);
+    method public void setTimeOfLastHwClockDiscontinuityInNs(long);
+    method public void setTimeUncertaintyInNs(double);
+    method public void setType(byte);
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final byte CLOCK_TYPE_GPS_TIME = 2; // 0x2
+    field public static final byte CLOCK_TYPE_LOCAL_HW_TIME = 1; // 0x1
+    field public static final byte CLOCK_TYPE_UNKNOWN = 0; // 0x0
+    field public static final android.os.Parcelable.Creator<android.location.GpsClock> CREATOR;
+  }
+
+  public static abstract class GpsClock.GpsClockType implements java.lang.annotation.Annotation {
+  }
+
+  public final class GpsMeasurement implements android.os.Parcelable {
+    method public int describeContents();
+    method public double getAccumulatedDeltaRangeInMeters();
+    method public short getAccumulatedDeltaRangeState();
+    method public double getAccumulatedDeltaRangeUncertaintyInMeters();
+    method public double getAzimuthInDeg();
+    method public double getAzimuthUncertaintyInDeg();
+    method public int getBitNumber();
+    method public long getCarrierCycles();
+    method public float getCarrierFrequencyInHz();
+    method public double getCarrierPhase();
+    method public double getCarrierPhaseUncertainty();
+    method public double getCn0InDbHz();
+    method public double getCodePhaseInChips();
+    method public double getCodePhaseUncertaintyInChips();
+    method public double getDopplerShiftInHz();
+    method public double getDopplerShiftUncertaintyInHz();
+    method public double getElevationInDeg();
+    method public double getElevationUncertaintyInDeg();
+    method public byte getLossOfLock();
+    method public byte getMultipathIndicator();
+    method public byte getPrn();
+    method public double getPseudorangeInMeters();
+    method public double getPseudorangeRateCarrierInMetersPerSec();
+    method public double getPseudorangeRateCarrierUncertaintyInMetersPerSec();
+    method public double getPseudorangeRateInMetersPerSec();
+    method public double getPseudorangeRateUncertaintyInMetersPerSec();
+    method public double getPseudorangeUncertaintyInMeters();
+    method public long getReceivedGpsTowInNs();
+    method public long getReceivedGpsTowUncertaintyInNs();
+    method public double getSnrInDb();
+    method public short getState();
+    method public short getTimeFromLastBitInMs();
+    method public double getTimeOffsetInNs();
+    method public boolean hasAzimuthInDeg();
+    method public boolean hasAzimuthUncertaintyInDeg();
+    method public boolean hasBitNumber();
+    method public boolean hasCarrierCycles();
+    method public boolean hasCarrierFrequencyInHz();
+    method public boolean hasCarrierPhase();
+    method public boolean hasCarrierPhaseUncertainty();
+    method public boolean hasCodePhaseInChips();
+    method public boolean hasCodePhaseUncertaintyInChips();
+    method public boolean hasDopplerShiftInHz();
+    method public boolean hasDopplerShiftUncertaintyInHz();
+    method public boolean hasElevationInDeg();
+    method public boolean hasElevationUncertaintyInDeg();
+    method public boolean hasPseudorangeInMeters();
+    method public boolean hasPseudorangeUncertaintyInMeters();
+    method public boolean hasSnrInDb();
+    method public boolean hasTimeFromLastBitInMs();
+    method public boolean isPseudorangeRateCorrected();
+    method public boolean isUsedInFix();
+    method public void reset();
+    method public void resetAzimuthInDeg();
+    method public void resetAzimuthUncertaintyInDeg();
+    method public void resetBitNumber();
+    method public void resetCarrierCycles();
+    method public void resetCarrierFrequencyInHz();
+    method public void resetCarrierPhase();
+    method public void resetCarrierPhaseUncertainty();
+    method public void resetCodePhaseInChips();
+    method public void resetCodePhaseUncertaintyInChips();
+    method public void resetDopplerShiftInHz();
+    method public void resetDopplerShiftUncertaintyInHz();
+    method public void resetElevationInDeg();
+    method public void resetElevationUncertaintyInDeg();
+    method public void resetPseudorangeInMeters();
+    method public void resetPseudorangeUncertaintyInMeters();
+    method public void resetSnrInDb();
+    method public void resetTimeFromLastBitInMs();
+    method public void set(android.location.GpsMeasurement);
+    method public void setAccumulatedDeltaRangeInMeters(double);
+    method public void setAccumulatedDeltaRangeState(short);
+    method public void setAccumulatedDeltaRangeUncertaintyInMeters(double);
+    method public void setAzimuthInDeg(double);
+    method public void setAzimuthUncertaintyInDeg(double);
+    method public void setBitNumber(int);
+    method public void setCarrierCycles(long);
+    method public void setCarrierFrequencyInHz(float);
+    method public void setCarrierPhase(double);
+    method public void setCarrierPhaseUncertainty(double);
+    method public void setCn0InDbHz(double);
+    method public void setCodePhaseInChips(double);
+    method public void setCodePhaseUncertaintyInChips(double);
+    method public void setDopplerShiftInHz(double);
+    method public void setDopplerShiftUncertaintyInHz(double);
+    method public void setElevationInDeg(double);
+    method public void setElevationUncertaintyInDeg(double);
+    method public void setLossOfLock(byte);
+    method public void setMultipathIndicator(byte);
+    method public void setPrn(byte);
+    method public void setPseudorangeInMeters(double);
+    method public void setPseudorangeRateCarrierInMetersPerSec(double);
+    method public void setPseudorangeRateCarrierUncertaintyInMetersPerSec(double);
+    method public void setPseudorangeRateInMetersPerSec(double);
+    method public void setPseudorangeRateUncertaintyInMetersPerSec(double);
+    method public void setPseudorangeUncertaintyInMeters(double);
+    method public void setReceivedGpsTowInNs(long);
+    method public void setReceivedGpsTowUncertaintyInNs(long);
+    method public void setSnrInDb(double);
+    method public void setState(short);
+    method public void setTimeFromLastBitInMs(short);
+    method public void setTimeOffsetInNs(double);
+    method public void setUsedInFix(boolean);
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final short ADR_STATE_CYCLE_SLIP = 4; // 0x4
+    field public static final short ADR_STATE_RESET = 2; // 0x2
+    field public static final short ADR_STATE_UNKNOWN = 0; // 0x0
+    field public static final short ADR_STATE_VALID = 1; // 0x1
+    field public static final android.os.Parcelable.Creator<android.location.GpsMeasurement> CREATOR;
+    field public static final byte LOSS_OF_LOCK_CYCLE_SLIP = 2; // 0x2
+    field public static final byte LOSS_OF_LOCK_OK = 1; // 0x1
+    field public static final byte LOSS_OF_LOCK_UNKNOWN = 0; // 0x0
+    field public static final byte MULTIPATH_INDICATOR_DETECTED = 1; // 0x1
+    field public static final byte MULTIPATH_INDICATOR_NOT_USED = 2; // 0x2
+    field public static final byte MULTIPATH_INDICATOR_UNKNOWN = 0; // 0x0
+    field public static final short STATE_BIT_SYNC = 2; // 0x2
+    field public static final short STATE_CODE_LOCK = 1; // 0x1
+    field public static final short STATE_MSEC_AMBIGUOUS = 16; // 0x10
+    field public static final short STATE_SUBFRAME_SYNC = 4; // 0x4
+    field public static final short STATE_TOW_DECODED = 8; // 0x8
+    field public static final short STATE_UNKNOWN = 0; // 0x0
+  }
+
+  public static abstract class GpsMeasurement.LossOfLockStatus implements java.lang.annotation.Annotation {
+  }
+
+  public static abstract class GpsMeasurement.MultipathIndicator implements java.lang.annotation.Annotation {
+  }
+
+  public final class GpsMeasurementsEvent implements android.os.Parcelable {
+    ctor public GpsMeasurementsEvent(android.location.GpsClock, android.location.GpsMeasurement[]);
+    method public int describeContents();
+    method public android.location.GpsClock getClock();
+    method public java.util.Collection<android.location.GpsMeasurement> getMeasurements();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.location.GpsMeasurementsEvent> CREATOR;
+    field public static final int STATUS_GPS_LOCATION_DISABLED = 2; // 0x2
+    field public static final int STATUS_NOT_SUPPORTED = 0; // 0x0
+    field public static final int STATUS_READY = 1; // 0x1
+  }
+
+  public static abstract class GpsMeasurementsEvent.Callback {
+    ctor public GpsMeasurementsEvent.Callback();
+    method public void onGpsMeasurementsReceived(android.location.GpsMeasurementsEvent);
+    method public void onStatusChanged(int);
+  }
+
+  public static abstract class GpsMeasurementsEvent.GpsMeasurementsStatus implements java.lang.annotation.Annotation {
+  }
+
+  public final class GpsNavigationMessage implements android.os.Parcelable {
+    method public int describeContents();
+    method public byte[] getData();
+    method public short getMessageId();
+    method public byte getPrn();
+    method public short getStatus();
+    method public short getSubmessageId();
+    method public byte getType();
+    method public void reset();
+    method public void set(android.location.GpsNavigationMessage);
+    method public void setData(byte[]);
+    method public void setMessageId(short);
+    method public void setPrn(byte);
+    method public void setStatus(short);
+    method public void setSubmessageId(short);
+    method public void setType(byte);
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.location.GpsNavigationMessage> CREATOR;
+    field public static final byte MESSAGE_TYPE_CNAV2 = 4; // 0x4
+    field public static final byte MESSAGE_TYPE_L1CA = 1; // 0x1
+    field public static final byte MESSAGE_TYPE_L2CNAV = 2; // 0x2
+    field public static final byte MESSAGE_TYPE_L5CNAV = 3; // 0x3
+    field public static final byte MESSAGE_TYPE_UNKNOWN = 0; // 0x0
+    field public static final short STATUS_PARITY_PASSED = 1; // 0x1
+    field public static final short STATUS_PARITY_REBUILT = 2; // 0x2
+    field public static final short STATUS_UNKNOWN = 0; // 0x0
+  }
+
+  public static abstract class GpsNavigationMessage.GpsNavigationMessageType implements java.lang.annotation.Annotation {
+  }
+
+  public final class GpsNavigationMessageEvent implements android.os.Parcelable {
+    ctor public GpsNavigationMessageEvent(android.location.GpsNavigationMessage);
+    method public int describeContents();
+    method public android.location.GpsNavigationMessage getNavigationMessage();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.location.GpsNavigationMessageEvent> CREATOR;
+    field public static final int STATUS_GPS_LOCATION_DISABLED = 2; // 0x2
+    field public static final int STATUS_NOT_SUPPORTED = 0; // 0x0
+    field public static final int STATUS_READY = 1; // 0x1
+  }
+
+  public static abstract class GpsNavigationMessageEvent.Callback {
+    ctor public GpsNavigationMessageEvent.Callback();
+    method public void onGpsNavigationMessageReceived(android.location.GpsNavigationMessageEvent);
+    method public void onStatusChanged(int);
+  }
+
+  public static abstract class GpsNavigationMessageEvent.GpsNavigationMessageStatus implements java.lang.annotation.Annotation {
+  }
+
   public final class GpsSatellite {
     method public float getAzimuth();
     method public float getElevation();
@@ -19120,8 +19454,10 @@
   }
 
   public class LocationManager {
-    method public boolean addGpsStatusListener(android.location.GpsStatus.Listener);
-    method public boolean addNmeaListener(android.location.GpsStatus.NmeaListener);
+    method public deprecated boolean addGpsStatusListener(android.location.GpsStatus.Listener);
+    method public deprecated boolean addNmeaListener(android.location.GpsStatus.NmeaListener);
+    method public boolean addNmeaListener(android.location.GnssNmeaListener);
+    method public boolean addNmeaListener(android.location.GnssNmeaListener, android.os.Handler);
     method public void addProximityAlert(double, double, float, long, android.app.PendingIntent);
     method public void addTestProvider(java.lang.String, boolean, boolean, boolean, boolean, boolean, boolean, boolean, int, int);
     method public void clearTestProviderEnabled(java.lang.String);
@@ -19129,14 +19465,22 @@
     method public void clearTestProviderStatus(java.lang.String);
     method public java.util.List<java.lang.String> getAllProviders();
     method public java.lang.String getBestProvider(android.location.Criteria, boolean);
-    method public android.location.GpsStatus getGpsStatus(android.location.GpsStatus);
+    method public deprecated android.location.GpsStatus getGpsStatus(android.location.GpsStatus);
+    method public int getGpsYearOfHardware();
     method public android.location.Location getLastKnownLocation(java.lang.String);
     method public android.location.LocationProvider getProvider(java.lang.String);
     method public java.util.List<java.lang.String> getProviders(boolean);
     method public java.util.List<java.lang.String> getProviders(android.location.Criteria, boolean);
     method public boolean isProviderEnabled(java.lang.String);
-    method public void removeGpsStatusListener(android.location.GpsStatus.Listener);
-    method public void removeNmeaListener(android.location.GpsStatus.NmeaListener);
+    method public boolean registerGnssStatusCallback(android.location.GnssStatusCallback);
+    method public boolean registerGnssStatusCallback(android.location.GnssStatusCallback, android.os.Handler);
+    method public boolean registerGpsMeasurementCallback(android.location.GpsMeasurementsEvent.Callback);
+    method public boolean registerGpsMeasurementCallback(android.location.GpsMeasurementsEvent.Callback, android.os.Handler);
+    method public boolean registerGpsNavigationMessageCallback(android.location.GpsNavigationMessageEvent.Callback);
+    method public boolean registerGpsNavigationMessageCallback(android.location.GpsNavigationMessageEvent.Callback, android.os.Handler);
+    method public deprecated void removeGpsStatusListener(android.location.GpsStatus.Listener);
+    method public deprecated void removeNmeaListener(android.location.GpsStatus.NmeaListener);
+    method public void removeNmeaListener(android.location.GnssNmeaListener);
     method public void removeProximityAlert(android.app.PendingIntent);
     method public void removeTestProvider(java.lang.String);
     method public void removeUpdates(android.location.LocationListener);
@@ -19154,6 +19498,9 @@
     method public void setTestProviderEnabled(java.lang.String, boolean);
     method public void setTestProviderLocation(java.lang.String, android.location.Location);
     method public void setTestProviderStatus(java.lang.String, int, android.os.Bundle, long);
+    method public void unregisterGnssStatusCallback(android.location.GnssStatusCallback);
+    method public void unregisterGpsMeasurementCallback(android.location.GpsMeasurementsEvent.Callback);
+    method public void unregisterGpsNavigationMessageCallback(android.location.GpsNavigationMessageEvent.Callback);
     field public static final java.lang.String GPS_PROVIDER = "gps";
     field public static final java.lang.String KEY_LOCATION_CHANGED = "location";
     field public static final java.lang.String KEY_PROVIDER_ENABLED = "providerEnabled";
@@ -19585,8 +19932,11 @@
 
   public class AudioRecordConfiguration implements android.os.Parcelable {
     method public int describeContents();
-    method public int getAudioSessionId();
+    method public android.media.AudioDeviceInfo getAudioDevice();
+    method public int getClientAudioSessionId();
     method public int getClientAudioSource();
+    method public android.media.AudioFormat getClientFormat();
+    method public android.media.AudioFormat getFormat();
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.media.AudioRecordConfiguration> CREATOR;
   }
@@ -19760,6 +20110,16 @@
     ctor public DeniedByServerException(java.lang.String);
   }
 
+  public abstract class DrmInitData {
+    ctor public DrmInitData();
+    method public abstract android.media.DrmInitData.SchemeInitData get(java.util.UUID);
+  }
+
+  public static final class DrmInitData.SchemeInitData {
+    field public final byte[] data;
+    field public final java.lang.String mimeType;
+  }
+
   public class ExifInterface {
     ctor public ExifInterface(java.lang.String) throws java.io.IOException;
     method public double getAltitude(double);
@@ -20450,6 +20810,7 @@
     ctor public MediaExtractor();
     method public boolean advance();
     method public long getCachedDuration();
+    method public android.media.DrmInitData getDrmInitData();
     method public java.util.Map<java.util.UUID, byte[]> getPsshInfo();
     method public boolean getSampleCryptoInfo(android.media.MediaCodec.CryptoInfo);
     method public int getSampleFlags();
@@ -20494,6 +20855,16 @@
     method public final void setInteger(java.lang.String, int);
     method public final void setLong(java.lang.String, long);
     method public final void setString(java.lang.String, java.lang.String);
+    field public static final int COLOR_RANGE_FULL = 1; // 0x1
+    field public static final int COLOR_RANGE_LIMITED = 2; // 0x2
+    field public static final int COLOR_STANDARD_BT2020 = 6; // 0x6
+    field public static final int COLOR_STANDARD_BT601_NTSC = 4; // 0x4
+    field public static final int COLOR_STANDARD_BT601_PAL = 2; // 0x2
+    field public static final int COLOR_STANDARD_BT709 = 1; // 0x1
+    field public static final int COLOR_TRANSFER_HLG = 7; // 0x7
+    field public static final int COLOR_TRANSFER_LINEAR = 1; // 0x1
+    field public static final int COLOR_TRANSFER_SDR_VIDEO = 3; // 0x3
+    field public static final int COLOR_TRANSFER_ST2084 = 6; // 0x6
     field public static final java.lang.String KEY_AAC_DRC_ATTENUATION_FACTOR = "aac-drc-cut-level";
     field public static final java.lang.String KEY_AAC_DRC_BOOST_FACTOR = "aac-drc-boost-level";
     field public static final java.lang.String KEY_AAC_DRC_HEAVY_COMPRESSION = "aac-drc-heavy-compression";
@@ -20509,6 +20880,9 @@
     field public static final java.lang.String KEY_CHANNEL_COUNT = "channel-count";
     field public static final java.lang.String KEY_CHANNEL_MASK = "channel-mask";
     field public static final java.lang.String KEY_COLOR_FORMAT = "color-format";
+    field public static final java.lang.String KEY_COLOR_RANGE = "color-range";
+    field public static final java.lang.String KEY_COLOR_STANDARD = "color-standard";
+    field public static final java.lang.String KEY_COLOR_TRANSFER = "color-transfer";
     field public static final java.lang.String KEY_COMPLEXITY = "complexity";
     field public static final java.lang.String KEY_DURATION = "durationUs";
     field public static final java.lang.String KEY_FLAC_COMPRESSION_LEVEL = "flac-compression-level";
@@ -22134,6 +22508,7 @@
     method public void setRatingType(int);
     method public void setSessionActivity(android.app.PendingIntent);
     field public static final int FLAG_HANDLES_MEDIA_BUTTONS = 1; // 0x1
+    field public static final int FLAG_HANDLES_PREPARE_ONLY = 4; // 0x4
     field public static final int FLAG_HANDLES_TRANSPORT_CONTROLS = 2; // 0x2
   }
 
@@ -22212,6 +22587,7 @@
     field public static final long ACTION_SKIP_TO_QUEUE_ITEM = 4096L; // 0x1000L
     field public static final long ACTION_STOP = 1L; // 0x1L
     field public static final android.os.Parcelable.Creator<android.media.session.PlaybackState> CREATOR;
+    field public static final java.lang.String EXTRA_PREPARE_ONLY = "android.media.session.extra.PREPARE_ONLY";
     field public static final long PLAYBACK_POSITION_UNKNOWN = -1L; // 0xffffffffffffffffL
     field public static final int STATE_BUFFERING = 6; // 0x6
     field public static final int STATE_CONNECTING = 8; // 0x8
@@ -22528,7 +22904,7 @@
     method public void onInputAdded(java.lang.String);
     method public void onInputRemoved(java.lang.String);
     method public void onInputStateChanged(java.lang.String, int);
-    method public void onTvInputInfoChanged(java.lang.String, android.media.tv.TvInputInfo);
+    method public void onTvInputInfoChanged(android.media.tv.TvInputInfo);
   }
 
   public abstract class TvInputService extends android.app.Service {
@@ -22536,7 +22912,7 @@
     method public final android.os.IBinder onBind(android.content.Intent);
     method public android.media.tv.TvInputService.RecordingSession onCreateRecordingSession(java.lang.String);
     method public abstract android.media.tv.TvInputService.Session onCreateSession(java.lang.String);
-    method public final void setTvInputInfo(java.lang.String, android.media.tv.TvInputInfo);
+    method public static final void setTvInputInfo(android.content.Context, android.media.tv.TvInputInfo);
     field public static final java.lang.String SERVICE_INTERFACE = "android.media.tv.TvInputService";
     field public static final java.lang.String SERVICE_META_DATA = "android.media.tv.input";
   }
@@ -22799,6 +23175,7 @@
     field public static final int OPERATION_GET_OBJECT_PROP_VALUE = 38915; // 0x9803
     field public static final int OPERATION_GET_OBJECT_REFERENCES = 38928; // 0x9810
     field public static final int OPERATION_GET_PARTIAL_OBJECT = 4123; // 0x101b
+    field public static final int OPERATION_GET_PARTIAL_OBJECT_64 = 38337; // 0x95c1
     field public static final int OPERATION_GET_STORAGE_INFO = 4101; // 0x1005
     field public static final int OPERATION_GET_STORAGE_I_DS = 4100; // 0x1004
     field public static final int OPERATION_GET_THUMB = 4106; // 0x100a
@@ -22836,6 +23213,7 @@
     method public android.mtp.MtpObjectInfo getObjectInfo(int);
     method public long getParent(int);
     method public long getPartialObject(int, long, long, byte[]) throws java.io.IOException;
+    method public long getPartialObject64(int, long, long, byte[]) throws java.io.IOException;
     method public long getStorageId(int);
     method public int[] getStorageIds();
     method public android.mtp.MtpStorageInfo getStorageInfo(int);
@@ -22844,7 +23222,7 @@
     method public boolean importFile(int, android.os.ParcelFileDescriptor);
     method public boolean open(android.hardware.usb.UsbDeviceConnection);
     method public android.mtp.MtpEvent readEvent(android.os.CancellationSignal);
-    method public boolean sendObject(int, int, android.os.ParcelFileDescriptor);
+    method public boolean sendObject(int, long, android.os.ParcelFileDescriptor);
     method public android.mtp.MtpObjectInfo sendObjectInfo(android.mtp.MtpObjectInfo);
   }
 
@@ -22875,23 +23253,31 @@
     method public final int getAssociationDesc();
     method public final int getAssociationType();
     method public final int getCompressedSize();
+    method public final long getCompressedSizeLong();
     method public final long getDateCreated();
     method public final long getDateModified();
     method public final int getFormat();
     method public final int getImagePixDepth();
+    method public final long getImagePixDepthLong();
     method public final int getImagePixHeight();
+    method public final long getImagePixHeightLong();
     method public final int getImagePixWidth();
+    method public final long getImagePixWidthLong();
     method public final java.lang.String getKeywords();
     method public final java.lang.String getName();
     method public final int getObjectHandle();
     method public final int getParent();
     method public final int getProtectionStatus();
     method public final int getSequenceNumber();
+    method public final long getSequenceNumberLong();
     method public final int getStorageId();
     method public final int getThumbCompressedSize();
+    method public final long getThumbCompressedSizeLong();
     method public final int getThumbFormat();
     method public final int getThumbPixHeight();
+    method public final long getThumbPixHeightLong();
     method public final int getThumbPixWidth();
+    method public final long getThumbPixWidthLong();
   }
 
   public static class MtpObjectInfo.Builder {
@@ -22900,24 +23286,24 @@
     method public android.mtp.MtpObjectInfo build();
     method public android.mtp.MtpObjectInfo.Builder setAssociationDesc(int);
     method public android.mtp.MtpObjectInfo.Builder setAssociationType(int);
-    method public android.mtp.MtpObjectInfo.Builder setCompressedSize(int);
+    method public android.mtp.MtpObjectInfo.Builder setCompressedSize(long);
     method public android.mtp.MtpObjectInfo.Builder setDateCreated(long);
     method public android.mtp.MtpObjectInfo.Builder setDateModified(long);
     method public android.mtp.MtpObjectInfo.Builder setFormat(int);
-    method public android.mtp.MtpObjectInfo.Builder setImagePixDepth(int);
-    method public android.mtp.MtpObjectInfo.Builder setImagePixHeight(int);
-    method public android.mtp.MtpObjectInfo.Builder setImagePixWidth(int);
+    method public android.mtp.MtpObjectInfo.Builder setImagePixDepth(long);
+    method public android.mtp.MtpObjectInfo.Builder setImagePixHeight(long);
+    method public android.mtp.MtpObjectInfo.Builder setImagePixWidth(long);
     method public android.mtp.MtpObjectInfo.Builder setKeywords(java.lang.String);
     method public android.mtp.MtpObjectInfo.Builder setName(java.lang.String);
     method public android.mtp.MtpObjectInfo.Builder setObjectHandle(int);
     method public android.mtp.MtpObjectInfo.Builder setParent(int);
     method public android.mtp.MtpObjectInfo.Builder setProtectionStatus(int);
-    method public android.mtp.MtpObjectInfo.Builder setSequenceNumber(int);
+    method public android.mtp.MtpObjectInfo.Builder setSequenceNumber(long);
     method public android.mtp.MtpObjectInfo.Builder setStorageId(int);
-    method public android.mtp.MtpObjectInfo.Builder setThumbCompressedSize(int);
+    method public android.mtp.MtpObjectInfo.Builder setThumbCompressedSize(long);
     method public android.mtp.MtpObjectInfo.Builder setThumbFormat(int);
-    method public android.mtp.MtpObjectInfo.Builder setThumbPixHeight(int);
-    method public android.mtp.MtpObjectInfo.Builder setThumbPixWidth(int);
+    method public android.mtp.MtpObjectInfo.Builder setThumbPixHeight(long);
+    method public android.mtp.MtpObjectInfo.Builder setThumbPixWidth(long);
   }
 
   public final class MtpStorageInfo {
@@ -28164,6 +28550,10 @@
     field public static final int OPEN = 32; // 0x20
   }
 
+  public class FileUriExposedException extends java.lang.RuntimeException {
+    ctor public FileUriExposedException(java.lang.String);
+  }
+
   public class Handler {
     ctor public Handler();
     ctor public Handler(android.os.Handler.Callback);
@@ -28755,6 +29145,7 @@
     method public android.os.StrictMode.VmPolicy.Builder detectLeakedSqlLiteObjects();
     method public android.os.StrictMode.VmPolicy.Builder penaltyDeath();
     method public android.os.StrictMode.VmPolicy.Builder penaltyDeathOnCleartextNetwork();
+    method public android.os.StrictMode.VmPolicy.Builder penaltyDeathOnFileUriExposure();
     method public android.os.StrictMode.VmPolicy.Builder penaltyDropBox();
     method public android.os.StrictMode.VmPolicy.Builder penaltyLog();
     method public android.os.StrictMode.VmPolicy.Builder setClassInstanceLimit(java.lang.Class, int);
@@ -28862,6 +29253,8 @@
     field public static final java.lang.String DISALLOW_USB_FILE_TRANSFER = "no_usb_file_transfer";
     field public static final java.lang.String ENSURE_VERIFY_APPS = "ensure_verify_apps";
     field public static final java.lang.String KEY_RESTRICTIONS_PENDING = "restrictions_pending";
+    field public static final int USER_CREATION_FAILED_NOT_PERMITTED = 1; // 0x1
+    field public static final int USER_CREATION_FAILED_NO_MORE_USERS = 2; // 0x2
   }
 
   public abstract class Vibrator {
@@ -29219,6 +29612,8 @@
     method public static void setDefaultValues(android.content.Context, java.lang.String, int, int, boolean);
     method public void setSharedPreferencesMode(int);
     method public void setSharedPreferencesName(java.lang.String);
+    method public void setStorageDefault();
+    method public void setStorageDeviceEncrypted();
     field public static final java.lang.String KEY_HAS_SET_DEFAULT_VALUES = "_has_set_default_values";
     field public static final java.lang.String METADATA_KEY_PREFERENCES = "android.preference";
   }
@@ -31216,7 +31611,9 @@
     method public static java.lang.String getSearchDocumentsQuery(android.net.Uri);
     method public static java.lang.String getTreeDocumentId(android.net.Uri);
     method public static boolean isDocumentUri(android.content.Context, android.net.Uri);
+    method public static boolean isTreeUri(android.net.Uri);
     method public static android.net.Uri moveDocument(android.content.ContentResolver, android.net.Uri, android.net.Uri, android.net.Uri);
+    method public static boolean removeDocument(android.content.ContentResolver, android.net.Uri, android.net.Uri);
     method public static android.net.Uri renameDocument(android.content.ContentResolver, android.net.Uri, java.lang.String);
     field public static final java.lang.String EXTRA_ERROR = "error";
     field public static final java.lang.String EXTRA_EXCLUDE_SELF = "android.provider.extra.EXCLUDE_SELF";
@@ -31242,6 +31639,7 @@
     field public static final int FLAG_SUPPORTS_COPY = 128; // 0x80
     field public static final int FLAG_SUPPORTS_DELETE = 4; // 0x4
     field public static final int FLAG_SUPPORTS_MOVE = 256; // 0x100
+    field public static final int FLAG_SUPPORTS_REMOVE = 2048; // 0x800
     field public static final int FLAG_SUPPORTS_RENAME = 64; // 0x40
     field public static final int FLAG_SUPPORTS_THUMBNAIL = 1; // 0x1
     field public static final int FLAG_SUPPORTS_WRITE = 2; // 0x2
@@ -31289,6 +31687,7 @@
     method public android.database.Cursor queryRecentDocuments(java.lang.String, java.lang.String[]) throws java.io.FileNotFoundException;
     method public abstract android.database.Cursor queryRoots(java.lang.String[]) throws java.io.FileNotFoundException;
     method public android.database.Cursor querySearchDocuments(java.lang.String, java.lang.String, java.lang.String[]) throws java.io.FileNotFoundException;
+    method public boolean removeDocument(java.lang.String, java.lang.String) throws java.io.FileNotFoundException;
     method public java.lang.String renameDocument(java.lang.String, java.lang.String) throws java.io.FileNotFoundException;
     method public final void revokeDocumentPermission(java.lang.String);
     method public final int update(android.net.Uri, android.content.ContentValues, java.lang.String, java.lang.String[]);
@@ -31668,6 +32067,7 @@
     field public static final java.lang.String ACTION_INPUT_METHOD_SETTINGS = "android.settings.INPUT_METHOD_SETTINGS";
     field public static final java.lang.String ACTION_INPUT_METHOD_SUBTYPE_SETTINGS = "android.settings.INPUT_METHOD_SUBTYPE_SETTINGS";
     field public static final java.lang.String ACTION_INTERNAL_STORAGE_SETTINGS = "android.settings.INTERNAL_STORAGE_SETTINGS";
+    field public static final java.lang.String ACTION_KEYBOARD_LAYOUT_SETTINGS = "android.settings.KEYBOARD_LAYOUT_SETTINGS";
     field public static final java.lang.String ACTION_LOCALE_SETTINGS = "android.settings.LOCALE_SETTINGS";
     field public static final java.lang.String ACTION_LOCATION_SOURCE_SETTINGS = "android.settings.LOCATION_SOURCE_SETTINGS";
     field public static final java.lang.String ACTION_MANAGE_ALL_APPLICATIONS_SETTINGS = "android.settings.MANAGE_ALL_APPLICATIONS_SETTINGS";
@@ -35420,14 +35820,14 @@
     field public static final java.lang.String SERVICE_INTERFACE = "android.telecom.CallScreeningService";
   }
 
-  public class CallScreeningService.CallResponse {
+  public static class CallScreeningService.CallResponse {
     method public boolean getDisallowCall();
     method public boolean getRejectCall();
     method public boolean getSkipCallLog();
     method public boolean getSkipNotification();
   }
 
-  public class CallScreeningService.CallResponse.Builder {
+  public static class CallScreeningService.CallResponse.Builder {
     ctor public CallScreeningService.CallResponse.Builder();
     method public android.telecom.CallScreeningService.CallResponse build();
     method public android.telecom.CallScreeningService.CallResponse.Builder setDisallowCall(boolean);
@@ -36814,7 +37214,7 @@
     method public void testActivityTestCaseSetUpProperly() throws java.lang.Exception;
   }
 
-  public abstract class ActivityInstrumentationTestCase2 extends android.test.ActivityTestCase {
+  public abstract deprecated class ActivityInstrumentationTestCase2 extends android.test.ActivityTestCase {
     ctor public deprecated ActivityInstrumentationTestCase2(java.lang.String, java.lang.Class<T>);
     ctor public ActivityInstrumentationTestCase2(java.lang.Class<T>);
     method public T getActivity();
@@ -36822,14 +37222,14 @@
     method public void setActivityIntent(android.content.Intent);
   }
 
-  public abstract class ActivityTestCase extends android.test.InstrumentationTestCase {
+  public abstract deprecated class ActivityTestCase extends android.test.InstrumentationTestCase {
     ctor public ActivityTestCase();
     method protected android.app.Activity getActivity();
     method protected void scrubClass(java.lang.Class<?>) throws java.lang.IllegalAccessException;
     method protected void setActivity(android.app.Activity);
   }
 
-  public abstract class ActivityUnitTestCase extends android.test.ActivityTestCase {
+  public abstract deprecated class ActivityUnitTestCase extends android.test.ActivityTestCase {
     ctor public ActivityUnitTestCase(java.lang.Class<T>);
     method public T getActivity();
     method public int getFinishedActivityRequest();
@@ -36842,7 +37242,7 @@
     method protected T startActivity(android.content.Intent, android.os.Bundle, java.lang.Object);
   }
 
-  public class AndroidTestCase extends junit.framework.TestCase {
+  public deprecated class AndroidTestCase extends junit.framework.TestCase {
     ctor public AndroidTestCase();
     method public void assertActivityRequiresPermission(java.lang.String, java.lang.String, java.lang.String);
     method public void assertReadingContentUriRequiresPermission(android.net.Uri, java.lang.String);
@@ -36854,7 +37254,7 @@
     field protected android.content.Context mContext;
   }
 
-  public class AndroidTestRunner extends junit.runner.BaseTestRunner {
+  public deprecated class AndroidTestRunner extends junit.runner.BaseTestRunner {
     ctor public AndroidTestRunner();
     method public void addTestListener(junit.framework.TestListener);
     method public void clearTestListeners();
@@ -36875,7 +37275,7 @@
     method public void testStarted(java.lang.String);
   }
 
-  public abstract class ApplicationTestCase extends android.test.AndroidTestCase {
+  public abstract deprecated class ApplicationTestCase extends android.test.AndroidTestCase {
     ctor public ApplicationTestCase(java.lang.Class<T>);
     method protected final void createApplication();
     method public T getApplication();
@@ -36893,10 +37293,10 @@
     ctor public ComparisonFailure(java.lang.String, java.lang.String, java.lang.String);
   }
 
-  public abstract class FlakyTest implements java.lang.annotation.Annotation {
+  public abstract deprecated class FlakyTest implements java.lang.annotation.Annotation {
   }
 
-  public class InstrumentationTestCase extends junit.framework.TestCase {
+  public deprecated class InstrumentationTestCase extends junit.framework.TestCase {
     ctor public InstrumentationTestCase();
     method public android.app.Instrumentation getInstrumentation();
     method public deprecated void injectInsrumentation(android.app.Instrumentation);
@@ -36909,7 +37309,7 @@
     method public void sendRepeatedKeys(int...);
   }
 
-  public class InstrumentationTestRunner extends android.app.Instrumentation implements android.test.TestSuiteProvider {
+  public deprecated class InstrumentationTestRunner extends android.app.Instrumentation implements android.test.TestSuiteProvider {
     ctor public InstrumentationTestRunner();
     method public junit.framework.TestSuite getAllTests();
     method protected android.test.AndroidTestRunner getAndroidTestRunner();
@@ -36928,14 +37328,14 @@
     field public static final int REPORT_VALUE_RESULT_START = 1; // 0x1
   }
 
-  public class InstrumentationTestSuite extends junit.framework.TestSuite {
+  public deprecated class InstrumentationTestSuite extends junit.framework.TestSuite {
     ctor public InstrumentationTestSuite(android.app.Instrumentation);
     ctor public InstrumentationTestSuite(java.lang.String, android.app.Instrumentation);
     ctor public InstrumentationTestSuite(java.lang.Class, android.app.Instrumentation);
     method public void addTestSuite(java.lang.Class);
   }
 
-  public class IsolatedContext extends android.content.ContextWrapper {
+  public deprecated class IsolatedContext extends android.content.ContextWrapper {
     ctor public IsolatedContext(android.content.ContentResolver, android.content.Context);
     method public java.util.List<android.content.Intent> getAndClearBroadcastIntents();
   }
@@ -36945,7 +37345,7 @@
     method public T getLoaderResultSynchronously(android.content.Loader<T>);
   }
 
-  public final class MoreAsserts {
+  public final deprecated class MoreAsserts {
     method public static void assertAssignableFrom(java.lang.Class<?>, java.lang.Object);
     method public static void assertAssignableFrom(java.lang.Class<?>, java.lang.Class<?>);
     method public static java.util.regex.MatchResult assertContainsRegex(java.lang.String, java.lang.String, java.lang.String);
@@ -36984,7 +37384,7 @@
     method public static void checkEqualsAndHashCodeMethods(java.lang.Object, java.lang.Object, boolean);
   }
 
-  public abstract interface PerformanceTestCase {
+  public abstract deprecated interface PerformanceTestCase {
     method public abstract boolean isPerformanceOnly();
     method public abstract int startPerformance(android.test.PerformanceTestCase.Intermediates);
   }
@@ -37013,7 +37413,7 @@
     method public static android.content.ContentResolver newResolverWithContentProviderFromSql(android.content.Context, java.lang.String, java.lang.Class<T>, java.lang.String, java.lang.String, int, java.lang.String) throws java.lang.IllegalAccessException, java.lang.InstantiationException;
   }
 
-  public class RenamingDelegatingContext extends android.content.ContextWrapper {
+  public deprecated class RenamingDelegatingContext extends android.content.ContextWrapper {
     ctor public RenamingDelegatingContext(android.content.Context, java.lang.String);
     ctor public RenamingDelegatingContext(android.content.Context, android.content.Context, java.lang.String);
     method public java.lang.String getDatabasePrefix();
@@ -37022,7 +37422,7 @@
     method public static T providerWithRenamedContext(java.lang.Class<T>, android.content.Context, java.lang.String, boolean) throws java.lang.IllegalAccessException, java.lang.InstantiationException;
   }
 
-  public abstract class ServiceTestCase extends android.test.AndroidTestCase {
+  public abstract deprecated class ServiceTestCase extends android.test.AndroidTestCase {
     ctor public ServiceTestCase(java.lang.Class<T>);
     method protected android.os.IBinder bindService(android.content.Intent);
     method public android.app.Application getApplication();
@@ -37035,23 +37435,23 @@
     method public void testServiceTestCaseSetUpProperly() throws java.lang.Exception;
   }
 
-  public abstract class SingleLaunchActivityTestCase extends android.test.InstrumentationTestCase {
+  public abstract deprecated class SingleLaunchActivityTestCase extends android.test.InstrumentationTestCase {
     ctor public SingleLaunchActivityTestCase(java.lang.String, java.lang.Class<T>);
     method public T getActivity();
     method public void testActivityTestCaseSetUpProperly() throws java.lang.Exception;
   }
 
-  public class SyncBaseInstrumentation extends android.test.InstrumentationTestCase {
+  public deprecated class SyncBaseInstrumentation extends android.test.InstrumentationTestCase {
     ctor public SyncBaseInstrumentation();
     method protected void cancelSyncsandDisableAutoSync();
     method protected void syncProvider(android.net.Uri, java.lang.String, java.lang.String) throws java.lang.Exception;
   }
 
-  public abstract interface TestSuiteProvider {
+  public abstract deprecated interface TestSuiteProvider {
     method public abstract junit.framework.TestSuite getTestSuite();
   }
 
-  public class TouchUtils {
+  public deprecated class TouchUtils {
     ctor public TouchUtils();
     method public static void clickView(android.test.InstrumentationTestCase, android.view.View);
     method public static deprecated void drag(android.test.ActivityInstrumentationTestCase, float, float, float, float, int);
@@ -37086,10 +37486,10 @@
     method public static void touchAndCancelView(android.test.InstrumentationTestCase, android.view.View);
   }
 
-  public abstract class UiThreadTest implements java.lang.annotation.Annotation {
+  public abstract deprecated class UiThreadTest implements java.lang.annotation.Annotation {
   }
 
-  public class ViewAsserts {
+  public deprecated class ViewAsserts {
     method public static void assertBaselineAligned(android.view.View, android.view.View);
     method public static void assertBottomAligned(android.view.View, android.view.View);
     method public static void assertBottomAligned(android.view.View, android.view.View, int);
@@ -37114,11 +37514,11 @@
 
 package android.test.mock {
 
-  public class MockApplication extends android.app.Application {
+  public deprecated class MockApplication extends android.app.Application {
     ctor public MockApplication();
   }
 
-  public class MockContentProvider extends android.content.ContentProvider {
+  public deprecated class MockContentProvider extends android.content.ContentProvider {
     ctor protected MockContentProvider();
     ctor public MockContentProvider(android.content.Context);
     ctor public MockContentProvider(android.content.Context, java.lang.String, java.lang.String, android.content.pm.PathPermission[]);
@@ -37130,13 +37530,13 @@
     method public int update(android.net.Uri, android.content.ContentValues, java.lang.String, java.lang.String[]);
   }
 
-  public class MockContentResolver extends android.content.ContentResolver {
+  public deprecated class MockContentResolver extends android.content.ContentResolver {
     ctor public MockContentResolver();
     ctor public MockContentResolver(android.content.Context);
     method public void addProvider(java.lang.String, android.content.ContentProvider);
   }
 
-  public class MockContext extends android.content.Context {
+  public deprecated class MockContext extends android.content.Context {
     ctor public MockContext();
     method public boolean bindService(android.content.Intent, android.content.ServiceConnection, int);
     method public int checkCallingOrSelfPermission(java.lang.String);
@@ -37237,7 +37637,7 @@
     method public void unregisterReceiver(android.content.BroadcastReceiver);
   }
 
-  public class MockCursor implements android.database.Cursor {
+  public deprecated class MockCursor implements android.database.Cursor {
     ctor public MockCursor();
     method public void close();
     method public void copyStringToBuffer(int, android.database.CharArrayBuffer);
@@ -37282,13 +37682,13 @@
     method public void unregisterDataSetObserver(android.database.DataSetObserver);
   }
 
-  public class MockDialogInterface implements android.content.DialogInterface {
+  public deprecated class MockDialogInterface implements android.content.DialogInterface {
     ctor public MockDialogInterface();
     method public void cancel();
     method public void dismiss();
   }
 
-  public class MockPackageManager extends android.content.pm.PackageManager {
+  public deprecated class MockPackageManager extends android.content.pm.PackageManager {
     ctor public MockPackageManager();
     method public void addPackageToPreferred(java.lang.String);
     method public boolean addPermission(android.content.pm.PermissionInfo);
@@ -37376,7 +37776,7 @@
     method public void verifyPendingInstall(int, int);
   }
 
-  public class MockResources extends android.content.res.Resources {
+  public deprecated class MockResources extends android.content.res.Resources {
     ctor public MockResources();
   }
 
@@ -37417,19 +37817,19 @@
 
 package android.test.suitebuilder.annotation {
 
-  public abstract class LargeTest implements java.lang.annotation.Annotation {
+  public abstract deprecated class LargeTest implements java.lang.annotation.Annotation {
   }
 
-  public abstract class MediumTest implements java.lang.annotation.Annotation {
+  public abstract deprecated class MediumTest implements java.lang.annotation.Annotation {
   }
 
-  public abstract class SmallTest implements java.lang.annotation.Annotation {
+  public abstract deprecated class SmallTest implements java.lang.annotation.Annotation {
   }
 
-  public abstract class Smoke implements java.lang.annotation.Annotation {
+  public abstract deprecated class Smoke implements java.lang.annotation.Annotation {
   }
 
-  public abstract class Suppress implements java.lang.annotation.Annotation {
+  public abstract deprecated class Suppress implements java.lang.annotation.Annotation {
   }
 
 }
diff --git a/api/test-removed.txt b/api/test-removed.txt
index 6b7961e..2c6729d 100644
--- a/api/test-removed.txt
+++ b/api/test-removed.txt
@@ -201,7 +201,7 @@
 
 package android.test.mock {
 
-  public class MockPackageManager extends android.content.pm.PackageManager {
+  public deprecated class MockPackageManager extends android.content.pm.PackageManager {
     method public deprecated java.lang.String getDefaultBrowserPackageName(int);
     method public deprecated boolean setDefaultBrowserPackageName(java.lang.String, int);
   }
diff --git a/cmds/am/src/com/android/commands/am/Am.java b/cmds/am/src/com/android/commands/am/Am.java
index 72e8c3b..d45bc5d 100644
--- a/cmds/am/src/com/android/commands/am/Am.java
+++ b/cmds/am/src/com/android/commands/am/Am.java
@@ -47,7 +47,6 @@
 import android.content.pm.UserInfo;
 import android.content.res.Configuration;
 import android.graphics.Rect;
-import android.net.Uri;
 import android.os.Binder;
 import android.os.Build;
 import android.os.Bundle;
@@ -79,7 +78,6 @@
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.Comparator;
-import java.util.HashSet;
 import java.util.List;
 
 public class Am extends BaseCommand {
@@ -159,6 +157,7 @@
                 "       am stack start <DISPLAY_ID> <INTENT>\n" +
                 "       am stack movetask <TASK_ID> <STACK_ID> [true|false]\n" +
                 "       am stack resize <STACK_ID> <LEFT,TOP,RIGHT,BOTTOM>\n" +
+                "       am stack resize-animated <STACK_ID> <LEFT,TOP,RIGHT,BOTTOM>\n" +
                 "       am stack resize-docked-stack <LEFT,TOP,RIGHT,BOTTOM> [<TASK_LEFT,TASK_TOP,TASK_RIGHT,TASK_BOTTOM>]\n" +
                 "       am stack size-docked-stack-test: <STEP_SIZE> <l|t|r|b> [DELAY_MS]\n" +
                 "       am stack move-top-activity-to-pinned-stack: <STACK_ID> <LEFT,TOP,RIGHT,BOTTOM>\n" +
@@ -1688,6 +1687,9 @@
             case "resize":
                 runStackResize();
                 break;
+            case "resize-animated":
+                runStackResizeAnimated();
+                break;
             case "resize-docked-stack":
                 runStackResizeDocked();
                 break;
@@ -1756,7 +1758,18 @@
             System.err.println("Error: invalid input bounds");
             return;
         }
-        resizeStack(stackId, bounds, 0);
+        resizeStack(stackId, bounds, 0, false);
+    }
+
+    private void runStackResizeAnimated() throws Exception {
+        String stackIdStr = nextArgRequired();
+        int stackId = Integer.valueOf(stackIdStr);
+        final Rect bounds = getBounds();
+        if (bounds == null) {
+            System.err.println("Error: invalid input bounds");
+            return;
+        }
+        resizeStack(stackId, bounds, 0, true);
     }
 
     private void runStackResizeDocked() throws Exception {
@@ -1773,14 +1786,15 @@
         }
     }
 
-    private void resizeStack(int stackId, Rect bounds, int delayMs) throws Exception {
+    private void resizeStack(int stackId, Rect bounds, int delayMs, boolean animate)
+            throws Exception {
         if (bounds == null) {
             showError("Error: invalid input bounds");
             return;
         }
 
         try {
-            mAm.resizeStack(stackId, bounds, false);
+            mAm.resizeStack(stackId, bounds, false, false, animate);
             Thread.sleep(delayMs);
         } catch (RemoteException e) {
             showError("Error: resizing stack " + e);
@@ -1894,7 +1908,7 @@
             maxChange = Math.min(stepSize, currentPoint - minPoint);
             currentPoint -= maxChange;
             setBoundsSide(bounds, side, currentPoint);
-            resizeStack(DOCKED_STACK_ID, bounds, delayMs);
+            resizeStack(DOCKED_STACK_ID, bounds, delayMs, false);
         }
 
         System.out.println("Growing docked stack side=" + side);
@@ -1902,7 +1916,7 @@
             maxChange = Math.min(stepSize, maxPoint - currentPoint);
             currentPoint += maxChange;
             setBoundsSide(bounds, side, currentPoint);
-            resizeStack(DOCKED_STACK_ID, bounds, delayMs);
+            resizeStack(DOCKED_STACK_ID, bounds, delayMs, false);
         }
 
         System.out.println("Back to Original size side=" + side);
@@ -1910,7 +1924,7 @@
             maxChange = Math.min(stepSize, currentPoint - startPoint);
             currentPoint -= maxChange;
             setBoundsSide(bounds, side, currentPoint);
-            resizeStack(DOCKED_STACK_ID, bounds, delayMs);
+            resizeStack(DOCKED_STACK_ID, bounds, delayMs, false);
         }
     }
 
diff --git a/core/java/android/accessibilityservice/AccessibilityService.java b/core/java/android/accessibilityservice/AccessibilityService.java
index 8bc17f8..be93926 100644
--- a/core/java/android/accessibilityservice/AccessibilityService.java
+++ b/core/java/android/accessibilityservice/AccessibilityService.java
@@ -49,7 +49,8 @@
 import java.util.List;
 
 /**
- * An accessibility service runs in the background and receives callbacks by the system
+ * Accessibility services are intended to assist users with disabilities in using
+ * Android devices and apps. They run in the background and receive callbacks by the system
  * when {@link AccessibilityEvent}s are fired. Such events denote some state transition
  * in the user interface, for example, the focus has changed, a button has been clicked,
  * etc. Such a service can optionally request the capability for querying the content
@@ -66,22 +67,31 @@
  * <h3>Lifecycle</h3>
  * <p>
  * The lifecycle of an accessibility service is managed exclusively by the system and
- * follows the established service life cycle. Additionally, starting or stopping an
- * accessibility service is triggered exclusively by an explicit user action through
- * enabling or disabling it in the device settings. After the system binds to a service it
- * calls {@link AccessibilityService#onServiceConnected()}. This method can be
- * overriden by clients that want to perform post binding setup.
+ * follows the established service life cycle. Starting an accessibility service is triggered
+ * exclusively by the user explicitly turning the service on in device settings. After the system
+ * binds to a service, it calls {@link AccessibilityService#onServiceConnected()}. This method can
+ * be overriden by clients that want to perform post binding setup.
+ * </p>
+ * <p>
+ * An accessibility service stops either when the user turns it off in device settings or when
+ * it calls {@link AccessibilityService#disableSelf()}.
  * </p>
  * <h3>Declaration</h3>
  * <p>
- * An accessibility is declared as any other service in an AndroidManifest.xml but it
- * must also specify that it handles the "android.accessibilityservice.AccessibilityService"
- * {@link android.content.Intent}. Failure to declare this intent will cause the system to
- * ignore the accessibility service. Additionally an accessibility service must request the
- * {@link android.Manifest.permission#BIND_ACCESSIBILITY_SERVICE} permission to ensure
- * that only the system
- * can bind to it. Failure to declare this intent will cause the system to ignore the
- * accessibility service. Following is an example declaration:
+ * An accessibility is declared as any other service in an AndroidManifest.xml, but it
+ * must do two things:
+ * <ul>
+ *     <ol>
+ *         Specify that it handles the "android.accessibilityservice.AccessibilityService"
+ *         {@link android.content.Intent}.
+ *     </ol>
+ *     <ol>
+ *         Request the {@link android.Manifest.permission#BIND_ACCESSIBILITY_SERVICE} permission to
+ *         ensure that only the system can bind to it.
+ *     </ol>
+ * </ul>
+ * If either of these items is missing, the system will ignore the accessibility service.
+ * Following is an example declaration:
  * </p>
  * <pre> &lt;service android:name=".MyAccessibilityService"
  *         android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE"&gt;
@@ -135,48 +145,24 @@
  * </ul>
  * <h3>Retrieving window content</h3>
  * <p>
- * A service can specify in its declaration that it can retrieve the active window
- * content which is represented as a tree of {@link AccessibilityNodeInfo}. Note that
+ * A service can specify in its declaration that it can retrieve window
+ * content which is represented as a tree of {@link AccessibilityWindowInfo} and
+ * {@link AccessibilityNodeInfo} objects. Note that
  * declaring this capability requires that the service declares its configuration via
  * an XML resource referenced by {@link #SERVICE_META_DATA}.
  * </p>
  * <p>
- * For security purposes an accessibility service can retrieve only the content of the
- * currently active window. The currently active window is defined as the window from
- * which was fired the last event of the following types:
- * {@link AccessibilityEvent#TYPE_WINDOW_STATE_CHANGED},
- * {@link AccessibilityEvent#TYPE_VIEW_HOVER_ENTER},
- * {@link AccessibilityEvent#TYPE_VIEW_HOVER_EXIT},
- * In other words, the last window that was shown or the last window that the user has touched
- * during touch exploration.
- * </p>
- * <p>
- * The entry point for retrieving window content is through calling
- * {@link AccessibilityEvent#getSource() AccessibilityEvent.getSource()} of the last received
- * event of the above types or a previous event from the same window
- * (see {@link AccessibilityEvent#getWindowId() AccessibilityEvent.getWindowId()}). Invoking
- * this method will return an {@link AccessibilityNodeInfo} that can be used to traverse the
- * window content which represented as a tree of such objects.
+ * Window content may be retrieved with
+ * {@link AccessibilityEvent#getSource() AccessibilityEvent.getSource()},
+ * {@link AccessibilityService#findFocus(int)},
+ * {@link AccessibilityService#getWindows()}, or
+ * {@link AccessibilityService#getRootInActiveWindow()}.
  * </p>
  * <p class="note">
  * <strong>Note</strong> An accessibility service may have requested to be notified for
- * a subset of the event types, thus be unaware that the active window has changed. Therefore
- * accessibility service that would like to retrieve window content should:
- * <ul>
- * <li>
- * Register for all event types with no notification timeout and keep track for the active
- * window by calling {@link AccessibilityEvent#getWindowId()} of the last received event and
- * compare this with the {@link AccessibilityNodeInfo#getWindowId()} before calling retrieval
- * methods on the latter.
- * </li>
- * <li>
- * Prepare that a retrieval method on {@link AccessibilityNodeInfo} may fail since the
- * active window has changed and the service did not get the accessibility event yet. Note
- * that it is possible to have a retrieval method failing even adopting the strategy
- * specified in the previous bullet because the accessibility event dispatch is asynchronous
- * and crosses process boundaries.
- * </li>
- * </ul>
+ * a subset of the event types, and thus be unaware when the node hierarchy has changed. It is also
+ * possible for a node to contain outdated information because the window content may change at any
+ * time.
  * </p>
  * <h3>Notification strategy</h3>
  * <p>
@@ -328,7 +314,6 @@
      *     android:settingsActivity="foo.bar.TestBackActivity"
      *     android:canRetrieveWindowContent="true"
      *     android:canRequestTouchExplorationMode="true"
-     *     android:canRequestEnhancedWebAccessibility="true"
      *     . . .
      * /&gt;</pre>
      */
@@ -528,6 +513,14 @@
      * is currently touching or the window with input focus, if the user is not
      * touching any window.
      * <p>
+     * The currently active window is defined as the window that most recently fired one
+     * of the following events:
+     * {@link AccessibilityEvent#TYPE_WINDOW_STATE_CHANGED},
+     * {@link AccessibilityEvent#TYPE_VIEW_HOVER_ENTER},
+     * {@link AccessibilityEvent#TYPE_VIEW_HOVER_EXIT}.
+     * In other words, the last window shown that also has input focus.
+     * </p>
+     * <p>
      * <strong>Note:</strong> In order to access the root node your service has
      * to declare the capability to retrieve window content by setting the
      * {@link android.R.styleable#AccessibilityService_canRetrieveWindowContent}
@@ -541,8 +534,8 @@
     }
 
     /**
-     * This method allows accessibility service turn itself off
-     * and the service will become disabled from the Settings.
+     * Disables the service. After calling this method, the service will be disabled and settings
+     * will show that it is turned off.
      */
     public final void disableSelf() {
         final IAccessibilityServiceConnection connection =
@@ -1054,7 +1047,7 @@
      * property in its meta-data. For details refer to {@link #SERVICE_META_DATA}.
      * Also the service has to opt-in to retrieve the interactive windows by
      * setting the {@link AccessibilityServiceInfo#FLAG_RETRIEVE_INTERACTIVE_WINDOWS}
-     * flag.Otherwise, the search will be performed only in the active window.
+     * flag. Otherwise, the search will be performed only in the active window.
      * </p>
      *
      * @param focus The focus to find. One of {@link AccessibilityNodeInfo#FOCUS_INPUT} or
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 11154f2..dc3f64a 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -4397,7 +4397,7 @@
             String resolvedType = null;
             if (fillInIntent != null) {
                 fillInIntent.migrateExtraStreamToClipData();
-                fillInIntent.prepareToLeaveProcess();
+                fillInIntent.prepareToLeaveProcess(this);
                 resolvedType = fillInIntent.resolveTypeIfNeeded(getContentResolver());
             }
             int result = ActivityManagerNative.getDefault()
@@ -4629,7 +4629,7 @@
                     intent.putExtra(Intent.EXTRA_REFERRER, referrer);
                 }
                 intent.migrateExtraStreamToClipData();
-                intent.prepareToLeaveProcess();
+                intent.prepareToLeaveProcess(this);
                 result = ActivityManagerNative.getDefault()
                     .startActivity(mMainThread.getApplicationThread(), getBasePackageName(),
                             intent, intent.resolveTypeIfNeeded(getContentResolver()), mToken,
@@ -4700,7 +4700,7 @@
         if (mParent == null) {
             try {
                 intent.migrateExtraStreamToClipData();
-                intent.prepareToLeaveProcess();
+                intent.prepareToLeaveProcess(this);
                 return ActivityManagerNative.getDefault()
                     .startNextMatchingActivity(mToken, intent, options);
             } catch (RemoteException e) {
@@ -5128,7 +5128,7 @@
             if (false) Log.v(TAG, "Finishing self: token=" + mToken);
             try {
                 if (resultData != null) {
-                    resultData.prepareToLeaveProcess();
+                    resultData.prepareToLeaveProcess(this);
                 }
                 if (ActivityManagerNative.getDefault()
                         .finishActivity(mToken, resultCode, resultData, finishTask)) {
@@ -5355,7 +5355,7 @@
             @PendingIntent.Flags int flags) {
         String packageName = getPackageName();
         try {
-            data.prepareToLeaveProcess();
+            data.prepareToLeaveProcess(this);
             IIntentSender target =
                 ActivityManagerNative.getDefault().getIntentSender(
                         ActivityManager.INTENT_SENDER_ACTIVITY_RESULT, packageName,
@@ -6335,10 +6335,10 @@
                 resultData = mResultData;
             }
             if (resultData != null) {
-                resultData.prepareToLeaveProcess();
+                resultData.prepareToLeaveProcess(this);
             }
             try {
-                upIntent.prepareToLeaveProcess();
+                upIntent.prepareToLeaveProcess(this);
                 return ActivityManagerNative.getDefault().navigateUpTo(mToken, upIntent,
                         resultCode, resultData);
             } catch (RemoteException e) {
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index 420bf31..90feab4 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -564,8 +564,7 @@
          * there isn't a display gap.
          */
         public static boolean preserveWindowOnTaskMove(int stackId) {
-            return stackId == FULLSCREEN_WORKSPACE_STACK_ID
-                    || stackId == DOCKED_STACK_ID || stackId == PINNED_STACK_ID;
+            return stackId == FULLSCREEN_WORKSPACE_STACK_ID || stackId == DOCKED_STACK_ID;
         }
 
         /**
@@ -616,6 +615,14 @@
         public static boolean keepVisibleDeadAppWindowOnScreen(int stackId) {
             return stackId != PINNED_STACK_ID;
         }
+
+        /**
+         * Returns true if the backdrop on the client side should match the frame of the window.
+         * Returns false, if the backdrop should be fullscreen.
+         */
+        public static boolean useWindowFrameForBackdrop(int stackId) {
+            return stackId == FREEFORM_WORKSPACE_STACK_ID || stackId == PINNED_STACK_ID;
+        }
     }
 
     /**
diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java
index 42ff8e8..cd5797e 100644
--- a/core/java/android/app/ActivityManagerNative.java
+++ b/core/java/android/app/ActivityManagerNative.java
@@ -816,7 +816,9 @@
                 r = Rect.CREATOR.createFromParcel(data);
             }
             final boolean allowResizeInDockedMode = data.readInt() == 1;
-            resizeStack(stackId, r, allowResizeInDockedMode);
+            final boolean preserveWindows = data.readInt() == 1;
+            final boolean animate = data.readInt() == 1;
+            resizeStack(stackId, r, allowResizeInDockedMode, preserveWindows, animate);
             reply.writeNoException();
             return true;
         }
@@ -3815,9 +3817,8 @@
         return res;
     }
     @Override
-    public void resizeStack(int stackId, Rect r, boolean allowResizeInDockedMode)
-            throws RemoteException
-    {
+    public void resizeStack(int stackId, Rect r, boolean allowResizeInDockedMode,
+            boolean preserveWindows, boolean animate) throws RemoteException {
         Parcel data = Parcel.obtain();
         Parcel reply = Parcel.obtain();
         data.writeInterfaceToken(IActivityManager.descriptor);
@@ -3829,6 +3830,8 @@
             data.writeInt(0);
         }
         data.writeInt(allowResizeInDockedMode ? 1 : 0);
+        data.writeInt(preserveWindows ? 1 : 0);
+        data.writeInt(animate ? 1 : 0);
         mRemote.transact(RESIZE_STACK_TRANSACTION, data, reply, 0);
         reply.readException();
         data.recycle();
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 1e7457c..100e67b 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -56,6 +56,7 @@
 import android.opengl.GLUtils;
 import android.os.AsyncTask;
 import android.os.Binder;
+import android.os.Build;
 import android.os.Bundle;
 import android.os.Debug;
 import android.os.DropBoxManager;
@@ -4959,16 +4960,22 @@
         }
 
         /**
-         * For apps targetting SDK Honeycomb or later, we don't allow
-         * network usage on the main event loop / UI thread.
-         *
-         * Note to those grepping:  this is what ultimately throws
-         * NetworkOnMainThreadException ...
+         * For apps targetting Honeycomb or later, we don't allow network usage
+         * on the main event loop / UI thread. This is what ultimately throws
+         * {@link NetworkOnMainThreadException}.
          */
-        if (data.appInfo.targetSdkVersion > 9) {
+        if (data.appInfo.targetSdkVersion >= Build.VERSION_CODES.HONEYCOMB) {
             StrictMode.enableDeathOnNetwork();
         }
 
+        /**
+         * For apps targetting N or later, we don't allow file:// Uri exposure.
+         * This is what ultimately throws {@link FileUriExposedException}.
+         */
+        if (data.appInfo.targetSdkVersion >= Build.VERSION_CODES.N) {
+            StrictMode.enableDeathOnFileUriExposure();
+        }
+
         NetworkSecurityPolicy.getInstance().setCleartextTrafficPermitted(
                 (data.appInfo.flags & ApplicationInfo.FLAG_USES_CLEARTEXT_TRAFFIC) != 0);
 
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index fab3740..0d6e93d 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -752,7 +752,7 @@
             String resolvedType = null;
             if (fillInIntent != null) {
                 fillInIntent.migrateExtraStreamToClipData();
-                fillInIntent.prepareToLeaveProcess();
+                fillInIntent.prepareToLeaveProcess(this);
                 resolvedType = fillInIntent.resolveTypeIfNeeded(getContentResolver());
             }
             int result = ActivityManagerNative.getDefault()
@@ -773,7 +773,7 @@
         warnIfCallingFromSystemProcess();
         String resolvedType = intent.resolveTypeIfNeeded(getContentResolver());
         try {
-            intent.prepareToLeaveProcess();
+            intent.prepareToLeaveProcess(this);
             ActivityManagerNative.getDefault().broadcastIntent(
                     mMainThread.getApplicationThread(), intent, resolvedType, null,
                     Activity.RESULT_OK, null, null, null, AppOpsManager.OP_NONE, null, false, false,
@@ -790,7 +790,7 @@
         String[] receiverPermissions = receiverPermission == null ? null
                 : new String[] {receiverPermission};
         try {
-            intent.prepareToLeaveProcess();
+            intent.prepareToLeaveProcess(this);
             ActivityManagerNative.getDefault().broadcastIntent(
                     mMainThread.getApplicationThread(), intent, resolvedType, null,
                     Activity.RESULT_OK, null, null, receiverPermissions, AppOpsManager.OP_NONE,
@@ -805,7 +805,7 @@
         warnIfCallingFromSystemProcess();
         String resolvedType = intent.resolveTypeIfNeeded(getContentResolver());
         try {
-            intent.prepareToLeaveProcess();
+            intent.prepareToLeaveProcess(this);
             ActivityManagerNative.getDefault().broadcastIntent(
                     mMainThread.getApplicationThread(), intent, resolvedType, null,
                     Activity.RESULT_OK, null, null, receiverPermissions, AppOpsManager.OP_NONE,
@@ -822,7 +822,7 @@
         String[] receiverPermissions = receiverPermission == null ? null
                 : new String[] {receiverPermission};
         try {
-            intent.prepareToLeaveProcess();
+            intent.prepareToLeaveProcess(this);
             ActivityManagerNative.getDefault().broadcastIntent(
                     mMainThread.getApplicationThread(), intent, resolvedType, null,
                     Activity.RESULT_OK, null, null, receiverPermissions, AppOpsManager.OP_NONE,
@@ -839,7 +839,7 @@
         String[] receiverPermissions = receiverPermission == null ? null
                 : new String[] {receiverPermission};
         try {
-            intent.prepareToLeaveProcess();
+            intent.prepareToLeaveProcess(this);
             ActivityManagerNative.getDefault().broadcastIntent(
                     mMainThread.getApplicationThread(), intent, resolvedType, null,
                     Activity.RESULT_OK, null, null, receiverPermissions, appOp, null, false, false,
@@ -856,7 +856,7 @@
         String[] receiverPermissions = receiverPermission == null ? null
                 : new String[] {receiverPermission};
         try {
-            intent.prepareToLeaveProcess();
+            intent.prepareToLeaveProcess(this);
             ActivityManagerNative.getDefault().broadcastIntent(
                     mMainThread.getApplicationThread(), intent, resolvedType, null,
                     Activity.RESULT_OK, null, null, receiverPermissions, AppOpsManager.OP_NONE,
@@ -919,7 +919,7 @@
         String[] receiverPermissions = receiverPermission == null ? null
                 : new String[] {receiverPermission};
         try {
-            intent.prepareToLeaveProcess();
+            intent.prepareToLeaveProcess(this);
             ActivityManagerNative.getDefault().broadcastIntent(
                 mMainThread.getApplicationThread(), intent, resolvedType, rd,
                 initialCode, initialData, initialExtras, receiverPermissions, appOp,
@@ -933,7 +933,7 @@
     public void sendBroadcastAsUser(Intent intent, UserHandle user) {
         String resolvedType = intent.resolveTypeIfNeeded(getContentResolver());
         try {
-            intent.prepareToLeaveProcess();
+            intent.prepareToLeaveProcess(this);
             ActivityManagerNative.getDefault().broadcastIntent(mMainThread.getApplicationThread(),
                     intent, resolvedType, null, Activity.RESULT_OK, null, null, null,
                     AppOpsManager.OP_NONE, null, false, false, user.getIdentifier());
@@ -955,7 +955,7 @@
         String[] receiverPermissions = receiverPermission == null ? null
                 : new String[] {receiverPermission};
         try {
-            intent.prepareToLeaveProcess();
+            intent.prepareToLeaveProcess(this);
             ActivityManagerNative.getDefault().broadcastIntent(
                     mMainThread.getApplicationThread(), intent, resolvedType, null,
                     Activity.RESULT_OK, null, null, receiverPermissions, appOp, null, false, false,
@@ -1006,7 +1006,7 @@
         String[] receiverPermissions = receiverPermission == null ? null
                 : new String[] {receiverPermission};
         try {
-            intent.prepareToLeaveProcess();
+            intent.prepareToLeaveProcess(this);
             ActivityManagerNative.getDefault().broadcastIntent(
                 mMainThread.getApplicationThread(), intent, resolvedType, rd,
                 initialCode, initialData, initialExtras, receiverPermissions,
@@ -1022,7 +1022,7 @@
         warnIfCallingFromSystemProcess();
         String resolvedType = intent.resolveTypeIfNeeded(getContentResolver());
         try {
-            intent.prepareToLeaveProcess();
+            intent.prepareToLeaveProcess(this);
             ActivityManagerNative.getDefault().broadcastIntent(
                 mMainThread.getApplicationThread(), intent, resolvedType, null,
                 Activity.RESULT_OK, null, null, null, AppOpsManager.OP_NONE, null, false, true,
@@ -1058,7 +1058,7 @@
         }
         String resolvedType = intent.resolveTypeIfNeeded(getContentResolver());
         try {
-            intent.prepareToLeaveProcess();
+            intent.prepareToLeaveProcess(this);
             ActivityManagerNative.getDefault().broadcastIntent(
                 mMainThread.getApplicationThread(), intent, resolvedType, rd,
                 initialCode, initialData, initialExtras, null,
@@ -1077,7 +1077,7 @@
             intent.setDataAndType(intent.getData(), resolvedType);
         }
         try {
-            intent.prepareToLeaveProcess();
+            intent.prepareToLeaveProcess(this);
             ActivityManagerNative.getDefault().unbroadcastIntent(
                     mMainThread.getApplicationThread(), intent, getUserId());
         } catch (RemoteException e) {
@@ -1090,7 +1090,7 @@
     public void sendStickyBroadcastAsUser(Intent intent, UserHandle user) {
         String resolvedType = intent.resolveTypeIfNeeded(getContentResolver());
         try {
-            intent.prepareToLeaveProcess();
+            intent.prepareToLeaveProcess(this);
             ActivityManagerNative.getDefault().broadcastIntent(
                 mMainThread.getApplicationThread(), intent, resolvedType, null,
                 Activity.RESULT_OK, null, null, null, AppOpsManager.OP_NONE, null, false, true,
@@ -1105,7 +1105,7 @@
     public void sendStickyBroadcastAsUser(Intent intent, UserHandle user, Bundle options) {
         String resolvedType = intent.resolveTypeIfNeeded(getContentResolver());
         try {
-            intent.prepareToLeaveProcess();
+            intent.prepareToLeaveProcess(this);
             ActivityManagerNative.getDefault().broadcastIntent(
                 mMainThread.getApplicationThread(), intent, resolvedType, null,
                 Activity.RESULT_OK, null, null, null, AppOpsManager.OP_NONE, options, false, true,
@@ -1140,7 +1140,7 @@
         }
         String resolvedType = intent.resolveTypeIfNeeded(getContentResolver());
         try {
-            intent.prepareToLeaveProcess();
+            intent.prepareToLeaveProcess(this);
             ActivityManagerNative.getDefault().broadcastIntent(
                 mMainThread.getApplicationThread(), intent, resolvedType, rd,
                 initialCode, initialData, initialExtras, null,
@@ -1159,7 +1159,7 @@
             intent.setDataAndType(intent.getData(), resolvedType);
         }
         try {
-            intent.prepareToLeaveProcess();
+            intent.prepareToLeaveProcess(this);
             ActivityManagerNative.getDefault().unbroadcastIntent(
                     mMainThread.getApplicationThread(), intent, user.getIdentifier());
         } catch (RemoteException e) {
@@ -1262,7 +1262,7 @@
     private ComponentName startServiceCommon(Intent service, UserHandle user) {
         try {
             validateServiceIntent(service);
-            service.prepareToLeaveProcess();
+            service.prepareToLeaveProcess(this);
             ComponentName cn = ActivityManagerNative.getDefault().startService(
                 mMainThread.getApplicationThread(), service, service.resolveTypeIfNeeded(
                             getContentResolver()), getOpPackageName(), user.getIdentifier());
@@ -1291,7 +1291,7 @@
     private boolean stopServiceCommon(Intent service, UserHandle user) {
         try {
             validateServiceIntent(service);
-            service.prepareToLeaveProcess();
+            service.prepareToLeaveProcess(this);
             int res = ActivityManagerNative.getDefault().stopService(
                 mMainThread.getApplicationThread(), service,
                 service.resolveTypeIfNeeded(getContentResolver()), user.getIdentifier());
@@ -1339,7 +1339,7 @@
                     < android.os.Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
                 flags |= BIND_WAIVE_PRIORITY;
             }
-            service.prepareToLeaveProcess();
+            service.prepareToLeaveProcess(this);
             int res = ActivityManagerNative.getDefault().bindService(
                 mMainThread.getApplicationThread(), getActivityToken(), service,
                 service.resolveTypeIfNeeded(getContentResolver()),
diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java
index 22de2ff..5b3ffe0 100644
--- a/core/java/android/app/IActivityManager.java
+++ b/core/java/android/app/IActivityManager.java
@@ -146,8 +146,8 @@
     public void moveTaskToDockedStack(int taskId, int createMode, boolean toTop, boolean animate,
             Rect initialBounds) throws RemoteException;
     public boolean moveTopActivityToPinnedStack(int stackId, Rect bounds) throws RemoteException;
-    public void resizeStack(int stackId, Rect bounds, boolean allowResizeInDockedMode)
-            throws RemoteException;
+    public void resizeStack(int stackId, Rect bounds, boolean allowResizeInDockedMode,
+            boolean preserveWindows, boolean animate) throws RemoteException;
 
     /**
      * Resizes the docked stack, and all other stacks as the result of the dock stack bounds change.
diff --git a/core/java/android/app/IWallpaperManager.aidl b/core/java/android/app/IWallpaperManager.aidl
index 7a0e7f6..fa0fbd1 100644
--- a/core/java/android/app/IWallpaperManager.aidl
+++ b/core/java/android/app/IWallpaperManager.aidl
@@ -35,10 +35,16 @@
      * 'which' is some combination of:
      *   FLAG_SET_SYSTEM
      *   FLAG_SET_LOCK
+     *
+     * A 'null' cropHint rectangle is explicitly permitted as a sentinel for "whatever
+     * the source image's bounding rect is."
+     *
+     * The completion callback's "onWallpaperChanged()" method is invoked when the
+     * new wallpaper content is ready to display.
      */
     ParcelFileDescriptor setWallpaper(String name, in String callingPackage,
-            out Bundle extras, int which);
-    
+            in Rect cropHint, out Bundle extras, int which, IWallpaperManagerCallback completion);
+
     /**
      * Set the live wallpaper. This only affects the system wallpaper.
      */
@@ -54,14 +60,14 @@
      */
     ParcelFileDescriptor getWallpaper(IWallpaperManagerCallback cb,
             out Bundle outParams);
-    
+
     /**
      * If the current system wallpaper is a live wallpaper component, return the
      * information about that wallpaper.  Otherwise, if it is a static image,
      * simply return null.
      */
     WallpaperInfo getWallpaperInfo();
-    
+
     /**
      * Clear the system wallpaper.
      */
diff --git a/core/java/android/app/Instrumentation.java b/core/java/android/app/Instrumentation.java
index f52ab55..9a88f2c 100644
--- a/core/java/android/app/Instrumentation.java
+++ b/core/java/android/app/Instrumentation.java
@@ -1503,7 +1503,7 @@
         }
         try {
             intent.migrateExtraStreamToClipData();
-            intent.prepareToLeaveProcess();
+            intent.prepareToLeaveProcess(who);
             int result = ActivityManagerNative.getDefault()
                 .startActivity(whoThread, who.getBasePackageName(), intent,
                         intent.resolveTypeIfNeeded(who.getContentResolver()),
@@ -1561,7 +1561,7 @@
             String[] resolvedTypes = new String[intents.length];
             for (int i=0; i<intents.length; i++) {
                 intents[i].migrateExtraStreamToClipData();
-                intents[i].prepareToLeaveProcess();
+                intents[i].prepareToLeaveProcess(who);
                 resolvedTypes[i] = intents[i].resolveTypeIfNeeded(who.getContentResolver());
             }
             int result = ActivityManagerNative.getDefault()
@@ -1622,7 +1622,7 @@
         }
         try {
             intent.migrateExtraStreamToClipData();
-            intent.prepareToLeaveProcess();
+            intent.prepareToLeaveProcess(who);
             int result = ActivityManagerNative.getDefault()
                 .startActivity(whoThread, who.getBasePackageName(), intent,
                         intent.resolveTypeIfNeeded(who.getContentResolver()),
@@ -1682,7 +1682,7 @@
         }
         try {
             intent.migrateExtraStreamToClipData();
-            intent.prepareToLeaveProcess();
+            intent.prepareToLeaveProcess(who);
             int result = ActivityManagerNative.getDefault()
                 .startActivityAsUser(whoThread, who.getBasePackageName(), intent,
                         intent.resolveTypeIfNeeded(who.getContentResolver()),
@@ -1721,7 +1721,7 @@
         }
         try {
             intent.migrateExtraStreamToClipData();
-            intent.prepareToLeaveProcess();
+            intent.prepareToLeaveProcess(who);
             int result = ActivityManagerNative.getDefault()
                 .startActivityAsCaller(whoThread, who.getBasePackageName(), intent,
                         intent.resolveTypeIfNeeded(who.getContentResolver()),
@@ -1759,7 +1759,7 @@
         }
         try {
             intent.migrateExtraStreamToClipData();
-            intent.prepareToLeaveProcess();
+            intent.prepareToLeaveProcess(who);
             int result = appTask.startActivity(whoThread.asBinder(), who.getBasePackageName(),
                     intent, intent.resolveTypeIfNeeded(who.getContentResolver()), options);
             checkStartActivityResult(result, intent);
diff --git a/core/java/android/app/PendingIntent.java b/core/java/android/app/PendingIntent.java
index edafe59..412b098 100644
--- a/core/java/android/app/PendingIntent.java
+++ b/core/java/android/app/PendingIntent.java
@@ -307,7 +307,7 @@
                 context.getContentResolver()) : null;
         try {
             intent.migrateExtraStreamToClipData();
-            intent.prepareToLeaveProcess();
+            intent.prepareToLeaveProcess(context);
             IIntentSender target =
                 ActivityManagerNative.getDefault().getIntentSender(
                     ActivityManager.INTENT_SENDER_ACTIVITY, packageName,
@@ -332,7 +332,7 @@
                 context.getContentResolver()) : null;
         try {
             intent.migrateExtraStreamToClipData();
-            intent.prepareToLeaveProcess();
+            intent.prepareToLeaveProcess(context);
             IIntentSender target =
                 ActivityManagerNative.getDefault().getIntentSender(
                     ActivityManager.INTENT_SENDER_ACTIVITY, packageName,
@@ -446,7 +446,7 @@
         String[] resolvedTypes = new String[intents.length];
         for (int i=0; i<intents.length; i++) {
             intents[i].migrateExtraStreamToClipData();
-            intents[i].prepareToLeaveProcess();
+            intents[i].prepareToLeaveProcess(context);
             resolvedTypes[i] = intents[i].resolveTypeIfNeeded(context.getContentResolver());
         }
         try {
@@ -472,7 +472,7 @@
         String[] resolvedTypes = new String[intents.length];
         for (int i=0; i<intents.length; i++) {
             intents[i].migrateExtraStreamToClipData();
-            intents[i].prepareToLeaveProcess();
+            intents[i].prepareToLeaveProcess(context);
             resolvedTypes[i] = intents[i].resolveTypeIfNeeded(context.getContentResolver());
         }
         try {
@@ -527,7 +527,7 @@
         String resolvedType = intent != null ? intent.resolveTypeIfNeeded(
                 context.getContentResolver()) : null;
         try {
-            intent.prepareToLeaveProcess();
+            intent.prepareToLeaveProcess(context);
             IIntentSender target =
                 ActivityManagerNative.getDefault().getIntentSender(
                     ActivityManager.INTENT_SENDER_BROADCAST, packageName,
@@ -570,7 +570,7 @@
         String resolvedType = intent != null ? intent.resolveTypeIfNeeded(
                 context.getContentResolver()) : null;
         try {
-            intent.prepareToLeaveProcess();
+            intent.prepareToLeaveProcess(context);
             IIntentSender target =
                 ActivityManagerNative.getDefault().getIntentSender(
                     ActivityManager.INTENT_SENDER_SERVICE, packageName,
diff --git a/core/java/android/app/WallpaperManager.java b/core/java/android/app/WallpaperManager.java
index f103576..b0ffd21 100644
--- a/core/java/android/app/WallpaperManager.java
+++ b/core/java/android/app/WallpaperManager.java
@@ -64,6 +64,8 @@
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.util.List;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
 
 /**
  * Provides access to the system wallpaper. With WallpaperManager, you can
@@ -770,18 +772,26 @@
             return 0;
         }
         final Bundle result = new Bundle();
+        final WallpaperSetCompletion completion = new WallpaperSetCompletion();
         try {
             Resources resources = mContext.getResources();
             /* Set the wallpaper to the default values */
             ParcelFileDescriptor fd = sGlobals.mService.setWallpaper(
                     "res:" + resources.getResourceName(resid),
-                    mContext.getOpPackageName(), result, which);
+                    mContext.getOpPackageName(), null, result, which, completion);
             if (fd != null) {
                 FileOutputStream fos = null;
+                boolean ok = false;
                 try {
                     fos = new ParcelFileDescriptor.AutoCloseOutputStream(fd);
                     copyStreamToWallpaperFile(resources.openRawResource(resid), fos);
+                    // The 'close()' is the trigger for any server-side image manipulation,
+                    // so we must do that before waiting for completion.
+                    fos.close();
+                    completion.waitForCompletion();
                 } finally {
+                    // Might be redundant but completion shouldn't wait unless the write
+                    // succeeded; this is a fallback if it threw past the close+wait.
                     IoUtils.closeQuietly(fos);
                 }
             }
@@ -876,14 +886,17 @@
             return 0;
         }
         final Bundle result = new Bundle();
+        final WallpaperSetCompletion completion = new WallpaperSetCompletion();
         try {
             ParcelFileDescriptor fd = sGlobals.mService.setWallpaper(null,
-                    mContext.getOpPackageName(), result, which);
+                    mContext.getOpPackageName(), visibleCropHint, result, which, completion);
             if (fd != null) {
                 FileOutputStream fos = null;
                 try {
                     fos = new ParcelFileDescriptor.AutoCloseOutputStream(fd);
                     fullImage.compress(Bitmap.CompressFormat.PNG, 90, fos);
+                    fos.close();
+                    completion.waitForCompletion();
                 } finally {
                     IoUtils.closeQuietly(fos);
                 }
@@ -990,14 +1003,17 @@
             return 0;
         }
         final Bundle result = new Bundle();
+        final WallpaperSetCompletion completion = new WallpaperSetCompletion();
         try {
             ParcelFileDescriptor fd = sGlobals.mService.setWallpaper(null,
-                    mContext.getOpPackageName(), result, which);
+                    mContext.getOpPackageName(), visibleCropHint, result, which, completion);
             if (fd != null) {
                 FileOutputStream fos = null;
                 try {
                     fos = new ParcelFileDescriptor.AutoCloseOutputStream(fd);
                     copyStreamToWallpaperFile(bitmapData, fos);
+                    fos.close();
+                    completion.waitForCompletion();
                 } finally {
                     IoUtils.closeQuietly(fos);
                 }
@@ -1385,4 +1401,28 @@
 
         return null;
     }
+
+    // Private completion callback for setWallpaper() synchronization
+    private class WallpaperSetCompletion extends IWallpaperManagerCallback.Stub {
+        final CountDownLatch mLatch;
+
+        public WallpaperSetCompletion() {
+            mLatch = new CountDownLatch(1);
+        }
+
+        public void waitForCompletion() {
+            try {
+                mLatch.await(30, TimeUnit.SECONDS);
+            } catch (InterruptedException e) {
+                // This might be legit: the crop may take a very long time. Don't sweat
+                // it in that case; we are okay with display lagging behind in order to
+                // keep the caller from locking up indeterminately.
+            }
+        }
+
+        @Override
+        public void onWallpaperChanged() throws RemoteException {
+            mLatch.countDown();
+        }
+    }
 }
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index fef2a0e..e4e97a1 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -30,6 +30,7 @@
 import android.content.pm.ActivityInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
+import android.content.pm.UserInfo;
 import android.graphics.Bitmap;
 import android.net.ProxyInfo;
 import android.net.Uri;
@@ -2973,6 +2974,34 @@
     }
 
     /**
+     * Should be called when keyguard has been dismissed.
+     * @hide
+     */
+    public void reportKeyguardDismissed() {
+        if (mService != null) {
+            try {
+                mService.reportKeyguardDismissed();
+            } catch (RemoteException e) {
+                Log.w(TAG, REMOTE_EXCEPTION_MESSAGE, e);
+            }
+        }
+    }
+
+    /**
+     * Should be called when keyguard view has been shown to the user.
+     * @hide
+     */
+    public void reportKeyguardSecured() {
+        if (mService != null) {
+            try {
+                mService.reportKeyguardSecured();
+            } catch (RemoteException e) {
+                Log.w(TAG, REMOTE_EXCEPTION_MESSAGE, e);
+            }
+        }
+    }
+
+    /**
      * @hide
      * Sets the given package as the device owner.
      * Same as {@link #setDeviceOwner(ComponentName, String)} but without setting a device owner name.
@@ -5318,6 +5347,22 @@
     }
 
     /**
+     * Called by the system to obtain a {@link DevicePolicyManager} whose calls act on the parent
+     * profile.
+     *
+     * @hide
+     */
+    public DevicePolicyManager getParentProfileInstance(UserInfo uInfo) {
+        mContext.checkSelfPermission(
+                android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS);
+        if (!uInfo.isManagedProfile()) {
+            throw new SecurityException("The user " + uInfo.id
+                    + " does not have a parent profile.");
+        }
+        return new DevicePolicyManager(mContext, true);
+    }
+
+    /**
      * Called by a profile owner of a managed profile to set the color used for customization.
      * This color is used as background color of the confirm credentials screen for that user.
      * The default color is {@link android.graphics.Color#GRAY}.
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index 20d4a29..6333013 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -116,6 +116,9 @@
     void reportFailedPasswordAttempt(int userHandle);
     void reportSuccessfulPasswordAttempt(int userHandle);
 
+    void reportKeyguardDismissed();
+    void reportKeyguardSecured();
+
     boolean setDeviceOwner(in ComponentName who, String ownerName, int userId);
     ComponentName getDeviceOwnerComponent(boolean callingUserOnly);
     String getDeviceOwnerName();
diff --git a/core/java/android/app/usage/NetworkStatsManager.java b/core/java/android/app/usage/NetworkStatsManager.java
index e8373a1..1a9bf4e 100644
--- a/core/java/android/app/usage/NetworkStatsManager.java
+++ b/core/java/android/app/usage/NetworkStatsManager.java
@@ -52,10 +52,12 @@
  * {@link NetworkStats.Bucket#STATE_ALL} and all Bucket's roaming is going to be
  * {@link NetworkStats.Bucket#ROAMING_ALL}.
  * <p />
- * <b>NOTE:</b> Accessing stats for apps other than the calling app requires the permission
- * {@link android.Manifest.permission#PACKAGE_USAGE_STATS}, which is a system-level permission and
- * will not be granted to third-party apps. However, declaring the permission implies intention to
- * use the API and the user of the device can grant permission through the Settings application.
+ * <b>NOTE:</b> Calling {@link #querySummaryForDevice} or accessing stats for apps other than the
+ * calling app requires the permission {@link android.Manifest.permission#PACKAGE_USAGE_STATS},
+ * which is a system-level permission and will not be granted to third-party apps. However,
+ * declaring the permission implies intention to use the API and the user of the device can grant
+ * permission through the Settings application.
+ * <p />
  * Profile owner apps are automatically granted permission to query data on the profile they manage
  * (that is, for any query except {@link #querySummaryForDevice}). Device owner apps and carrier-
  * privileged apps likewise get access to usage data for all users on the device.
diff --git a/core/java/android/app/usage/UsageStats.java b/core/java/android/app/usage/UsageStats.java
index 0fce4e2..a88aa3125 100644
--- a/core/java/android/app/usage/UsageStats.java
+++ b/core/java/android/app/usage/UsageStats.java
@@ -165,14 +165,18 @@
                     mPackageName + "' with UsageStats for package '" + right.mPackageName + "'.");
         }
 
-        if (right.mEndTimeStamp > mEndTimeStamp) {
+        if (right.mBeginTimeStamp > mBeginTimeStamp) {
+            // The incoming UsageStat begins after this one, so use its last time used fields
+            // as the source of truth.
+            // We use the mBeginTimeStamp due to a bug where UsageStats files can overlap with
+            // regards to their mEndTimeStamp.
             mLastEvent = right.mLastEvent;
-            mEndTimeStamp = right.mEndTimeStamp;
             mLastTimeUsed = right.mLastTimeUsed;
             mBeginIdleTime = right.mBeginIdleTime;
             mLastTimeSystemUsed = right.mLastTimeSystemUsed;
         }
         mBeginTimeStamp = Math.min(mBeginTimeStamp, right.mBeginTimeStamp);
+        mEndTimeStamp = Math.max(mEndTimeStamp, right.mEndTimeStamp);
         mTotalTimeInForeground += right.mTotalTimeInForeground;
         mLaunchCount += right.mLaunchCount;
     }
diff --git a/core/java/android/auditing/SecurityLog.java b/core/java/android/auditing/SecurityLog.java
index 87dc1d8..8d8d2f5 100644
--- a/core/java/android/auditing/SecurityLog.java
+++ b/core/java/android/auditing/SecurityLog.java
@@ -34,7 +34,8 @@
     /** @hide */
     @Retention(RetentionPolicy.SOURCE)
     @IntDef({TAG_ADB_SHELL_INTERACTIVE, TAG_ADB_SHELL_CMD, TAG_SYNC_RECV_FILE, TAG_SYNC_SEND_FILE,
-        TAG_APP_PROCESS_START, TAG_DEVICE_UNLOCK_ATTEMPT, TAG_DEVICE_LOCKED})
+        TAG_APP_PROCESS_START, TAG_KEYGUARD_DISMISSED, TAG_KEYGUARD_DISMISS_AUTH_ATTEMPT,
+        TAG_KEYGUARD_SECURED})
     public @interface SECURITY_LOG_TAG {}
 
     /**
@@ -68,21 +69,24 @@
      * seinfo tag (String), SHA-256 hash of the APK in hexadecimal (String)
      */
     public static final int TAG_APP_PROCESS_START = SecurityLogTags.SECURITY_APP_PROCESS_START;
-
     /**
-     * Indicate that there has been an attempt to unlock the device. The log entry contains the
-     * following information about the attempt in order, accessible via
-     * {@link SecurityEvent#getData()}}: unlock result (integer, 1 for successful unlock, 0 for
-     * unsuccessful), unlock method (String)
+     * Indicate that keyguard is being dismissed.
+     * There is no extra payload in the log event.
      */
-    public static final int TAG_DEVICE_UNLOCK_ATTEMPT =
-            SecurityLogTags.SECURITY_DEVICE_UNLOCK_ATTEMPT;
-
+    public static final int TAG_KEYGUARD_DISMISSED =
+            SecurityLogTags.SECURITY_KEYGUARD_DISMISSED;
+    /**
+     * Indicate that there has been an authentication attempt to dismiss the keyguard. The log entry
+     * contains the attempt result (integer, 1 for successful, 0 for unsuccessful), accessible via
+     * {@link SecurityEvent#getData()}}
+     */
+    public static final int TAG_KEYGUARD_DISMISS_AUTH_ATTEMPT =
+            SecurityLogTags.SECURITY_KEYGUARD_DISMISS_AUTH_ATTEMPT;
     /**
      * Indicate that the device has been locked, either by user or by timeout.
+     * There is no extra payload in the log event.
      */
-    public static final int TAG_DEVICE_LOCKED = SecurityLogTags.SECURITY_DEVICE_LOCKED;
-
+    public static final int TAG_KEYGUARD_SECURED = SecurityLogTags.SECURITY_KEYGUARD_SECURED;
 
     /**
      * Returns if device logging is enabled. Log producers should only write new logs if this is
@@ -128,7 +132,9 @@
          * Returns the tag of this log entry, which specifies entry's semantics.
          * Could be one of {@link SecurityLog#TAG_SYNC_RECV_FILE},
          * {@link SecurityLog#TAG_SYNC_SEND_FILE}, {@link SecurityLog#TAG_ADB_SHELL_CMD},
-         * {@link SecurityLog#TAG_ADB_SHELL_INTERACTIVE}, {@link SecurityLog#TAG_APP_PROCESS_START}.
+         * {@link SecurityLog#TAG_ADB_SHELL_INTERACTIVE}, {@link SecurityLog#TAG_APP_PROCESS_START},
+         * {@link SecurityLog#TAG_KEYGUARD_DISMISSED}, {@link SecurityLog#TAG_KEYGUARD_SECURED},
+         * {@link SecurityLog#TAG_KEYGUARD_DISMISS_AUTH_ATTEMPT}.
          */
         public @SECURITY_LOG_TAG int getTag() {
             return mEvent.getTag();
diff --git a/core/java/android/auditing/SecurityLogTags.logtags b/core/java/android/auditing/SecurityLogTags.logtags
index 455acff..cf85894 100644
--- a/core/java/android/auditing/SecurityLogTags.logtags
+++ b/core/java/android/auditing/SecurityLogTags.logtags
@@ -3,9 +3,10 @@
 option java_package android.auditing
 
 210001 security_adb_shell_interactive
-210002 security_adb_shell_command        (command|3)
-210003 security_adb_sync_recv            (path|3)
-210004 security_adb_sync_send            (path|3)
-210005 security_app_process_start        (process|3),(start_time|2|3),(uid|1),(pid|1),(seinfo|3),(sha256|3)
-210006 security_device_unlock_attempt    (success|1),(method|3)
-210007 security_device_locked
\ No newline at end of file
+210002 security_adb_shell_command               (command|3)
+210003 security_adb_sync_recv                   (path|3)
+210004 security_adb_sync_send                   (path|3)
+210005 security_app_process_start               (process|3),(start_time|2|3),(uid|1),(pid|1),(seinfo|3),(sha256|3)
+210006 security_keyguard_dismissed
+210007 security_keyguard_dismiss_auth_attempt   (success|1)
+210008 security_keyguard_secured
diff --git a/core/java/android/content/BroadcastReceiver.java b/core/java/android/content/BroadcastReceiver.java
index 2260d7e..10e6fb2 100644
--- a/core/java/android/content/BroadcastReceiver.java
+++ b/core/java/android/content/BroadcastReceiver.java
@@ -522,7 +522,7 @@
         IActivityManager am = ActivityManagerNative.getDefault();
         IBinder binder = null;
         try {
-            service.prepareToLeaveProcess();
+            service.prepareToLeaveProcess(myContext);
             binder = am.peekService(service, service.resolveTypeIfNeeded(
                     myContext.getContentResolver()), myContext.getOpPackageName());
         } catch (RemoteException e) {
diff --git a/core/java/android/content/ClipData.java b/core/java/android/content/ClipData.java
index 6dfefac..0ec58ea 100644
--- a/core/java/android/content/ClipData.java
+++ b/core/java/android/content/ClipData.java
@@ -823,14 +823,14 @@
      *
      * @hide
      */
-    public void prepareToLeaveProcess() {
+    public void prepareToLeaveProcess(boolean leavingPackage) {
         final int size = mItems.size();
         for (int i = 0; i < size; i++) {
             final Item item = mItems.get(i);
             if (item.mIntent != null) {
-                item.mIntent.prepareToLeaveProcess();
+                item.mIntent.prepareToLeaveProcess(leavingPackage);
             }
-            if (item.mUri != null && StrictMode.vmFileUriExposureEnabled()) {
+            if (item.mUri != null && StrictMode.vmFileUriExposureEnabled() && leavingPackage) {
                 item.mUri.checkFileUriExposed("ClipData.Item.getUri()");
             }
         }
diff --git a/core/java/android/content/ClipboardManager.java b/core/java/android/content/ClipboardManager.java
index 5653cad..e67da2b 100644
--- a/core/java/android/content/ClipboardManager.java
+++ b/core/java/android/content/ClipboardManager.java
@@ -118,7 +118,7 @@
     public void setPrimaryClip(ClipData clip) {
         try {
             if (clip != null) {
-                clip.prepareToLeaveProcess();
+                clip.prepareToLeaveProcess(true);
             }
             getService().setPrimaryClip(clip, mContext.getOpPackageName());
         } catch (RemoteException e) {
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 426dafb..ee469da 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -1575,6 +1575,14 @@
             = "android.intent.extra.UNINSTALL_ALL_USERS";
 
     /**
+     * Specified when the uninstall confirmation dialog is not required to be shown.
+     * Use with {@link #ACTION_UNINSTALL_PACKAGE}
+     * @hide
+     */
+    public static final String EXTRA_SKIP_UNINSTALL_CONFIRMATION =
+            "android.intent.extra.SKIP_UNINSTALL_CONFIRMATION";
+
+    /**
      * A string associated with a {@link #ACTION_UPGRADE_SETUP} activity
      * describing the last run version of the platform that was setup.
      * @hide
@@ -2618,8 +2626,7 @@
      *   turned off</li>
      * </ul>
      *
-     * <p class="note">This is a protected intent that can only be sent
-     * by the system.
+     * <p class="note">This is a protected intent that can only be sent by the system.</p>
      */
     @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
     public static final String ACTION_AIRPLANE_MODE_CHANGED = "android.intent.action.AIRPLANE_MODE";
@@ -3210,6 +3217,18 @@
     public static final String
             ACTION_OPEN_EXTERNAL_DIRECTORY = "android.intent.action.OPEN_EXTERNAL_DIRECTORY";
 
+    /**
+     * Broadcast Action: List of dynamic sensor is changed due to new sensor being connected or
+     * exisiting sensor being disconnected.
+     *
+     * <p class="note">This is a protected intent that can only be sent by the system.</p>
+     *
+     * {@hide}
+     */
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    public static final String
+            ACTION_DYNAMIC_SENSOR_CHANGED = "android.intent.action.DYNAMIC_SENSOR_CHANGED";
+
     /** {@hide} */
     public static final String ACTION_MASTER_CLEAR = "android.intent.action.MASTER_CLEAR";
 
@@ -8893,23 +8912,46 @@
      *
      * @hide
      */
-    public void prepareToLeaveProcess() {
+    public void prepareToLeaveProcess(Context context) {
+        final boolean leavingPackage = (mComponent == null)
+                || !Objects.equals(mComponent.getPackageName(), context.getPackageName());
+        prepareToLeaveProcess(leavingPackage);
+    }
+
+    /**
+     * Prepare this {@link Intent} to leave an app process.
+     *
+     * @hide
+     */
+    public void prepareToLeaveProcess(boolean leavingPackage) {
         setAllowFds(false);
 
         if (mSelector != null) {
-            mSelector.prepareToLeaveProcess();
+            mSelector.prepareToLeaveProcess(leavingPackage);
         }
         if (mClipData != null) {
-            mClipData.prepareToLeaveProcess();
+            mClipData.prepareToLeaveProcess(leavingPackage);
         }
 
-        if (mData != null && StrictMode.vmFileUriExposureEnabled()) {
-            // There are several ACTION_MEDIA_* broadcasts that send file://
-            // Uris, so only check common actions.
-            if (ACTION_VIEW.equals(mAction) ||
-                    ACTION_EDIT.equals(mAction) ||
-                    ACTION_ATTACH_DATA.equals(mAction)) {
-                mData.checkFileUriExposed("Intent.getData()");
+        if (mData != null && StrictMode.vmFileUriExposureEnabled() && leavingPackage) {
+            switch (mAction) {
+                case ACTION_MEDIA_REMOVED:
+                case ACTION_MEDIA_UNMOUNTED:
+                case ACTION_MEDIA_CHECKING:
+                case ACTION_MEDIA_NOFS:
+                case ACTION_MEDIA_MOUNTED:
+                case ACTION_MEDIA_SHARED:
+                case ACTION_MEDIA_UNSHARED:
+                case ACTION_MEDIA_BAD_REMOVAL:
+                case ACTION_MEDIA_UNMOUNTABLE:
+                case ACTION_MEDIA_EJECT:
+                case ACTION_MEDIA_SCANNER_STARTED:
+                case ACTION_MEDIA_SCANNER_FINISHED:
+                case ACTION_MEDIA_SCANNER_SCAN_FILE:
+                    // Ignore legacy actions
+                    break;
+                default:
+                    mData.checkFileUriExposed("Intent.getData()");
             }
         }
     }
diff --git a/core/java/android/content/SharedPreferences.java b/core/java/android/content/SharedPreferences.java
index 1d16516..7f9e176 100644
--- a/core/java/android/content/SharedPreferences.java
+++ b/core/java/android/content/SharedPreferences.java
@@ -30,8 +30,7 @@
  * when they are committed to storage.  Objects that are returned from the
  * various <code>get</code> methods must be treated as immutable by the application.
  *
- * <p><em>Note: currently this class does not support use across multiple
- * processes.  This will be added later.</em>
+ * <p><em>Note: This class does not support use across multiple processes.</em>
  *
  * <div class="special reference">
  * <h3>Developer Guides</h3>
diff --git a/core/java/android/content/pm/ActivityInfo.java b/core/java/android/content/pm/ActivityInfo.java
index dedf07f5..91a8e0a 100644
--- a/core/java/android/content/pm/ActivityInfo.java
+++ b/core/java/android/content/pm/ActivityInfo.java
@@ -156,6 +156,35 @@
     public String targetActivity;
 
     /**
+     * Activity can not be resized and always occupies the fullscreen area with all windows fully
+     * visible.
+     * @hide
+     */
+    public static final int RESIZE_MODE_UNRESIZEABLE = 0;
+    /**
+     * Activity can not be resized and always occupies the fullscreen area with all windows cropped
+     * to either the task or stack bounds.
+     * @hide
+     */
+    public static final int RESIZE_MODE_CROP_WINDOWS = 1;
+    /**
+     * Activity is resizeable.
+     * @hide
+     */
+    public static final int RESIZE_MODE_RESIZEABLE = 2;
+    /**
+     * Activity is resizeable and supported picture-in-picture mode.
+     * @hide
+     */
+    public static final int RESIZE_MODE_RESIZEABLE_AND_PIPABLE = 3;
+    /**
+     * Value indicating if the resizing mode the activity supports.
+     * See {@link android.R.attr#resizeableActivity}.
+     * @hide
+     */
+    public int resizeMode;
+
+    /**
      * Bit in {@link #flags} indicating whether this activity is able to
      * run in multiple processes.  If
      * true, the system may instantiate it in the some process as the
@@ -283,20 +312,6 @@
     public static final int FLAG_ENABLE_VR_MODE = 0x8000;
 
     /**
-     * Bit in {@link #flags} indicating if the activity is resizeable to any dimension.
-     * See {@link android.R.attr#resizeableActivity}.
-     * @hide
-     */
-    public static final int FLAG_RESIZEABLE = 0x10000;
-
-    /**
-     * Bit in {@link #flags} indicating if the activity is supports picture-in-picture form of
-     * multi-window mode. See {@link android.R.attr#supportsPictureInPicture}.
-     * @hide
-     */
-    public static final int FLAG_SUPPORTS_PICTURE_IN_PICTURE = 0x20000;
-
-    /**
      * Bit in {@link #flags} indicating if the activity is always focusable regardless of if it is
      * in a task/stack whose activities are normally not focusable.
      * See android.R.attr#alwaysFocusable.
@@ -746,6 +761,7 @@
         maxRecents = orig.maxRecents;
         lockTaskLaunchMode = orig.lockTaskLaunchMode;
         layout = orig.layout;
+        resizeMode = orig.resizeMode;
     }
 
     /**
@@ -768,6 +784,22 @@
         }
     }
 
+    /** @hide */
+    public static final String resizeModeToString(int mode) {
+        switch (mode) {
+            case RESIZE_MODE_UNRESIZEABLE:
+                return "RESIZE_MODE_UNRESIZEABLE";
+            case RESIZE_MODE_CROP_WINDOWS:
+                return "RESIZE_MODE_CROP_WINDOWS";
+            case RESIZE_MODE_RESIZEABLE:
+                return "RESIZE_MODE_RESIZEABLE";
+            case RESIZE_MODE_RESIZEABLE_AND_PIPABLE:
+                return "RESIZE_MODE_RESIZEABLE_AND_PIPABLE";
+            default:
+                return "unknown=" + mode;
+        }
+    }
+
     public void dump(Printer pw, String prefix) {
         dump(pw, prefix, DUMP_FLAG_ALL);
     }
@@ -806,6 +838,7 @@
                     + layout.widthFraction + ", " + layout.height + "|"
                     + layout.heightFraction + ", " + layout.gravity);
         }
+        pw.println(prefix + "resizeMode=" + resizeModeToString(resizeMode));
         super.dumpBack(pw, prefix, flags);
     }
 
@@ -847,6 +880,7 @@
         } else {
             dest.writeInt(0);
         }
+        dest.writeInt(resizeMode);
     }
 
     public static final Parcelable.Creator<ActivityInfo> CREATOR
@@ -879,6 +913,7 @@
         if (source.readInt() == 1) {
             layout = new Layout(source);
         }
+        resizeMode = source.readInt();
     }
 
     public static final class Layout {
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 476dc46..3a94bf7 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -2017,6 +2017,11 @@
     public static final String FEATURE_SECURELY_REMOVES_USERS
             = "android.software.securely_removes_users";
 
+    /** {@hide} */
+    @SdkConstant(SdkConstantType.FEATURE)
+    public static final String FEATURE_FILE_BASED_ENCRYPTION
+            = "android.software.file_based_encryption";
+
     /**
      * Feature for {@link #getSystemAvailableFeatures} and {@link #hasSystemFeature}:
      * The device has a full implementation of the android.webkit.* APIs. Devices
@@ -3627,7 +3632,7 @@
      *         {@link #MATCH_ENCRYPTION_AWARE_AND_UNAWARE}, {@link #MATCH_ENCRYPTION_UNAWARE},
      *         {@link #MATCH_SYSTEM_ONLY}, {@link #MATCH_UNINSTALLED_PACKAGES}
      *         to modify the data returned.
-     * @param userId The userId of the user being queried.
+     * @param userHandle UserHandle of the user being queried.
      *
      * @return Returns a List of ResolveInfo objects containing one entry for each
      *         matching receiver, ordered from best to worst. If there are no matching
@@ -3648,9 +3653,19 @@
      *
      * @hide
      */
+    @SystemApi
+    public List<ResolveInfo> queryBroadcastReceiversAsUser(Intent intent,
+            @ResolveInfoFlags int flags, UserHandle userHandle) {
+        return queryBroadcastReceiversAsUser(intent, flags, userHandle.getIdentifier());
+    }
+
+    /**
+     * @hide
+     */
     public abstract List<ResolveInfo> queryBroadcastReceiversAsUser(Intent intent,
             @ResolveInfoFlags int flags, @UserIdInt int userId);
 
+
     /** {@hide} */
     @Deprecated
     public List<ResolveInfo> queryBroadcastReceivers(Intent intent,
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index a6fec9f..44cd003 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -16,15 +16,12 @@
 
 package android.content.pm;
 
-import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_BAD_MANIFEST;
-import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_BAD_PACKAGE_NAME;
-import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_CERTIFICATE_ENCODING;
-import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES;
-import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
-import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_NOT_APK;
-import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_NO_CERTIFICATES;
-import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION;
-import static android.os.Trace.TRACE_TAG_PACKAGE_MANAGER;
+import com.android.internal.R;
+import com.android.internal.util.ArrayUtils;
+import com.android.internal.util.XmlUtils;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
 
 import android.app.ActivityManager;
 import android.content.ComponentName;
@@ -55,15 +52,6 @@
 import android.util.jar.StrictJarFile;
 import android.view.Gravity;
 
-import com.android.internal.R;
-import com.android.internal.util.ArrayUtils;
-import com.android.internal.util.XmlUtils;
-
-import libcore.io.IoUtils;
-
-import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlPullParserException;
-
 import java.io.File;
 import java.io.IOException;
 import java.io.InputStream;
@@ -87,6 +75,25 @@
 import java.util.concurrent.atomic.AtomicReference;
 import java.util.zip.ZipEntry;
 
+import libcore.io.IoUtils;
+
+import static android.content.pm.ActivityInfo.FLAG_ALWAYS_FOCUSABLE;
+import static android.content.pm.ActivityInfo.FLAG_IMMERSIVE;
+import static android.content.pm.ActivityInfo.RESIZE_MODE_CROP_WINDOWS;
+import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE;
+import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE_AND_PIPABLE;
+import static android.content.pm.ActivityInfo.RESIZE_MODE_UNRESIZEABLE;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
+import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_BAD_MANIFEST;
+import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_BAD_PACKAGE_NAME;
+import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_CERTIFICATE_ENCODING;
+import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES;
+import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
+import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_NOT_APK;
+import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_NO_CERTIFICATES;
+import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION;
+import static android.os.Trace.TRACE_TAG_PACKAGE_MANAGER;
+
 /**
  * Parser for package files (APKs) on disk. This supports apps packaged either
  * as a single "monolithic" APK, or apps packaged as a "cluster" of multiple
@@ -3219,24 +3226,29 @@
                 a.info.flags |= ActivityInfo.FLAG_RESUME_WHILE_PAUSING;
             }
 
-            if (sa.getBoolean(R.styleable.AndroidManifestActivity_resizeableActivity,
-                    owner.applicationInfo.targetSdkVersion >= Build.VERSION_CODES.N)) {
-                a.info.flags |= ActivityInfo.FLAG_RESIZEABLE;
+            a.info.screenOrientation = sa.getInt(
+                    R.styleable.AndroidManifestActivity_screenOrientation,
+                    SCREEN_ORIENTATION_UNSPECIFIED);
 
-                if (sa.getBoolean(R.styleable.AndroidManifestActivity_supportsPictureInPicture,
-                        false)) {
-                    a.info.flags |= ActivityInfo.FLAG_SUPPORTS_PICTURE_IN_PICTURE;
+            a.info.resizeMode = RESIZE_MODE_UNRESIZEABLE;
+            if (owner.applicationInfo.targetSdkVersion >= Build.VERSION_CODES.N) {
+                if (sa.getBoolean(R.styleable.AndroidManifestActivity_resizeableActivity, true)) {
+                    if (sa.getBoolean(R.styleable.AndroidManifestActivity_supportsPictureInPicture,
+                            false)) {
+                        a.info.resizeMode = RESIZE_MODE_RESIZEABLE_AND_PIPABLE;
+                    } else {
+                        a.info.resizeMode = RESIZE_MODE_RESIZEABLE;
+                    }
                 }
+            } else if (a.info.screenOrientation == SCREEN_ORIENTATION_UNSPECIFIED
+                    && (a.info.flags & FLAG_IMMERSIVE) == 0) {
+                a.info.resizeMode = RESIZE_MODE_CROP_WINDOWS;
             }
 
             if (sa.getBoolean(R.styleable.AndroidManifestActivity_alwaysFocusable, false)) {
-                a.info.flags |= ActivityInfo.FLAG_ALWAYS_FOCUSABLE;
+                a.info.flags |= FLAG_ALWAYS_FOCUSABLE;
             }
 
-            a.info.screenOrientation = sa.getInt(
-                    R.styleable.AndroidManifestActivity_screenOrientation,
-                    ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED);
-
             a.info.lockTaskLaunchMode =
                     sa.getInt(R.styleable.AndroidManifestActivity_lockTaskMode, 0);
 
@@ -3478,6 +3490,8 @@
         info.parentActivityName = target.info.parentActivityName;
         info.maxRecents = target.info.maxRecents;
         info.layout = target.info.layout;
+        info.resizeMode = target.info.resizeMode;
+        info.encryptionAware = target.info.encryptionAware;
 
         Activity a = new Activity(mParseActivityAliasArgs, info);
         if (outError[0] != null) {
diff --git a/core/java/android/content/res/ColorStateList.java b/core/java/android/content/res/ColorStateList.java
index 19921b5..9e1b312 100644
--- a/core/java/android/content/res/ColorStateList.java
+++ b/core/java/android/content/res/ColorStateList.java
@@ -69,7 +69,7 @@
  * href="{@docRoot}guide/topics/resources/color-list-resource.html">Color State
  * List Resource</a>.</p>
  */
-public class ColorStateList implements Parcelable {
+public class ColorStateList extends ComplexColor implements Parcelable {
     private static final String TAG = "ColorStateList";
 
     private static final int DEFAULT_COLOR = Color.RED;
@@ -209,7 +209,7 @@
      * @return A new color state list for the current tag.
      */
     @NonNull
-    private static ColorStateList createFromXmlInner(@NonNull Resources r,
+    static ColorStateList createFromXmlInner(@NonNull Resources r,
             @NonNull XmlPullParser parser, @NonNull AttributeSet attrs, @Nullable Theme theme)
             throws XmlPullParserException, IOException {
         final String name = parser.getName();
@@ -340,6 +340,7 @@
      * @return whether a theme can be applied to this color state list
      * @hide only for resource preloading
      */
+    @Override
     public boolean canApplyTheme() {
         return mThemeAttrs != null;
     }
@@ -419,6 +420,7 @@
      *         attributes
      * @hide only for resource preloading
      */
+    @Override
     public ColorStateList obtainForTheme(Theme t) {
         if (t == null || !canApplyTheme()) {
             return this;
@@ -460,6 +462,7 @@
      *         otherwise.
      * @see #getColorForState(int[], int)
      */
+    @Override
     public boolean isStateful() {
         return mStateSpecs.length > 1;
     }
@@ -602,14 +605,14 @@
      * @return a factory that can create new instances of this ColorStateList
      * @hide only for resource preloading
      */
-    public ConstantState<ColorStateList> getConstantState() {
+    public ConstantState<ComplexColor> getConstantState() {
         if (mFactory == null) {
             mFactory = new ColorStateListFactory(this);
         }
         return mFactory;
     }
 
-    private static class ColorStateListFactory extends ConstantState<ColorStateList> {
+    private static class ColorStateListFactory extends ConstantState<ComplexColor> {
         private final ColorStateList mSrc;
 
         public ColorStateListFactory(ColorStateList src) {
@@ -628,7 +631,7 @@
 
         @Override
         public ColorStateList newInstance(Resources res, Theme theme) {
-            return mSrc.obtainForTheme(theme);
+            return (ColorStateList) mSrc.obtainForTheme(theme);
         }
     }
 
diff --git a/core/java/android/content/res/ComplexColor.java b/core/java/android/content/res/ComplexColor.java
new file mode 100644
index 0000000..d96ec62
--- /dev/null
+++ b/core/java/android/content/res/ComplexColor.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.content.res;
+
+import android.annotation.ColorInt;
+import android.content.res.Resources.Theme;
+import android.graphics.Color;
+
+/**
+ * Defines an abstract class for the complex color information, like
+ * {@link android.content.res.ColorStateList} or {@link android.content.res.GradientColor}
+ */
+public abstract class ComplexColor {
+    /**
+     * @return {@code true}  if this ComplexColor changes color based on state, {@code false}
+     * otherwise.
+     */
+    public boolean isStateful() { return false; }
+
+    /**
+     * @return the default color.
+     */
+    @ColorInt
+    public abstract int getDefaultColor();
+
+    /**
+     * @hide only for resource preloading
+     *
+     */
+    public abstract ConstantState<ComplexColor> getConstantState();
+
+    /**
+     * @hide only for resource preloading
+     */
+    public abstract boolean canApplyTheme();
+
+    /**
+     * @hide only for resource preloading
+     */
+    public abstract ComplexColor obtainForTheme(Theme t);
+}
diff --git a/core/java/android/content/res/GradientColor.java b/core/java/android/content/res/GradientColor.java
new file mode 100644
index 0000000..98ef2ea
--- /dev/null
+++ b/core/java/android/content/res/GradientColor.java
@@ -0,0 +1,523 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.content.res;
+
+import android.annotation.ColorInt;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.res.Resources.Theme;
+
+import com.android.internal.R;
+import com.android.internal.util.GrowingArrayUtils;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import android.graphics.LinearGradient;
+import android.graphics.RadialGradient;
+import android.graphics.Shader;
+import android.graphics.SweepGradient;
+import android.graphics.drawable.GradientDrawable;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.util.Xml;
+
+import java.io.IOException;
+
+
+public class GradientColor extends ComplexColor {
+    private static final String TAG = "GradientColor";
+
+    private static final boolean DBG_GRADIENT = false;
+
+    /** Lazily-created factory for this GradientColor. */
+    private GradientColorFactory mFactory;
+
+    private int mChangingConfigurations;
+    private int mDefaultColor;
+
+    // After parsing all the attributes from XML, this shader is the ultimate result containing
+    // all the XML information.
+    private Shader mShader = null;
+
+    // Below are the attributes at the root element <gradient>
+    private int mGradientType = GradientDrawable.LINEAR_GRADIENT;
+
+    private float mCenterX = 0f;
+    private float mCenterY = 0f;
+
+    private float mStartX = 0f;
+    private float mStartY = 0f;
+    private float mEndX = 0f;
+    private float mEndY = 0f;
+
+    private int mStartColor = 0;
+    private int mCenterColor = 0;
+    private int mEndColor = 0;
+    private boolean mHasCenterColor = false;
+
+    private float mGradientRadius = 0f;
+
+    // Below are the attributes for the <item> element.
+    private int[] mItemColors;
+    private float[] mItemOffsets;
+
+    // Theme attributes for the root and item elements.
+    private int[] mThemeAttrs;
+    private int[][] mItemsThemeAttrs;
+
+    private GradientColor() {
+    }
+
+    private GradientColor(GradientColor copy) {
+        if (copy != null) {
+            mChangingConfigurations = copy.mChangingConfigurations;
+            mDefaultColor = copy.mDefaultColor;
+            mShader = copy.mShader;
+            mGradientType = copy.mGradientType;
+            mCenterX = copy.mCenterX;
+            mCenterY = copy.mCenterY;
+            mStartX = copy.mStartX;
+            mStartY = copy.mStartY;
+            mEndX = copy.mEndX;
+            mEndY = copy.mEndY;
+            mStartColor = copy.mStartColor;
+            mCenterColor = copy.mCenterColor;
+            mEndColor = copy.mEndColor;
+            mHasCenterColor = copy.mHasCenterColor;
+            mGradientRadius = copy.mGradientRadius;
+
+            if (copy.mItemColors != null) {
+                mItemColors = copy.mItemColors.clone();
+            }
+            if (copy.mItemOffsets != null) {
+                mItemOffsets = copy.mItemOffsets.clone();
+            }
+
+            if (copy.mThemeAttrs != null) {
+                mThemeAttrs = copy.mThemeAttrs.clone();
+            }
+            if (copy.mItemsThemeAttrs != null) {
+                mItemsThemeAttrs = copy.mItemsThemeAttrs.clone();
+            }
+        }
+    }
+
+    /**
+     * Update the root level's attributes, either for inflate or applyTheme.
+     */
+    private void updateRootElementState(TypedArray a) {
+        // Extract the theme attributes, if any.
+        mThemeAttrs = a.extractThemeAttrs();
+
+        mStartX = a.getFloat(
+                R.styleable.GradientColor_startX, mStartX);
+        mStartY = a.getFloat(
+                R.styleable.GradientColor_startY, mStartY);
+        mEndX = a.getFloat(
+                R.styleable.GradientColor_endX, mEndX);
+        mEndY = a.getFloat(
+                R.styleable.GradientColor_endY, mEndY);
+
+        mCenterX = a.getFloat(
+                R.styleable.GradientColor_centerX, mCenterX);
+        mCenterY = a.getFloat(
+                R.styleable.GradientColor_centerY, mCenterY);
+
+        mGradientType = a.getInt(
+                R.styleable.GradientColor_type, mGradientType);
+
+        mStartColor = a.getColor(
+                R.styleable.GradientColor_startColor, mStartColor);
+        mHasCenterColor |= a.hasValue(
+                R.styleable.GradientColor_centerColor);
+        mCenterColor = a.getColor(
+                R.styleable.GradientColor_centerColor, mCenterColor);
+        mEndColor = a.getColor(
+                R.styleable.GradientColor_endColor, mEndColor);
+
+        if (DBG_GRADIENT) {
+            Log.v(TAG, "hasCenterColor is " + mHasCenterColor);
+            if (mHasCenterColor) {
+                Log.v(TAG, "centerColor:" + mCenterColor);
+            }
+            Log.v(TAG, "startColor: " + mStartColor);
+            Log.v(TAG, "endColor: " + mEndColor);
+        }
+
+        mGradientRadius = a.getFloat(R.styleable.GradientColor_gradientRadius,
+                mGradientRadius);
+    }
+
+    /**
+     * Check if the XML content is valid.
+     *
+     * @throws XmlPullParserException if errors were found.
+     */
+    private void validateXmlContent() throws XmlPullParserException {
+        if (mGradientRadius <= 0
+                && mGradientType == GradientDrawable.RADIAL_GRADIENT) {
+            throw new XmlPullParserException(
+                    "<gradient> tag requires 'gradientRadius' "
+                            + "attribute with radial type");
+        }
+    }
+
+    /**
+     * The shader information will be applied to the native VectorDrawable's path.
+     * @hide
+     */
+    public Shader getShader() {
+        return mShader;
+    }
+
+    /**
+     * A public method to create GradientColor from a XML resource.
+     */
+    public static GradientColor createFromXml(Resources r, XmlResourceParser parser, Theme theme)
+            throws XmlPullParserException, IOException {
+        final AttributeSet attrs = Xml.asAttributeSet(parser);
+
+        int type;
+        while ((type = parser.next()) != XmlPullParser.START_TAG
+                && type != XmlPullParser.END_DOCUMENT) {
+            // Seek parser to start tag.
+        }
+
+        if (type != XmlPullParser.START_TAG) {
+            throw new XmlPullParserException("No start tag found");
+        }
+
+        return createFromXmlInner(r, parser, attrs, theme);
+    }
+
+    /**
+     * Create from inside an XML document. Called on a parser positioned at a
+     * tag in an XML document, tries to create a GradientColor from that tag.
+     *
+     * @return A new GradientColor for the current tag.
+     * @throws XmlPullParserException if the current tag is not &lt;gradient>
+     */
+    @NonNull
+    static GradientColor createFromXmlInner(@NonNull Resources r,
+            @NonNull XmlPullParser parser, @NonNull AttributeSet attrs, @Nullable Theme theme)
+            throws XmlPullParserException, IOException {
+        final String name = parser.getName();
+        if (!name.equals("gradient")) {
+            throw new XmlPullParserException(
+                    parser.getPositionDescription() + ": invalid gradient color tag " + name);
+        }
+
+        final GradientColor gradientColor = new GradientColor();
+        gradientColor.inflate(r, parser, attrs, theme);
+        return gradientColor;
+    }
+
+    /**
+     * Fill in this object based on the contents of an XML "gradient" element.
+     */
+    private void inflate(@NonNull Resources r, @NonNull XmlPullParser parser,
+            @NonNull AttributeSet attrs, @Nullable Theme theme)
+            throws XmlPullParserException, IOException {
+        final TypedArray a = Resources.obtainAttributes(r, theme, attrs, R.styleable.GradientColor);
+        updateRootElementState(a);
+        mChangingConfigurations |= a.getChangingConfigurations();
+        a.recycle();
+
+        // Check correctness and throw exception if errors found.
+        validateXmlContent();
+
+        inflateChildElements(r, parser, attrs, theme);
+
+        onColorsChange();
+    }
+
+    /**
+     * Inflates child elements "item"s for each color stop.
+     *
+     * Note that at root level, we need to save ThemeAttrs for theme applied later.
+     * Here similarly, at each child item, we need to save the theme's attributes, and apply theme
+     * later as applyItemsAttrsTheme().
+     */
+    private void inflateChildElements(@NonNull Resources r, @NonNull XmlPullParser parser,
+            @NonNull AttributeSet attrs, @NonNull Theme theme)
+            throws XmlPullParserException, IOException {
+        final int innerDepth = parser.getDepth() + 1;
+        int type;
+        int depth;
+
+        // Pre-allocate the array with some size, for better performance.
+        float[] offsetList = new float[20];
+        int[] colorList = new int[offsetList.length];
+        int[][] themeAttrsList = new int[offsetList.length][];
+
+        int listSize = 0;
+        boolean hasUnresolvedAttrs = false;
+        while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+                && ((depth = parser.getDepth()) >= innerDepth
+                || type != XmlPullParser.END_TAG)) {
+            if (type != XmlPullParser.START_TAG) {
+                continue;
+            }
+            if (depth > innerDepth || !parser.getName().equals("item")) {
+                continue;
+            }
+
+            final TypedArray a = Resources.obtainAttributes(r, theme, attrs,
+                    R.styleable.GradientColorItem);
+            boolean hasColor = a.hasValue(R.styleable.GradientColorItem_color);
+            boolean hasOffset = a.hasValue(R.styleable.GradientColorItem_offset);
+            if (!hasColor || !hasOffset) {
+                throw new XmlPullParserException(
+                        parser.getPositionDescription()
+                                + ": <item> tag requires a 'color' attribute and a 'offset' "
+                                + "attribute!");
+            }
+
+            final int[] themeAttrs = a.extractThemeAttrs();
+            int color = a.getColor(R.styleable.GradientColorItem_color, 0);
+            float offset = a.getFloat(R.styleable.GradientColorItem_offset, 0);
+
+            if (DBG_GRADIENT) {
+                Log.v(TAG, "new item color " + color + " " + Integer.toHexString(color));
+                Log.v(TAG, "offset" + offset);
+            }
+            mChangingConfigurations |= a.getChangingConfigurations();
+            a.recycle();
+
+            if (themeAttrs != null) {
+                hasUnresolvedAttrs = true;
+            }
+
+            colorList = GrowingArrayUtils.append(colorList, listSize, color);
+            offsetList = GrowingArrayUtils.append(offsetList, listSize, offset);
+            themeAttrsList = GrowingArrayUtils.append(themeAttrsList, listSize, themeAttrs);
+            listSize++;
+        }
+        if (listSize > 0) {
+            if (hasUnresolvedAttrs) {
+                mItemsThemeAttrs = new int[listSize][];
+                System.arraycopy(themeAttrsList, 0, mItemsThemeAttrs, 0, listSize);
+            } else {
+                mItemsThemeAttrs = null;
+            }
+
+            mItemColors = new int[listSize];
+            mItemOffsets = new float[listSize];
+            System.arraycopy(colorList, 0, mItemColors, 0, listSize);
+            System.arraycopy(offsetList, 0, mItemOffsets, 0, listSize);
+        }
+    }
+
+    /**
+     * Apply theme to all the items.
+     */
+    private void applyItemsAttrsTheme(Theme t) {
+        if (mItemsThemeAttrs == null) {
+            return;
+        }
+
+        boolean hasUnresolvedAttrs = false;
+
+        final int[][] themeAttrsList = mItemsThemeAttrs;
+        final int N = themeAttrsList.length;
+        for (int i = 0; i < N; i++) {
+            if (themeAttrsList[i] != null) {
+                final TypedArray a = t.resolveAttributes(themeAttrsList[i],
+                        R.styleable.GradientColorItem);
+
+                // Extract the theme attributes, if any, before attempting to
+                // read from the typed array. This prevents a crash if we have
+                // unresolved attrs.
+                themeAttrsList[i] = a.extractThemeAttrs(themeAttrsList[i]);
+                if (themeAttrsList[i] != null) {
+                    hasUnresolvedAttrs = true;
+                }
+
+                mItemColors[i] = a.getColor(R.styleable.GradientColorItem_color, mItemColors[i]);
+                mItemOffsets[i] = a.getFloat(R.styleable.GradientColorItem_offset, mItemOffsets[i]);
+                if (DBG_GRADIENT) {
+                    Log.v(TAG, "applyItemsAttrsTheme Colors[i] " + i + " " +
+                            Integer.toHexString(mItemColors[i]));
+                    Log.v(TAG, "Offsets[i] " + i + " " + mItemOffsets[i]);
+                }
+
+                // Account for any configuration changes.
+                mChangingConfigurations |= a.getChangingConfigurations();
+
+                a.recycle();
+            }
+        }
+
+        if (!hasUnresolvedAttrs) {
+            mItemsThemeAttrs = null;
+        }
+    }
+
+    private void onColorsChange() {
+        int[] tempColors = null;
+        float[] tempOffsets = null;
+
+        if (mItemColors != null) {
+            int length = mItemColors.length;
+            tempColors = new int[length];
+            tempOffsets = new float[length];
+
+            for (int i = 0; i < length; i++) {
+                tempColors[i] = mItemColors[i];
+                tempOffsets[i] = mItemOffsets[i];
+            }
+        } else {
+            if (mHasCenterColor) {
+                tempColors = new int[3];
+                tempColors[0] = mStartColor;
+                tempColors[1] = mCenterColor;
+                tempColors[2] = mEndColor;
+
+                tempOffsets = new float[3];
+                tempOffsets[0] = 0.0f;
+                // Since 0.5f is default value, try to take the one that isn't 0.5f
+                tempOffsets[1] = 0.5f;
+                tempOffsets[2] = 1f;
+            } else {
+                tempColors = new int[2];
+                tempColors[0] = mStartColor;
+                tempColors[1] = mEndColor;
+            }
+        }
+        if (tempColors.length < 2) {
+            Log.w(TAG, "<gradient> tag requires 2 color values specified!" + tempColors.length
+                    + " " + tempColors);
+        }
+
+        if (mGradientType == GradientDrawable.LINEAR_GRADIENT) {
+            mShader = new LinearGradient(mStartX, mStartY, mEndX, mEndY, tempColors, tempOffsets,
+                    Shader.TileMode.CLAMP);
+        } else {
+            if (mGradientType == GradientDrawable.RADIAL_GRADIENT) {
+                mShader = new RadialGradient(mCenterX, mCenterY, mGradientRadius, tempColors,
+                        tempOffsets, Shader.TileMode.CLAMP);
+            } else {
+                mShader = new SweepGradient(mCenterX, mCenterY, tempColors, tempOffsets);
+            }
+        }
+        mDefaultColor = tempColors[0];
+    }
+
+    /**
+     * For Gradient color, the default color is not very useful, since the gradient will override
+     * the color information anyway.
+     */
+    @Override
+    @ColorInt
+    public int getDefaultColor() {
+        return mDefaultColor;
+    }
+
+    /**
+     * Similar to ColorStateList, setup constant state and its factory.
+     * @hide only for resource preloading
+     */
+    @Override
+    public ConstantState<ComplexColor> getConstantState() {
+        if (mFactory == null) {
+            mFactory = new GradientColorFactory(this);
+        }
+        return mFactory;
+    }
+
+    private static class GradientColorFactory extends ConstantState<ComplexColor> {
+        private final GradientColor mSrc;
+
+        public GradientColorFactory(GradientColor src) {
+            mSrc = src;
+        }
+
+        @Override
+        public int getChangingConfigurations() {
+            return mSrc.mChangingConfigurations;
+        }
+
+        @Override
+        public GradientColor newInstance() {
+            return mSrc;
+        }
+
+        @Override
+        public GradientColor newInstance(Resources res, Theme theme) {
+            return mSrc.obtainForTheme(theme);
+        }
+    }
+
+    /**
+     * Returns an appropriately themed gradient color.
+     *
+     * @param t the theme to apply
+     * @return a copy of the gradient color the theme applied, or the
+     * gradient itself if there were no unresolved theme
+     * attributes
+     * @hide only for resource preloading
+     */
+    @Override
+    public GradientColor obtainForTheme(Theme t) {
+        if (t == null || !canApplyTheme()) {
+            return this;
+        }
+
+        final GradientColor clone = new GradientColor(this);
+        clone.applyTheme(t);
+        return clone;
+    }
+
+    private void applyTheme(Theme t) {
+        if (mThemeAttrs != null) {
+            applyRootAttrsTheme(t);
+        }
+        if (mItemsThemeAttrs != null) {
+            applyItemsAttrsTheme(t);
+        }
+        onColorsChange();
+    }
+
+    private void applyRootAttrsTheme(Theme t) {
+        final TypedArray a = t.resolveAttributes(mThemeAttrs, R.styleable.GradientColor);
+        // mThemeAttrs will be set to null if if there are no theme attributes in the
+        // typed array.
+        mThemeAttrs = a.extractThemeAttrs(mThemeAttrs);
+        // merging the attributes update inside the updateRootElementState().
+        updateRootElementState(a);
+
+        // Account for any configuration changes.
+        mChangingConfigurations |= a.getChangingConfigurations();
+        a.recycle();
+    }
+
+
+    /**
+     * Returns whether a theme can be applied to this gradient color, which
+     * usually indicates that the gradient color has unresolved theme
+     * attributes.
+     *
+     * @return whether a theme can be applied to this gradient color.
+     * @hide only for resource preloading
+     */
+    @Override
+    public boolean canApplyTheme() {
+        return mThemeAttrs != null || mItemsThemeAttrs != null;
+    }
+
+}
diff --git a/core/java/android/content/res/Resources.java b/core/java/android/content/res/Resources.java
index e404429..4967d05 100644
--- a/core/java/android/content/res/Resources.java
+++ b/core/java/android/content/res/Resources.java
@@ -56,6 +56,7 @@
 import android.util.Pools.SynchronizedPool;
 import android.util.Slog;
 import android.util.TypedValue;
+import android.util.Xml;
 import android.view.ViewDebug;
 import android.view.ViewHierarchyEncoder;
 
@@ -115,8 +116,8 @@
     private static final LongSparseArray<ConstantState>[] sPreloadedDrawables;
     private static final LongSparseArray<ConstantState> sPreloadedColorDrawables
             = new LongSparseArray<>();
-    private static final LongSparseArray<android.content.res.ConstantState<ColorStateList>>
-            sPreloadedColorStateLists = new LongSparseArray<>();
+    private static final LongSparseArray<android.content.res.ConstantState<ComplexColor>>
+            sPreloadedComplexColors = new LongSparseArray<>();
 
     // Pool of TypedArrays targeted to this Resources object.
     final SynchronizedPool<TypedArray> mTypedArrayPool = new SynchronizedPool<>(5);
@@ -133,7 +134,7 @@
     private final Configuration mTmpConfig = new Configuration();
     private final DrawableCache mDrawableCache = new DrawableCache(this);
     private final DrawableCache mColorDrawableCache = new DrawableCache(this);
-    private final ConfigurationBoundResourceCache<ColorStateList> mColorStateListCache =
+    private final ConfigurationBoundResourceCache<ComplexColor> mComplexColorCache =
             new ConfigurationBoundResourceCache<>(this);
     private final ConfigurationBoundResourceCache<Animator> mAnimatorCache =
             new ConfigurationBoundResourceCache<>(this);
@@ -1987,7 +1988,7 @@
 
             mDrawableCache.onConfigurationChange(configChanges);
             mColorDrawableCache.onConfigurationChange(configChanges);
-            mColorStateListCache.onConfigurationChange(configChanges);
+            mComplexColorCache.onConfigurationChange(configChanges);
             mAnimatorCache.onConfigurationChange(configChanges);
             mStateListAnimatorCache.onConfigurationChange(configChanges);
 
@@ -2613,6 +2614,82 @@
         return dr;
     }
 
+    /**
+     * Given the value and id, we can get the XML filename as in value.data, based on that, we
+     * first try to load CSL from the cache. If not found, try to get from the constant state.
+     * Last, parse the XML and generate the CSL.
+     */
+    private ComplexColor loadComplexColorFromName(Theme theme, TypedValue value, int id) {
+        final long key = (((long) value.assetCookie) << 32) | value.data;
+        final ConfigurationBoundResourceCache<ComplexColor> cache = mComplexColorCache;
+        ComplexColor complexColor = cache.getInstance(key, theme);
+        if (complexColor != null) {
+            return complexColor;
+        }
+
+        final android.content.res.ConstantState<ComplexColor> factory =
+                sPreloadedComplexColors.get(key);
+
+        if (factory != null) {
+            complexColor = factory.newInstance(this, theme);
+        }
+        if (complexColor == null) {
+            complexColor = loadComplexColorForCookie(value, id, theme);
+        }
+
+        if (complexColor != null) {
+            if (mPreloading) {
+                if (verifyPreloadConfig(value.changingConfigurations, 0, value.resourceId,
+                        "color")) {
+                    sPreloadedComplexColors.put(key, complexColor.getConstantState());
+                }
+            } else {
+                cache.put(key, theme, complexColor.getConstantState());
+            }
+        }
+        return complexColor;
+    }
+
+    @Nullable
+    public ComplexColor loadComplexColor(@NonNull TypedValue value, int id, Theme theme) {
+        if (TRACE_FOR_PRELOAD) {
+            // Log only framework resources
+            if ((id >>> 24) == 0x1) {
+                final String name = getResourceName(id);
+                if (name != null) android.util.Log.d("loadComplexColor", name);
+            }
+        }
+
+        final long key = (((long) value.assetCookie) << 32) | value.data;
+
+        // Handle inline color definitions.
+        if (value.type >= TypedValue.TYPE_FIRST_COLOR_INT
+                && value.type <= TypedValue.TYPE_LAST_COLOR_INT) {
+            return getColorStateListFromInt(value, key);
+        }
+
+        final String file = value.string.toString();
+
+        ComplexColor complexColor;
+        if (file.endsWith(".xml")) {
+            try {
+                complexColor = loadComplexColorFromName(theme, value, id);
+            } catch (Exception e) {
+                final NotFoundException rnf = new NotFoundException(
+                        "File " + file + " from complex color resource ID #0x"
+                                + Integer.toHexString(id));
+                rnf.initCause(e);
+                throw rnf;
+            }
+        } else {
+            throw new NotFoundException(
+                    "File " + file + " from drawable resource ID #0x"
+                            + Integer.toHexString(id) + ": .xml extension required");
+        }
+
+        return complexColor;
+    }
+
     @Nullable
     ColorStateList loadColorStateList(TypedValue value, int id, Theme theme)
             throws NotFoundException {
@@ -2626,63 +2703,57 @@
 
         final long key = (((long) value.assetCookie) << 32) | value.data;
 
-        ColorStateList csl;
-
         // Handle inline color definitions.
         if (value.type >= TypedValue.TYPE_FIRST_COLOR_INT
                 && value.type <= TypedValue.TYPE_LAST_COLOR_INT) {
-            final android.content.res.ConstantState<ColorStateList> factory =
-                    sPreloadedColorStateLists.get(key);
-            if (factory != null) {
-                return factory.newInstance();
-            }
-
-            csl = ColorStateList.valueOf(value.data);
-
-            if (mPreloading) {
-                if (verifyPreloadConfig(value.changingConfigurations, 0, value.resourceId,
-                        "color")) {
-                    sPreloadedColorStateLists.put(key, csl.getConstantState());
-                }
-            }
-
-            return csl;
+            return getColorStateListFromInt(value, key);
         }
 
-        final ConfigurationBoundResourceCache<ColorStateList> cache = mColorStateListCache;
-        csl = cache.getInstance(key, theme);
-        if (csl != null) {
-            return csl;
+        ComplexColor complexColor = loadComplexColorFromName(theme, value, id);
+        if (complexColor != null && complexColor instanceof ColorStateList) {
+            return (ColorStateList) complexColor;
         }
 
-        final android.content.res.ConstantState<ColorStateList> factory =
-                sPreloadedColorStateLists.get(key);
+        throw new NotFoundException(
+                "Can't find ColorStateList from drawable resource ID #0x"
+                        + Integer.toHexString(id));
+    }
+
+    @NonNull
+    private ColorStateList getColorStateListFromInt(@NonNull  TypedValue value, long key) {
+        ColorStateList csl;
+        final android.content.res.ConstantState<ComplexColor> factory =
+                sPreloadedComplexColors.get(key);
         if (factory != null) {
-            csl = factory.newInstance(this, theme);
+            return (ColorStateList) factory.newInstance();
         }
 
-        if (csl == null) {
-            csl = loadColorStateListForCookie(value, id, theme);
-        }
+        csl = ColorStateList.valueOf(value.data);
 
-        if (csl != null) {
-            if (mPreloading) {
-                if (verifyPreloadConfig(value.changingConfigurations, 0, value.resourceId,
-                        "color")) {
-                    sPreloadedColorStateLists.put(key, csl.getConstantState());
-                }
-            } else {
-                cache.put(key, theme, csl.getConstantState());
+        if (mPreloading) {
+            if (verifyPreloadConfig(value.changingConfigurations, 0, value.resourceId,
+                    "color")) {
+                sPreloadedComplexColors.put(key, csl.getConstantState());
             }
         }
 
         return csl;
     }
 
-    private ColorStateList loadColorStateListForCookie(TypedValue value, int id, Theme theme) {
+    /**
+     * Load a ComplexColor based on the XML file content. The result can be a GradientColor or
+     * ColorStateList. Note that pure color will be wrapped into a ColorStateList.
+     *
+     * We deferred the parser creation to this function b/c we need to differentiate b/t gradient
+     * and selector tag.
+     *
+     * @return a ComplexColor (GradientColor or ColorStateList) based on the XML file content.
+     */
+    @Nullable
+    private ComplexColor loadComplexColorForCookie(TypedValue value, int id, Theme theme) {
         if (value.string == null) {
             throw new UnsupportedOperationException(
-                    "Can't convert to color state list: type=0x" + value.type);
+                    "Can't convert to ComplexColor: type=0x" + value.type);
         }
 
         final String file = value.string.toString();
@@ -2692,29 +2763,45 @@
             if ((id >>> 24) == 0x1) {
                 final String name = getResourceName(id);
                 if (name != null) {
-                    Log.d(TAG, "Loading framework color state list #" + Integer.toHexString(id)
+                    Log.d(TAG, "Loading framework ComplexColor #" + Integer.toHexString(id)
                             + ": " + name + " at " + file);
                 }
             }
         }
 
         if (DEBUG_LOAD) {
-            Log.v(TAG, "Loading color state list for cookie " + value.assetCookie + ": " + file);
+            Log.v(TAG, "Loading ComplexColor for cookie " + value.assetCookie + ": " + file);
         }
 
-        final ColorStateList csl;
+        ComplexColor complexColor = null;
 
         Trace.traceBegin(Trace.TRACE_TAG_RESOURCES, file);
         if (file.endsWith(".xml")) {
             try {
-                final XmlResourceParser rp = loadXmlResourceParser(
-                        file, id, value.assetCookie, "colorstatelist");
-                csl = ColorStateList.createFromXml(this, rp, theme);
-                rp.close();
+                final XmlResourceParser parser = loadXmlResourceParser(
+                        file, id, value.assetCookie, "ComplexColor");
+
+                final AttributeSet attrs = Xml.asAttributeSet(parser);
+                int type;
+                while ((type = parser.next()) != XmlPullParser.START_TAG
+                        && type != XmlPullParser.END_DOCUMENT) {
+                    // Seek parser to start tag.
+                }
+                if (type != XmlPullParser.START_TAG) {
+                    throw new XmlPullParserException("No start tag found");
+                }
+
+                final String name = parser.getName();
+                if (name.equals("gradient")) {
+                    complexColor = GradientColor.createFromXmlInner(this, parser, attrs, theme);
+                } else if (name.equals("selector")) {
+                    complexColor = ColorStateList.createFromXmlInner(this, parser, attrs, theme);
+                }
+                parser.close();
             } catch (Exception e) {
                 Trace.traceEnd(Trace.TRACE_TAG_RESOURCES);
                 final NotFoundException rnf = new NotFoundException(
-                        "File " + file + " from color state list resource ID #0x"
+                        "File " + file + " from ComplexColor resource ID #0x"
                                 + Integer.toHexString(id));
                 rnf.initCause(e);
                 throw rnf;
@@ -2727,7 +2814,7 @@
         }
         Trace.traceEnd(Trace.TRACE_TAG_RESOURCES);
 
-        return csl;
+        return complexColor;
     }
 
     /**
diff --git a/core/java/android/content/res/TypedArray.java b/core/java/android/content/res/TypedArray.java
index cc65e1e..6067577 100644
--- a/core/java/android/content/res/TypedArray.java
+++ b/core/java/android/content/res/TypedArray.java
@@ -454,6 +454,39 @@
     }
 
     /**
+     * Retrieve the ComplexColor for the attribute at <var>index</var>.
+     * The value may be either a {@link android.content.res.ColorStateList} which can wrap a simple
+     * color value or a {@link android.content.res.GradientColor}
+     * <p>
+     * This method will return {@code null} if the attribute is not defined or
+     * is not an integer color, color state list or GradientColor.
+     *
+     * @param index Index of attribute to retrieve.
+     *
+     * @return ComplexColor for the attribute, or {@code null} if not defined.
+     * @throws RuntimeException if the attribute if the TypedArray has already
+     *         been recycled.
+     * @throws UnsupportedOperationException if the attribute is defined but is
+     *         not an integer color, color state list or GradientColor.
+     */
+    @Nullable
+    public ComplexColor getComplexColor(@StyleableRes int index) {
+        if (mRecycled) {
+            throw new RuntimeException("Cannot make calls to a recycled instance!");
+        }
+
+        final TypedValue value = mValue;
+        if (getValueAt(index*AssetManager.STYLE_NUM_ENTRIES, value)) {
+            if (value.type == TypedValue.TYPE_ATTRIBUTE) {
+                throw new UnsupportedOperationException(
+                        "Failed to resolve attribute at index " + index + ": " + value);
+            }
+            return mResources.loadComplexColor(value, value.resourceId, mTheme);
+        }
+        return null;
+    }
+
+    /**
      * Retrieve the ColorStateList for the attribute at <var>index</var>.
      * The value may be either a single solid color or a reference to
      * a color or complex {@link android.content.res.ColorStateList}
diff --git a/core/java/android/hardware/Sensor.java b/core/java/android/hardware/Sensor.java
index c710f21..841e5b0 100644
--- a/core/java/android/hardware/Sensor.java
+++ b/core/java/android/hardware/Sensor.java
@@ -17,8 +17,10 @@
 
 package android.hardware;
 
-import android.os.Build;
 import android.annotation.SystemApi;
+import android.os.Build;
+
+import java.util.UUID;
 
 /**
  * Class representing a sensor. Use {@link SensorManager#getSensorList} to get
@@ -622,6 +624,29 @@
 
     public static final String STRING_TYPE_HEART_BEAT = "android.sensor.heart_beat";
     /**
+     * A constant describing a dynamic sensor meta event sensor.
+     *
+     * A sensor event of this type is received when a dynamic sensor is added to or removed from
+     * the system. This sensor type should always use special trigger report mode ({@code
+     * SensorManager.REPORTING_MODE_SPECIAL_TRIGGER}).
+     *
+     * @hide This sensor is expected to be used only by system services.
+     */
+    @SystemApi
+    public static final int TYPE_DYNAMIC_SENSOR_META = 32;
+
+    /**
+     * A constant string describing a dynamic sensor meta event sensor.
+     *
+     * @see #TYPE_DYNAMIC_SENSOR_META
+     *
+     * @hide This sensor is expected to only be used by the system service
+     */
+    @SystemApi
+    public static final String STRING_TYPE_DYNAMIC_SENSOR_META =
+            "android.sensor.dynamic_sensor_meta";
+
+    /**
      * A constant describing all sensor types.
      */
 
@@ -708,7 +733,11 @@
             1, // SENSOR_TYPE_PICK_UP_GESTURE
             1, // SENSOR_TYPE_WRIST_TILT_GESTURE
             1, // SENSOR_TYPE_DEVICE_ORIENTATION
-            16, // SENSOR_TYPE_POSE_6DOF
+            16,// SENSOR_TYPE_POSE_6DOF
+            1, // SENSOR_TYPE_STATIONARY_DETECT
+            1, // SENSOR_TYPE_MOTION_DETECT
+            1, // SENSOR_TYPE_HEART_BEAT
+            2, // SENSOR_TYPE_DYNAMIC_SENSOR_META
     };
 
     /**
@@ -734,12 +763,10 @@
         }
         int offset = sensor.mType;
         if (offset >= sSensorReportingModes.length) {
-            // we don't know about this sensor, so this is probably a
-            // vendor-defined sensor, in that case, we don't know how many value
-            // it has
-            // so we return the maximum and assume the app will know.
-            // FIXME: sensor HAL should advertise how much data is returned per
-            // sensor
+            // we don't know about this sensor, so this is probably a vendor-defined sensor, in that
+            // case, we don't know how many value it has so we return the maximum and assume the app
+            // will know.
+            // FIXME: sensor HAL should advertise how much data is returned per sensor
             return 16;
         }
         return sSensorReportingModes[offset];
@@ -763,6 +790,7 @@
     private String  mRequiredPermission;
     private int     mMaxDelay;
     private int     mFlags;
+    private UUID    mUuid;
 
     Sensor() {
     }
@@ -851,6 +879,13 @@
     }
 
     /**
+     * @return The type of this sensor as a string.
+     */
+    public UUID getUuid() {
+        return mUuid;
+    }
+
+    /**
      * @hide
      * @return The permission required to access this sensor. If empty, no permission is required.
      */
@@ -1033,6 +1068,9 @@
             case TYPE_DEVICE_ORIENTATION:
                 mStringType = STRING_TYPE_DEVICE_ORIENTATION;
                 return true;
+            case TYPE_DYNAMIC_SENSOR_META:
+                mStringType = STRING_TYPE_DYNAMIC_SENSOR_META;
+                return true;
             default:
                 return false;
         }
diff --git a/core/java/android/hardware/SensorAdditionalInfo.java b/core/java/android/hardware/SensorAdditionalInfo.java
new file mode 100644
index 0000000..8e5b8a3
--- /dev/null
+++ b/core/java/android/hardware/SensorAdditionalInfo.java
@@ -0,0 +1,142 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware;
+
+import android.annotation.IntDef;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * This class represents a {@link android.hardware.Sensor Sensor} additional information frame,
+ * which is reported through listener callback {@link
+ * android.hardware.SensorEventCallback#onSensorAdditionalInfo onSensorAdditionalInfo}.
+ *
+ * @see SensorManager
+ * @see SensorEventListener3
+ * @see Sensor
+ *
+ */
+
+public class SensorAdditionalInfo {
+
+    /**
+     * The sensor that generated this event. See
+     * {@link android.hardware.SensorManager SensorManager} for details.
+     */
+    public final Sensor sensor;
+
+    /**
+     * Type of this additional info frame.
+     */
+    public final int type;
+
+    /**
+     * Sequence number of frame for a certain type.
+     */
+    public final int serial;
+
+    /**
+     * Additional info payload data represented in float values. Depending on the type of
+     * information, this may be null.
+     */
+    public final float[] floatValues;
+
+    /**
+     * Additional info payload data represented in int values. Depending on the type of information,
+     * this may be null.
+     */
+    public final int[] intValues;
+
+    /**
+     * Typical values of additional infomation type. The set of values is subject to extension in
+     * newer versions and vendors have the freedom of define their own custom values.
+     *
+     * @hide
+     */
+    @IntDef({TYPE_FRAME_BEGIN, TYPE_FRAME_END, TYPE_UNTRACKED_DELAY, TYPE_INTERNAL_TEMPERATURE,
+             TYPE_VEC3_CALIBRATION, TYPE_SENSOR_PLACEMENT, TYPE_SAMPLING})
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface AdditionalInfoType {}
+
+    /**
+     * Mark the beginning of a set of additional info frames.
+     */
+    public static final int TYPE_FRAME_BEGIN = 0;
+
+    /**
+     * Mark the end of a set of additional info frames.
+     */
+    public static final int TYPE_FRAME_END = 1;
+
+    /**
+     * Untracked delay. Delays that are introduced by data processing, such as filtering, which is
+     * not taken into account by sensor timestamps.
+     *
+     * Payload:
+     *     floatValues[0]: delay estimation in seconds
+     *     floatValues[1]: delay estimation standard deviation
+     */
+    public static final int TYPE_UNTRACKED_DELAY = 0x10000;
+
+    /**
+     * Internal temperature. Sensor hardware device internal temperature.
+     *
+     * Payload:
+     *     floatValues[0]: internal temperature in Celsius.
+     */
+    public static final int TYPE_INTERNAL_TEMPERATURE = 0x10001;
+
+    /**
+     * Vector calibration parameter. Calibration applied to a sensor with 3 elements vector output,
+     * such as accelerometer, gyro, etc.
+     *
+     * Payload:
+     *     floatValues[0..11]: First 3 rows of a homogenous matrix in row major order that captures
+     *     any linear transformation, including rotation, scaling, shear, shift.
+     */
+    public static final int TYPE_VEC3_CALIBRATION = 0x10002;
+
+    /**
+     * Sensor placement. Describes location and installation angle of the sensor device.
+     *
+     * Payload:
+     *     floatValues[0..11]: First 3 rows of homogeneous matrix in row major order that describes
+     *     the location and orientation of the sensor. Origin of reference will be the mobile device
+     *     geometric sensor. Reference frame is defined as the same as Android sensor frame.
+     */
+    public static final int TYPE_SENSOR_PLACEMENT = 0x10003;
+
+    /**
+     * Sampling parameter. Describes the raw sample period and estimated jitter of sample time in
+     * terms of standard deviation.
+     *
+     * Payload:
+     *     floatValues[0]: raw sample period in seconds.
+     *     floatValues[1]: standard deviation of sampling period.
+     */
+    public static final int TYPE_SAMPLING = 0x10004;
+
+    SensorAdditionalInfo(
+            Sensor aSensor, int aType, int aSerial, int [] aIntValues, float [] aFloatValues) {
+        sensor = aSensor;
+        type = aType;
+        serial = aSerial;
+        intValues = aIntValues;
+        floatValues = aFloatValues;
+    }
+}
diff --git a/core/java/android/hardware/SensorEventCallback.java b/core/java/android/hardware/SensorEventCallback.java
new file mode 100644
index 0000000..bac212a
--- /dev/null
+++ b/core/java/android/hardware/SensorEventCallback.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware;
+
+/**
+ * Used for receiving sensor additional information frames.
+ */
+public abstract class SensorEventCallback implements SensorEventListener2 {
+
+    /**
+     * Called when sensor values have changed.
+     *
+     * @see android.hardware.SensorEventListener#onSensorChanged(SensorEvent)
+     */
+    @Override
+    public void onSensorChanged(SensorEvent event) {}
+
+    /**
+     * Called when the accuracy of the registered sensor has changed.
+     *
+     * @see android.hardware.SensorEventListener#onAccuracyChanged(Sensor, int)
+     */
+    @Override
+    public void onAccuracyChanged(Sensor sensor, int accuracy) {}
+
+    /**
+     * Called after flush() is completed.
+     *
+     * @see android.hardware.SensorEventListener2#onFlushCompleted(Sensor)
+     */
+    @Override
+    public void onFlushCompleted(Sensor sensor) {}
+
+    /**
+     * Called when a sensor additional information frame is available.
+     *
+     * @param info A {@link android.hardware.SensorAdditionalInfo SensorAdditionalInfo} frame
+     * reported from sensor hardware.
+     */
+    public void onSensorAdditionalInfo(SensorAdditionalInfo info) {}
+}
diff --git a/core/java/android/hardware/SensorManager.java b/core/java/android/hardware/SensorManager.java
index 5d405f9..f0b17c30 100644
--- a/core/java/android/hardware/SensorManager.java
+++ b/core/java/android/hardware/SensorManager.java
@@ -377,6 +377,12 @@
     protected abstract List<Sensor> getFullSensorList();
 
     /**
+     * Gets the full list of dynamic sensors that are available.
+     * @hide
+     */
+    protected abstract List<Sensor> getFullDynamicSensorList();
+
+    /**
      * @return available sensors.
      * @deprecated This method is deprecated, use
      *             {@link SensorManager#getSensorList(int)} instead
@@ -430,6 +436,38 @@
     }
 
     /**
+     * Use this method to get a list of available dynamic sensors of a certain type.
+     * Make multiple calls to get sensors of different types or use
+     * {@link android.hardware.Sensor#TYPE_ALL Sensor.TYPE_ALL} to get all dynamic sensors.
+     *
+     * <p class="note">
+     * NOTE: Both wake-up and non wake-up sensors matching the given type are
+     * returned. Check {@link Sensor#isWakeUpSensor()} to know the wake-up properties
+     * of the returned {@link Sensor}.
+     * </p>
+     *
+     * @param type of sensors requested
+     *
+     * @return a list of dynamic sensors matching the requested type.
+     *
+     * @see Sensor
+     */
+    public List<Sensor> getDynamicSensorList(int type) {
+        // cache the returned lists the first time
+        final List<Sensor> fullList = getFullDynamicSensorList();
+        if (type == Sensor.TYPE_ALL) {
+            return Collections.unmodifiableList(fullList);
+        } else {
+            List<Sensor> list = new ArrayList();
+            for (Sensor i : fullList) {
+                if (i.getType() == type)
+                    list.add(i);
+            }
+            return Collections.unmodifiableList(list);
+        }
+    }
+
+    /**
      * Use this method to get the default sensor for a given type. Note that the
      * returned sensor could be a composite sensor, and its data could be
      * averaged or filtered. If you need to access the raw sensors use
@@ -841,6 +879,86 @@
     /** @hide */
     protected abstract boolean flushImpl(SensorEventListener listener);
 
+
+    /**
+     * Used for receiving notifications from the SensorManager when dynamic sensors are connected or
+     * disconnected.
+     */
+    public static abstract class DynamicSensorConnectionCallback {
+        /**
+         * Called when there is a dynamic sensor being connected to the system.
+         *
+         * @param sensor the newly connected sensor. See {@link android.hardware.Sensor Sensor}.
+         */
+        public void onDynamicSensorConnected(Sensor sensor) {}
+
+        /**
+         * Called when there is a dynamic sensor being disconnected from the system.
+         *
+         * @param sensor the disconnected sensor. See {@link android.hardware.Sensor Sensor}.
+         */
+        public void onDynamicSensorDisconnected(Sensor sensor) {}
+    }
+
+
+    /**
+     * Add a {@link android.hardware.SensorManager.DynamicSensorConnectionCallback
+     * DynamicSensorConnectionCallback} to receive dynamic sensor connection callbacks. Repeat
+     * registration with the already registered callback object will have no additional effect.
+     *
+     * @param callback An object that implements the
+     *        {@link android.hardware.SensorManager.DynamicSensorConnectionCallback
+     *        DynamicSensorConnectionCallback}
+     *        interface for receiving callbacks.
+     * @see #addDynamicSensorCallback(DynamicSensorConnectionCallback, Handler)
+     *
+     * @throws IllegalArgumentException when callback is null.
+     */
+    public void registerDynamicSensorCallback(DynamicSensorConnectionCallback callback) {
+        registerDynamicSensorCallback(callback, null);
+    }
+
+    /**
+     * Add a {@link android.hardware.SensorManager.DynamicSensorConnectionCallback
+     * DynamicSensorConnectionCallback} to receive dynamic sensor connection callbacks. Repeat
+     * registration with the already registered callback object will have no additional effect.
+     *
+     * @param callback An object that implements the
+     *        {@link android.hardware.SensorManager.DynamicSensorConnectionCallback
+     *        DynamicSensorConnectionCallback} interface for receiving callbacks.
+     * @param handler The {@link android.os.Handler Handler} the {@link
+     *        android.hardware.SensorManager.DynamicSensorConnectionCallback
+     *        sensor connection events} will be delivered to.
+     *
+     * @throws IllegalArgumentException when callback is null.
+     */
+    public void registerDynamicSensorCallback(
+            DynamicSensorConnectionCallback callback, Handler handler) {
+        registerDynamicSensorCallbackImpl(callback, handler);
+    }
+
+    /**
+     * Remove a {@link android.hardware.SensorManager.DynamicSensorConnectionCallback
+     * DynamicSensorConnectionCallback} to stop sending dynamic sensor connection events to that
+     * callback.
+     *
+     * @param callback An object that implements the
+     *        {@link android.hardware.SensorManager.DynamicSensorConnectionCallback
+     *        DynamicSensorConnectionCallback}
+     *        interface for receiving callbacks.
+     */
+    public void unregisterDynamicSensorCallback(DynamicSensorConnectionCallback callback) {
+        unregisterDynamicSensorCallbackImpl(callback);
+    }
+
+    /** @hide */
+    protected abstract void registerDynamicSensorCallbackImpl(
+            DynamicSensorConnectionCallback callback, Handler handler);
+
+    /** @hide */
+    protected abstract void unregisterDynamicSensorCallbackImpl(
+            DynamicSensorConnectionCallback callback);
+
     /**
      * <p>
      * Computes the inclination matrix <b>I</b> as well as the rotation matrix
diff --git a/core/java/android/hardware/SystemSensorManager.java b/core/java/android/hardware/SystemSensorManager.java
index 2fe8fb6..c4afbfb 100644
--- a/core/java/android/hardware/SystemSensorManager.java
+++ b/core/java/android/hardware/SystemSensorManager.java
@@ -17,7 +17,10 @@
 package android.hardware;
 
 import android.Manifest;
+import android.content.BroadcastReceiver;
 import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
 import android.content.pm.PackageManager;
 import android.os.Handler;
 import android.os.Looper;
@@ -32,6 +35,7 @@
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
 
 /**
  * Sensor manager implementation that communicates with the built-in
@@ -40,10 +44,14 @@
  * @hide
  */
 public class SystemSensorManager extends SensorManager {
+    //TODO: disable extra logging before release
+    private static boolean DEBUG_DYNAMIC_SENSOR = true;
+
     private static native void nativeClassInit();
     private static native long nativeCreate(String opPackageName);
     private static native boolean nativeGetSensorAtIndex(long nativeInstance,
             Sensor sensor, int index);
+    private static native void nativeGetDynamicSensors(long nativeInstance, List<Sensor> list);
     private static native boolean nativeIsDataInjectionEnabled(long nativeInstance);
 
     private static boolean sSensorModuleInitialized = false;
@@ -52,7 +60,10 @@
     private final Object mLock = new Object();
 
     private final ArrayList<Sensor> mFullSensorsList = new ArrayList<>();
-    private final SparseArray<Sensor> mHandleToSensor = new SparseArray<>();
+    private List<Sensor> mFullDynamicSensorsList = new ArrayList<>();
+    private boolean mDynamicSensorListDirty = true;
+
+    private final HashMap<Integer, Sensor> mHandleToSensor = new HashMap<>();
 
     // Listener list
     private final HashMap<SensorEventListener, SensorEventQueue> mSensorListeners =
@@ -60,6 +71,11 @@
     private final HashMap<TriggerEventListener, TriggerEventQueue> mTriggerListeners =
             new HashMap<TriggerEventListener, TriggerEventQueue>();
 
+    // Dynamic Sensor callbacks
+    private HashMap<DynamicSensorConnectionCallback, Handler>
+            mDynamicSensorCallbacks = new HashMap<>();
+    private BroadcastReceiver mDynamicSensorBroadcastReceiver;
+
     // Looper associated with the context in which this instance was created.
     private final Looper mMainLooper;
     private final int mTargetSdkLevel;
@@ -84,7 +100,7 @@
                 Sensor sensor = new Sensor();
                 if (!nativeGetSensorAtIndex(mNativeInstance, sensor, index)) break;
                 mFullSensorsList.add(sensor);
-                mHandleToSensor.append(sensor.getHandle(), sensor);
+                mHandleToSensor.put(sensor.getHandle(), sensor);
             }
         }
     }
@@ -96,6 +112,15 @@
         return mFullSensorsList;
     }
 
+    /** @hide */
+    @Override
+    protected List<Sensor> getFullDynamicSensorList() {
+        // only set up broadcast receiver if the application tries to find dynamic sensors or
+        // explicitly register a DynamicSensorConnectionCallback
+        setupDynamicSensorBroadcastReceiver();
+        updateDynamicSensorList();
+        return mFullDynamicSensorsList;
+    }
 
     /** @hide */
     @Override
@@ -274,6 +299,187 @@
         }
     }
 
+    private void cleanupSensorConnection(Sensor sensor) {
+        mHandleToSensor.remove(sensor.getHandle());
+
+        if (sensor.getReportingMode() == Sensor.REPORTING_MODE_ONE_SHOT) {
+            synchronized(mTriggerListeners) {
+                for (TriggerEventListener l: mTriggerListeners.keySet()) {
+                    if (DEBUG_DYNAMIC_SENSOR){
+                        Log.i(TAG, "removed trigger listener" + l.toString() +
+                                   " due to sensor disconnection");
+                    }
+                    cancelTriggerSensorImpl(l, sensor, true);
+                }
+            }
+        } else {
+            synchronized(mSensorListeners) {
+                for (SensorEventListener l: mSensorListeners.keySet()) {
+                    if (DEBUG_DYNAMIC_SENSOR){
+                        Log.i(TAG, "removed event listener" + l.toString() +
+                                   " due to sensor disconnection");
+                    }
+                    unregisterListenerImpl(l, sensor);
+                }
+            }
+        }
+    }
+
+    private void updateDynamicSensorList() {
+        synchronized(mFullDynamicSensorsList) {
+            if (mDynamicSensorListDirty) {
+                List<Sensor> list = new ArrayList<>();
+                nativeGetDynamicSensors(mNativeInstance, list);
+
+                final List<Sensor> updatedList = new ArrayList<>();
+                final List<Sensor> addedList = new ArrayList<>();
+                final List<Sensor> removedList = new ArrayList<>();
+
+                boolean changed = diffSortedSensorList(
+                        mFullDynamicSensorsList, list, updatedList, addedList, removedList);
+
+                if (changed) {
+                    if (DEBUG_DYNAMIC_SENSOR) {
+                        Log.i(TAG, "DYNS dynamic sensor list cached should be updated");
+                    }
+                    mFullDynamicSensorsList = updatedList;
+
+                    for (Sensor s: addedList) {
+                        mHandleToSensor.put(s.getHandle(), s);
+                    }
+
+                    Handler mainHandler = new Handler(mContext.getMainLooper());
+
+                    for (Map.Entry<DynamicSensorConnectionCallback, Handler> entry :
+                            mDynamicSensorCallbacks.entrySet()) {
+                        final DynamicSensorConnectionCallback callback = entry.getKey();
+                        Handler handler =
+                                entry.getValue() == null ? mainHandler : entry.getValue();
+
+                        handler.post(new Runnable() {
+                            @Override
+                            public void run() {
+                                for (Sensor s: addedList) {
+                                    callback.onDynamicSensorConnected(s);
+                                }
+                                for (Sensor s: removedList) {
+                                    callback.onDynamicSensorDisconnected(s);
+                                }
+                            }
+                        });
+                    }
+
+                    for (Sensor s: removedList) {
+                        cleanupSensorConnection(s);
+                    }
+                }
+
+                mDynamicSensorListDirty = false;
+            }
+        }
+    }
+
+    private void setupDynamicSensorBroadcastReceiver() {
+        if (mDynamicSensorBroadcastReceiver == null) {
+            mDynamicSensorBroadcastReceiver = new BroadcastReceiver() {
+                @Override
+                public void onReceive(Context context, Intent intent) {
+                    if (intent.getAction() == Intent.ACTION_DYNAMIC_SENSOR_CHANGED) {
+                        if (DEBUG_DYNAMIC_SENSOR) {
+                            Log.i(TAG, "DYNS received DYNAMIC_SENSOR_CHANED broadcast");
+                        }
+                        // Dynamic sensors probably changed
+                        mDynamicSensorListDirty = true;
+                        updateDynamicSensorList();
+                    }
+                }
+            };
+
+            IntentFilter filter = new IntentFilter("dynamic_sensor_change");
+            filter.addAction(Intent.ACTION_DYNAMIC_SENSOR_CHANGED);
+            mContext.registerReceiver(mDynamicSensorBroadcastReceiver, filter);
+        }
+    }
+
+    private void teardownDynamicSensorBroadcastReceiver() {
+        mDynamicSensorCallbacks.clear();
+        mContext.unregisterReceiver(mDynamicSensorBroadcastReceiver);
+        mDynamicSensorBroadcastReceiver = null;
+    }
+
+    /** @hide */
+    protected void registerDynamicSensorCallbackImpl(
+            DynamicSensorConnectionCallback callback, Handler handler) {
+        if (DEBUG_DYNAMIC_SENSOR) {
+            Log.i(TAG, "DYNS Register dynamic sensor callback");
+        }
+
+        if (callback == null) {
+            throw new IllegalArgumentException("callback cannot be null");
+        }
+        if (mDynamicSensorCallbacks.containsKey(callback)) {
+            // has been already registered, ignore
+            return;
+        }
+
+        setupDynamicSensorBroadcastReceiver();
+        mDynamicSensorCallbacks.put(callback, handler);
+    }
+
+    /** @hide */
+    protected void unregisterDynamicSensorCallbackImpl(
+            DynamicSensorConnectionCallback callback) {
+        if (DEBUG_DYNAMIC_SENSOR) {
+            Log.i(TAG, "Removing dynamic sensor listerner");
+        }
+        mDynamicSensorCallbacks.remove(callback);
+    }
+
+    /*
+     * Find the difference of two List<Sensor> assuming List are sorted by handle of sensor,
+     * assuming the input list is already sorted by handle. Inputs are ol and nl; outputs are
+     * updated, added and removed. Any of the output lists can be null in case the result is not
+     * interested.
+     */
+    private static boolean diffSortedSensorList(
+            List<Sensor> oldList, List<Sensor> newList, List<Sensor> updated,
+            List<Sensor> added, List<Sensor> removed) {
+
+        boolean changed = false;
+
+        int i = 0, j = 0;
+        while (true) {
+            if (j < oldList.size() && ( i >= newList.size() ||
+                    newList.get(i).getHandle() > oldList.get(j).getHandle()) ) {
+                changed = true;
+                if (removed != null) {
+                    removed.add(oldList.get(j));
+                }
+                ++j;
+            } else if (i < newList.size() && ( j >= oldList.size() ||
+                    newList.get(i).getHandle() < oldList.get(j).getHandle())) {
+                changed = true;
+                if (added != null) {
+                    added.add(newList.get(i));
+                }
+                if (updated != null) {
+                    updated.add(newList.get(i));
+                }
+                ++i;
+            } else if (i < newList.size() && j < oldList.size() &&
+                    newList.get(i).getHandle() == oldList.get(j).getHandle()) {
+                if (updated != null) {
+                    updated.add(oldList.get(j));
+                }
+                ++i;
+                ++j;
+            } else {
+                break;
+            }
+        }
+        return changed;
+    }
+
     /*
      * BaseEventQueue is the communication channel with the sensor service,
      * SensorEventQueue, TriggerEventQueue are subclases and there is one-to-one mapping between
@@ -284,7 +490,7 @@
      */
     private static abstract class BaseEventQueue {
         private static native long nativeInitBaseEventQueue(long nativeManager,
-                WeakReference<BaseEventQueue> eventQWeak, MessageQueue msgQ, float[] scratch,
+                WeakReference<BaseEventQueue> eventQWeak, MessageQueue msgQ,
                 String packageName, int mode, String opPackageName);
         private static native int nativeEnableSensor(long eventQ, int handle, int rateUs,
                 int maxBatchReportLatencyUs);
@@ -297,9 +503,7 @@
         private long nSensorEventQueue;
         private final SparseBooleanArray mActiveSensors = new SparseBooleanArray();
         protected final SparseIntArray mSensorAccuracies = new SparseIntArray();
-        protected final SparseBooleanArray mFirstEvent = new SparseBooleanArray();
         private final CloseGuard mCloseGuard = CloseGuard.get();
-        private final float[] mScratch = new float[16];
         protected final SystemSensorManager mManager;
 
         protected static final int OPERATING_MODE_NORMAL = 0;
@@ -308,7 +512,7 @@
         BaseEventQueue(Looper looper, SystemSensorManager manager, int mode, String packageName) {
             if (packageName == null) packageName = "";
             nSensorEventQueue = nativeInitBaseEventQueue(manager.mNativeInstance,
-                    new WeakReference<>(this), looper.getQueue(), mScratch,
+                    new WeakReference<>(this), looper.getQueue(),
                     packageName, mode, manager.mContext.getOpPackageName());
             mCloseGuard.open("dispose");
             mManager = manager;
@@ -348,7 +552,7 @@
                         mActiveSensors.put(handle, false);
                         removeSensorEvent(sensor);
                     } else {
-                        // it should never happen -- just ignore.
+                        // sensor just disconnected -- just ignore.
                     }
                 }
             }
@@ -420,6 +624,11 @@
                 long timestamp);
         protected abstract void dispatchFlushCompleteEvent(int handle);
 
+        protected void dispatchAdditionalInfoEvent(
+                int handle, int type, int serial, float[] floatValues, int[] intValues) {
+            // default implementation is do nothing
+        }
+
         protected abstract void addSensorEvent(Sensor sensor);
         protected abstract void removeSensorEvent(Sensor sensor);
     }
@@ -456,6 +665,11 @@
         protected void dispatchSensorEvent(int handle, float[] values, int inAccuracy,
                 long timestamp) {
             final Sensor sensor = mManager.mHandleToSensor.get(handle);
+            if (sensor == null) {
+                // sensor disconnected
+                return;
+            }
+
             SensorEvent t = null;
             synchronized (mSensorsEvents) {
                 t = mSensorsEvents.get(handle);
@@ -481,14 +695,37 @@
             mListener.onSensorChanged(t);
         }
 
+        // Called from native code.
         @SuppressWarnings("unused")
+        @Override
         protected void dispatchFlushCompleteEvent(int handle) {
             if (mListener instanceof SensorEventListener2) {
                 final Sensor sensor = mManager.mHandleToSensor.get(handle);
+                if (sensor == null) {
+                    // sensor disconnected
+                    return;
+                }
                 ((SensorEventListener2)mListener).onFlushCompleted(sensor);
             }
             return;
         }
+
+        // Called from native code.
+        @SuppressWarnings("unused")
+        @Override
+        protected void dispatchAdditionalInfoEvent(
+                int handle, int type, int serial, float[] floatValues, int[] intValues) {
+            if (mListener instanceof SensorEventCallback) {
+                final Sensor sensor = mManager.mHandleToSensor.get(handle);
+                if (sensor == null) {
+                    // sensor disconnected
+                    return;
+                }
+                SensorAdditionalInfo info =
+                        new SensorAdditionalInfo(sensor, type, serial, intValues, floatValues);
+                ((SensorEventCallback)mListener).onSensorAdditionalInfo(info);
+            }
+        }
     }
 
     static final class TriggerEventQueue extends BaseEventQueue {
@@ -523,6 +760,10 @@
         protected void dispatchSensorEvent(int handle, float[] values, int accuracy,
                 long timestamp) {
             final Sensor sensor = mManager.mHandleToSensor.get(handle);
+            if (sensor == null) {
+                // sensor disconnected
+                return;
+            }
             TriggerEvent t = null;
             synchronized (mTriggerEvents) {
                 t = mTriggerEvents.get(handle);
diff --git a/core/java/android/hardware/input/KeyboardLayout.java b/core/java/android/hardware/input/KeyboardLayout.java
index 584008c..2cafa08 100644
--- a/core/java/android/hardware/input/KeyboardLayout.java
+++ b/core/java/android/hardware/input/KeyboardLayout.java
@@ -16,8 +16,10 @@
 
 package android.hardware.input;
 
+import android.annotation.NonNull;
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.util.LocaleList;
 
 import java.util.Locale;
 
@@ -32,7 +34,8 @@
     private final String mLabel;
     private final String mCollection;
     private final int mPriority;
-    private final Locale[] mLocales;
+    @NonNull
+    private final LocaleList mLocales;
     private final int mVendorId;
     private final int mProductId;
 
@@ -47,16 +50,12 @@
     };
 
     public KeyboardLayout(String descriptor, String label, String collection, int priority,
-            Locale[] locales, int vid, int pid) {
+            LocaleList locales, int vid, int pid) {
         mDescriptor = descriptor;
         mLabel = label;
         mCollection = collection;
         mPriority = priority;
-        if (locales != null) {
-            mLocales = locales;
-        } else {
-            mLocales = new Locale[0];
-        }
+        mLocales = locales;
         mVendorId = vid;
         mProductId = pid;
     }
@@ -66,11 +65,7 @@
         mLabel = source.readString();
         mCollection = source.readString();
         mPriority = source.readInt();
-        int N = source.readInt();
-        mLocales = new Locale[N];
-        for (int i = 0; i < N; i++) {
-            mLocales[i] = Locale.forLanguageTag(source.readString());
-        }
+        mLocales = LocaleList.CREATOR.createFromParcel(source);
         mVendorId = source.readInt();
         mProductId = source.readInt();
     }
@@ -108,7 +103,7 @@
      * This may be empty if a locale has not been assigned to this keyboard layout.
      * @return The keyboard layout's intended locale.
      */
-    public Locale[] getLocales() {
+    public LocaleList getLocales() {
         return mLocales;
     }
 
@@ -141,14 +136,7 @@
         dest.writeString(mLabel);
         dest.writeString(mCollection);
         dest.writeInt(mPriority);
-        if (mLocales != null) {
-            dest.writeInt(mLocales.length);
-            for (Locale l : mLocales) {
-                dest.writeString(l.toLanguageTag());
-            }
-        } else {
-            dest.writeInt(0);
-        }
+        mLocales.writeToParcel(dest, 0);
         dest.writeInt(mVendorId);
         dest.writeInt(mProductId);
     }
diff --git a/core/java/android/hardware/location/ContextHubInfo.aidl b/core/java/android/hardware/location/ContextHubInfo.aidl
new file mode 100644
index 0000000..1a9221a
--- /dev/null
+++ b/core/java/android/hardware/location/ContextHubInfo.aidl
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.location;
+/*
+@hide
+*/
+parcelable ContextHubInfo;
diff --git a/core/java/android/hardware/location/ContextHubInfo.java b/core/java/android/hardware/location/ContextHubInfo.java
new file mode 100644
index 0000000..e47c541
--- /dev/null
+++ b/core/java/android/hardware/location/ContextHubInfo.java
@@ -0,0 +1,351 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.hardware.location;
+
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.Arrays;
+
+/**
+ * @hide
+  */
+@SystemApi
+public class ContextHubInfo {
+    private int mId;
+    private String mName;
+    private String mVendor;
+    private String mToolchain;
+    private int mPlatformVersion;
+    private int mStaticSwVersion;
+    private int mToolchainVersion;
+    private float mPeakMips;
+    private float mStoppedPowerDrawMw;
+    private float mSleepPowerDrawMw;
+    private float mPeakPowerDrawMw;
+
+    private int[] mSupportedSensors;
+
+    private MemoryRegion[] mMemoryRegions;
+
+    public ContextHubInfo() {
+    }
+
+    /**
+     * get the context hub unique identifer
+     *
+     * @return int - unique system wide identifier
+     */
+    public int getId() {
+        return mId;
+    }
+
+    /**
+     * set the context hub unique identifer
+     *
+     * @param id - unique system wide identifier for the hub
+     */
+    public void setId(int id) {
+        mId = id;
+    }
+
+    /**
+     * get a string as a hub name
+     *
+     * @return String - a name for the hub
+     */
+    public String getName() {
+        return mName;
+    }
+
+    /**
+     * set a string as the hub name
+     *
+     * @param String - the name for the hub
+     */
+    public void setName(String name) {
+        mName = name;
+    }
+
+    /**
+     * get a string as the vendor name
+     *
+     * @return String - a name for the vendor
+     */
+    public String getVendor() {
+        return mVendor;
+    }
+
+    /**
+     * set a string as the vendor name
+     *
+     * @param String - a name for the vendor
+     */
+    public void setVendor(String vendor) {
+        mVendor = vendor;
+    }
+
+    /**
+     * get tool chain string
+     *
+     * @return String - description of the tool chain
+     */
+    public String getToolchain() {
+        return mToolchain;
+    }
+
+    /**
+     * set tool chain string
+     *
+     * @param String - description of the tool chain
+     */
+    public void setToolchain(String toolchain) {
+        mToolchain = toolchain;
+    }
+
+    /**
+     * get platform version
+     *
+     * @return int - platform version number
+     */
+    public int getPlatformVersion() {
+        return mPlatformVersion;
+    }
+
+    /**
+     * set platform version
+     *
+     * @param platformVersion - platform version number
+     */
+    public void setPlatformVersion(int platformVersion) {
+        mPlatformVersion = platformVersion;
+    }
+
+    /**
+     * get static platform version number
+     *
+     * @return int - platform version number
+     */
+    public int getStaticSwVersion() {
+        return mStaticSwVersion;
+    }
+
+    /**
+     * set platform software version
+     *
+     * @param staticSwVersion - platform static s/w version number
+     */
+    public void setStaticSwVersion(int staticSwVersion) {
+        mStaticSwVersion = staticSwVersion;
+    }
+
+    /**
+     * get the tool chain version
+     *
+     * @return int - the tool chain version
+     */
+    public int getToolchainVersion() {
+        return mToolchainVersion;
+    }
+
+    /**
+     * set the tool chain version number
+     *
+     * @param toolchainVersion - tool chain version number
+     */
+    public void setToolchainVersion(int toolchainVersion) {
+        mToolchainVersion = toolchainVersion;
+    }
+
+    /**
+     * get the peak processing mips the hub can support
+     *
+     * @return float - peak MIPS that this hub can deliver
+     */
+    public float getPeakMips() {
+        return mPeakMips;
+    }
+
+    /**
+     * set the peak mips that this hub can support
+     *
+     * @param peakMips - peak mips this hub can deliver
+     */
+    public void setPeakMips(float peakMips) {
+        mPeakMips = peakMips;
+    }
+
+    /**
+     * get the stopped power draw in milliwatts
+     * This assumes that the hub enter a stopped state - which is
+     * different from the sleep state. Latencies on exiting the
+     * sleep state are typically higher and expect to be in multiple
+     * milliseconds.
+     *
+     * @return float - power draw by the hub in stopped state
+     */
+    public float getStoppedPowerDrawMw() {
+        return mStoppedPowerDrawMw;
+    }
+
+    /**
+     * Set the power consumed by the hub in stopped state
+     *
+     * @param stoppedPowerDrawMw - stopped power in milli watts
+     */
+    public void setStoppedPowerDrawMw(float stoppedPowerDrawMw) {
+        mStoppedPowerDrawMw = stoppedPowerDrawMw;
+    }
+
+    /**
+     * get the power draw of the hub in sleep mode. This assumes
+     * that the hub supports a sleep mode in which the power draw is
+     * lower than the power consumed when the hub is actively
+     * processing. As a guideline, assume that the hub should be
+     * able to enter sleep mode if it knows reliably on completion
+     * of some task that the next interrupt/scheduled work item is
+     * at least 250 milliseconds later.
+     *
+     * @return float - sleep power draw in milli watts
+     */
+    public float getSleepPowerDrawMw() {
+        return mSleepPowerDrawMw;
+    }
+
+    /**
+     * Set the sleep power draw in milliwatts
+     *
+     * @param sleepPowerDrawMw - sleep power draw in milliwatts.
+     */
+    public void setSleepPowerDrawMw(float sleepPowerDrawMw) {
+        mSleepPowerDrawMw = sleepPowerDrawMw;
+    }
+
+    /**
+     * get the peak powe draw of the hub. This is the power consumed
+     * by the hub at maximum load.
+     *
+     * @return float - peak power draw
+     */
+    public float getPeakPowerDrawMw() {
+        return mPeakPowerDrawMw;
+    }
+
+    /**
+     * set the peak power draw of the hub
+     *
+     * @param peakPowerDrawMw - peak power draw of the hub in
+     *                        milliwatts.
+     */
+    public void setPeakPowerDrawMw(float peakPowerDrawMw) {
+        mPeakPowerDrawMw = peakPowerDrawMw;
+    }
+
+    /**
+     * get the sensors supported by this hub
+     *
+     * @return int[] - all the supported sensors on this hub
+     *
+     * @see ContextHubManager
+     */
+    public int[] getSupportedSensors() {
+        return Arrays.copyOf(mSupportedSensors, mSupportedSensors.length);
+    }
+
+    /**
+     * get the various memory regions on this hub
+     *
+     * @return MemoryRegion[] - all the memory regions on this hub
+     *
+     * @see MemoryRegion
+     */
+    public MemoryRegion[] getMemoryRegions() {
+        return Arrays.copyOf(mMemoryRegions, mMemoryRegions.length);
+    }
+
+    /**
+     * set the supported sensors on this hub
+     *
+     * @param supportedSensors - supported sensors on this hub
+     */
+    public void setSupportedSensors(int[] supportedSensors) {
+        mSupportedSensors = Arrays.copyOf(supportedSensors, supportedSensors.length);
+    }
+
+    /**
+     * set memory regions for this hub
+     *
+     * @param memoryRegions - memory regions information
+     *
+     * @see MemoryRegion
+     */
+    public void setMemoryRegions(MemoryRegion[] memoryRegions) {
+        mMemoryRegions = Arrays.copyOf(memoryRegions, memoryRegions.length);
+    }
+
+    private ContextHubInfo(Parcel in) {
+        mId = in.readInt();
+        mName = in.readString();
+        mVendor = in.readString();
+        mToolchain = in.readString();
+        mPlatformVersion = in.readInt();
+        mToolchainVersion = in.readInt();
+        mStaticSwVersion = in.readInt();
+        mPeakMips = in.readFloat();
+        mStoppedPowerDrawMw = in.readFloat();
+        mSleepPowerDrawMw = in.readFloat();
+        mPeakPowerDrawMw = in.readFloat();
+
+        int numSupportedSensors = in.readInt();
+        mSupportedSensors = new int[numSupportedSensors];
+        in.readIntArray(mSupportedSensors);
+        mMemoryRegions = in.createTypedArray(MemoryRegion.CREATOR);
+    }
+
+    public int describeContents() {
+        return 0;
+    }
+
+    public void writeToParcel(Parcel out, int flags) {
+        out.writeInt(mId);
+        out.writeString(mName);
+        out.writeString(mVendor);
+        out.writeString(mToolchain);
+        out.writeInt(mPlatformVersion);
+        out.writeInt(mToolchainVersion);
+        out.writeInt(mStaticSwVersion);
+        out.writeFloat(mPeakMips);
+        out.writeFloat(mStoppedPowerDrawMw);
+        out.writeFloat(mSleepPowerDrawMw);
+        out.writeFloat(mPeakPowerDrawMw);
+
+        out.writeInt(mSupportedSensors.length);
+        out.writeIntArray(mSupportedSensors);
+        out.writeTypedArray(mMemoryRegions, flags);
+    }
+
+    public static final Parcelable.Creator<ContextHubInfo> CREATOR
+            = new Parcelable.Creator<ContextHubInfo>() {
+        public ContextHubInfo createFromParcel(Parcel in) {
+            return new ContextHubInfo(in);
+        }
+
+        public ContextHubInfo[] newArray(int size) {
+            return new ContextHubInfo[size];
+        }
+    };
+}
diff --git a/core/java/android/hardware/location/ContextHubManager.java b/core/java/android/hardware/location/ContextHubManager.java
new file mode 100644
index 0000000..301b2e4
--- /dev/null
+++ b/core/java/android/hardware/location/ContextHubManager.java
@@ -0,0 +1,278 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.hardware.location;
+
+import android.annotation.SystemApi;
+import android.hardware.location.NanoAppInstanceInfo;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.ServiceConnection;
+import android.Manifest;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.util.Log;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+
+/**
+ * A class that exposes the Context hubs on a device to
+ * applicaions.
+ *
+ * Please not that this class is not expected to be used by
+ * unbundled applications. Also, calling applications are
+ * expected to have LOCTION_HARDWARE premissions to use this
+ * class.
+ *
+ * @hide
+ */
+@SystemApi
+public final class ContextHubManager {
+
+    private static final String TAG = "ContextHubManager";
+    private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+    private static final String HARDWARE_PERMISSION = Manifest.permission.LOCATION_HARDWARE;
+    private static final String ENFORCE_HW_PERMISSION_MESSAGE = "Permission '"
+            + HARDWARE_PERMISSION + "' not granted to access ContextHub Hardware";
+
+    private Context mContext;
+    private IContextHubService mContextHubService;
+    private boolean mContextHubConnected;
+
+    /**
+     * A special context hub identifer meaning any possible hub on
+     * the system.
+     */
+    public static final int ANY_HUB       = -1;
+    /**
+     * A constant denoting a message to load a a Nano App
+     */
+    public static final int MSG_LOAD_NANO_APP   = 1;
+    /**
+     * A constant denoting a message to unload a a Nano App
+     */
+    public static final int MSG_UNLOAD_NANO_APP = 2;
+    /**
+     * A constant denoting a message to send a message
+     */
+    public static final int MSG_DATA_SEND       = 3;
+
+
+    /**
+     * Get a handle to all the context hubs in the system
+     * @return array of context hub handles
+     */
+    public int[] getContexthubHandles() {
+        int[] retVal = null;
+        if(mContextHubConnected) {
+            try {
+                retVal = mContextHubService.getContextHubHandles();
+            }catch (RemoteException e) {
+                Log.e (TAG, "Could not fetch context hub handles :" + e.toString());
+            }
+        }
+        return retVal;
+    }
+
+    /**
+     * Get more information about a specific hub.
+     *
+     * @param contexthubHandle Handle of context hub
+     *
+     * @return ContextHubInfo  returned information about the hub
+     *
+     * @see ContextHubInfo
+     */
+    public ContextHubInfo getContexthubInfo(int contexthubHandle) {
+        ContextHubInfo retVal = null;
+        if(mContextHubConnected) {
+            try {
+                retVal = mContextHubService.getContextHubInfo(contexthubHandle);
+            }catch (RemoteException e) {
+                Log.e (TAG, "Could not fetch context hub info :" + e.toString());
+            }
+        }
+
+        return(retVal);
+    }
+
+    /**
+     * Load a nanoapp on a specified context hub
+     *
+     * @param hubHandle handle of context hub to load the app on.
+     * @param app the nanoApp to load on the hub
+     *
+     * @return int nanoAppInstance of the loaded nanoApp on success,
+     *         -1 otherwise
+     *
+     * @see NanoApp
+     */
+    public int loadNanoApp(int hubHandle, NanoApp app) {
+        int retVal = -1;
+
+        if(mContextHubConnected) {
+            try {
+                retVal = mContextHubService.loadNanoApp(hubHandle, app);
+            }catch (RemoteException e) {
+                Log.e (TAG, "Could not fetch load nanoApp :" + e.toString());
+            }
+        }
+
+        return retVal;
+    }
+
+    /**
+     * Unload a specified nanoApp
+     *
+     * @param nanoAppInstanceHandle handle of the nanoApp to load
+     *
+     * @return int  0 on success, -1 otherewise
+     */
+    public int unloadNanoApp(int nanoAppInstanceHandle) {
+        int retVal = -1;
+
+        if(mContextHubConnected) {
+            try {
+                retVal = mContextHubService.unloadNanoApp(nanoAppInstanceHandle);
+            }catch (RemoteException e) {
+                Log.e (TAG, "Could not fetch unload nanoApp :" + e.toString());
+            }
+        }
+
+        return retVal;
+    }
+
+    /**
+     * get information about the nano app instance
+     *
+     * @param nanoAppInstanceHandle handle of the nanoAppInstance
+     *
+     * @return NanoAppInstanceInfo Inforamtion about the nano app
+     *         instance.
+     *
+     * @see NanoAppInstanceInfo
+     */
+    public NanoAppInstanceInfo getNanoAppInstanceInfo(int nanoAppInstanceHandle) {
+        NanoAppInstanceInfo retVal = null;
+
+        if(mContextHubConnected) {
+            try {
+                retVal = mContextHubService.getNanoAppInstanceInfo(nanoAppInstanceHandle);
+            }catch (RemoteException e) {
+                Log.e (TAG, "Could not fetch nanoApp info :" + e.toString());
+            }
+        }
+
+        return retVal;
+    }
+
+    /**
+     * Find a specified nano app on the system
+     *
+     * @param hubHandle handle of hub to search for nano app
+     * @param filter filter specifying the search criteria for app
+     *
+     * @see NanoAppFilter
+     *
+     * @return Integer[] Array of handles to any found nano apps
+     */
+    public Integer[] findNanoAppOnHub(int hubHandle, NanoAppFilter filter) {
+        int[] temp;
+        Integer[] retVal = null;
+
+        if(mContextHubConnected) {
+            try {
+                temp = mContextHubService.findNanoAppOnHub(hubHandle, filter);
+                retVal = new Integer[temp.length];
+                for (int i = 0; i < temp.length; i++) {
+                    retVal[i] = temp[i];
+                }
+            }catch (RemoteException e) {
+                Log.e (TAG, "Could not query nanoApp instance :" + e.toString());
+            }
+        }
+
+        return retVal;
+    }
+
+    /**
+     * Send a message to a spcific nano app instance on a context
+     * hub
+     *
+     *
+     * @param hubHandle handle of the hub to send the message to
+     * @param nanoAppHandle  handle of the nano app to send to
+     * @param msg Message to be sent
+     *
+     * @see ContextHubMessage
+     *
+     * @return int 0 on success, -1 otherwise
+     */
+    public int sendMessage(int hubHandle, int nanoAppHandle, ContextHubMessage msg) {
+        int retVal = -1;
+
+        if(mContextHubConnected) {
+            try {
+                retVal = mContextHubService.sendMessage(hubHandle, nanoAppHandle, msg);
+            }catch (RemoteException e) {
+                Log.e (TAG, "Could not fetch send message :" + e.toString());
+            }
+        }
+
+        return retVal;
+    }
+
+    private void checkPermissions() {
+        mContext.enforceCallingPermission(HARDWARE_PERMISSION, ENFORCE_HW_PERMISSION_MESSAGE);
+    }
+
+    private IContextHubCallback.Stub mClientCallback = new IContextHubCallback.Stub() {
+        @Override
+        public void onMessageReceipt(int hubId, int nanoAppId, ContextHubMessage msg) throws RemoteException {
+
+        }
+    };
+
+    private ContextHubManager(Context context) {
+        checkPermissions();
+        mContext = context;
+        mContextHubConnected = false;
+    }
+
+    private ServiceConnection mServiceConnection = new ServiceConnection() {
+        @Override
+        public void onServiceConnected(ComponentName name, IBinder service) {
+            mContextHubService = IContextHubService.Stub.asInterface(service);
+            mContextHubConnected = true;
+
+            // Register our Callback
+            try {
+                mContextHubService.registerCallBack(mClientCallback);
+            } catch (RemoteException e) {
+                Log.e(TAG, "Could not register callback with context hub service :" + e.toString());
+            }
+            Log.d(TAG, "contexthub manager connected to " + name.toString());
+        }
+
+        @Override
+        public void onServiceDisconnected(ComponentName name) {
+            mContextHubService = null;
+            mContextHubConnected = false;
+            Log.d(TAG, "contexthub manager disconnected from " + name.toString());
+        }
+    };
+
+}
diff --git a/core/java/android/hardware/location/ContextHubMessage.aidl b/core/java/android/hardware/location/ContextHubMessage.aidl
new file mode 100644
index 0000000..915f1ec
--- /dev/null
+++ b/core/java/android/hardware/location/ContextHubMessage.aidl
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.location;
+/*
+@hide
+*/
+parcelable ContextHubMessage;
+
diff --git a/core/java/android/hardware/location/ContextHubMessage.java b/core/java/android/hardware/location/ContextHubMessage.java
new file mode 100644
index 0000000..954e97d
--- /dev/null
+++ b/core/java/android/hardware/location/ContextHubMessage.java
@@ -0,0 +1,129 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.location;
+
+
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.Arrays;
+
+/**
+ * @hide
+ */
+@SystemApi
+public class ContextHubMessage {
+    private int mType;
+    private int mVersion;
+    private byte[]mData;
+
+    /**
+     * Get the message type
+     *
+     * @return int - message type
+     */
+    public int getMsgType() {
+        return mType;
+    }
+
+    /**
+     * get message version
+     *
+     * @return int - message version
+     */
+    public int getVersion() {
+        return mVersion;
+    }
+
+    /**
+     * get message data
+     *
+     * @return byte[] - message data buffer
+     */
+    public byte[] getData() {
+        return Arrays.copyOf(mData, mData.length);
+    }
+
+    /**
+     * set message type
+     *
+     * @param msgType - message type
+     */
+    public void setMsgType(int msgType) {
+        mType = msgType;
+    }
+
+    /**
+     * Set message version
+     *
+     * @param version - message version
+     */
+    public void setVersion(int version) {
+        mVersion = version;
+    }
+
+    /**
+     * set message data
+     *
+     * @param data - message buffer
+     */
+    public void setMsgData(byte[] data) {
+        mData = Arrays.copyOf(data, data.length);
+    }
+
+    /**
+     * Constructor for a context hub message
+     *
+     * @param msgType - message type
+     * @param version - version
+     * @param data    - message buffer
+     */
+    public ContextHubMessage(int msgType, int version, byte[] data) {
+        mType = msgType;
+        mVersion = version;
+        mData = Arrays.copyOf(data, data.length);
+    }
+
+    public int describeContents() {
+        return 0;
+    }
+
+    private ContextHubMessage(Parcel in) {
+        mType = in.readInt();
+        mVersion = in.readInt();
+        byte[] byteBuffer = new byte[in.readInt()];
+        in.readByteArray(byteBuffer);
+    }
+    public void writeToParcel(Parcel out, int flags) {
+        out.writeInt(mType);
+        out.writeInt(mVersion);
+        out.writeInt(mData.length);
+        out.writeByteArray(mData);
+    }
+
+    public static final Parcelable.Creator<ContextHubMessage> CREATOR
+            = new Parcelable.Creator<ContextHubMessage>() {
+        public ContextHubMessage createFromParcel(Parcel in) {
+            return new ContextHubMessage(in);
+        }
+
+        public ContextHubMessage[] newArray(int size) {
+            return new ContextHubMessage[size];
+        }
+    };
+}
diff --git a/core/java/android/hardware/location/ContextHubService.java b/core/java/android/hardware/location/ContextHubService.java
new file mode 100644
index 0000000..a2a13c6
--- /dev/null
+++ b/core/java/android/hardware/location/ContextHubService.java
@@ -0,0 +1,203 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.location;
+
+import android.app.Service;
+import android.content.Context;
+import android.content.Intent;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.util.Log;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+
+/**
+ * @hide
+ */
+public class ContextHubService extends Service {
+
+    private static final String TAG = "ContextHubService";
+    private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+
+    private static ContextHubService sSingletonInstance;
+    private static final Object sSingletonInstanceLock = new Object();
+
+    private HashMap<Integer, ContextHubInfo> mHubHash;
+    private HashMap<Integer, NanoAppInstanceInfo> mNanoAppHash;
+    private ContextHubInfo[] mContexthubInfo;
+
+
+    private native int nativeSendMessage(int[] header, byte[] data);
+    private native ContextHubInfo[] nativeInitialize();
+
+    private int onMessageReceipt(int[] header, byte[] data) {
+        return 0;
+    }
+    private void initialize() {
+        mContexthubInfo = nativeInitialize();
+
+        mHubHash = new HashMap<Integer, ContextHubInfo>();
+
+        for (int i = 0; i < mContexthubInfo.length; i++) {
+            mHubHash.put(i + 1, mContexthubInfo[i]); // Avoiding zero
+        }
+    }
+
+    private ContextHubService(Context context) {
+        initialize();
+        Log.d(TAG, "Created from " + context.toString());
+    }
+
+    public static ContextHubService getInstance(Context context) {
+        synchronized (sSingletonInstanceLock) {
+            if (sSingletonInstance == null) {
+                sSingletonInstance = new ContextHubService(context);
+            }
+            return sSingletonInstance;
+        }
+    }
+
+    @Override
+    public void onCreate() {
+        super.onCreate();
+    }
+
+    @Override
+    public IBinder onBind(Intent intent) {
+        return null;
+    }
+
+    private final IContextHubService.Stub mBinder = new IContextHubService.Stub() {
+
+        private  IContextHubCallback callback;
+
+        @Override
+        public int registerCallBack(IContextHubCallback callback) throws RemoteException{
+            this.callback = callback;
+            return 0;
+        }
+
+        @Override
+        public int[] getContextHubHandles() throws RemoteException {
+            int [] returnArray = new int[mHubHash.size()];
+            int i = 0;
+            for (int key : mHubHash.keySet()) {
+                // Add any filtering here
+                returnArray[i] = key;
+                i++;
+            }
+            return returnArray;
+        }
+
+        @Override
+        public ContextHubInfo getContextHubInfo(int contexthubHandle) throws RemoteException {
+            return mHubHash.get(contexthubHandle);
+        }
+
+        @Override
+        public int loadNanoApp(int hubHandle, NanoApp app) throws RemoteException {
+            if (!mHubHash.containsKey(hubHandle)) {
+                return -1;
+            } else {
+                // Call Native interface here
+                int[] msgHeader = new int[8];
+                msgHeader[0] = ContextHubManager.MSG_LOAD_NANO_APP;
+                msgHeader[1] = app.getAppId();
+                msgHeader[2] = app.getAppVersion();
+                msgHeader[3] = 0; // LOADING_HINTS
+                msgHeader[4] = hubHandle;
+
+                int handle = nativeSendMessage(msgHeader, app.getAppBinary());
+
+                // if successful, add an entry to mNanoAppHash
+
+                if(handle > 0) {
+                    return 0;
+                } else {
+
+                    return -1;
+                }
+            }
+        }
+
+        @Override
+        public int unloadNanoApp(int nanoAppInstanceHandle) throws RemoteException {
+            if(!mNanoAppHash.containsKey(nanoAppInstanceHandle)) {
+                return -1;
+            } else {
+                NanoAppInstanceInfo info = mNanoAppHash.get(nanoAppInstanceHandle);
+                // Call Native interface here
+                int[] msgHeader = new int[8];
+                msgHeader[0] = ContextHubManager.MSG_UNLOAD_NANO_APP;
+                msgHeader[1] = info.getContexthubId();
+                msgHeader[2] = info.getHandle();
+
+                int result = nativeSendMessage(msgHeader, null);
+                // if successful, remove the entry in mNanoAppHash
+                if(result == 0) {
+                    mNanoAppHash.remove(nanoAppInstanceHandle);
+                }
+                return(result);
+            }
+        }
+
+        @Override
+        public NanoAppInstanceInfo getNanoAppInstanceInfo(int nanoAppInstanceHandle) throws RemoteException {
+            // This assumes that all the nanoAppInfo is current. This is reasonable
+            // for the use cases for tightly controlled nanoApps.
+            //
+            if(!mNanoAppHash.containsKey(nanoAppInstanceHandle)) {
+                return(mNanoAppHash.get(nanoAppInstanceHandle));
+            } else {
+                return null;
+            }
+        }
+
+        @Override
+        public int[] findNanoAppOnHub(int hubHandle, NanoAppFilter filter) throws RemoteException {
+            ArrayList<Integer> foundInstances = new ArrayList<Integer>();
+
+            for(Integer nanoAppInstance : mNanoAppHash.keySet()) {
+                NanoAppInstanceInfo info = mNanoAppHash.get(nanoAppInstance);
+
+                if(filter.testMatch(info)){
+                    foundInstances.add(nanoAppInstance);
+                }
+            }
+
+            int[] retArray = new int[foundInstances.size()];
+            for (int i = 0; i < foundInstances.size(); i++) {
+                retArray[i] = foundInstances.get(i).intValue();
+            }
+
+            return retArray;
+        }
+
+        @Override
+        public int sendMessage(int hubHandle, int nanoAppHandle, ContextHubMessage msg) throws RemoteException {
+            int[] msgHeader = new int[8];
+            msgHeader[0] = ContextHubManager.MSG_DATA_SEND;
+            msgHeader[1] = hubHandle;
+            msgHeader[2] = nanoAppHandle;
+            msgHeader[3] = msg.getMsgType();
+            msgHeader[4] = msg.getVersion();
+
+            return (nativeSendMessage(msgHeader, msg.getData()));
+        }
+    };
+}
diff --git a/core/java/android/hardware/location/IContextHubCallback.aidl b/core/java/android/hardware/location/IContextHubCallback.aidl
new file mode 100644
index 0000000..45b1ef4
--- /dev/null
+++ b/core/java/android/hardware/location/IContextHubCallback.aidl
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.location;
+
+import android.hardware.location.ContextHubMessage;
+
+/** @hide */
+oneway interface IContextHubCallback {
+    void onMessageReceipt(int hubId, int nanoAppId, in ContextHubMessage msg);
+}
diff --git a/core/java/android/hardware/location/IContextHubService.aidl b/core/java/android/hardware/location/IContextHubService.aidl
new file mode 100644
index 0000000..b2db0b2
--- /dev/null
+++ b/core/java/android/hardware/location/IContextHubService.aidl
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.location;
+
+// Declare any non-default types here with import statements
+import android.hardware.location.ContextHubMessage;
+import android.hardware.location.ContextHubInfo;
+import android.hardware.location.NanoApp;
+import android.hardware.location.NanoAppInstanceInfo;
+import android.hardware.location.NanoAppFilter;
+import android.hardware.location.IContextHubCallback;
+
+/** @hide */
+interface IContextHubService {
+
+    // register a callback to receive messages
+    int registerCallBack(in IContextHubCallback callback);
+
+    // Gets a list of available context hub handles
+    int[] getContextHubHandles();
+
+    // Get the properties of a hub
+    ContextHubInfo getContextHubInfo(int contextHubHandle);
+
+    // Load a nanoapp on a specified context hub
+    int loadNanoApp(int hubHandle, in NanoApp app);
+
+    // Unload a nanoapp instance
+    int unloadNanoApp(int nanoAppInstanceHandle);
+
+    // get information about a nanoAppInstance
+    NanoAppInstanceInfo getNanoAppInstanceInfo(int nanoAppInstanceHandle);
+
+    // find all nanoApp instances matching some filter
+    int[] findNanoAppOnHub(int hubHandle, in NanoAppFilter filter);
+
+    // send a message to a nanoApp
+    int sendMessage(int hubHandle, int nanoAppHandle, in ContextHubMessage msg);
+}
diff --git a/core/java/android/hardware/location/MemoryRegion.java b/core/java/android/hardware/location/MemoryRegion.java
new file mode 100644
index 0000000..e8c7615
--- /dev/null
+++ b/core/java/android/hardware/location/MemoryRegion.java
@@ -0,0 +1,115 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.location;
+
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * @hide
+ */
+
+@SystemApi
+public class MemoryRegion implements Parcelable{
+
+    private int mSizeBytes;
+    private int mSizeBytesFree;
+    private boolean mIsReadable;
+    private boolean mIsWritable;
+    private boolean mIsExecutable;
+
+    /**
+     * get the capacity of the memory region in bytes
+     *
+     * @return int - the memory capacity in bytes
+     */
+    public int getCapacityBytes() {
+        return mSizeBytes;
+    }
+
+    /**
+     * get the free capacity of the memory region in bytes
+     *
+     * @return int - free bytes
+     */
+    public int getFreeCapacityBytes() {
+        return mSizeBytesFree;
+    }
+
+    /**
+     * Is the memory readable
+     *
+     * @return boolean - true if memory is readable, false otherwise
+     */
+    public boolean isReadable() {
+        return mIsReadable;
+    }
+
+    /**
+     * Is the memory writable
+     *
+     * @return boolean - true if memory is writable, false otherwise
+     */
+    public boolean isWritable() {
+        return mIsWritable;
+    }
+
+    /**
+     * Is the memory executable
+     *
+     * @return boolean - true if memory is executable, false
+     *         otherwise
+     */
+    public boolean isExecutable() {
+        return mIsExecutable;
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeInt(mSizeBytes);
+        dest.writeInt(mSizeBytesFree);
+        dest.writeInt(mIsReadable ? 1 : 0);
+        dest.writeInt(mIsWritable ? 1 : 0);
+        dest.writeInt(mIsExecutable ? 1 : 0);
+    }
+
+    public MemoryRegion(Parcel source) {
+        mSizeBytes = source.readInt();
+        mSizeBytesFree = source.readInt();
+        mIsReadable = source.readInt() != 0;
+        mIsWritable = source.readInt() != 0;
+        mIsExecutable = source.readInt() != 0;
+    }
+
+    public static final Parcelable.Creator<MemoryRegion> CREATOR
+            = new Parcelable.Creator<MemoryRegion>() {
+        public MemoryRegion createFromParcel(Parcel in) {
+            return new MemoryRegion(in);
+        }
+
+        public MemoryRegion[] newArray(int size) {
+            return new MemoryRegion[size];
+        }
+    };
+
+}
diff --git a/core/java/android/hardware/location/NanoApp.aidl b/core/java/android/hardware/location/NanoApp.aidl
new file mode 100644
index 0000000..d32c44a
--- /dev/null
+++ b/core/java/android/hardware/location/NanoApp.aidl
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.location;
+/*
+@hide
+*/
+parcelable NanoApp;
diff --git a/core/java/android/hardware/location/NanoApp.java b/core/java/android/hardware/location/NanoApp.java
new file mode 100644
index 0000000..36d181f
--- /dev/null
+++ b/core/java/android/hardware/location/NanoApp.java
@@ -0,0 +1,290 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.location;
+
+
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * A class describing nano apps.
+ * A nano app is a piece of executable code that can be
+ * downloaded onto a specific architecture. These are targtted
+ * for low power compute domains on a device.
+ *
+ * Nano apps are expected to be used only by bundled apps only
+ * at this time.
+ *
+ * @hide
+ */
+@SystemApi
+public class NanoApp {
+    private String mPublisher;
+    private String mName;
+
+    private int mAppId;
+    private int mAppVersion;
+
+    private int mNeededReadMemBytes;
+    private int mNeededWriteMemBytes;
+    private int mNeededExecMemBytes;
+
+    private int[] mNeededSensors;
+    private int[] mOutputEvents;
+    private byte[] mAppBinary;
+
+    public NanoApp() {
+    }
+
+    /**
+     * Set the publisher name
+     *
+     * @param publisher name of the publisher of this nano app
+     */
+    public void setPublisher(String publisher) {
+        mPublisher = publisher;
+    }
+
+    /**
+     * set the name of the app
+     *
+     * @param name   name of the app
+     */
+    public void setName(String name) {
+        mName = name;
+    }
+
+    /**
+     * set the app identifier
+     *
+     * @param appId  add identifier
+     */
+    public void setAppId(int appId) {
+        mAppId = appId;
+    }
+
+    /**
+     * Set the app version
+     *
+     * @param appVersion app version
+     */
+    public void setAppVersion(int appVersion) {
+        mAppVersion = appVersion;
+    }
+
+    /**
+     * set memory needed as read only
+     *
+     * @param neededReadMemBytes
+     *               read only memory needed in bytes
+     */
+    public void setNeededReadMemBytes(int neededReadMemBytes) {
+        mNeededReadMemBytes = neededReadMemBytes;
+    }
+
+    /**
+     * set writable memory needed in bytes
+     *
+     * @param neededWriteMemBytes
+     *               writable memory needed in bytes
+     */
+    public void setNeededWriteMemBytes(int neededWriteMemBytes) {
+        mNeededWriteMemBytes = neededWriteMemBytes;
+    }
+
+    /**
+     * set executable memory needed
+     *
+     * @param neededExecMemBytes
+     *               executable memory needed in bytes
+     */
+    public void setNeededExecMemBytes(int neededExecMemBytes) {
+        mNeededExecMemBytes = neededExecMemBytes;
+    }
+
+    /**
+     * set the sensors needed for this app
+     *
+     * @param neededSensors
+     *               needed Sensors
+     */
+    public void setNeededSensors(int[] neededSensors) {
+        mNeededSensors = neededSensors;
+    }
+
+    public void setOutputEvents(int[] outputEvents) {
+        mOutputEvents = outputEvents;
+    }
+
+    /**
+     * set output events returned by the nano app
+     *
+     * @param appBinary generated events
+     */
+    public void setAppBinary(byte[] appBinary) {
+        mAppBinary = appBinary;
+    }
+
+
+    /**
+     * get the publisher name
+     *
+     * @return publisher name
+     */
+    public String getPublisher() {
+      return mPublisher;
+    }
+
+    /**
+     * get the name of the app
+     *
+     * @return app name
+     */
+    public String getName() {
+    return mName;
+    }
+
+    /**
+     * get the identifier of the app
+     *
+     * @return identifier for this app
+     */
+    public int getAppId() {
+      return mAppId;
+    }
+
+    /**
+     * get the app version
+     *
+     * @return app version
+     */
+    public int getAppVersion() {
+      return mAppVersion;
+    }
+
+    /**
+     * get the ammount of readable memory needed by this app
+     *
+     * @return readable memory needed in bytes
+     */
+    public int getNeededReadMemBytes() {
+      return mNeededReadMemBytes;
+    }
+
+    /**
+     * get the ammount og writable memory needed in bytes
+     *
+     * @return writable memory needed in bytes
+     */
+    public int getNeededWriteMemBytes() {
+      return mNeededWriteMemBytes;
+    }
+
+    /**
+     * executable memory needed in bytes
+     *
+     * @return executable memory needed in bytes
+     */
+    public int getNeededExecMemBytes() {
+      return mNeededExecMemBytes;
+    }
+
+    /**
+     * get the sensors needed by this app
+     *
+     * @return sensors needed
+     */
+    public int[] getNeededSensors() {
+      return mNeededSensors;
+    }
+
+    /**
+     * get the events generated by this app
+     *
+     * @return generated events
+     */
+    public int[] getOutputEvents() {
+      return mOutputEvents;
+    }
+
+    /**
+     * get the binary for this app
+     *
+     * @return app binary
+     */
+    public byte[] getAppBinary() {
+      return mAppBinary;
+    }
+
+    private NanoApp(Parcel in) {
+        mPublisher = in.readString();
+        mName = in.readString();
+
+        mAppId = in.readInt();
+        mAppVersion = in.readInt();
+        mNeededReadMemBytes = in.readInt();
+        mNeededWriteMemBytes = in.readInt();
+        mNeededExecMemBytes = in.readInt();
+
+        int mNeededSensorsLength = in.readInt();
+        mNeededSensors = new int[mNeededSensorsLength];
+        in.readIntArray(mNeededSensors);
+
+        int mOutputEventsLength = in.readInt();
+        mOutputEvents = new int[mOutputEventsLength];
+        in.readIntArray(mOutputEvents);
+
+        int binaryLength = in.readInt();
+        mAppBinary = new byte[binaryLength];
+        in.readByteArray(mAppBinary);
+    }
+
+    public int describeContents() {
+        return 0;
+    }
+
+    public void writeToParcel(Parcel out, int flags) {
+        out.writeString(mPublisher);
+        out.writeString(mName);
+        out.writeInt(mAppId);
+        out.writeInt(mAppVersion);
+        out.writeInt(mNeededReadMemBytes);
+        out.writeInt(mNeededWriteMemBytes);
+        out.writeInt(mNeededExecMemBytes);
+
+        out.writeInt(mNeededSensors.length);
+        out.writeIntArray(mNeededSensors);
+
+        out.writeInt(mOutputEvents.length);
+        out.writeIntArray(mOutputEvents);
+
+        out.writeInt(mAppBinary.length);
+        out.writeByteArray(mAppBinary);
+    }
+
+    public static final Parcelable.Creator<NanoApp> CREATOR
+            = new Parcelable.Creator<NanoApp>() {
+        public NanoApp createFromParcel(Parcel in) {
+            return new NanoApp(in);
+        }
+
+        public NanoApp[] newArray(int size) {
+            return new NanoApp[size];
+        }
+    };
+}
diff --git a/core/java/android/hardware/location/NanoAppFilter.aidl b/core/java/android/hardware/location/NanoAppFilter.aidl
new file mode 100644
index 0000000..cc6d475
--- /dev/null
+++ b/core/java/android/hardware/location/NanoAppFilter.aidl
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.location;
+/*
+@hide
+*/
+parcelable NanoAppFilter;
diff --git a/core/java/android/hardware/location/NanoAppFilter.java b/core/java/android/hardware/location/NanoAppFilter.java
new file mode 100644
index 0000000..ac341e4
--- /dev/null
+++ b/core/java/android/hardware/location/NanoAppFilter.java
@@ -0,0 +1,139 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.location;
+
+
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * @hide
+ */
+@SystemApi
+public class NanoAppFilter {
+
+    // The appId, can be set to APP_ID_ANY
+    private long mAppId;
+
+    // Version to filter apps
+    private int mAppVersion;
+
+    // filtering spec for version
+    private int mVersionRestrictionMask;
+
+    // If APP_ID is any, then a match is performef with the vendor mask
+    private long mAppIdVendorMask;
+
+    // Id of the context hub this instance is expected on
+    private int mContextHubId;
+
+    /**
+     * Flag indicating any version. With this flag set, all versions shall match provided version.
+     */
+    public static final int FLAGS_VERSION_ANY = -1;
+    /**
+     * If this flag is set, only versions strictly greater than the version specified shall match.
+     */
+    public static final int FLAGS_VERSION_GREAT_THAN  = 2;
+    /**
+     * If this flag is set, only versions strictly less than the version specified shall match.
+     */
+    public static final int FLAGS_VERSION_LESS_THAN   = 4;
+    public static final int FLAGS_VERSION_STRICTLY_EQUAL = 8;
+
+    /**
+     * If this flag is set, only versions strictly equal to the version specified shall match.
+     */
+    public static final int APP_ANY = -1;
+
+    /**
+     * If this flag is set, all vendors shall match.
+     */
+    public static final int VENDOR_ANY = -1;
+
+    /**
+     * If this flag is set, any hub shall match.
+     */
+    public static final int HUB_ANY = -1;
+
+    private NanoAppFilter(Parcel in) {
+        mAppId = in.readLong();
+        mAppVersion = in.readInt();
+        mVersionRestrictionMask = in.readInt();
+        mAppIdVendorMask = in.readInt();
+    }
+
+    public int describeContents() {
+        return 0;
+    }
+
+    public void writeToParcel(Parcel out, int flags) {
+
+        out.writeLong(mAppId);
+        out.writeInt(mAppVersion);
+        out.writeInt(mVersionRestrictionMask);
+        out.writeLong(mAppIdVendorMask);
+    }
+
+    /**
+     * Create a filter
+     *
+     * @param appId       application id
+     * @param appVersion  application version
+     * @param versionMask version
+     * @param vendorMask  vendor
+     */
+    public NanoAppFilter(long appId, int appVersion, int versionMask, long vendorMask) {
+        mAppId = appId;
+        mAppVersion = appVersion;
+        mVersionRestrictionMask = versionMask;
+        mAppIdVendorMask = vendorMask;
+    }
+
+    private boolean versionsMatch(int versionRestrictionMask, int expected, int actual){
+        // some refactoring of version restriction mask is needed, until then, return all
+        return true;
+    }
+    /**
+     *
+     * @param nano app instance info
+     *
+     * @return true if this is a match, false otherwise
+     */
+    public boolean testMatch(NanoAppInstanceInfo info) {
+        if ((mContextHubId == HUB_ANY || info.getContexthubId() == mContextHubId) &&
+                (mAppId == APP_ANY || info.getAppId() == mAppId) &&
+               // (mAppIdVendorMask == VENDOR_ANY) TODO : Expose Vendor mask cleanly
+                (versionsMatch(mVersionRestrictionMask, mAppVersion, info.getAppVersion()))) {
+            return true;
+        } else {
+            return false;
+        }
+    }
+
+    public static final Parcelable.Creator<NanoAppFilter> CREATOR
+            = new Parcelable.Creator<NanoAppFilter>() {
+        public NanoAppFilter createFromParcel(Parcel in) {
+            return new NanoAppFilter(in);
+        }
+
+        public NanoAppFilter[] newArray(int size) {
+            return new NanoAppFilter[size];
+        }
+    };
+}
diff --git a/core/java/android/hardware/location/NanoAppInstanceInfo.aidl b/core/java/android/hardware/location/NanoAppInstanceInfo.aidl
new file mode 100644
index 0000000..c8c40d7
--- /dev/null
+++ b/core/java/android/hardware/location/NanoAppInstanceInfo.aidl
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.location;
+/*
+@hide
+*/
+parcelable NanoAppInstanceInfo;
\ No newline at end of file
diff --git a/core/java/android/hardware/location/NanoAppInstanceInfo.java b/core/java/android/hardware/location/NanoAppInstanceInfo.java
new file mode 100644
index 0000000..ac62919
--- /dev/null
+++ b/core/java/android/hardware/location/NanoAppInstanceInfo.java
@@ -0,0 +1,302 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.location;
+
+
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * @hide
+ */
+@SystemApi
+public class NanoAppInstanceInfo {
+    private String mPublisher;
+    private String mName;
+
+    private int mAppId;
+    private int mAppVersion;
+
+    private int mNeededReadMemBytes;
+    private int mNeededWriteMemBytes;
+    private int mNeededExecMemBytes;
+
+    private int[] mNeededSensors;
+    private int[] mOutputEvents;
+
+    private int mContexthubId;
+    private int mHandle;
+
+    public NanoAppInstanceInfo() {
+    }
+
+    /**
+     * get the publisher of this app
+     *
+     * @return String - name of the publisher
+     */
+    public String getPublisher() {
+        return mPublisher;
+    }
+
+
+    /**
+     * set the publisher name for the app
+     *
+     * @param publisher - name of the publisher
+     */
+    public void setPublisher(String publisher) {
+        mPublisher = publisher;
+    }
+
+    /**
+     * get the name of the app
+     *
+     * @return String - name of the app
+     */
+    public String getName() {
+        return mName;
+    }
+
+    /**
+     * set the name of the app
+     *
+     * @param name - name of the app
+     */
+    public void setName(String name) {
+        mName = name;
+    }
+
+    /**
+     * Get the application identifier
+     *
+     * @return int - application identifier
+     */
+    public int getAppId() {
+        return mAppId;
+    }
+
+    /**
+     * Set the application identifier
+     *
+     * @param appId - application identifier
+     */
+    public void setAppId(int appId) {
+        mAppId = appId;
+    }
+
+    /**
+     * Set the application version
+     *
+     * @return int - version of the app
+     */
+    public int getAppVersion() {
+        return mAppVersion;
+    }
+
+    /**
+     * Set the application version
+     *
+     * @param appVersion - version of the app
+     */
+    public void setAppVersion(int appVersion) {
+        mAppVersion = appVersion;
+    }
+
+    /**
+     * Get the read memory needed by the app
+     *
+     * @return int - readable memory needed in bytes
+     */
+    public int getNeededReadMemBytes() {
+        return mNeededReadMemBytes;
+    }
+
+    /**
+     * Set the read memory needed by the app
+     *
+     * @param neededReadMemBytes - readable Memory needed in bytes
+     */
+    public void setNeededReadMemBytes(int neededReadMemBytes) {
+        mNeededReadMemBytes = neededReadMemBytes;
+    }
+
+    /**
+     *  get writable memory needed by the app
+     *
+     * @return int - writable memory needed by the app
+     */
+    public int getNeededWriteMemBytes() {
+        return mNeededWriteMemBytes;
+    }
+
+    /**
+     * set writable memory needed by the app
+     *
+     * @param neededWriteMemBytes - writable memory needed by the
+     *                            app
+     */
+    public void setNeededWriteMemBytes(int neededWriteMemBytes) {
+        mNeededWriteMemBytes = neededWriteMemBytes;
+    }
+
+    /**
+     * get executable memory needed by the app
+     *
+     * @return int - executable memory needed by the app
+     */
+    public int getNeededExecMemBytes() {
+        return mNeededExecMemBytes;
+    }
+
+    /**
+     * set executable memory needed by the app
+     *
+     * @param neededExecMemBytes - executable memory needed by the
+     *                           app
+     */
+    public void setNeededExecMemBytes(int neededExecMemBytes) {
+        mNeededExecMemBytes = neededExecMemBytes;
+    }
+
+    /**
+     * Get the sensors needed by this app
+     *
+     * @return int[] all the required sensors needed by this app
+     */
+    public int[] getNeededSensors() {
+        return mNeededSensors;
+    }
+
+    /**
+     * set the sensors needed by this app
+     *
+     * @param neededSensors - all the sensors needed by this app
+     */
+    public void setNeededSensors(int[] neededSensors) {
+        mNeededSensors = neededSensors;
+    }
+
+    /**
+     * get the events generated by this app
+     *
+     * @return all the events that can be generated by this app
+     */
+    public int[] getOutputEvents() {
+        return mOutputEvents;
+    }
+
+    /**
+     * set the output events that can be generated by this app
+     *
+     * @param outputEvents - the events that may be generated by
+     *                     this app
+     */
+    public void setOutputEvents(int[] outputEvents) {
+        mOutputEvents = outputEvents;
+    }
+
+    /**
+     * get the context hub identifier
+     *
+     * @return int - system unique hub identifier
+     */
+    public int getContexthubId() {
+        return mContexthubId;
+    }
+
+    /**
+     * set the context hub identifier
+     *
+     * @param contexthubId - system wide unique identifier
+     */
+    public void setContexthubId(int contexthubId) {
+        mContexthubId = contexthubId;
+    }
+
+    /**
+     * get a handle to the nano app instance
+     *
+     * @return int - handle to this instance
+     */
+    public int getHandle() {
+        return mHandle;
+    }
+
+    /**
+     * set the handle for an app instance
+     *
+     * @param handle - handle to this instance
+     */
+    public void setHandle(int handle) {
+        mHandle = handle;
+    }
+
+
+    private NanoAppInstanceInfo(Parcel in) {
+        mPublisher = in.readString();
+        mName = in.readString();
+
+        mAppId = in.readInt();
+        mAppVersion = in.readInt();
+        mNeededReadMemBytes = in.readInt();
+        mNeededWriteMemBytes = in.readInt();
+        mNeededExecMemBytes = in.readInt();
+
+        int mNeededSensorsLength = in.readInt();
+        mNeededSensors = new int[mNeededSensorsLength];
+        in.readIntArray(mNeededSensors);
+
+        int mOutputEventsLength = in.readInt();
+        mOutputEvents = new int[mOutputEventsLength];
+        in.readIntArray(mOutputEvents);
+    }
+
+    public int describeContents() {
+        return 0;
+    }
+
+    public void writeToParcel(Parcel out, int flags) {
+        out.writeString(mPublisher);
+        out.writeString(mName);
+        out.writeInt(mAppId);
+        out.writeInt(mAppVersion);
+        out.writeInt(mContexthubId);
+        out.writeInt(mNeededReadMemBytes);
+        out.writeInt(mNeededWriteMemBytes);
+        out.writeInt(mNeededExecMemBytes);
+
+        out.writeInt(mNeededSensors.length);
+        out.writeIntArray(mNeededSensors);
+
+        out.writeInt(mOutputEvents.length);
+        out.writeIntArray(mOutputEvents);
+
+    }
+
+    public static final Parcelable.Creator<NanoAppInstanceInfo> CREATOR
+            = new Parcelable.Creator<NanoAppInstanceInfo>() {
+        public NanoAppInstanceInfo createFromParcel(Parcel in) {
+            return new NanoAppInstanceInfo(in);
+        }
+
+        public NanoAppInstanceInfo[] newArray(int size) {
+            return new NanoAppInstanceInfo[size];
+        }
+    };
+}
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index 92c721b..25b38c0 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -1189,7 +1189,7 @@
             return TYPE_NONE;
         }
 
-        // Do this only for SUPL, until GpsLocationProvider is fixed. http://b/25876485 .
+        // Do this only for SUPL, until GnssLocationProvider is fixed. http://b/25876485 .
         if (!netCap.hasCapability(NetworkCapabilities.NET_CAPABILITY_SUPL)) {
             // NOTE: if this causes app breakage, we should not just comment out this early return;
             // instead, we should make this early return conditional on the requesting app's target
@@ -3170,7 +3170,7 @@
 
     // Checks whether the calling app can use the legacy routing API (startUsingNetworkFeature,
     // stopUsingNetworkFeature, requestRouteToHost), and if not throw UnsupportedOperationException.
-    // TODO: convert the existing system users (Tethering, GpsLocationProvider) to the new APIs and
+    // TODO: convert the existing system users (Tethering, GnssLocationProvider) to the new APIs and
     // remove these exemptions. Note that this check is not secure, and apps can still access these
     // functions by accessing ConnectivityService directly. However, it should be clear that doing
     // so is unsupported and may break in the future. http://b/22728205
diff --git a/core/java/android/net/NetworkCapabilities.java b/core/java/android/net/NetworkCapabilities.java
index 3bd12c0..e27c0fb 100644
--- a/core/java/android/net/NetworkCapabilities.java
+++ b/core/java/android/net/NetworkCapabilities.java
@@ -197,6 +197,19 @@
             (1 << NET_CAPABILITY_CAPTIVE_PORTAL);
 
     /**
+     * Network specifier for factories which want to match any network specifier
+     * (NS) in a request. Behavior:
+     * <li>Empty NS in request matches any network factory NS</li>
+     * <li>Empty NS in the network factory NS only matches a request with an
+     * empty NS</li>
+     * <li>"*" (this constant) NS in the network factory matches requests with
+     * any NS</li>
+     *
+     * @hide
+     */
+    public static final String MATCH_ALL_REQUESTS_NETWORK_SPECIFIER = "*";
+
+    /**
      * Network capabilities that are not allowed in NetworkRequests. This exists because the
      * NetworkFactory / NetworkAgent model does not deal well with the situation where a
      * capability's presence cannot be known in advance. If such a capability is requested, then we
@@ -596,7 +609,8 @@
     }
     private boolean satisfiedBySpecifier(NetworkCapabilities nc) {
         return (TextUtils.isEmpty(mNetworkSpecifier) ||
-                mNetworkSpecifier.equals(nc.mNetworkSpecifier));
+                mNetworkSpecifier.equals(nc.mNetworkSpecifier) ||
+                MATCH_ALL_REQUESTS_NETWORK_SPECIFIER.equals(nc.mNetworkSpecifier));
     }
     private boolean equalsSpecifier(NetworkCapabilities nc) {
         if (TextUtils.isEmpty(mNetworkSpecifier)) {
diff --git a/core/java/android/net/NetworkRequest.java b/core/java/android/net/NetworkRequest.java
index 7da4818..f1edcbe 100644
--- a/core/java/android/net/NetworkRequest.java
+++ b/core/java/android/net/NetworkRequest.java
@@ -188,6 +188,10 @@
          *                         networks.
          */
         public Builder setNetworkSpecifier(String networkSpecifier) {
+            if (NetworkCapabilities.MATCH_ALL_REQUESTS_NETWORK_SPECIFIER.equals(networkSpecifier)) {
+                throw new IllegalArgumentException("Invalid network specifier - must not be '"
+                        + NetworkCapabilities.MATCH_ALL_REQUESTS_NETWORK_SPECIFIER + "'");
+            }
             mNetworkCapabilities.setNetworkSpecifier(networkSpecifier);
             return this;
         }
diff --git a/core/java/android/net/Uri.java b/core/java/android/net/Uri.java
index 4a8dfbc..53b027b 100644
--- a/core/java/android/net/Uri.java
+++ b/core/java/android/net/Uri.java
@@ -2343,7 +2343,7 @@
      */
     public void checkFileUriExposed(String location) {
         if ("file".equals(getScheme())) {
-            StrictMode.onFileUriExposed(location);
+            StrictMode.onFileUriExposed(this, location);
         }
     }
 
diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java
index 9180506..52fa2ed 100644
--- a/core/java/android/os/BatteryStats.java
+++ b/core/java/android/os/BatteryStats.java
@@ -201,9 +201,14 @@
     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 GLOBAL_WIFI_CONTROLLER_DATA = "gwfcd";
+    private static final String WIFI_CONTROLLER_DATA = "wfcd";
+    private static final String GLOBAL_BLUETOOTH_CONTROLLER_DATA = "gble";
+    private static final String BLUETOOTH_CONTROLLER_DATA = "ble";
     private static final String MISC_DATA = "m";
     private static final String GLOBAL_NETWORK_DATA = "gn";
+    private static final String GLOBAL_MODEM_CONTROLLER_DATA = "gmcd";
+    private static final String MODEM_CONTROLLER_DATA = "mcd";
     private static final String HISTORY_STRING_POOL = "hsp";
     private static final String HISTORY_DATA = "h";
     private static final String SCREEN_BRIGHTNESS_DATA = "br";
@@ -271,6 +276,39 @@
     }
 
     /**
+     * Container class that aggregates counters for transmit, receive, and idle state of a
+     * radio controller.
+     */
+    public static abstract class ControllerActivityCounter {
+        /**
+         * @return a non-null {@link LongCounter} representing time spent (milliseconds) in the
+         * idle state.
+         */
+        public abstract LongCounter getIdleTimeCounter();
+
+        /**
+         * @return a non-null {@link LongCounter} representing time spent (milliseconds) in the
+         * receive state.
+         */
+        public abstract LongCounter getRxTimeCounter();
+
+        /**
+         * An array of {@link LongCounter}, representing various transmit levels, where each level
+         * may draw a different amount of power. The levels themselves are controller-specific.
+         * @return non-null array of {@link LongCounter}s representing time spent (milliseconds) in
+         * various transmit level states.
+         */
+        public abstract LongCounter[] getTxTimeCounters();
+
+        /**
+         * @return a non-null {@link LongCounter} representing the power consumed by the controller
+         * in all states, measured in milli-ampere-milliseconds (mAms). The counter may always
+         * yield a value of 0 if the device doesn't support power calculations.
+         */
+        public abstract LongCounter getPowerCounter();
+    }
+
+    /**
      * State for keeping track of timing information.
      */
     public static abstract class Timer {
@@ -367,25 +405,9 @@
          */
         public abstract ArrayMap<String, ? extends Pkg> getPackageStats();
 
-        /**
-         * Returns the time in milliseconds that this app kept the WiFi controller in the
-         * specified state <code>type</code>.
-         * @param type one of {@link #CONTROLLER_IDLE_TIME}, {@link #CONTROLLER_RX_TIME}, or
-         *             {@link #CONTROLLER_TX_TIME}.
-         * @param which one of {@link #STATS_CURRENT}, {@link #STATS_SINCE_CHARGED}, or
-         *              {@link #STATS_SINCE_UNPLUGGED}.
-         */
-        public abstract long getWifiControllerActivity(int type, int which);
-
-        /**
-         * Returns the time in milliseconds that this app kept the Bluetooth controller in the
-         * specified state <code>type</code>.
-         * @param type one of {@link #CONTROLLER_IDLE_TIME}, {@link #CONTROLLER_RX_TIME}, or
-         *             {@link #CONTROLLER_TX_TIME}.
-         * @param which one of {@link #STATS_CURRENT}, {@link #STATS_SINCE_CHARGED}, or
-         *              {@link #STATS_SINCE_UNPLUGGED}.
-         */
-        public abstract long getBluetoothControllerActivity(int type, int which);
+        public abstract ControllerActivityCounter getWifiControllerActivity();
+        public abstract ControllerActivityCounter getBluetoothControllerActivity();
+        public abstract ControllerActivityCounter getModemControllerActivity();
 
         /**
          * {@hide}
@@ -2031,43 +2053,47 @@
     public abstract long getNetworkActivityBytes(int type, int which);
     public abstract long getNetworkActivityPackets(int type, int which);
 
-    public static final int CONTROLLER_IDLE_TIME = 0;
-    public static final int CONTROLLER_RX_TIME = 1;
-    public static final int CONTROLLER_TX_TIME = 2;
-    public static final int CONTROLLER_POWER_DRAIN = 3;
-    public static final int NUM_CONTROLLER_ACTIVITY_TYPES = CONTROLLER_POWER_DRAIN + 1;
-
-    /**
-     * Returns true if the BatteryStats object has detailed bluetooth power reports.
-     * When true, calling {@link #getBluetoothControllerActivity(int, int)} will yield the
-     * actual power data.
-     */
-    public abstract boolean hasBluetoothActivityReporting();
-
-    /**
-     * For {@link #CONTROLLER_IDLE_TIME}, {@link #CONTROLLER_RX_TIME}, and
-     * {@link #CONTROLLER_TX_TIME}, returns the time spent (in milliseconds) in the
-     * respective state.
-     * For {@link #CONTROLLER_POWER_DRAIN}, returns the power used by the controller in
-     * milli-ampere-milliseconds (mAms).
-     */
-    public abstract long getBluetoothControllerActivity(int type, int which);
-
     /**
      * Returns true if the BatteryStats object has detailed WiFi power reports.
-     * When true, calling {@link #getWifiControllerActivity(int, int)} will yield the
+     * When true, calling {@link #getWifiControllerActivity()} will yield the
      * actual power data.
      */
     public abstract boolean hasWifiActivityReporting();
 
     /**
-     * For {@link #CONTROLLER_IDLE_TIME}, {@link #CONTROLLER_RX_TIME}, and
-     * {@link #CONTROLLER_TX_TIME}, returns the time spent (in milliseconds) in the
-     * respective state.
-     * For {@link #CONTROLLER_POWER_DRAIN}, returns the power used by the controller in
-     * milli-ampere-milliseconds (mAms).
+     * Returns a {@link ControllerActivityCounter} which is an aggregate of the times spent
+     * in various radio controller states, such as transmit, receive, and idle.
+     * @return non-null {@link ControllerActivityCounter}
      */
-    public abstract long getWifiControllerActivity(int type, int which);
+    public abstract ControllerActivityCounter getWifiControllerActivity();
+
+    /**
+     * Returns true if the BatteryStats object has detailed bluetooth power reports.
+     * When true, calling {@link #getBluetoothControllerActivity()} will yield the
+     * actual power data.
+     */
+    public abstract boolean hasBluetoothActivityReporting();
+
+    /**
+     * Returns a {@link ControllerActivityCounter} which is an aggregate of the times spent
+     * in various radio controller states, such as transmit, receive, and idle.
+     * @return non-null {@link ControllerActivityCounter}
+     */
+    public abstract ControllerActivityCounter getBluetoothControllerActivity();
+
+    /**
+     * Returns true if the BatteryStats object has detailed modem power reports.
+     * When true, calling {@link #getModemControllerActivity()} will yield the
+     * actual power data.
+     */
+    public abstract boolean hasModemActivityReporting();
+
+    /**
+     * Returns a {@link ControllerActivityCounter} which is an aggregate of the times spent
+     * in various radio controller states, such as transmit, receive, and idle.
+     * @return non-null {@link ControllerActivityCounter}
+     */
+    public abstract ControllerActivityCounter getModemControllerActivity();
 
     /**
      * Return the wall clock time when battery stats data collection started.
@@ -2496,6 +2522,17 @@
         return ",";
     }
     
+    private static final void dumpLineHeader(PrintWriter pw, int uid, String category,
+                                             String type) {
+        pw.print(BATTERY_STATS_CHECKIN_VERSION);
+        pw.print(',');
+        pw.print(uid);
+        pw.print(',');
+        pw.print(category);
+        pw.print(',');
+        pw.print(type);
+    }
+
     /**
      * Dump a comma-separated line of values for terse checkin mode.
      * 
@@ -2506,14 +2543,7 @@
      */
     private static final void dumpLine(PrintWriter pw, int uid, String category, String type, 
            Object... args ) {
-        pw.print(BATTERY_STATS_CHECKIN_VERSION);
-        pw.print(',');
-        pw.print(uid);
-        pw.print(',');
-        pw.print(category);
-        pw.print(',');
-        pw.print(type);
-
+        dumpLineHeader(pw, uid, category, type);
         for (Object arg : args) {
             pw.print(',');
             pw.print(arg);
@@ -2546,6 +2576,140 @@
     }
 
     /**
+     * Checks if the ControllerActivityCounter has any data worth dumping.
+     */
+    private static boolean controllerActivityHasData(ControllerActivityCounter counter, int which) {
+        if (counter == null) {
+            return false;
+        }
+
+        if (counter.getIdleTimeCounter().getCountLocked(which) != 0
+                || counter.getRxTimeCounter().getCountLocked(which) != 0
+                || counter.getPowerCounter().getCountLocked(which) != 0) {
+            return true;
+        }
+
+        for (LongCounter c : counter.getTxTimeCounters()) {
+            if (c.getCountLocked(which) != 0) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Dumps the ControllerActivityCounter if it has any data worth dumping.
+     * The order of the arguments in the final check in line is:
+     *
+     * idle, rx, power, tx...
+     *
+     * where tx... is one or more transmit level times.
+     */
+    private static final void dumpControllerActivityLine(PrintWriter pw, int uid, String category,
+                                                         String type,
+                                                         ControllerActivityCounter counter,
+                                                         int which) {
+        if (!controllerActivityHasData(counter, which)) {
+            return;
+        }
+
+        dumpLineHeader(pw, uid, category, type);
+        pw.print(",");
+        pw.print(counter.getIdleTimeCounter().getCountLocked(which));
+        pw.print(",");
+        pw.print(counter.getRxTimeCounter().getCountLocked(which));
+        pw.print(",");
+        pw.print(counter.getPowerCounter().getCountLocked(which) / (1000 * 60 * 60));
+        for (LongCounter c : counter.getTxTimeCounters()) {
+            pw.print(",");
+            pw.print(c.getCountLocked(which));
+        }
+        pw.println();
+    }
+
+    private final void printControllerActivityIfInteresting(PrintWriter pw, StringBuilder sb,
+                                                            String prefix, String controllerName,
+                                                            ControllerActivityCounter counter,
+                                                            int which) {
+        if (controllerActivityHasData(counter, which)) {
+            printControllerActivity(pw, sb, prefix, controllerName, counter, which);
+        }
+    }
+
+    private final void printControllerActivity(PrintWriter pw, StringBuilder sb, String prefix,
+                                               String controllerName,
+                                               ControllerActivityCounter counter, int which) {
+        final long idleTimeMs = counter.getIdleTimeCounter().getCountLocked(which);
+        final long rxTimeMs = counter.getRxTimeCounter().getCountLocked(which);
+        final long powerDrainMaMs = counter.getPowerCounter().getCountLocked(which);
+        long totalTxTimeMs = 0;
+        for (LongCounter txState : counter.getTxTimeCounters()) {
+            totalTxTimeMs += txState.getCountLocked(which);
+        }
+
+        final long totalTimeMs = idleTimeMs + rxTimeMs + totalTxTimeMs;
+
+        sb.setLength(0);
+        sb.append(prefix);
+        sb.append("  ");
+        sb.append(controllerName);
+        sb.append(" Idle time:   ");
+        formatTimeMs(sb, idleTimeMs);
+        sb.append("(");
+        sb.append(formatRatioLocked(idleTimeMs, totalTimeMs));
+        sb.append(")");
+        pw.println(sb.toString());
+
+        sb.setLength(0);
+        sb.append(prefix);
+        sb.append("  ");
+        sb.append(controllerName);
+        sb.append(" Rx time:     ");
+        formatTimeMs(sb, rxTimeMs);
+        sb.append("(");
+        sb.append(formatRatioLocked(rxTimeMs, totalTimeMs));
+        sb.append(")");
+        pw.println(sb.toString());
+
+        sb.setLength(0);
+        sb.append(prefix);
+        sb.append("  ");
+        sb.append(controllerName);
+        sb.append(" Tx time:     ");
+        formatTimeMs(sb, totalTxTimeMs);
+        sb.append("(");
+        sb.append(formatRatioLocked(totalTxTimeMs, totalTimeMs));
+        sb.append(")");
+        pw.println(sb.toString());
+
+        final int numTxLvls = counter.getTxTimeCounters().length;
+        if (numTxLvls > 1) {
+            for (int lvl = 0; lvl < numTxLvls; lvl++) {
+                final long txLvlTimeMs = counter.getTxTimeCounters()[lvl].getCountLocked(which);
+                sb.setLength(0);
+                sb.append(prefix);
+                sb.append("    [");
+                sb.append(lvl);
+                sb.append("] ");
+                formatTimeMs(sb, txLvlTimeMs);
+                sb.append("(");
+                sb.append(formatRatioLocked(txLvlTimeMs, totalTxTimeMs));
+                sb.append(")");
+                pw.println(sb.toString());
+            }
+        }
+
+        sb.setLength(0);
+        sb.append(prefix);
+        sb.append("  ");
+        sb.append(controllerName);
+        sb.append(" Power drain: ").append(
+                BatteryStatsHelper.makemAh(powerDrainMaMs / (double) (1000*60*60)));
+        sb.append("mAh");
+        pw.println(sb.toString());
+    }
+
+    /**
      * Temporary for settings.
      */
     public final void dumpCheckinLocked(Context context, PrintWriter pw, int which, int reqUid) {
@@ -2637,24 +2801,22 @@
                 mobileRxTotalBytes, mobileTxTotalBytes, wifiRxTotalBytes, wifiTxTotalBytes,
                 mobileRxTotalPackets, mobileTxTotalPackets, wifiRxTotalPackets, wifiTxTotalPackets);
 
+        // Dump Modem controller stats
+        dumpControllerActivityLine(pw, 0 /* uid */, category, GLOBAL_MODEM_CONTROLLER_DATA,
+                getModemControllerActivity(), which);
+
         // 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));
+        dumpLine(pw, 0 /* uid */, category, GLOBAL_WIFI_DATA, wifiOnTime / 1000,
+                wifiRunningTime / 1000, /* legacy fields follow, keep at 0 */ 0, 0, 0, 0);
+
+        dumpControllerActivityLine(pw, 0 /* uid */, category, GLOBAL_WIFI_CONTROLLER_DATA,
+                getWifiControllerActivity(), which);
 
         // 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));
+        dumpControllerActivityLine(pw, 0 /* uid */, category, GLOBAL_BLUETOOTH_CONTROLLER_DATA,
+                getBluetoothControllerActivity(), which);
 
         // Dump misc stats
         dumpLine(pw, 0 /* uid */, category, MISC_DATA,
@@ -2865,21 +3027,25 @@
                         mobileActiveTime, mobileActiveCount);
             }
 
+            // Dump modem controller data, per UID.
+            dumpControllerActivityLine(pw, uid, category, MODEM_CONTROLLER_DATA,
+                    u.getModemControllerActivity(), which);
+
+            // Dump Wifi controller data, per UID.
             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 || uidWifiIdleTimeMs != 0 || uidWifiRxTimeMs != 0
-                    || uidWifiTxTimeMs != 0) {
-                dumpLine(pw, uid, category, WIFI_DATA,
-                        fullWifiLockOnTime, wifiScanTime, uidWifiRunningTime, wifiScanCount,
-                        uidWifiIdleTimeMs, uidWifiRxTimeMs, uidWifiTxTimeMs);
+                    || uidWifiRunningTime != 0) {
+                dumpLine(pw, uid, category, WIFI_DATA, fullWifiLockOnTime, wifiScanTime,
+                        uidWifiRunningTime, wifiScanCount,
+                        /* legacy fields follow, keep at 0 */ 0, 0, 0, 0);
             }
 
+            dumpControllerActivityLine(pw, uid, category, WIFI_CONTROLLER_DATA,
+                    u.getWifiControllerActivity(), which);
+
             if (u.hasUserActivity()) {
                 args = new Object[Uid.NUM_USER_ACTIVITY_TYPES];
                 boolean hasData = false;
@@ -3409,6 +3575,8 @@
             pw.println(sb.toString());
         }
 
+        printControllerActivity(pw, sb, prefix, "Radio", getModemControllerActivity(), which);
+
         pw.print(prefix);
                 pw.print("  Wi-Fi total received: "); pw.print(formatBytesLocked(wifiRxTotalBytes));
                 pw.print(", sent: "); pw.print(formatBytesLocked(wifiTxTotalBytes));
@@ -3494,85 +3662,14 @@
         if (!didOne) sb.append(" (no activity)");
         pw.println(sb.toString());
 
-        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 wifiPowerDrainMaMs = getWifiControllerActivity(CONTROLLER_POWER_DRAIN, which);
-        final long wifiTotalTimeMs = wifiIdleTimeMs + wifiRxTimeMs + wifiTxTimeMs;
-
-        sb.setLength(0);
-        sb.append(prefix);
-        sb.append("  WiFi Idle time: "); formatTimeMs(sb, wifiIdleTimeMs);
-        sb.append("(");
-        sb.append(formatRatioLocked(wifiIdleTimeMs, wifiTotalTimeMs));
-        sb.append(")");
-        pw.println(sb.toString());
-
-        sb.setLength(0);
-        sb.append(prefix);
-        sb.append("  WiFi Rx time:   "); formatTimeMs(sb, wifiRxTimeMs);
-        sb.append("(");
-        sb.append(formatRatioLocked(wifiRxTimeMs, wifiTotalTimeMs));
-        sb.append(")");
-        pw.println(sb.toString());
-
-        sb.setLength(0);
-        sb.append(prefix);
-        sb.append("  WiFi Tx time:   "); formatTimeMs(sb, wifiTxTimeMs);
-        sb.append("(");
-        sb.append(formatRatioLocked(wifiTxTimeMs, wifiTotalTimeMs));
-        sb.append(")");
-        pw.println(sb.toString());
-
-        sb.setLength(0);
-        sb.append(prefix);
-        sb.append("  WiFi Power drain: ").append(
-                BatteryStatsHelper.makemAh(wifiPowerDrainMaMs / (double) (1000*60*60)));
-        sb.append("mAh");
-        pw.println(sb.toString());
+        printControllerActivity(pw, sb, prefix, "WiFi", getWifiControllerActivity(), which);
 
         pw.print(prefix);
         pw.print("  Bluetooth total received: "); pw.print(formatBytesLocked(btRxTotalBytes));
         pw.print(", sent: "); pw.println(formatBytesLocked(btTxTotalBytes));
 
-        final long bluetoothIdleTimeMs =
-                getBluetoothControllerActivity(CONTROLLER_IDLE_TIME, which);
-        final long bluetoothRxTimeMs = getBluetoothControllerActivity(CONTROLLER_RX_TIME, which);
-        final long bluetoothTxTimeMs = getBluetoothControllerActivity(CONTROLLER_TX_TIME, which);
-        final long bluetoothTotalTimeMs = bluetoothIdleTimeMs + bluetoothRxTimeMs +
-                bluetoothTxTimeMs;
-
-        sb.setLength(0);
-        sb.append(prefix);
-        sb.append("  Bluetooth Idle time: "); formatTimeMs(sb, bluetoothIdleTimeMs);
-        sb.append("(");
-        sb.append(formatRatioLocked(bluetoothIdleTimeMs, bluetoothTotalTimeMs));
-        sb.append(")");
-        pw.println(sb.toString());
-
-        sb.setLength(0);
-        sb.append(prefix);
-        sb.append("  Bluetooth Rx time:   "); formatTimeMs(sb, bluetoothRxTimeMs);
-        sb.append("(");
-        sb.append(formatRatioLocked(bluetoothRxTimeMs, bluetoothTotalTimeMs));
-        sb.append(")");
-        pw.println(sb.toString());
-
-        sb.setLength(0);
-        sb.append(prefix);
-        sb.append("  Bluetooth Tx time:   "); formatTimeMs(sb, bluetoothTxTimeMs);
-        sb.append("(");
-        sb.append(formatRatioLocked(bluetoothTxTimeMs, bluetoothTotalTimeMs));
-        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());
+        printControllerActivity(pw, sb, prefix, "Bluetooth", getBluetoothControllerActivity(),
+                which);
 
         pw.println();
 
@@ -3897,6 +3994,9 @@
                 pw.println(sb.toString());
             }
 
+            printControllerActivityIfInteresting(pw, sb, prefix + "  ", "Modem",
+                    u.getModemControllerActivity(), which);
+
             if (wifiRxBytes > 0 || wifiTxBytes > 0 || wifiRxPackets > 0 || wifiTxPackets > 0) {
                 pw.print(prefix); pw.print("    Wi-Fi network: ");
                         pw.print(formatBytesLocked(wifiRxBytes)); pw.print(" received, ");
@@ -3925,26 +4025,8 @@
                 pw.println(sb.toString());
             }
 
-            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);
-            final long uidWifiTotalTimeMs = uidWifiIdleTimeMs + uidWifiRxTimeMs + uidWifiTxTimeMs;
-            if (uidWifiTotalTimeMs > 0) {
-                sb.setLength(0);
-                sb.append(prefix).append("    WiFi Idle time: ");
-                formatTimeMs(sb, uidWifiIdleTimeMs);
-                sb.append("(").append(formatRatioLocked(uidWifiIdleTimeMs, uidWifiTotalTimeMs))
-                        .append(")\n");
-
-                sb.append(prefix).append("    WiFi Rx time:   "); formatTimeMs(sb, uidWifiRxTimeMs);
-                sb.append("(").append(formatRatioLocked(uidWifiRxTimeMs, uidWifiTotalTimeMs))
-                        .append(")\n");
-
-                sb.append(prefix).append("    WiFi Tx time:   "); formatTimeMs(sb, uidWifiTxTimeMs);
-                sb.append("(").append(formatRatioLocked(uidWifiTxTimeMs, uidWifiTotalTimeMs))
-                        .append(")");
-                pw.println(sb.toString());
-            }
+            printControllerActivityIfInteresting(pw, sb, prefix + "  ", "WiFi",
+                    u.getWifiControllerActivity(), which);
 
             if (btRxBytes > 0 || btTxBytes > 0) {
                 pw.print(prefix); pw.print("    Bluetooth network: ");
@@ -3953,30 +4035,6 @@
                 pw.println(" sent");
             }
 
-            final long uidBtIdleTimeMs = u.getBluetoothControllerActivity(CONTROLLER_IDLE_TIME,
-                    which);
-            final long uidBtRxTimeMs = u.getBluetoothControllerActivity(CONTROLLER_RX_TIME, which);
-            final long uidBtTxTimeMs = u.getBluetoothControllerActivity(CONTROLLER_TX_TIME, which);
-            final long uidBtTotalTimeMs = uidBtIdleTimeMs + uidBtRxTimeMs + uidBtTxTimeMs;
-            if (uidBtTotalTimeMs > 0) {
-                sb.setLength(0);
-                sb.append(prefix).append("    Bluetooth Idle time: ");
-                formatTimeMs(sb, uidBtIdleTimeMs);
-                sb.append("(").append(formatRatioLocked(uidBtIdleTimeMs, uidBtTotalTimeMs))
-                        .append(")\n");
-
-                sb.append(prefix).append("    Bluetooth Rx time:   ");
-                formatTimeMs(sb, uidBtRxTimeMs);
-                sb.append("(").append(formatRatioLocked(uidBtRxTimeMs, uidBtTotalTimeMs))
-                        .append(")\n");
-
-                sb.append(prefix).append("    Bluetooth Tx time:   ");
-                formatTimeMs(sb, uidBtTxTimeMs);
-                sb.append("(").append(formatRatioLocked(uidBtTxTimeMs, uidBtTotalTimeMs))
-                        .append(")");
-                pw.println(sb.toString());
-            }
-
             if (u.hasUserActivity()) {
                 boolean hasData = false;
                 for (int i=0; i<Uid.NUM_USER_ACTIVITY_TYPES; i++) {
diff --git a/core/java/android/os/FileUriExposedException.java b/core/java/android/os/FileUriExposedException.java
new file mode 100644
index 0000000..e47abe2
--- /dev/null
+++ b/core/java/android/os/FileUriExposedException.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os;
+
+import android.content.Intent;
+
+/**
+ * The exception that is thrown when an application exposes a {@code file://}
+ * {@link android.net.Uri} to another app.
+ * <p>
+ * This exposure is discouraged since the receiving app may not have access to
+ * the shared path. For example, the receiving app may not have requested the
+ * {@link android.Manifest.permission#READ_EXTERNAL_STORAGE} runtime permission,
+ * or the platform may be sharing the {@link android.net.Uri} across user
+ * profile boundaries.
+ * <p>
+ * Instead, apps should use {@code content://} Uris so the platform can extend
+ * temporary permission for the receiving app to access the resource.
+ * <p>
+ * This is only thrown for applications targeting {@link Build.VERSION_CODES#N}
+ * or higher. Applications targeting earlier SDK versions are allowed to share
+ * {@code file://} {@link android.net.Uri}, but it's strongly discouraged.
+ *
+ * @see android.support.v4.content.FileProvider
+ * @see Intent#FLAG_GRANT_READ_URI_PERMISSION
+ */
+public class FileUriExposedException extends RuntimeException {
+    public FileUriExposedException(String message) {
+        super(message);
+    }
+}
diff --git a/core/java/android/os/Process.java b/core/java/android/os/Process.java
index eca2c3b..1552a3c 100644
--- a/core/java/android/os/Process.java
+++ b/core/java/android/os/Process.java
@@ -150,6 +150,12 @@
     public static final int AUDIOSERVER_UID = 1041;
 
     /**
+     * Defines the UID/GID for the cameraserver process
+     * @hide
+     */
+    public static final int CAMERASERVER_UID = 1046;
+
+    /**
      * Defines the start of a range of UIDs (and GIDs), going from this
      * number to {@link #LAST_APPLICATION_UID} that are reserved for assigning
      * to applications.
diff --git a/core/java/android/os/StrictMode.java b/core/java/android/os/StrictMode.java
index f1672df..91d88da 100644
--- a/core/java/android/os/StrictMode.java
+++ b/core/java/android/os/StrictMode.java
@@ -24,6 +24,7 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.ServiceConnection;
+import android.net.Uri;
 import android.util.ArrayMap;
 import android.util.Log;
 import android.util.Printer;
@@ -46,7 +47,6 @@
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.HashMap;
-import java.util.Map;
 import java.util.concurrent.atomic.AtomicInteger;
 
 /**
@@ -243,42 +243,15 @@
 
     // Byte 3: Penalty
 
-    /**
-     * @hide
-     */
+    /** {@hide} */
     public static final int PENALTY_LOG = 0x01 << 16;  // normal android.util.Log
-
-    // Used for both process and thread policy:
-
-    /**
-     * @hide
-     */
+    /** {@hide} */
     public static final int PENALTY_DIALOG = 0x02 << 16;
-
-    /**
-     * Death on any detected violation.
-     *
-     * @hide
-     */
+    /** {@hide} */
     public static final int PENALTY_DEATH = 0x04 << 16;
-
-    /**
-     * Death just for detected network usage.
-     *
-     * @hide
-     */
-    public static final int PENALTY_DEATH_ON_NETWORK = 0x08 << 16;
-
-    /**
-     * Flash the screen during violations.
-     *
-     * @hide
-     */
+    /** {@hide} */
     public static final int PENALTY_FLASH = 0x10 << 16;
-
-    /**
-     * @hide
-     */
+    /** {@hide} */
     public static final int PENALTY_DROPBOX = 0x20 << 16;
 
     /**
@@ -294,12 +267,28 @@
      */
     public static final int PENALTY_GATHER = 0x40 << 16;
 
+    // Byte 4: Special cases
+
+    /**
+     * Death when network traffic is detected on main thread.
+     *
+     * @hide
+     */
+    public static final int PENALTY_DEATH_ON_NETWORK = 0x01 << 24;
+
     /**
      * Death when cleartext network traffic is detected.
      *
      * @hide
      */
-    public static final int PENALTY_DEATH_ON_CLEARTEXT_NETWORK = 0x80 << 16;
+    public static final int PENALTY_DEATH_ON_CLEARTEXT_NETWORK = 0x02 << 24;
+
+    /**
+     * Death when file exposure is detected.
+     *
+     * @hide
+     */
+    public static final int PENALTY_DEATH_ON_FILE_URI_EXPOSURE = 0x04 << 24;
 
     /**
      * Mask of all the penalty bits valid for thread policies.
@@ -312,7 +301,7 @@
      * Mask of all the penalty bits valid for VM policies.
      */
     private static final int VM_PENALTY_MASK = PENALTY_LOG | PENALTY_DEATH | PENALTY_DROPBOX
-            | PENALTY_DEATH_ON_CLEARTEXT_NETWORK;
+            | PENALTY_DEATH_ON_CLEARTEXT_NETWORK | PENALTY_DEATH_ON_FILE_URI_EXPOSURE;
 
     /** {@hide} */
     public static final int NETWORK_POLICY_ACCEPT = 0;
@@ -748,10 +737,22 @@
             }
 
             /**
-             * Detect when a {@code file://} {@link android.net.Uri} is exposed beyond this
-             * app. The receiving app may not have access to the sent path.
-             * Instead, when sharing files between apps, {@code content://}
-             * should be used with permission grants.
+             * Detect when this application exposes a {@code file://}
+             * {@link android.net.Uri} to another app.
+             * <p>
+             * This exposure is discouraged since the receiving app may not have
+             * access to the shared path. For example, the receiving app may not
+             * have requested the
+             * {@link android.Manifest.permission#READ_EXTERNAL_STORAGE} runtime
+             * permission, or the platform may be sharing the
+             * {@link android.net.Uri} across user profile boundaries.
+             * <p>
+             * Instead, apps should use {@code content://} Uris so the platform
+             * can extend temporary permission for the receiving app to access
+             * the resource.
+             *
+             * @see android.support.v4.content.FileProvider
+             * @see Intent#FLAG_GRANT_READ_URI_PERMISSION
              */
             public Builder detectFileUriExposure() {
                 return enable(DETECT_VM_FILE_URI_EXPOSURE);
@@ -798,6 +799,16 @@
             }
 
             /**
+             * Crashes the whole process when a {@code file://}
+             * {@link android.net.Uri} is exposed beyond this app.
+             *
+             * @see #detectFileUriExposure()
+             */
+            public Builder penaltyDeathOnFileUriExposure() {
+                return enable(PENALTY_DEATH_ON_FILE_URI_EXPOSURE);
+            }
+
+            /**
              * Log detected violations to the system log.
              */
             public Builder penaltyLog() {
@@ -1111,6 +1122,25 @@
     }
 
     /**
+     * Used by the framework to make file usage a fatal error.
+     *
+     * @hide
+     */
+    public static void enableDeathOnFileUriExposure() {
+        sVmPolicyMask |= DETECT_VM_FILE_URI_EXPOSURE | PENALTY_DEATH_ON_FILE_URI_EXPOSURE;
+    }
+
+    /**
+     * Used by lame internal apps that haven't done the hard work to get
+     * themselves off file:// Uris yet.
+     *
+     * @hide
+     */
+    public static void disableDeathOnFileUriExposure() {
+        sVmPolicyMask &= ~(DETECT_VM_FILE_URI_EXPOSURE | PENALTY_DEATH_ON_FILE_URI_EXPOSURE);
+    }
+
+    /**
      * Parses the BlockGuard policy mask out from the Exception's
      * getMessage() String value.  Kinda gross, but least
      * invasive.  :/
@@ -1755,9 +1785,13 @@
     /**
      * @hide
      */
-    public static void onFileUriExposed(String location) {
-        final String message = "file:// Uri exposed through " + location;
-        onVmPolicyViolation(null, new Throwable(message));
+    public static void onFileUriExposed(Uri uri, String location) {
+        final String message = uri + " exposed beyond app through " + location;
+        if ((sVmPolicyMask & PENALTY_DEATH_ON_FILE_URI_EXPOSURE) != 0) {
+            throw new FileUriExposedException(message);
+        } else {
+            onVmPolicyViolation(null, new Throwable(message));
+        }
     }
 
     /**
diff --git a/core/java/android/os/UserHandle.java b/core/java/android/os/UserHandle.java
index bcc1213..344d06e 100644
--- a/core/java/android/os/UserHandle.java
+++ b/core/java/android/os/UserHandle.java
@@ -144,6 +144,7 @@
     }
 
     /** @hide */
+    @SystemApi
     public static UserHandle of(@UserIdInt int userId) {
         return userId == USER_SYSTEM ? SYSTEM : new UserHandle(userId);
     }
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index fe834a1..1ac798b 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -21,6 +21,7 @@
 import android.annotation.RequiresPermission;
 import android.annotation.SystemApi;
 import android.annotation.UserIdInt;
+import android.app.Activity;
 import android.app.ActivityManager;
 import android.app.ActivityManagerNative;
 import android.app.admin.DevicePolicyManager;
@@ -621,6 +622,20 @@
     /** @hide */
     public static final int PIN_VERIFICATION_SUCCESS = -1;
 
+    /**
+     * Error result indicating that this user is not allowed to add other users on this device.
+     * This is a result code returned from the activity created by the intent
+     * {@link #createUserCreationIntent(String, String, String, PersistableBundle)}.
+     */
+    public static final int USER_CREATION_FAILED_NOT_PERMITTED = Activity.RESULT_FIRST_USER;
+
+    /**
+     * Error result indicating that no more users can be created on this device.
+     * This is a result code returned from the activity created by the intent
+     * {@link #createUserCreationIntent(String, String, String, PersistableBundle)}.
+     */
+    public static final int USER_CREATION_FAILED_NO_MORE_USERS = Activity.RESULT_FIRST_USER + 1;
+
     /** @hide */
     public static UserManager get(Context context) {
         return (UserManager) context.getSystemService(Context.USER_SERVICE);
@@ -1194,7 +1209,10 @@
      * If this device does not support multiple users, null is returned.
      * <p/>
      * The intent should be launched using startActivityForResult and the return result will
-     * indicate if the user consented to adding a new user and if the operation succeeded.
+     * indicate if the user consented to adding a new user and if the operation succeeded. Any
+     * errors in creating the user will be returned in the result code. If the user cancels the
+     * request, the return result will be {@link Activity#RESULT_CANCELED}. On success, the
+     * result code will be {@link Activity#RESULT_OK}.
      * <p/>
      * The new user is created but not initialized. After switching into the user for the first
      * time, the preferred user name and account information are used by the setup process for that
@@ -1211,6 +1229,8 @@
      *                       Handler)}.
      * @return An Intent that can be launched from an Activity or null if creating users is not
      *         supported on this device.
+     * @see #USER_CREATION_FAILED_NOT_PERMITTED
+     * @see #USER_CREATION_FAILED_NO_MORE_USERS
      */
     public static Intent createUserCreationIntent(@Nullable String userName,
             @Nullable String accountName,
@@ -1362,7 +1382,7 @@
     /**
      * Returns information for all users on this device.
      * Requires {@link android.Manifest.permission#MANAGE_USERS} permission.
-     * @return the list of users that were created.
+     * @return the list of users that exist on the device.
      * @hide
      */
     public List<UserInfo> getUsers() {
@@ -1375,6 +1395,29 @@
     }
 
     /**
+     * Returns serial numbers of all users on this device.
+     * Requires {@link android.Manifest.permission#MANAGE_USERS} permission.
+     *
+     * @param excludeDying specify if the list should exclude users being removed.
+     * @return the list of serial numbers of users that exist on the device.
+     * @hide
+     */
+    @SystemApi
+    public long[] getSerialNumbersOfUsers(boolean excludeDying) {
+        try {
+            List<UserInfo> users = mService.getUsers(excludeDying);
+            long[] result = new long[users.size()];
+            for (int i = 0; i < result.length; i++) {
+                result[i] = users.get(i).serialNumber;
+            }
+            return result;
+        } catch (RemoteException re) {
+            Log.w(TAG, "Could not get users list", re);
+            return null;
+        }
+    }
+
+    /**
      * @return the user's account name, null if not found.
      * @hide
      */
diff --git a/core/java/android/preference/PreferenceManager.java b/core/java/android/preference/PreferenceManager.java
index fda6326..ebb12fd 100644
--- a/core/java/android/preference/PreferenceManager.java
+++ b/core/java/android/preference/PreferenceManager.java
@@ -16,6 +16,7 @@
 
 package android.preference;
 
+import android.annotation.SystemApi;
 import android.annotation.XmlRes;
 import android.app.Activity;
 import android.content.Context;
@@ -24,8 +25,8 @@
 import android.content.SharedPreferences;
 import android.content.pm.ActivityInfo;
 import android.content.pm.PackageManager;
-import android.content.pm.ResolveInfo;
 import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.pm.ResolveInfo;
 import android.content.res.XmlResourceParser;
 import android.os.Bundle;
 import android.util.Log;
@@ -110,7 +111,13 @@
      * managed by this instance.
      */
     private int mSharedPreferencesMode;
-    
+
+    private static final int STORAGE_DEFAULT = 0;
+    private static final int STORAGE_DEVICE_ENCRYPTED = 1;
+    private static final int STORAGE_CREDENTIAL_ENCRYPTED = 2;
+
+    private int mStorage = STORAGE_DEFAULT;
+
     /**
      * The {@link PreferenceScreen} at the root of the preference hierarchy.
      */
@@ -343,6 +350,46 @@
     }
 
     /**
+     * Sets the storage location used internally by this class to be the default
+     * provided by the hosting {@link Context}.
+     */
+    public void setStorageDefault() {
+        mStorage = STORAGE_DEFAULT;
+        mSharedPreferences = null;
+    }
+
+    /**
+     * Explicitly set the storage location used internally by this class to be
+     * device-encrypted storage.
+     * <p>
+     * Data stored in device-encrypted storage is typically encrypted with a key
+     * tied to the physical device, and it can be accessed when the device has
+     * booted successfully, both <em>before and after</em> the user has
+     * authenticated with their credentials (such as a lock pattern or PIN).
+     * Because device-encrypted data is available before user authentication,
+     * you should carefully consider what data you store using this mode.
+     *
+     * @see Context#createDeviceEncryptedStorageContext()
+     */
+    public void setStorageDeviceEncrypted() {
+        mStorage = STORAGE_DEVICE_ENCRYPTED;
+        mSharedPreferences = null;
+    }
+
+    /**
+     * Explicitly set the storage location used internally by this class to be
+     * credential-encrypted storage.
+     *
+     * @see Context#createCredentialEncryptedStorageContext()
+     * @hide
+     */
+    @SystemApi
+    public void setStorageCredentialEncrypted() {
+        mStorage = STORAGE_CREDENTIAL_ENCRYPTED;
+        mSharedPreferences = null;
+    }
+
+    /**
      * Gets a SharedPreferences instance that preferences managed by this will
      * use.
      * 
@@ -351,7 +398,20 @@
      */
     public SharedPreferences getSharedPreferences() {
         if (mSharedPreferences == null) {
-            mSharedPreferences = mContext.getSharedPreferences(mSharedPreferencesName,
+            final Context storageContext;
+            switch (mStorage) {
+                case STORAGE_DEVICE_ENCRYPTED:
+                    storageContext = mContext.createDeviceEncryptedStorageContext();
+                    break;
+                case STORAGE_CREDENTIAL_ENCRYPTED:
+                    storageContext = mContext.createCredentialEncryptedStorageContext();
+                    break;
+                default:
+                    storageContext = mContext;
+                    break;
+            }
+
+            mSharedPreferences = storageContext.getSharedPreferences(mSharedPreferencesName,
                     mSharedPreferencesMode);
         }
         
diff --git a/core/java/android/provider/DocumentsContract.java b/core/java/android/provider/DocumentsContract.java
index cdd88f6..032d83d 100644
--- a/core/java/android/provider/DocumentsContract.java
+++ b/core/java/android/provider/DocumentsContract.java
@@ -234,6 +234,9 @@
          * @see #FLAG_DIR_PREFERS_LAST_MODIFIED
          * @see #FLAG_VIRTUAL_DOCUMENT
          * @see #FLAG_ARCHIVE
+         * @see #FLAG_SUPPORTS_COPY
+         * @see #FLAG_SUPPORTS_MOVE
+         * @see #FLAG_SUPPORTS_REMOVE
          */
         public static final String COLUMN_FLAGS = "flags";
 
@@ -371,6 +374,15 @@
         public static final int FLAG_ARCHIVE = 1 << 10;
 
         /**
+         * Flag indicating that a document can be removed from a parent.
+         *
+         * @see #COLUMN_FLAGS
+         * @see DocumentsContract#removeDocument(ContentProviderClient, Uri, Uri)
+         * @see DocumentsProvider#removeDocument(String, String)
+         */
+        public static final int FLAG_SUPPORTS_REMOVE = 1 << 11;
+
+        /**
          * Flag indicating that document titles should be hidden when viewing
          * this directory in a larger format grid. For example, a directory
          * containing only images may want the image thumbnails to speak for
@@ -612,6 +624,8 @@
     public static final String METHOD_MOVE_DOCUMENT = "android:moveDocument";
     /** {@hide} */
     public static final String METHOD_IS_CHILD_DOCUMENT = "android:isChildDocument";
+    /** {@hide} */
+    public static final String METHOD_REMOVE_DOCUMENT = "android:removeDocument";
 
     /** {@hide} */
     public static final String EXTRA_PARENT_URI = "parentUri";
@@ -835,7 +849,12 @@
         return uri != null && ContentResolver.SCHEME_CONTENT.equals(uri.getScheme());
     }
 
-    /** {@hide} */
+    /**
+     * Test if the given URI represents a {@link Document} tree.
+     *
+     * @see #buildTreeDocumentUri(String, String)
+     * @see #getTreeDocumentId(Uri, String)
+     */
     public static boolean isTreeUri(Uri uri) {
         final List<String> paths = uri.getPathSegments();
         return (paths.size() >= 2 && PATH_TREE.equals(paths.get(0)));
@@ -1204,6 +1223,41 @@
     }
 
     /**
+     * Removes the given document from a parent directory.
+     *
+     * <p>In contrast to {@link #deleteDocument} it requires specifying the parent.
+     * This method is especially useful if the document can be in multiple parents.
+     *
+     * @param documentUri document with {@link Document#FLAG_SUPPORTS_REMOVE}
+     * @param parentDocumentUri parent document of the document to remove.
+     * @return true if the document was removed successfully.
+     */
+    public static boolean removeDocument(ContentResolver resolver, Uri documentUri,
+            Uri parentDocumentUri) {
+        final ContentProviderClient client = resolver.acquireUnstableContentProviderClient(
+                documentUri.getAuthority());
+        try {
+            removeDocument(client, documentUri, parentDocumentUri);
+            return true;
+        } catch (Exception e) {
+            Log.w(TAG, "Failed to remove document", e);
+            return false;
+        } finally {
+            ContentProviderClient.releaseQuietly(client);
+        }
+    }
+
+    /** {@hide} */
+    public static void removeDocument(ContentProviderClient client, Uri documentUri,
+            Uri parentDocumentUri) throws RemoteException {
+        final Bundle in = new Bundle();
+        in.putParcelable(DocumentsContract.EXTRA_URI, documentUri);
+        in.putParcelable(DocumentsContract.EXTRA_PARENT_URI, parentDocumentUri);
+
+        client.call(METHOD_REMOVE_DOCUMENT, null, in);
+    }
+
+    /**
      * Open the given image for thumbnail purposes, using any embedded EXIF
      * thumbnail if available, and providing orientation hints from the parent
      * image.
diff --git a/core/java/android/provider/DocumentsProvider.java b/core/java/android/provider/DocumentsProvider.java
index bae928d..7a82cd1e 100644
--- a/core/java/android/provider/DocumentsProvider.java
+++ b/core/java/android/provider/DocumentsProvider.java
@@ -21,6 +21,7 @@
 import static android.provider.DocumentsContract.METHOD_DELETE_DOCUMENT;
 import static android.provider.DocumentsContract.METHOD_IS_CHILD_DOCUMENT;
 import static android.provider.DocumentsContract.METHOD_MOVE_DOCUMENT;
+import static android.provider.DocumentsContract.METHOD_REMOVE_DOCUMENT;
 import static android.provider.DocumentsContract.METHOD_RENAME_DOCUMENT;
 import static android.provider.DocumentsContract.buildDocumentUri;
 import static android.provider.DocumentsContract.buildDocumentUriMaybeUsingTree;
@@ -300,6 +301,25 @@
             throws FileNotFoundException {
         throw new UnsupportedOperationException("Move not supported");
     }
+
+    /**
+     * Removes the requested document or a document tree.
+     *
+     * <p>In contrast to {@link #deleteDocument} it requires specifying the parent.
+     * This method is especially useful if the document can be in multiple parents.
+     *
+     * <p>It's the responsibility of the provider to revoke grants if the document is
+     * removed from the last parent, and effectively the document is deleted.
+     *
+     * @param documentId the document to remove.
+     * @param parentDocumentId the parent of the document to move.
+     */
+    @SuppressWarnings("unused")
+    public boolean removeDocument(String documentId, String parentDocumentId)
+            throws FileNotFoundException {
+        throw new UnsupportedOperationException("Remove not supported");
+    }
+
     /**
      * Return all roots currently provided. To display to users, you must define
      * at least one root. You should avoid making network requests to keep this
@@ -822,6 +842,17 @@
             // Original document no longer exists, clean up any grants.
             revokeDocumentPermission(documentId);
 
+        } else if (METHOD_REMOVE_DOCUMENT.equals(method)) {
+            final Uri parentSourceUri = extras.getParcelable(DocumentsContract.EXTRA_PARENT_URI);
+            final String parentSourceId = DocumentsContract.getDocumentId(parentSourceUri);
+
+            enforceReadPermissionInner(parentSourceUri, getCallingPackage(), null);
+            enforceWritePermissionInner(documentUri, getCallingPackage(), null);
+            removeDocument(documentId, parentSourceId);
+
+            // It's responsibility of the provider to revoke any grants, as the document may be
+            // still attached to another parents.
+
         } else {
             throw new UnsupportedOperationException("Method not supported " + method);
         }
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index bc0d7d6..c9c0cde 100755
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -501,6 +501,23 @@
             "android.settings.USER_DICTIONARY_SETTINGS";
 
     /**
+     * Activity Action: Show settings to configure the hardware keyboard layout.
+     * <p>
+     * In some cases, a matching Activity may not exist, so ensure you
+     * safeguard against this.
+     * <p>
+     *
+     * @see android.hardware.input.InputManager#ACTION_QUERY_KEYBOARD_LAYOUTS
+     * <p>
+     * Input: Nothing.
+     * <p>
+     * Output: Nothing.
+     */
+    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+    public static final String ACTION_KEYBOARD_LAYOUT_SETTINGS =
+            "android.settings.KEYBOARD_LAYOUT_SETTINGS";
+
+    /**
      * Activity Action: Adds a word to the user dictionary.
      * <p>
      * In some cases, a matching Activity may not exist, so ensure you
diff --git a/core/java/android/service/voice/VoiceInteractionSession.java b/core/java/android/service/voice/VoiceInteractionSession.java
index 0c6a0c6..6ff9fe7 100644
--- a/core/java/android/service/voice/VoiceInteractionSession.java
+++ b/core/java/android/service/voice/VoiceInteractionSession.java
@@ -1170,7 +1170,7 @@
         }
         try {
             intent.migrateExtraStreamToClipData();
-            intent.prepareToLeaveProcess();
+            intent.prepareToLeaveProcess(mContext);
             int res = mSystemService.startVoiceActivity(mToken, intent,
                     intent.resolveType(mContext.getContentResolver()));
             Instrumentation.checkStartActivityResult(res, intent);
diff --git a/core/java/android/test/AndroidTestCase.java b/core/java/android/test/AndroidTestCase.java
index 2ecbfae..1e6bd9c 100644
--- a/core/java/android/test/AndroidTestCase.java
+++ b/core/java/android/test/AndroidTestCase.java
@@ -29,7 +29,13 @@
 
 /**
  * Extend this if you need to access Resources or other things that depend on Activity Context.
+ *
+ * @deprecated Use
+ * <a href="{@docRoot}reference/android/support/test/InstrumentationRegistry.html">
+ * InstrumentationRegistry</a> instead. New tests should be written using the
+ * <a href="{@docRoot}tools/testing-support-library/index.html">Android Testing Support Library</a>.
  */
+@Deprecated
 public class AndroidTestCase extends TestCase {
 
     protected Context mContext;
diff --git a/core/java/android/test/FlakyTest.java b/core/java/android/test/FlakyTest.java
index 919767f..4e5c4e3 100644
--- a/core/java/android/test/FlakyTest.java
+++ b/core/java/android/test/FlakyTest.java
@@ -26,7 +26,13 @@
  * test methods. When the annotation is present, the test method is re-executed if
  * the test fails. The total number of executions is specified by the tolerance and
  * defaults to 1.
+ *
+ * @deprecated Use
+ * <a href="{@docRoot}reference/android/support/test/filters/FlakyTest.html">
+ * FlakyTest</a> instead. New tests should be written using the
+ * <a href="{@docRoot}tools/testing-support-library/index.html">Android Testing Support Library</a>.
  */
+@Deprecated
 @Target(ElementType.METHOD)
 @Retention(RetentionPolicy.RUNTIME)
 public @interface FlakyTest {
diff --git a/core/java/android/test/InstrumentationTestCase.java b/core/java/android/test/InstrumentationTestCase.java
index ca427ea..6b79314 100644
--- a/core/java/android/test/InstrumentationTestCase.java
+++ b/core/java/android/test/InstrumentationTestCase.java
@@ -32,7 +32,13 @@
 
 /**
  * A test case that has access to {@link Instrumentation}.
+ *
+ * @deprecated Use
+ * <a href="{@docRoot}reference/android/support/test/InstrumentationRegistry.html">
+ * InstrumentationRegistry</a> instead. New tests should be written using the
+ * <a href="{@docRoot}tools/testing-support-library/index.html">Android Testing Support Library</a>.
  */
+@Deprecated
 public class InstrumentationTestCase extends TestCase {
 
     private Instrumentation mInstrumentation;
@@ -40,7 +46,7 @@
     /**
      * Injects instrumentation into this test case. This method is
      * called by the test runner during test setup.
-     * 
+     *
      * @param instrumentation the instrumentation to use with this instance
      */
     public void injectInstrumentation(Instrumentation instrumentation) {
diff --git a/core/java/android/test/InstrumentationTestSuite.java b/core/java/android/test/InstrumentationTestSuite.java
index 7a78ffb..a53fa26 100644
--- a/core/java/android/test/InstrumentationTestSuite.java
+++ b/core/java/android/test/InstrumentationTestSuite.java
@@ -25,7 +25,13 @@
 /**
  * A {@link junit.framework.TestSuite} that injects {@link android.app.Instrumentation} into
  * {@link InstrumentationTestCase} before running them.
+ *
+ * @deprecated Use
+ * <a href="{@docRoot}reference/android/support/test/InstrumentationRegistry.html">
+ * InstrumentationRegistry</a> instead. New tests should be written using the
+ * <a href="{@docRoot}tools/testing-support-library/index.html">Android Testing Support Library</a>.
  */
+@Deprecated
 public class InstrumentationTestSuite extends TestSuite {
 
     private final Instrumentation mInstrumentation;
diff --git a/core/java/android/test/PerformanceTestCase.java b/core/java/android/test/PerformanceTestCase.java
index 679ad40..65bd4a4 100644
--- a/core/java/android/test/PerformanceTestCase.java
+++ b/core/java/android/test/PerformanceTestCase.java
@@ -18,10 +18,11 @@
 
 /**
  * More complex interface performance for test cases.
- * 
+ *
  * If you want your test to be used as a performance test, you must
  * implement this interface.
  */
+@Deprecated
 public interface PerformanceTestCase
 {
     /**
@@ -37,27 +38,27 @@
     }
 
     /**
-     * Set up to begin performance tests.  The 'intermediates' is a
+     * Set up to begin performance tests. The 'intermediates' is a
      * communication channel to send back intermediate performance numbers --
      * if you use it, you will probably want to ensure your test is only
      * executed once by returning 1.  Otherwise, return 0 to allow the test
      * harness to decide the number of iterations.
-     * 
+     *
      * <p>If you return a non-zero iteration count, you should call
      * {@link Intermediates#startTiming intermediates.startTiming} and
      * {@link Intermediates#finishTiming intermediates.endTiming} to report the
      * duration of the test whose performance should actually be measured.
-     * 
+     *
      * @param intermediates Callback for sending intermediate results.
-     * 
+     *
      * @return int Maximum number of iterations to run, or 0 to let the caller
-     *         decide.
+     * decide.
      */
     int startPerformance(Intermediates intermediates);
-    
+
     /**
      * This method is used to determine what modes this test case can run in.
-     * 
+     *
      * @return true if this test case can only be run in performance mode.
      */
     boolean isPerformanceOnly();
diff --git a/core/java/android/test/UiThreadTest.java b/core/java/android/test/UiThreadTest.java
index cd92231..cd06ab8 100644
--- a/core/java/android/test/UiThreadTest.java
+++ b/core/java/android/test/UiThreadTest.java
@@ -26,7 +26,13 @@
  * When the annotation is present, the test method is executed on the application's
  * main thread (or UI thread.) Note that instrumentation methods may not be used
  * when this annotation is present.
+ *
+ * @deprecated Use
+ * <a href="{@docRoot}reference/android/support/test/annotation/UiThreadTest.html">
+ * UiThreadTest</a> instead. New tests should be written using the
+ * <a href="{@docRoot}tools/testing-support-library/index.html">Android Testing Support Library</a>.
  */
+@Deprecated
 @Target(ElementType.METHOD)
 @Retention(RetentionPolicy.RUNTIME)
 public @interface UiThreadTest {
diff --git a/core/java/android/test/suitebuilder/annotation/LargeTest.java b/core/java/android/test/suitebuilder/annotation/LargeTest.java
index a6269e7..dc77ee6 100644
--- a/core/java/android/test/suitebuilder/annotation/LargeTest.java
+++ b/core/java/android/test/suitebuilder/annotation/LargeTest.java
@@ -23,7 +23,13 @@
 
 /**
  * Marks a test that should run as part of the large tests.
+ *
+ * @deprecated Use
+ * <a href="{@docRoot}reference/android/support/test/filters/LargeTest.html">
+ * LargeTest</a> instead. New tests should be written using the
+ * <a href="{@docRoot}tools/testing-support-library/index.html">Android Testing Support Library</a>.
  */
+@Deprecated
 @Retention(RetentionPolicy.RUNTIME)
 @Target({ElementType.METHOD, ElementType.TYPE})
 public @interface LargeTest {
diff --git a/core/java/android/test/suitebuilder/annotation/MediumTest.java b/core/java/android/test/suitebuilder/annotation/MediumTest.java
index 8afeb91..b941da0 100644
--- a/core/java/android/test/suitebuilder/annotation/MediumTest.java
+++ b/core/java/android/test/suitebuilder/annotation/MediumTest.java
@@ -24,7 +24,12 @@
 /**
  * Marks a test that should run as part of the medium tests.
  *
+ * @deprecated Use
+ * <a href="{@docRoot}reference/android/support/test/filters/MediumTest.html">
+ * MediumTest</a> instead. New tests should be written using the
+ * <a href="{@docRoot}tools/testing-support-library/index.html">Android Testing Support Library</a>.
  */
+@Deprecated
 @Retention(RetentionPolicy.RUNTIME)
 @Target({ElementType.METHOD, ElementType.TYPE})
 public @interface MediumTest {
diff --git a/core/java/android/test/suitebuilder/annotation/SmallTest.java b/core/java/android/test/suitebuilder/annotation/SmallTest.java
index ad530e2..d3c74f0 100644
--- a/core/java/android/test/suitebuilder/annotation/SmallTest.java
+++ b/core/java/android/test/suitebuilder/annotation/SmallTest.java
@@ -23,7 +23,13 @@
 
 /**
  * Marks a test that should run as part of the small tests.
+ *
+ * @deprecated Use
+ * <a href="{@docRoot}reference/android/support/test/filters/SmallTest.html">
+ * SmallTest</a> instead. New tests should be written using the
+ * <a href="{@docRoot}tools/testing-support-library/index.html">Android Testing Support Library</a>.
  */
+@Deprecated
 @Retention(RetentionPolicy.RUNTIME)
 @Target({ElementType.METHOD, ElementType.TYPE})
 public @interface SmallTest {
diff --git a/core/java/android/test/suitebuilder/annotation/Smoke.java b/core/java/android/test/suitebuilder/annotation/Smoke.java
index 237e033..aac2937 100644
--- a/core/java/android/test/suitebuilder/annotation/Smoke.java
+++ b/core/java/android/test/suitebuilder/annotation/Smoke.java
@@ -27,7 +27,11 @@
  * will run all tests with this annotation.
  *
  * @see android.test.suitebuilder.SmokeTestSuiteBuilder
+ *
+ * @deprecated New tests should be written using the
+ * <a href="{@docRoot}tools/testing-support-library/index.html">Android Testing Support Library</a>.
  */
+@Deprecated
 @Retention(RetentionPolicy.RUNTIME)
 @Target({ElementType.METHOD, ElementType.TYPE})
 public @interface Smoke {
diff --git a/core/java/android/test/suitebuilder/annotation/Suppress.java b/core/java/android/test/suitebuilder/annotation/Suppress.java
index f16c8fa..629a3cf 100644
--- a/core/java/android/test/suitebuilder/annotation/Suppress.java
+++ b/core/java/android/test/suitebuilder/annotation/Suppress.java
@@ -26,7 +26,12 @@
  * suite. If the annotation appears on the class then no tests in that class will be included. If
  * the annotation appears only on a test method then only that method will be excluded.
  *
+ * @deprecated Use
+ * <a href="{@docRoot}reference/android/support/test/filters/Suppress.html">
+ * Suppress</a> instead. New tests should be written using the
+ * <a href="{@docRoot}tools/testing-support-library/index.html">Android Testing Support Library</a>.
  */
+@Deprecated
 @Retention(RetentionPolicy.RUNTIME)
 @Target({ElementType.METHOD, ElementType.TYPE})
 public @interface Suppress {
diff --git a/core/java/android/view/KeyboardShortcutGroup.java b/core/java/android/view/KeyboardShortcutGroup.java
index 013255b..57d07c0 100644
--- a/core/java/android/view/KeyboardShortcutGroup.java
+++ b/core/java/android/view/KeyboardShortcutGroup.java
@@ -32,6 +32,8 @@
 public final class KeyboardShortcutGroup implements Parcelable {
     private final CharSequence mLabel;
     private final List<KeyboardShortcutInfo> mItems;
+    // The system group looks different UI wise.
+    private boolean mSystemGroup;
 
     /**
      * @param label The title to be used for this group, or null if there is none.
@@ -50,10 +52,33 @@
         this(label, Collections.<KeyboardShortcutInfo>emptyList());
     }
 
+    /**
+     * @param label The title to be used for this group, or null if there is none.
+     * @param items The set of items to be included.
+     * @param isSystemGroup Set this to {@code true} if this is s system group.
+     * @hide
+     */
+    public KeyboardShortcutGroup(@Nullable CharSequence label,
+            @NonNull List<KeyboardShortcutInfo> items, boolean isSystemGroup) {
+        mLabel = label;
+        mItems = new ArrayList<>(checkNotNull(items));
+        mSystemGroup = isSystemGroup;
+    }
+
+    /**
+     * @param label The title to be used for this group, or null if there is none.
+     * @param isSystemGroup Set this to {@code true} if this is s system group.
+     * @hide
+     */
+    public KeyboardShortcutGroup(@Nullable CharSequence label, boolean isSystemGroup) {
+        this(label, Collections.<KeyboardShortcutInfo>emptyList(), isSystemGroup);
+    }
+
     private KeyboardShortcutGroup(Parcel source) {
         mItems = new ArrayList<>();
         mLabel = source.readCharSequence();
         source.readTypedList(mItems, KeyboardShortcutInfo.CREATOR);
+        mSystemGroup = source.readInt() == 1;
     }
 
     /**
@@ -70,6 +95,11 @@
         return mItems;
     }
 
+    /** @hide **/
+    public boolean isSystemGroup() {
+        return mSystemGroup;
+    }
+
     /**
      * Adds an item to the existing list.
      *
@@ -88,6 +118,7 @@
     public void writeToParcel(Parcel dest, int flags) {
         dest.writeCharSequence(mLabel);
         dest.writeTypedList(mItems);
+        dest.writeInt(mSystemGroup ? 1 : 0);
     }
 
     public static final Creator<KeyboardShortcutGroup> CREATOR =
@@ -99,4 +130,4 @@
             return new KeyboardShortcutGroup[size];
         }
     };
-}
\ No newline at end of file
+}
diff --git a/core/java/android/view/PointerIcon.java b/core/java/android/view/PointerIcon.java
index 7ba046b..81bb638 100644
--- a/core/java/android/view/PointerIcon.java
+++ b/core/java/android/view/PointerIcon.java
@@ -139,9 +139,7 @@
 
     private static final PointerIcon gNullIcon = new PointerIcon(STYLE_NULL);
     private static final SparseArray<PointerIcon> gSystemIcons = new SparseArray<PointerIcon>();
-
-    /** @hide */
-    public static boolean sUseLargeIcons = false;
+    private static boolean sUseLargeIcons = false;
 
     private final int mStyle;
     private int mSystemIconResourceId;
@@ -235,6 +233,15 @@
     }
 
     /**
+     * Updates wheter accessibility large icons are used or not.
+     * @hide
+     */
+    public static void setUseLargeIcons(boolean use) {
+        sUseLargeIcons = use;
+        gSystemIcons.clear();
+    }
+
+    /**
      * Creates a custom pointer from the given bitmap and hotspot information.
      *
      * @param bitmap The bitmap for the icon.
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index 772eeec..0c5a5fc 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -589,14 +589,14 @@
         public static final int TYPE_VOICE_INTERACTION = FIRST_SYSTEM_WINDOW+31;
 
         /**
-         * Window type: Windows that are overlaid <em>only</em> by an {@link
+         * Window type: Windows that are overlaid <em>only</em> by a connected {@link
          * android.accessibilityservice.AccessibilityService} for interception of
          * user interactions without changing the windows an accessibility service
          * can introspect. In particular, an accessibility service can introspect
          * only windows that a sighted user can interact with which is they can touch
          * these windows or can type into these windows. For example, if there
          * is a full screen accessibility overlay that is touchable, the windows
-         * below it will be introspectable by an accessibility service regardless
+         * below it will be introspectable by an accessibility service even though
          * they are covered by a touchable window.
          */
         public static final int TYPE_ACCESSIBILITY_OVERLAY = FIRST_SYSTEM_WINDOW+32;
diff --git a/core/java/android/view/accessibility/AccessibilityNodeInfo.java b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
index 1327ea1..b673386 100644
--- a/core/java/android/view/accessibility/AccessibilityNodeInfo.java
+++ b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
@@ -38,8 +38,8 @@
 /**
  * This class represents a node of the window content as well as actions that
  * can be requested from its source. From the point of view of an
- * {@link android.accessibilityservice.AccessibilityService} a window content is
- * presented as tree of accessibility node info which may or may not map one-to-one
+ * {@link android.accessibilityservice.AccessibilityService} a window's content is
+ * presented as a tree of accessibility node infos, which may or may not map one-to-one
  * to the view hierarchy. In other words, a custom view is free to report itself as
  * a tree of accessibility node info.
  * </p>
@@ -50,7 +50,7 @@
  * <p>
  * Please refer to {@link android.accessibilityservice.AccessibilityService} for
  * details about how to obtain a handle to window content as a tree of accessibility
- * node info as well as familiarizing with the security model.
+ * node info as well as details about the security model.
  * </p>
  * <div class="special reference">
  * <h3>Developer Guides</h3>
@@ -2422,18 +2422,30 @@
     }
 
     /**
-     * Gets the text selection start.
+     * Gets the text selection start or the cursor position.
+     * <p>
+     * If no text is selected, both this method and
+     * {@link AccessibilityNodeInfo#getTextSelectionEnd()} return the same value:
+     * the current location of the cursor.
+     * </p>
      *
-     * @return The text selection start if there is selection or -1.
+     * @return The text selection start, the cursor location if there is no selection, or -1 if
+     *         there is no text selection and no cursor.
      */
     public int getTextSelectionStart() {
         return mTextSelectionStart;
     }
 
     /**
-     * Gets the text selection end.
+     * Gets the text selection end if text is selected.
+     * <p>
+     * If no text is selected, both this method and
+     * {@link AccessibilityNodeInfo#getTextSelectionStart()} return the same value:
+     * the current location of the cursor.
+     * </p>
      *
-     * @return The text selection end if there is selection or -1.
+     * @return The text selection end, the cursor location if there is no selection, or -1 if
+     *         there is no text selection and no cursor.
      */
     public int getTextSelectionEnd() {
         return mTextSelectionEnd;
diff --git a/core/java/com/android/internal/app/ConfirmUserCreationActivity.java b/core/java/com/android/internal/app/ConfirmUserCreationActivity.java
index 53d7793..03da9bc 100644
--- a/core/java/com/android/internal/app/ConfirmUserCreationActivity.java
+++ b/core/java/com/android/internal/app/ConfirmUserCreationActivity.java
@@ -64,6 +64,10 @@
 
         String message = checkUserCreationRequirements();
 
+        if (message == null) {
+            finish();
+            return;
+        }
         final AlertController.AlertParams ap = mAlertParams;
         ap.mMessage = message;
         ap.mPositiveButtonText = getString(android.R.string.ok);
@@ -104,11 +108,11 @@
         mCanProceed = true;
         final String appName = appInfo.loadLabel(getPackageManager()).toString();
         if (cantCreateUser) {
-            message = getString(R.string.user_creation_cannot_add, appName);
-            mCanProceed = false;
+            setResult(UserManager.USER_CREATION_FAILED_NOT_PERMITTED);
+            return null;
         } else if (cantCreateAnyMoreUsers) {
-            message = getString(R.string.user_creation_cannot_add_any_more, appName);
-            mCanProceed = false;
+            setResult(UserManager.USER_CREATION_FAILED_NO_MORE_USERS);
+            return null;
         } else if (accountExists) {
             message = getString(R.string.user_creation_account_exists, appName, mAccountName);
         } else {
diff --git a/core/java/com/android/internal/app/NetInitiatedActivity.java b/core/java/com/android/internal/app/NetInitiatedActivity.java
index b951f50e..d3bae16 100644
--- a/core/java/com/android/internal/app/NetInitiatedActivity.java
+++ b/core/java/com/android/internal/app/NetInitiatedActivity.java
@@ -133,7 +133,7 @@
         notificationId = -1;
     }
 
-    // Respond to NI Handler under GpsLocationProvider, 1 = accept, 2 = deny
+    // Respond to NI Handler under GnssLocationProvider, 1 = accept, 2 = deny
     private void sendUserResponse(int response) {
         if (DEBUG) Log.d(TAG, "sendUserResponse, response: " + response);
         LocationManager locationManager = (LocationManager)
diff --git a/core/java/com/android/internal/app/ResolverActivity.java b/core/java/com/android/internal/app/ResolverActivity.java
index aa38de7..ec148c55 100644
--- a/core/java/com/android/internal/app/ResolverActivity.java
+++ b/core/java/com/android/internal/app/ResolverActivity.java
@@ -1231,7 +1231,9 @@
                 }
 
                 // Filter out any activities that the launched uid does not
-                // have permission for.  We don't do this when we have an explicit
+                // have permission for.
+                // Also filter out those that are suspended because they couldn't
+                // be started. We don't do this when we have an explicit
                 // list of resolved activities, because that only happens when
                 // we are being subclassed, so we can safely launch whatever
                 // they gave us.
@@ -1242,7 +1244,9 @@
                         int granted = ActivityManager.checkComponentPermission(
                                 ai.permission, mLaunchedFromUid,
                                 ai.applicationInfo.uid, ai.exported);
-                        if (granted != PackageManager.PERMISSION_GRANTED) {
+                        boolean suspended = (ai.applicationInfo.flags
+                                & ApplicationInfo.FLAG_SUSPENDED) != 0;
+                        if (granted != PackageManager.PERMISSION_GRANTED || suspended) {
                             // Access not allowed!
                             if (mOrigResolveList == currentResolveList) {
                                 mOrigResolveList = new ArrayList<>(mOrigResolveList);
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index 57220b1..c71c131 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -41,6 +41,7 @@
 import android.os.SystemProperties;
 import android.os.WorkSource;
 import android.telephony.DataConnectionRealTimeInfo;
+import android.telephony.ModemActivityInfo;
 import android.telephony.ServiceState;
 import android.telephony.SignalStrength;
 import android.telephony.TelephonyManager;
@@ -106,7 +107,7 @@
     private static final int MAGIC = 0xBA757475; // 'BATSTATS'
 
     // Current on-disk Parcel version
-    private static final int VERSION = 139 + (USE_OLD_HISTORY ? 1000 : 0);
+    private static final int VERSION = 140 + (USE_OLD_HISTORY ? 1000 : 0);
 
     // Maximum number of items we will record in the history.
     private static final int MAX_HISTORY_ITEMS = 2000;
@@ -119,6 +120,12 @@
     // in to one common name.
     private static final int MAX_WAKELOCKS_PER_UID = 100;
 
+    // Number of transmit power states the Wifi controller can be in.
+    private static final int NUM_WIFI_TX_LEVELS = 1;
+
+    // Number of transmit power states the Bluetooth controller can be in.
+    private static final int NUM_BT_TX_LEVELS = 1;
+
     private final JournaledFile mFile;
     public final AtomicFile mCheckinFile;
     public final AtomicFile mDailyFile;
@@ -379,11 +386,38 @@
     final LongSamplingCounter[] mNetworkPacketActivityCounters =
             new LongSamplingCounter[NUM_NETWORK_ACTIVITY_TYPES];
 
-    final LongSamplingCounter[] mBluetoothActivityCounters =
-            new LongSamplingCounter[NUM_CONTROLLER_ACTIVITY_TYPES];
+    /**
+     * The WiFi controller activity (time in tx, rx, idle, and power consumed) for the device.
+     */
+    ControllerActivityCounterImpl mWifiActivity;
 
-    final LongSamplingCounter[] mWifiActivityCounters =
-            new LongSamplingCounter[NUM_CONTROLLER_ACTIVITY_TYPES];
+    /**
+     * The Bluetooth controller activity (time in tx, rx, idle, and power consumed) for the device.
+     */
+    ControllerActivityCounterImpl mBluetoothActivity;
+
+    /**
+     * The Modem controller activity (time in tx, rx, idle, and power consumed) for the device.
+     */
+    ControllerActivityCounterImpl mModemActivity;
+
+    /**
+     * Whether the device supports WiFi controller energy reporting. This is set to true on
+     * the first WiFi energy report. See {@link #mWifiActivity}.
+     */
+    boolean mHasWifiReporting = false;
+
+    /**
+     * Whether the device supports Bluetooth controller energy reporting. This is set to true on
+     * the first Bluetooth energy report. See {@link #mBluetoothActivity}.
+     */
+    boolean mHasBluetoothReporting = false;
+
+    /**
+     * Whether the device supports Modem controller energy reporting. This is set to true on
+     * the first Modem energy report. See {@link #mModemActivity}.
+     */
+    boolean mHasModemReporting = false;
 
     boolean mWifiOn;
     StopwatchTimer mWifiOnTimer;
@@ -479,8 +513,6 @@
     private final NetworkStats.Entry mTmpNetworkStatsEntry = new NetworkStats.Entry();
 
     private PowerProfile mPowerProfile;
-    private boolean mHasWifiEnergyReporting = false;
-    private boolean mHasBluetoothEnergyReporting = false;
 
     /*
      * Holds a SamplingTimer associated with each kernel wakelock name being tracked.
@@ -1770,6 +1802,131 @@
         public abstract T instantiateObject();
     }
 
+    public static class ControllerActivityCounterImpl extends ControllerActivityCounter
+            implements Parcelable {
+        private final LongSamplingCounter mIdleTimeMillis;
+        private final LongSamplingCounter mRxTimeMillis;
+        private final LongSamplingCounter[] mTxTimeMillis;
+        private final LongSamplingCounter mPowerDrainMaMs;
+
+        public ControllerActivityCounterImpl(TimeBase timeBase, int numTxStates) {
+            mIdleTimeMillis = new LongSamplingCounter(timeBase);
+            mRxTimeMillis = new LongSamplingCounter(timeBase);
+            mTxTimeMillis = new LongSamplingCounter[numTxStates];
+            for (int i = 0; i < numTxStates; i++) {
+                mTxTimeMillis[i] = new LongSamplingCounter(timeBase);
+            }
+            mPowerDrainMaMs = new LongSamplingCounter(timeBase);
+        }
+
+        public ControllerActivityCounterImpl(TimeBase timeBase, int numTxStates, Parcel in) {
+            mIdleTimeMillis = new LongSamplingCounter(timeBase, in);
+            mRxTimeMillis = new LongSamplingCounter(timeBase, in);
+            final int recordedTxStates = in.readInt();
+            if (recordedTxStates != numTxStates) {
+                throw new ParcelFormatException("inconsistent tx state lengths");
+            }
+
+            mTxTimeMillis = new LongSamplingCounter[numTxStates];
+            for (int i = 0; i < numTxStates; i++) {
+                mTxTimeMillis[i] = new LongSamplingCounter(timeBase, in);
+            }
+            mPowerDrainMaMs = new LongSamplingCounter(timeBase, in);
+        }
+
+        public void readSummaryFromParcel(Parcel in) {
+            mIdleTimeMillis.readSummaryFromParcelLocked(in);
+            mRxTimeMillis.readSummaryFromParcelLocked(in);
+            final int recordedTxStates = in.readInt();
+            if (recordedTxStates != mTxTimeMillis.length) {
+                throw new ParcelFormatException("inconsistent tx state lengths");
+            }
+            for (LongSamplingCounter counter : mTxTimeMillis) {
+                counter.readSummaryFromParcelLocked(in);
+            }
+            mPowerDrainMaMs.readSummaryFromParcelLocked(in);
+        }
+
+        @Override
+        public int describeContents() {
+            return 0;
+        }
+
+        public void writeSummaryToParcel(Parcel dest) {
+            mIdleTimeMillis.writeSummaryFromParcelLocked(dest);
+            mRxTimeMillis.writeSummaryFromParcelLocked(dest);
+            dest.writeInt(mTxTimeMillis.length);
+            for (LongSamplingCounter counter : mTxTimeMillis) {
+                counter.writeSummaryFromParcelLocked(dest);
+            }
+            mPowerDrainMaMs.writeSummaryFromParcelLocked(dest);
+        }
+
+        @Override
+        public void writeToParcel(Parcel dest, int flags) {
+            mIdleTimeMillis.writeToParcel(dest);
+            mRxTimeMillis.writeToParcel(dest);
+            dest.writeInt(mTxTimeMillis.length);
+            for (LongSamplingCounter counter : mTxTimeMillis) {
+                counter.writeToParcel(dest);
+            }
+            mPowerDrainMaMs.writeToParcel(dest);
+        }
+
+        public void reset(boolean detachIfReset) {
+            mIdleTimeMillis.reset(detachIfReset);
+            mRxTimeMillis.reset(detachIfReset);
+            for (LongSamplingCounter counter : mTxTimeMillis) {
+                counter.reset(detachIfReset);
+            }
+            mPowerDrainMaMs.reset(detachIfReset);
+        }
+
+        public void detach() {
+            mIdleTimeMillis.detach();
+            mRxTimeMillis.detach();
+            for (LongSamplingCounter counter : mTxTimeMillis) {
+                counter.detach();
+            }
+            mPowerDrainMaMs.detach();
+        }
+
+        /**
+         * @return a LongSamplingCounter, measuring time spent in the idle state in
+         * milliseconds.
+         */
+        @Override
+        public LongSamplingCounter getIdleTimeCounter() {
+            return mRxTimeMillis;
+        }
+
+        /**
+         * @return a LongSamplingCounter, measuring time spent in the receive state in
+         * milliseconds.
+         */
+        @Override
+        public LongSamplingCounter getRxTimeCounter() {
+            return mRxTimeMillis;
+        }
+
+        /**
+         * @return a LongSamplingCounter[], measuring time spent in various transmit states in
+         * milliseconds.
+         */
+        @Override
+        public LongSamplingCounter[] getTxTimeCounters() {
+            return mTxTimeMillis;
+        }
+
+        /**
+         * @return a LongSamplingCounter, measuring power use in milli-ampere milliseconds (mAmS).
+         */
+        @Override
+        public LongSamplingCounter getPowerCounter() {
+            return mPowerDrainMaMs;
+        }
+    }
+
     /*
      * Get the wakeup reason counter, and create a new one if one
      * doesn't already exist.
@@ -3198,7 +3355,7 @@
                 mMobileRadioActivePerAppTimer.startRunningLocked(elapsedRealtime);
             } else {
                 mMobileRadioActiveTimer.stopRunningLocked(realElapsedRealtimeMs);
-                updateMobileRadioStateLocked(realElapsedRealtimeMs);
+                updateMobileRadioStateLocked(realElapsedRealtimeMs, null);
                 mMobileRadioActivePerAppTimer.stopRunningLocked(realElapsedRealtimeMs);
             }
         }
@@ -4144,8 +4301,7 @@
         // During device boot, qtaguid isn't enabled until after the inital
         // loading of battery stats. Now that they're enabled, take our initial
         // snapshot for future delta calculation.
-        final long elapsedRealtimeMs = SystemClock.elapsedRealtime();
-        updateMobileRadioStateLocked(elapsedRealtimeMs);
+        updateMobileRadioStateLocked(SystemClock.elapsedRealtime(), null);
         updateWifiStateLocked(null);
     }
 
@@ -4328,26 +4484,34 @@
         return mWifiSignalStrengthsTimer[strengthBin].getCountLocked(which);
     }
 
-    @Override public boolean hasBluetoothActivityReporting() {
-        return mHasBluetoothEnergyReporting;
+    @Override
+    public ControllerActivityCounter getBluetoothControllerActivity() {
+        return mBluetoothActivity;
     }
 
-    @Override public long getBluetoothControllerActivity(int type, int which) {
-        if (type >= 0 && type < mBluetoothActivityCounters.length) {
-            return mBluetoothActivityCounters[type].getCountLocked(which);
-        }
-        return 0;
+    @Override
+    public ControllerActivityCounter getWifiControllerActivity() {
+        return mWifiActivity;
     }
 
-    @Override public boolean hasWifiActivityReporting() {
-        return mHasWifiEnergyReporting;
+    @Override
+    public ControllerActivityCounter getModemControllerActivity() {
+        return mModemActivity;
     }
 
-    @Override public long getWifiControllerActivity(int type, int which) {
-        if (type >= 0 && type < mWifiActivityCounters.length) {
-            return mWifiActivityCounters[type].getCountLocked(which);
-        }
-        return 0;
+    @Override
+    public boolean hasBluetoothActivityReporting() {
+        return mHasBluetoothReporting;
+    }
+
+    @Override
+    public boolean hasWifiActivityReporting() {
+        return mHasWifiReporting;
+    }
+
+    @Override
+    public boolean hasModemActivityReporting() {
+        return mHasModemReporting;
     }
 
     @Override
@@ -4457,15 +4621,21 @@
 
         /**
          * The amount of time this uid has kept the WiFi controller in idle, tx, and rx mode.
+         * Can be null if the UID has had no such activity.
          */
-        LongSamplingCounter[] mWifiControllerTime =
-                new LongSamplingCounter[NUM_CONTROLLER_ACTIVITY_TYPES];
+        private ControllerActivityCounterImpl mWifiControllerActivity;
 
         /**
          * The amount of time this uid has kept the Bluetooth controller in idle, tx, and rx mode.
+         * Can be null if the UID has had no such activity.
          */
-        LongSamplingCounter[] mBluetoothControllerTime =
-                new LongSamplingCounter[NUM_CONTROLLER_ACTIVITY_TYPES];
+        private ControllerActivityCounterImpl mBluetoothControllerActivity;
+
+        /**
+         * The amount of time this uid has kept the Modem controller in idle, tx, and rx mode.
+         * Can be null if the UID has had no such activity.
+         */
+        private ControllerActivityCounterImpl mModemControllerActivity;
 
         /**
          * The CPU times we had at the last history details update.
@@ -4684,18 +4854,43 @@
             }
         }
 
-        public void noteWifiControllerActivityLocked(int type, long timeMs) {
-            if (mWifiControllerTime[type] == null) {
-                mWifiControllerTime[type] = new LongSamplingCounter(mOnBatteryTimeBase);
-            }
-            mWifiControllerTime[type].addCountLocked(timeMs);
+        @Override
+        public ControllerActivityCounter getWifiControllerActivity() {
+            return mWifiControllerActivity;
         }
 
-        public void noteBluetoothControllerActivityLocked(int type, long timeMs) {
-            if (mBluetoothControllerTime[type] == null) {
-                mBluetoothControllerTime[type] = new LongSamplingCounter(mOnBatteryTimeBase);
+        @Override
+        public ControllerActivityCounter getBluetoothControllerActivity() {
+            return mBluetoothControllerActivity;
+        }
+
+        @Override
+        public ControllerActivityCounter getModemControllerActivity() {
+            return mModemControllerActivity;
+        }
+
+        public ControllerActivityCounterImpl getOrCreateWifiControllerActivityLocked() {
+            if (mWifiControllerActivity == null) {
+                mWifiControllerActivity = new ControllerActivityCounterImpl(mOnBatteryTimeBase,
+                        NUM_BT_TX_LEVELS);
             }
-            mBluetoothControllerTime[type].addCountLocked(timeMs);
+            return mWifiControllerActivity;
+        }
+
+        public ControllerActivityCounterImpl getOrCreateBluetoothControllerActivityLocked() {
+            if (mBluetoothControllerActivity == null) {
+                mBluetoothControllerActivity = new ControllerActivityCounterImpl(mOnBatteryTimeBase,
+                        NUM_BT_TX_LEVELS);
+            }
+            return mBluetoothControllerActivity;
+        }
+
+        public ControllerActivityCounterImpl getOrCreateModemControllerActivityLocked() {
+            if (mModemControllerActivity == null) {
+                mModemControllerActivity = new ControllerActivityCounterImpl(mOnBatteryTimeBase,
+                        ModemActivityInfo.TX_POWER_LEVELS);
+            }
+            return mModemControllerActivity;
         }
 
         public StopwatchTimer createAudioTurnedOnTimerLocked() {
@@ -5083,24 +5278,6 @@
             return 0;
         }
 
-        @Override
-        public long getWifiControllerActivity(int type, int which) {
-            if (type >= 0 && type < NUM_CONTROLLER_ACTIVITY_TYPES &&
-                    mWifiControllerTime[type] != null) {
-                return mWifiControllerTime[type].getCountLocked(which);
-            }
-            return 0;
-        }
-
-        @Override
-        public long getBluetoothControllerActivity(int type, int which) {
-            if (type >= 0 && type < NUM_CONTROLLER_ACTIVITY_TYPES &&
-                    mBluetoothControllerTime[type] != null) {
-                return mBluetoothControllerTime[type].getCountLocked(which);
-            }
-            return 0;
-        }
-
         void initNetworkActivityLocked() {
             mNetworkByteActivityCounters = new LongSamplingCounter[NUM_NETWORK_ACTIVITY_TYPES];
             mNetworkPacketActivityCounters = new LongSamplingCounter[NUM_NETWORK_ACTIVITY_TYPES];
@@ -5190,14 +5367,16 @@
                 mMobileRadioActiveCount.reset(false);
             }
 
-            for (int i = 0; i < NUM_CONTROLLER_ACTIVITY_TYPES; i++) {
-                if (mWifiControllerTime[i] != null) {
-                    mWifiControllerTime[i].reset(false);
-                }
+            if (mWifiControllerActivity != null) {
+                mWifiControllerActivity.reset(false);
+            }
 
-                if (mBluetoothControllerTime[i] != null) {
-                    mBluetoothControllerTime[i].reset(false);
-                }
+            if (mBluetoothActivity != null) {
+                mBluetoothActivity.reset(false);
+            }
+
+            if (mModemActivity != null) {
+                mModemActivity.reset(false);
             }
 
             mUserCpuTime.reset(false);
@@ -5342,15 +5521,18 @@
                     }
                 }
 
-                for (int i = 0; i < NUM_CONTROLLER_ACTIVITY_TYPES; i++) {
-                    if (mWifiControllerTime[i] != null) {
-                        mWifiControllerTime[i].detach();
-                    }
-
-                    if (mBluetoothControllerTime[i] != null) {
-                        mBluetoothControllerTime[i].detach();
-                    }
+                if (mWifiControllerActivity != null) {
+                    mWifiControllerActivity.detach();
                 }
+
+                if (mBluetoothControllerActivity != null) {
+                    mBluetoothControllerActivity.detach();
+                }
+
+                if (mModemControllerActivity != null) {
+                    mModemControllerActivity.detach();
+                }
+
                 mPids.clear();
 
                 mUserCpuTime.detach();
@@ -5521,22 +5703,25 @@
                 out.writeInt(0);
             }
 
-            for (int i = 0; i < NUM_CONTROLLER_ACTIVITY_TYPES; i++) {
-                if (mWifiControllerTime[i] != null) {
-                    out.writeInt(1);
-                    mWifiControllerTime[i].writeToParcel(out);
-                } else {
-                    out.writeInt(0);
-                }
+            if (mWifiControllerActivity != null) {
+                out.writeInt(1);
+                mWifiControllerActivity.writeToParcel(out, 0);
+            } else {
+                out.writeInt(0);
             }
 
-            for (int i = 0; i < NUM_CONTROLLER_ACTIVITY_TYPES; i++) {
-                if (mBluetoothControllerTime[i] != null) {
-                    out.writeInt(1);
-                    mBluetoothControllerTime[i].writeToParcel(out);
-                } else {
-                    out.writeInt(0);
-                }
+            if (mBluetoothControllerActivity != null) {
+                out.writeInt(1);
+                mBluetoothControllerActivity.writeToParcel(out, 0);
+            } else {
+                out.writeInt(0);
+            }
+
+            if (mModemControllerActivity != null) {
+                out.writeInt(1);
+                mModemControllerActivity.writeToParcel(out, 0);
+            } else {
+                out.writeInt(0);
             }
 
             mUserCpuTime.writeToParcel(out);
@@ -5727,20 +5912,25 @@
                 mNetworkPacketActivityCounters = null;
             }
 
-            for (int i = 0; i < NUM_CONTROLLER_ACTIVITY_TYPES; i++) {
-                if (in.readInt() != 0) {
-                    mWifiControllerTime[i] = new LongSamplingCounter(mOnBatteryTimeBase, in);
-                } else {
-                    mWifiControllerTime[i] = null;
-                }
+            if (in.readInt() != 0) {
+                mWifiControllerActivity = new ControllerActivityCounterImpl(mOnBatteryTimeBase,
+                        NUM_WIFI_TX_LEVELS, in);
+            } else {
+                mWifiControllerActivity = null;
             }
 
-            for (int i = 0; i < NUM_CONTROLLER_ACTIVITY_TYPES; i++) {
-                if (in.readInt() != 0) {
-                    mBluetoothControllerTime[i] = new LongSamplingCounter(mOnBatteryTimeBase, in);
-                } else {
-                    mBluetoothControllerTime[i] = null;
-                }
+            if (in.readInt() != 0) {
+                mBluetoothControllerActivity = new ControllerActivityCounterImpl(mOnBatteryTimeBase,
+                        NUM_BT_TX_LEVELS, in);
+            } else {
+                mBluetoothControllerActivity = null;
+            }
+
+            if (in.readInt() != 0) {
+                mModemControllerActivity = new ControllerActivityCounterImpl(mOnBatteryTimeBase,
+                        ModemActivityInfo.TX_POWER_LEVELS, in);
+            } else {
+                mModemControllerActivity = null;
             }
 
             mUserCpuTime = new LongSamplingCounter(mOnBatteryTimeBase, in);
@@ -6916,10 +7106,12 @@
             mNetworkByteActivityCounters[i] = new LongSamplingCounter(mOnBatteryTimeBase);
             mNetworkPacketActivityCounters[i] = new LongSamplingCounter(mOnBatteryTimeBase);
         }
-        for (int i = 0; i < NUM_CONTROLLER_ACTIVITY_TYPES; i++) {
-            mBluetoothActivityCounters[i] = new LongSamplingCounter(mOnBatteryTimeBase);
-            mWifiActivityCounters[i] = new LongSamplingCounter(mOnBatteryTimeBase);
-        }
+        mWifiActivity = new ControllerActivityCounterImpl(mOnBatteryTimeBase, NUM_WIFI_TX_LEVELS);
+        mBluetoothActivity = new ControllerActivityCounterImpl(mOnBatteryTimeBase,
+                NUM_BT_TX_LEVELS);
+        mModemActivity = new ControllerActivityCounterImpl(mOnBatteryTimeBase,
+                ModemActivityInfo.TX_POWER_LEVELS);
+
         mMobileRadioActiveTimer = new StopwatchTimer(null, -400, null, mOnBatteryTimeBase);
         mMobileRadioActivePerAppTimer = new StopwatchTimer(null, -401, null, mOnBatteryTimeBase);
         mMobileRadioActiveAdjustedTime = new LongSamplingCounter(mOnBatteryTimeBase);
@@ -7567,10 +7759,9 @@
         for (int i=0; i<NUM_WIFI_SIGNAL_STRENGTH_BINS; i++) {
             mWifiSignalStrengthsTimer[i].reset(false);
         }
-        for (int i=0; i< NUM_CONTROLLER_ACTIVITY_TYPES; i++) {
-            mBluetoothActivityCounters[i].reset(false);
-            mWifiActivityCounters[i].reset(false);
-        }
+        mWifiActivity.reset(false);
+        mBluetoothActivity.reset(false);
+        mModemActivity.reset(false);
         mNumConnectivityChange = mLoadedNumConnectivityChange = mUnpluggedNumConnectivityChange = 0;
 
         for (int i=0; i<mUidStats.size(); i++) {
@@ -7781,7 +7972,7 @@
         }
 
         if (info != null) {
-            mHasWifiEnergyReporting = true;
+            mHasWifiReporting = true;
 
             // Measured in mAms
             final long txTimeMs = info.getControllerTxTimeMillis();
@@ -7861,8 +8052,11 @@
                                 + scanRxTimeSinceMarkMs + " ms  Tx:"
                                 + scanTxTimeSinceMarkMs + " ms)");
                     }
-                    uid.noteWifiControllerActivityLocked(CONTROLLER_RX_TIME, scanRxTimeSinceMarkMs);
-                    uid.noteWifiControllerActivityLocked(CONTROLLER_TX_TIME, scanTxTimeSinceMarkMs);
+
+                    ControllerActivityCounterImpl activityCounter =
+                            uid.getOrCreateWifiControllerActivityLocked();
+                    activityCounter.getRxTimeCounter().addCountLocked(scanRxTimeSinceMarkMs);
+                    activityCounter.getTxTimeCounters()[0].addCountLocked(scanTxTimeSinceMarkMs);
                     leftOverRxTimeMs -= scanRxTimeSinceMarkMs;
                     leftOverTxTimeMs -= scanTxTimeSinceMarkMs;
                 }
@@ -7881,7 +8075,8 @@
                         Slog.d(TAG, "  IdleTime for UID " + uid.getUid() + ": "
                                 + myIdleTimeMs + " ms");
                     }
-                    uid.noteWifiControllerActivityLocked(CONTROLLER_IDLE_TIME, myIdleTimeMs);
+                    uid.getOrCreateWifiControllerActivityLocked().getIdleTimeCounter()
+                            .addCountLocked(myIdleTimeMs);
                 }
             }
 
@@ -7898,7 +8093,8 @@
                 if (DEBUG_ENERGY) {
                     Slog.d(TAG, "  TxTime for UID " + uid.getUid() + ": " + myTxTimeMs + " ms");
                 }
-                uid.noteWifiControllerActivityLocked(CONTROLLER_TX_TIME, myTxTimeMs);
+                uid.getOrCreateWifiControllerActivityLocked().getTxTimeCounters()[0]
+                        .addCountLocked(myTxTimeMs);
             }
 
             // Distribute the remaining Rx power appropriately between all apps that received
@@ -7909,25 +8105,23 @@
                 if (DEBUG_ENERGY) {
                     Slog.d(TAG, "  RxTime for UID " + uid.getUid() + ": " + myRxTimeMs + " ms");
                 }
-                uid.noteWifiControllerActivityLocked(CONTROLLER_RX_TIME, myRxTimeMs);
+                uid.getOrCreateWifiControllerActivityLocked().getRxTimeCounter()
+                        .addCountLocked(myRxTimeMs);
             }
 
             // Any left over power use will be picked up by the WiFi category in BatteryStatsHelper.
 
             // Update WiFi controller stats.
-            mWifiActivityCounters[CONTROLLER_RX_TIME].addCountLocked(
-                    info.getControllerRxTimeMillis());
-            mWifiActivityCounters[CONTROLLER_TX_TIME].addCountLocked(
-                    info.getControllerTxTimeMillis());
-            mWifiActivityCounters[CONTROLLER_IDLE_TIME].addCountLocked(
-                    info.getControllerIdleTimeMillis());
+            mWifiActivity.getRxTimeCounter().addCountLocked(info.getControllerRxTimeMillis());
+            mWifiActivity.getTxTimeCounters()[0].addCountLocked(info.getControllerTxTimeMillis());
+            mWifiActivity.getIdleTimeCounter().addCountLocked(info.getControllerIdleTimeMillis());
 
             // POWER_WIFI_CONTROLLER_OPERATING_VOLTAGE is measured in mV, so convert to V.
             final double opVolt = mPowerProfile.getAveragePower(
                     PowerProfile.POWER_WIFI_CONTROLLER_OPERATING_VOLTAGE) / 1000.0;
             if (opVolt != 0) {
                 // We store the power drain as mAms.
-                mWifiActivityCounters[CONTROLLER_POWER_DRAIN].addCountLocked(
+                mWifiActivity.getPowerCounter().addCountLocked(
                         (long)(info.getControllerEnergyUsed() / opVolt));
             }
         }
@@ -7936,9 +8130,10 @@
     /**
      * Distribute Cell radio energy info and network traffic to apps.
      */
-    public void updateMobileRadioStateLocked(final long elapsedRealtimeMs) {
+    public void updateMobileRadioStateLocked(final long elapsedRealtimeMs,
+                                             final ModemActivityInfo activityInfo) {
         if (DEBUG_ENERGY) {
-            Slog.d(TAG, "Updating mobile radio stats");
+            Slog.d(TAG, "Updating mobile radio stats with " + activityInfo);
         }
 
         NetworkStats delta = null;
@@ -7951,60 +8146,112 @@
             return;
         }
 
-        if (delta == null || !mOnBatteryInternal) {
+        if (!mOnBatteryInternal) {
             return;
         }
 
         long radioTime = mMobileRadioActivePerAppTimer.getTimeSinceMarkLocked(
                 elapsedRealtimeMs * 1000);
         mMobileRadioActivePerAppTimer.setMark(elapsedRealtimeMs);
-        long totalPackets = delta.getTotalPackets();
 
-        final int size = delta.size();
-        for (int i = 0; i < size; i++) {
-            final NetworkStats.Entry entry = delta.getValues(i, mTmpNetworkStatsEntry);
+        long totalRxPackets = 0;
+        long totalTxPackets = 0;
+        if (delta != null) {
+            final int size = delta.size();
+            for (int i = 0; i < size; i++) {
+                final NetworkStats.Entry entry = delta.getValues(i, mTmpNetworkStatsEntry);
+                if (entry.rxPackets == 0 || entry.txPackets == 0) {
+                    continue;
+                }
 
-            if (entry.rxBytes == 0 || entry.txBytes == 0) {
-                continue;
+                if (DEBUG_ENERGY) {
+                    Slog.d(TAG, "Mobile uid " + entry.uid + ": delta rx=" + entry.rxBytes
+                            + " tx=" + entry.txBytes + " rxPackets=" + entry.rxPackets
+                            + " txPackets=" + entry.txPackets);
+                }
+
+                totalRxPackets += entry.rxPackets;
+                totalTxPackets += entry.txPackets;
+
+                final Uid u = getUidStatsLocked(mapUid(entry.uid));
+                u.noteNetworkActivityLocked(NETWORK_MOBILE_RX_DATA, entry.rxBytes, entry.rxPackets);
+                u.noteNetworkActivityLocked(NETWORK_MOBILE_TX_DATA, entry.txBytes, entry.txPackets);
+
+                mNetworkByteActivityCounters[NETWORK_MOBILE_RX_DATA].addCountLocked(
+                        entry.rxBytes);
+                mNetworkByteActivityCounters[NETWORK_MOBILE_TX_DATA].addCountLocked(
+                        entry.txBytes);
+                mNetworkPacketActivityCounters[NETWORK_MOBILE_RX_DATA].addCountLocked(
+                        entry.rxPackets);
+                mNetworkPacketActivityCounters[NETWORK_MOBILE_TX_DATA].addCountLocked(
+                        entry.txPackets);
             }
 
-            if (DEBUG_ENERGY) {
-                Slog.d(TAG, "Mobile uid " + entry.uid + ": delta rx=" + entry.rxBytes
-                        + " tx=" + entry.txBytes + " rxPackets=" + entry.rxPackets
-                        + " txPackets=" + entry.txPackets);
-            }
+            // Now distribute proportional blame to the apps that did networking.
+            long totalPackets = totalRxPackets + totalTxPackets;
+            if (totalPackets > 0) {
+                for (int i = 0; i < size; i++) {
+                    final NetworkStats.Entry entry = delta.getValues(i, mTmpNetworkStatsEntry);
+                    if (entry.rxPackets == 0 && entry.txPackets == 0) {
+                        continue;
+                    }
 
-            final Uid u = getUidStatsLocked(mapUid(entry.uid));
-            u.noteNetworkActivityLocked(NETWORK_MOBILE_RX_DATA, entry.rxBytes,
-                    entry.rxPackets);
-            u.noteNetworkActivityLocked(NETWORK_MOBILE_TX_DATA, entry.txBytes,
-                    entry.txPackets);
+                    final Uid u = getUidStatsLocked(mapUid(entry.uid));
+
+                    // Distribute total radio active time in to this app.
+                    final long appPackets = entry.rxPackets + entry.txPackets;
+                    final long appRadioTime = (radioTime * appPackets) / totalPackets;
+                    u.noteMobileRadioActiveTimeLocked(appRadioTime);
+
+                    // Remove this app from the totals, so that we don't lose any time
+                    // due to rounding.
+                    radioTime -= appRadioTime;
+                    totalPackets -= appPackets;
+
+                    if (activityInfo != null) {
+                        ControllerActivityCounterImpl activityCounter =
+                                u.getOrCreateModemControllerActivityLocked();
+                        if (entry.rxPackets != 0) {
+                            final long rxMs = (entry.rxPackets * activityInfo.getRxTimeMillis())
+                                    / totalRxPackets;
+                            activityCounter.getRxTimeCounter().addCountLocked(rxMs);
+                        }
+
+                        if (entry.txPackets != 0) {
+                            for (int lvl = 0; lvl < ModemActivityInfo.TX_POWER_LEVELS; lvl++) {
+                                long txMs = entry.txPackets * activityInfo.getTxTimeMillis()[lvl];
+                                txMs /= totalTxPackets;
+                                activityCounter.getTxTimeCounters()[lvl].addCountLocked(txMs);
+                            }
+                        }
+                    }
+                }
+            }
 
             if (radioTime > 0) {
-                // Distribute total radio active time in to this app.
-                long appPackets = entry.rxPackets + entry.txPackets;
-                long appRadioTime = (radioTime*appPackets)/totalPackets;
-                u.noteMobileRadioActiveTimeLocked(appRadioTime);
-                // Remove this app from the totals, so that we don't lose any time
-                // due to rounding.
-                radioTime -= appRadioTime;
-                totalPackets -= appPackets;
+                // Whoops, there is some radio time we can't blame on an app!
+                mMobileRadioActiveUnknownTime.addCountLocked(radioTime);
+                mMobileRadioActiveUnknownCount.addCountLocked(1);
             }
-
-            mNetworkByteActivityCounters[NETWORK_MOBILE_RX_DATA].addCountLocked(
-                    entry.rxBytes);
-            mNetworkByteActivityCounters[NETWORK_MOBILE_TX_DATA].addCountLocked(
-                    entry.txBytes);
-            mNetworkPacketActivityCounters[NETWORK_MOBILE_RX_DATA].addCountLocked(
-                    entry.rxPackets);
-            mNetworkPacketActivityCounters[NETWORK_MOBILE_TX_DATA].addCountLocked(
-                    entry.txPackets);
         }
 
-        if (radioTime > 0) {
-            // Whoops, there is some radio time we can't blame on an app!
-            mMobileRadioActiveUnknownTime.addCountLocked(radioTime);
-            mMobileRadioActiveUnknownCount.addCountLocked(1);
+        if (activityInfo != null) {
+            mHasModemReporting = true;
+            mModemActivity.getIdleTimeCounter().addCountLocked(activityInfo.getIdleTimeMillis());
+            mModemActivity.getRxTimeCounter().addCountLocked(activityInfo.getRxTimeMillis());
+            for (int lvl = 0; lvl < ModemActivityInfo.TX_POWER_LEVELS; lvl++) {
+                mModemActivity.getTxTimeCounters()[lvl]
+                        .addCountLocked(activityInfo.getTxTimeMillis()[lvl]);
+            }
+
+            // POWER_MODEM_CONTROLLER_OPERATING_VOLTAGE is measured in mV, so convert to V.
+            final double opVolt = mPowerProfile.getAveragePower(
+                    PowerProfile.POWER_MODEM_CONTROLLER_OPERATING_VOLTAGE) / 1000.0;
+            if (opVolt != 0) {
+                // We store the power drain as mAms.
+                mModemActivity.getPowerCounter().addCountLocked(
+                        (long) (activityInfo.getEnergyUsed() / opVolt));
+            }
         }
     }
 
@@ -8018,12 +8265,12 @@
         }
 
         if (info != null && mOnBatteryInternal) {
-            mHasBluetoothEnergyReporting = true;
-            mBluetoothActivityCounters[CONTROLLER_RX_TIME].addCountLocked(
+            mHasBluetoothReporting = true;
+            mBluetoothActivity.getRxTimeCounter().addCountLocked(
                     info.getControllerRxTimeMillis());
-            mBluetoothActivityCounters[CONTROLLER_TX_TIME].addCountLocked(
+            mBluetoothActivity.getTxTimeCounters()[0].addCountLocked(
                     info.getControllerTxTimeMillis());
-            mBluetoothActivityCounters[CONTROLLER_IDLE_TIME].addCountLocked(
+            mBluetoothActivity.getIdleTimeCounter().addCountLocked(
                     info.getControllerIdleTimeMillis());
 
             // POWER_BLUETOOTH_CONTROLLER_OPERATING_VOLTAGE is measured in mV, so convert to V.
@@ -8031,7 +8278,7 @@
                     PowerProfile.POWER_BLUETOOTH_CONTROLLER_OPERATING_VOLTAGE) / 1000.0;
             if (opVolt != 0) {
                 // We store the power drain as mAms.
-                mBluetoothActivityCounters[CONTROLLER_POWER_DRAIN].addCountLocked(
+                mBluetoothActivity.getPowerCounter().addCountLocked(
                         (long) (info.getControllerEnergyUsed() / opVolt));
             }
 
@@ -9337,12 +9584,12 @@
         for (int i=0; i<NUM_WIFI_SIGNAL_STRENGTH_BINS; i++) {
             mWifiSignalStrengthsTimer[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);
-        }
+        mWifiActivity.readSummaryFromParcel(in);
+        mBluetoothActivity.readSummaryFromParcel(in);
+        mModemActivity.readSummaryFromParcel(in);
+        mHasWifiReporting = in.readInt() != 0;
+        mHasBluetoothReporting = in.readInt() != 0;
+        mHasModemReporting = in.readInt() != 0;
 
         mNumConnectivityChange = mLoadedNumConnectivityChange = in.readInt();
         mFlashlightOnNesting = 0;
@@ -9671,12 +9918,13 @@
         for (int i=0; i<NUM_WIFI_SIGNAL_STRENGTH_BINS; i++) {
             mWifiSignalStrengthsTimer[i].writeSummaryFromParcelLocked(out, NOWREAL_SYS);
         }
-        for (int i=0; i< NUM_CONTROLLER_ACTIVITY_TYPES; i++) {
-            mBluetoothActivityCounters[i].writeSummaryFromParcelLocked(out);
-        }
-        for (int i=0; i< NUM_CONTROLLER_ACTIVITY_TYPES; i++) {
-            mWifiActivityCounters[i].writeSummaryFromParcelLocked(out);
-        }
+        mWifiActivity.writeSummaryToParcel(out);
+        mBluetoothActivity.writeSummaryToParcel(out);
+        mModemActivity.writeSummaryToParcel(out);
+        out.writeInt(mHasWifiReporting ? 1 : 0);
+        out.writeInt(mHasBluetoothReporting ? 1 : 0);
+        out.writeInt(mHasModemReporting ? 1 : 0);
+
         out.writeInt(mNumConnectivityChange);
         mFlashlightOnTimer.writeSummaryFromParcelLocked(out, NOWREAL_SYS);
         mCameraOnTimer.writeSummaryFromParcelLocked(out, NOWREAL_SYS);
@@ -10019,15 +10267,17 @@
             mWifiSignalStrengthsTimer[i] = new StopwatchTimer(null, -800-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);
-        }
 
-        mHasWifiEnergyReporting = in.readInt() != 0;
-        mHasBluetoothEnergyReporting = in.readInt() != 0;
+        mWifiActivity = new ControllerActivityCounterImpl(mOnBatteryTimeBase,
+                NUM_WIFI_TX_LEVELS, in);
+        mBluetoothActivity = new ControllerActivityCounterImpl(mOnBatteryTimeBase,
+                NUM_BT_TX_LEVELS, in);
+        mModemActivity = new ControllerActivityCounterImpl(mOnBatteryTimeBase,
+                ModemActivityInfo.TX_POWER_LEVELS, in);
+        mHasWifiReporting = in.readInt() != 0;
+        mHasBluetoothReporting = in.readInt() != 0;
+        mHasModemReporting = in.readInt() != 0;
+
         mNumConnectivityChange = in.readInt();
         mLoadedNumConnectivityChange = in.readInt();
         mUnpluggedNumConnectivityChange = in.readInt();
@@ -10174,14 +10424,13 @@
         for (int i=0; i<NUM_WIFI_SIGNAL_STRENGTH_BINS; i++) {
             mWifiSignalStrengthsTimer[i].writeToParcel(out, uSecRealtime);
         }
-        for (int i=0; i< NUM_CONTROLLER_ACTIVITY_TYPES; i++) {
-            mBluetoothActivityCounters[i].writeToParcel(out);
-        }
-        for (int i=0; i< NUM_CONTROLLER_ACTIVITY_TYPES; i++) {
-            mWifiActivityCounters[i].writeToParcel(out);
-        }
-        out.writeInt(mHasWifiEnergyReporting ? 1 : 0);
-        out.writeInt(mHasBluetoothEnergyReporting ? 1 : 0);
+        mWifiActivity.writeToParcel(out, 0);
+        mBluetoothActivity.writeToParcel(out, 0);
+        mModemActivity.writeToParcel(out, 0);
+        out.writeInt(mHasWifiReporting ? 1 : 0);
+        out.writeInt(mHasBluetoothReporting ? 1 : 0);
+        out.writeInt(mHasModemReporting ? 1 : 0);
+
         out.writeInt(mNumConnectivityChange);
         out.writeInt(mLoadedNumConnectivityChange);
         out.writeInt(mUnpluggedNumConnectivityChange);
diff --git a/core/java/com/android/internal/os/BluetoothPowerCalculator.java b/core/java/com/android/internal/os/BluetoothPowerCalculator.java
index 1f59672..531d1fa 100644
--- a/core/java/com/android/internal/os/BluetoothPowerCalculator.java
+++ b/core/java/com/android/internal/os/BluetoothPowerCalculator.java
@@ -40,15 +40,15 @@
     @Override
     public void calculateRemaining(BatterySipper app, BatteryStats stats, long rawRealtimeUs,
                                    long rawUptimeUs, int statsType) {
-        final long idleTimeMs = stats.getBluetoothControllerActivity(
-                BatteryStats.CONTROLLER_IDLE_TIME, statsType);
-        final long txTimeMs = stats.getBluetoothControllerActivity(
-                BatteryStats.CONTROLLER_TX_TIME, statsType);
-        final long rxTimeMs = stats.getBluetoothControllerActivity(
-                BatteryStats.CONTROLLER_RX_TIME, statsType);
+        final BatteryStats.ControllerActivityCounter counter =
+                stats.getBluetoothControllerActivity();
+
+        final long idleTimeMs = counter.getIdleTimeCounter().getCountLocked(statsType);
+        final long txTimeMs = counter.getTxTimeCounters()[0].getCountLocked(statsType);
+        final long rxTimeMs = counter.getRxTimeCounter().getCountLocked(statsType);
         final long totalTimeMs = idleTimeMs + txTimeMs + rxTimeMs;
-        double powerMah = stats.getBluetoothControllerActivity(
-                BatteryStats.CONTROLLER_POWER_DRAIN, statsType) / (double)(1000*60*60);
+        double powerMah = counter.getPowerCounter().getCountLocked(statsType)
+                 / (double)(1000*60*60);
 
         if (powerMah == 0) {
             // Some devices do not report the power, so calculate it.
diff --git a/core/java/com/android/internal/os/PowerProfile.java b/core/java/com/android/internal/os/PowerProfile.java
index aaa9f73..14ebe22 100644
--- a/core/java/com/android/internal/os/PowerProfile.java
+++ b/core/java/com/android/internal/os/PowerProfile.java
@@ -93,6 +93,12 @@
     public static final String POWER_BLUETOOTH_CONTROLLER_OPERATING_VOLTAGE =
             "bluetooth.controller.voltage";
 
+    public static final String POWER_MODEM_CONTROLLER_IDLE = "modem.controller.idle";
+    public static final String POWER_MODEM_CONTROLLER_RX = "modem.controller.rx";
+    public static final String POWER_MODEM_CONTROLLER_TX = "modem.controller.tx";
+    public static final String POWER_MODEM_CONTROLLER_OPERATING_VOLTAGE =
+            "modem.controller.voltage";
+
     /**
      * Power consumption when GPS is on.
      */
@@ -100,17 +106,23 @@
 
     /**
      * Power consumption when Bluetooth driver is on.
+     * @deprecated
      */
+    @Deprecated
     public static final String POWER_BLUETOOTH_ON = "bluetooth.on";
 
     /**
      * Power consumption when Bluetooth driver is transmitting/receiving.
+     * @deprecated
      */
+    @Deprecated
     public static final String POWER_BLUETOOTH_ACTIVE = "bluetooth.active";
 
     /**
      * Power consumption when Bluetooth driver gets an AT command.
+     * @deprecated
      */
+    @Deprecated
     public static final String POWER_BLUETOOTH_AT_CMD = "bluetooth.at";
 
 
diff --git a/core/java/com/android/internal/os/WifiPowerCalculator.java b/core/java/com/android/internal/os/WifiPowerCalculator.java
index 146c0f8..2a27f70 100644
--- a/core/java/com/android/internal/os/WifiPowerCalculator.java
+++ b/core/java/com/android/internal/os/WifiPowerCalculator.java
@@ -39,10 +39,14 @@
     @Override
     public void calculateApp(BatterySipper app, BatteryStats.Uid u, long rawRealtimeUs,
                              long rawUptimeUs, int statsType) {
-        final long idleTime = u.getWifiControllerActivity(BatteryStats.CONTROLLER_IDLE_TIME,
-                statsType);
-        final long txTime = u.getWifiControllerActivity(BatteryStats.CONTROLLER_TX_TIME, statsType);
-        final long rxTime = u.getWifiControllerActivity(BatteryStats.CONTROLLER_RX_TIME, statsType);
+        final BatteryStats.ControllerActivityCounter counter = u.getWifiControllerActivity();
+        if (counter == null) {
+            return;
+        }
+
+        final long idleTime = counter.getIdleTimeCounter().getCountLocked(statsType);
+        final long txTime = counter.getTxTimeCounters()[0].getCountLocked(statsType);
+        final long rxTime = counter.getRxTimeCounter().getCountLocked(statsType);
         app.wifiRunningTimeMs = idleTime + rxTime + txTime;
         app.wifiPowerMah =
                 ((idleTime * mIdleCurrentMa) + (txTime * mTxCurrentMa) + (rxTime * mRxCurrentMa))
@@ -67,16 +71,15 @@
     @Override
     public void calculateRemaining(BatterySipper app, BatteryStats stats, long rawRealtimeUs,
                                    long rawUptimeUs, int statsType) {
-        final long idleTimeMs = stats.getWifiControllerActivity(BatteryStats.CONTROLLER_IDLE_TIME,
-                statsType);
-        final long rxTimeMs = stats.getWifiControllerActivity(BatteryStats.CONTROLLER_RX_TIME,
-                statsType);
-        final long txTimeMs = stats.getWifiControllerActivity(BatteryStats.CONTROLLER_TX_TIME,
-                statsType);
+        final BatteryStats.ControllerActivityCounter counter = stats.getWifiControllerActivity();
+
+        final long idleTimeMs = counter.getIdleTimeCounter().getCountLocked(statsType);
+        final long txTimeMs = counter.getTxTimeCounters()[0].getCountLocked(statsType);
+        final long rxTimeMs = counter.getRxTimeCounter().getCountLocked(statsType);
         app.wifiRunningTimeMs = idleTimeMs + rxTimeMs + txTimeMs;
 
-        double powerDrainMah = stats.getWifiControllerActivity(BatteryStats.CONTROLLER_POWER_DRAIN,
-                statsType) / (double)(1000*60*60);
+        double powerDrainMah = counter.getPowerCounter().getCountLocked(statsType)
+                / (double)(1000*60*60);
         if (powerDrainMah == 0) {
             // Some controllers do not report power drain, so we can calculate it here.
             powerDrainMah = ((idleTimeMs * mIdleCurrentMa) + (txTimeMs * mTxCurrentMa)
diff --git a/core/java/com/android/internal/util/GrowingArrayUtils.java b/core/java/com/android/internal/util/GrowingArrayUtils.java
index b4d2d730..968d920 100644
--- a/core/java/com/android/internal/util/GrowingArrayUtils.java
+++ b/core/java/com/android/internal/util/GrowingArrayUtils.java
@@ -97,6 +97,21 @@
     }
 
     /**
+     * Primitive float version of {@link #append(Object[], int, Object)}.
+     */
+    public static float[] append(float[] array, int currentSize, float element) {
+        assert currentSize <= array.length;
+
+        if (currentSize + 1 > array.length) {
+            float[] newArray = ArrayUtils.newUnpaddedFloatArray(growSize(currentSize));
+            System.arraycopy(array, 0, newArray, 0, currentSize);
+            array = newArray;
+        }
+        array[currentSize] = element;
+        return array;
+    }
+
+    /**
      * Inserts an element into the array at the specified index, growing the array if there is no
      * more room.
      *
diff --git a/core/java/com/android/internal/widget/FloatingToolbar.java b/core/java/com/android/internal/widget/FloatingToolbar.java
index 3a00469..976ef4b 100644
--- a/core/java/com/android/internal/widget/FloatingToolbar.java
+++ b/core/java/com/android/internal/widget/FloatingToolbar.java
@@ -1264,7 +1264,7 @@
         }
 
         private void maybeComputeTransitionDurationScale() {
-            if (mMainPanelSize == null || mOverflowPanel == null) {
+            if (mMainPanelSize != null && mOverflowPanelSize != null) {
                 int w = mMainPanelSize.getWidth() - mOverflowPanelSize.getWidth();
                 int h = mOverflowPanelSize.getHeight() - mMainPanelSize.getHeight();
                 mTransitionDurationScale = (int) (Math.sqrt(w * w + h * h) /
diff --git a/core/java/com/android/internal/widget/ILockSettings.aidl b/core/java/com/android/internal/widget/ILockSettings.aidl
index 4e4552d..b07e36a 100644
--- a/core/java/com/android/internal/widget/ILockSettings.aidl
+++ b/core/java/com/android/internal/widget/ILockSettings.aidl
@@ -39,4 +39,5 @@
     void registerStrongAuthTracker(in IStrongAuthTracker tracker);
     void unregisterStrongAuthTracker(in IStrongAuthTracker tracker);
     void requireStrongAuth(int strongAuthReason, int userId);
+    void systemReady();
 }
diff --git a/core/jni/android_graphics_drawable_VectorDrawable.cpp b/core/jni/android_graphics_drawable_VectorDrawable.cpp
index 53d4c6a..563ec8b 100644
--- a/core/jni/android_graphics_drawable_VectorDrawable.cpp
+++ b/core/jni/android_graphics_drawable_VectorDrawable.cpp
@@ -90,6 +90,18 @@
             strokeLineJoin);
 }
 
+static void updateFullPathFillGradient(JNIEnv*, jobject, jlong pathPtr, jlong fillGradientPtr) {
+    VectorDrawable::FullPath* path = reinterpret_cast<VectorDrawable::FullPath*>(pathPtr);
+    SkShader* fillShader = reinterpret_cast<SkShader*>(fillGradientPtr);
+    path->setFillGradient(fillShader);
+}
+
+static void updateFullPathStrokeGradient(JNIEnv*, jobject, jlong pathPtr, jlong strokeGradientPtr) {
+    VectorDrawable::FullPath* path = reinterpret_cast<VectorDrawable::FullPath*>(pathPtr);
+    SkShader* strokeShader = reinterpret_cast<SkShader*>(strokeGradientPtr);
+    path->setStrokeGradient(strokeShader);
+}
+
 static jboolean getFullPathProperties(JNIEnv* env, jobject, jlong fullPathPtr,
         jbyteArray outProperties, jint length) {
     VectorDrawable::FullPath* fullPath = reinterpret_cast<VectorDrawable::FullPath*>(fullPathPtr);
@@ -331,6 +343,8 @@
         {"nCreateFullPath", "!()J", (void*)createEmptyFullPath},
         {"nCreateFullPath", "!(J)J", (void*)createFullPath},
         {"nUpdateFullPathProperties", "!(JFIFIFFFFFII)V", (void*)updateFullPathPropertiesAndStrokeStyles},
+        {"nUpdateFullPathFillGradient", "!(JJ)V", (void*)updateFullPathFillGradient},
+        {"nUpdateFullPathStrokeGradient", "!(JJ)V", (void*)updateFullPathStrokeGradient},
         {"nGetFullPathProperties", "(J[BI)Z", (void*)getFullPathProperties},
         {"nGetGroupProperties", "(J[FI)Z", (void*)getGroupProperties},
 
diff --git a/core/jni/android_hardware_Camera.cpp b/core/jni/android_hardware_Camera.cpp
index 414eba7..806fcc3 100644
--- a/core/jni/android_hardware_Camera.cpp
+++ b/core/jni/android_hardware_Camera.cpp
@@ -534,7 +534,7 @@
     if (halVersion == CAMERA_HAL_API_VERSION_NORMAL_CONNECT) {
         // Default path: hal version is don't care, do normal camera connect.
         camera = Camera::connect(cameraId, clientName,
-                Camera::USE_CALLING_UID);
+                Camera::USE_CALLING_UID, Camera::USE_CALLING_PID);
     } else {
         jint status = Camera::connectLegacy(cameraId, halVersion, clientName,
                 Camera::USE_CALLING_UID, camera);
diff --git a/core/jni/android_hardware_SensorManager.cpp b/core/jni/android_hardware_SensorManager.cpp
index 2e5cda0..4842e1b 100644
--- a/core/jni/android_hardware_SensorManager.cpp
+++ b/core/jni/android_hardware_SensorManager.cpp
@@ -23,6 +23,7 @@
 
 #include <utils/Log.h>
 #include <utils/Looper.h>
+#include <utils/Vector.h>
 
 #include <gui/Sensor.h>
 #include <gui/SensorManager.h>
@@ -39,12 +40,14 @@
     jclass clazz;
     jmethodID dispatchSensorEvent;
     jmethodID dispatchFlushCompleteEvent;
+    jmethodID dispatchAdditionalInfoEvent;
 } gBaseEventQueueClassInfo;
 
 namespace android {
 
 struct SensorOffsets
 {
+    jclass      clazz;
     jfieldID    name;
     jfieldID    vendor;
     jfieldID    version;
@@ -60,8 +63,13 @@
     jfieldID    maxDelay;
     jfieldID    flags;
     jmethodID   setType;
+    jmethodID   init;
 } gSensorOffsets;
 
+struct ListOffsets {
+    jclass      clazz;
+    jmethodID   add;
+} gListOffsets;
 
 /*
  * The method below are not thread-safe and not intended to be
@@ -70,8 +78,10 @@
 static void
 nativeClassInit (JNIEnv *_env, jclass _this)
 {
-    jclass sensorClass = _env->FindClass("android/hardware/Sensor");
+    //android.hardware.Sensor
     SensorOffsets& sensorOffsets = gSensorOffsets;
+    jclass sensorClass = (jclass) _env->NewGlobalRef(_env->FindClass("android/hardware/Sensor"));
+    sensorOffsets.clazz       = sensorClass;
     sensorOffsets.name        = _env->GetFieldID(sensorClass, "mName",      "Ljava/lang/String;");
     sensorOffsets.vendor      = _env->GetFieldID(sensorClass, "mVendor",    "Ljava/lang/String;");
     sensorOffsets.version     = _env->GetFieldID(sensorClass, "mVersion",   "I");
@@ -88,7 +98,15 @@
                                                         "Ljava/lang/String;");
     sensorOffsets.maxDelay    = _env->GetFieldID(sensorClass, "mMaxDelay",  "I");
     sensorOffsets.flags = _env->GetFieldID(sensorClass, "mFlags",  "I");
+
     sensorOffsets.setType = _env->GetMethodID(sensorClass, "setType", "(I)Z");
+    sensorOffsets.init = _env->GetMethodID(sensorClass, "<init>", "()V");
+
+    // java.util.List;
+    ListOffsets& listOffsets = gListOffsets;
+    jclass listClass = (jclass) _env->NewGlobalRef(_env->FindClass("java/util/List"));
+    listOffsets.clazz = listClass;
+    listOffsets.add = _env->GetMethodID(listClass, "add", "(Ljava/lang/Object;)Z");
 }
 
 /**
@@ -141,6 +159,46 @@
     return (jlong) &SensorManager::getInstanceForPackage(String16(opPackageNameUtf.c_str()));
 }
 
+static jobject
+translateNativeSensorToJavaSensor(JNIEnv *env, jobject sensor, const Sensor& nativeSensor) {
+    const SensorOffsets& sensorOffsets(gSensorOffsets);
+
+    if (sensor == NULL) {
+        // Sensor sensor = new Sensor();
+        sensor = env->NewObject(sensorOffsets.clazz, sensorOffsets.init, "");
+    }
+
+    if (sensor != NULL) {
+        jstring name = env->NewStringUTF(nativeSensor.getName().string());
+        jstring vendor = env->NewStringUTF(nativeSensor.getVendor().string());
+        jstring requiredPermission =
+                env->NewStringUTF(nativeSensor.getRequiredPermission().string());
+
+        env->SetObjectField(sensor, sensorOffsets.name,      name);
+        env->SetObjectField(sensor, sensorOffsets.vendor,    vendor);
+        env->SetIntField(sensor, sensorOffsets.version,      nativeSensor.getVersion());
+        env->SetIntField(sensor, sensorOffsets.handle,       nativeSensor.getHandle());
+        env->SetFloatField(sensor, sensorOffsets.range,      nativeSensor.getMaxValue());
+        env->SetFloatField(sensor, sensorOffsets.resolution, nativeSensor.getResolution());
+        env->SetFloatField(sensor, sensorOffsets.power,      nativeSensor.getPowerUsage());
+        env->SetIntField(sensor, sensorOffsets.minDelay,     nativeSensor.getMinDelay());
+        env->SetIntField(sensor, sensorOffsets.fifoReservedEventCount,
+                         nativeSensor.getFifoReservedEventCount());
+        env->SetIntField(sensor, sensorOffsets.fifoMaxEventCount,
+                         nativeSensor.getFifoMaxEventCount());
+        env->SetObjectField(sensor, sensorOffsets.requiredPermission,
+                            requiredPermission);
+        env->SetIntField(sensor, sensorOffsets.maxDelay, nativeSensor.getMaxDelay());
+        env->SetIntField(sensor, sensorOffsets.flags, nativeSensor.getFlags());
+        if (env->CallBooleanMethod(sensor, sensorOffsets.setType, nativeSensor.getType())
+                == JNI_FALSE) {
+            jstring stringType = getInternedString(env, &nativeSensor.getStringType());
+            env->SetObjectField(sensor, sensorOffsets.stringType, stringType);
+        }
+    }
+    return sensor;
+}
+
 static jboolean
 nativeGetSensorAtIndex(JNIEnv *env, jclass clazz, jlong sensorManager, jobject sensor, jint index)
 {
@@ -180,6 +238,24 @@
     return true;
 }
 
+static void
+nativeGetDynamicSensors(JNIEnv *env, jclass clazz, jlong sensorManager, jobject sensorList) {
+
+    SensorManager* mgr = reinterpret_cast<SensorManager*>(sensorManager);
+    const ListOffsets& listOffsets(gListOffsets);
+
+    Vector<Sensor> nativeList;
+
+    mgr->getDynamicSensorList(nativeList);
+
+    ALOGI("DYNS native SensorManager.getDynamicSensorList return %d sensors", nativeList.size());
+    for (size_t i = 0; i < nativeList.size(); ++i) {
+        jobject sensor = translateNativeSensorToJavaSensor(env, NULL, nativeList[i]);
+        // add to list
+        env->CallBooleanMethod(sensorList, listOffsets.add, sensor);
+    }
+}
+
 static jboolean nativeIsDataInjectionEnabled(JNIEnv *_env, jclass _this, jlong sensorManager) {
     SensorManager* mgr = reinterpret_cast<SensorManager*>(sensorManager);
     return mgr->isDataInjectionEnabled();
@@ -191,21 +267,25 @@
     sp<SensorEventQueue> mSensorQueue;
     sp<MessageQueue> mMessageQueue;
     jobject mReceiverWeakGlobal;
-    jfloatArray mScratch;
+    jfloatArray mFloatScratch;
+    jintArray   mIntScratch;
 public:
     Receiver(const sp<SensorEventQueue>& sensorQueue,
             const sp<MessageQueue>& messageQueue,
-            jobject receiverWeak, jfloatArray scratch) {
+            jobject receiverWeak) {
         JNIEnv* env = AndroidRuntime::getJNIEnv();
         mSensorQueue = sensorQueue;
         mMessageQueue = messageQueue;
         mReceiverWeakGlobal = env->NewGlobalRef(receiverWeak);
-        mScratch = (jfloatArray)env->NewGlobalRef(scratch);
+
+        mIntScratch = (jintArray) env->NewGlobalRef(env->NewIntArray(16));
+        mFloatScratch = (jfloatArray) env->NewGlobalRef(env->NewFloatArray(16));
     }
     ~Receiver() {
         JNIEnv* env = AndroidRuntime::getJNIEnv();
         env->DeleteGlobalRef(mReceiverWeakGlobal);
-        env->DeleteGlobalRef(mScratch);
+        env->DeleteGlobalRef(mFloatScratch);
+        env->DeleteGlobalRef(mIntScratch);
     }
     sp<SensorEventQueue> getSensorEventQueue() const {
         return mSensorQueue;
@@ -234,9 +314,19 @@
                 if (buffer[i].type == SENSOR_TYPE_STEP_COUNTER) {
                     // step-counter returns a uint64, but the java API only deals with floats
                     float value = float(buffer[i].u64.step_counter);
-                    env->SetFloatArrayRegion(mScratch, 0, 1, &value);
+                    env->SetFloatArrayRegion(mFloatScratch, 0, 1, &value);
+                } else if (buffer[i].type == SENSOR_TYPE_DYNAMIC_SENSOR_META) {
+                    float value[2];
+                    value[0] = buffer[i].dynamic_sensor_meta.connected ? 1.f: 0.f;
+                    value[1] = float(buffer[i].dynamic_sensor_meta.handle);
+                    env->SetFloatArrayRegion(mFloatScratch, 0, 2, value);
+                } else if (buffer[i].type == SENSOR_TYPE_ADDITIONAL_INFO) {
+                    env->SetIntArrayRegion(mIntScratch, 0, 14,
+                                           buffer[i].additional_info.data_int32);
+                    env->SetFloatArrayRegion(mFloatScratch, 0, 14,
+                                             buffer[i].additional_info.data_float);
                 } else {
-                    env->SetFloatArrayRegion(mScratch, 0, 16, buffer[i].data);
+                    env->SetFloatArrayRegion(mFloatScratch, 0, 16, buffer[i].data);
                 }
 
                 if (buffer[i].type == SENSOR_TYPE_META_DATA) {
@@ -247,7 +337,21 @@
                                             gBaseEventQueueClassInfo.dispatchFlushCompleteEvent,
                                             buffer[i].meta_data.sensor);
                     }
-                } else {
+                } else if (buffer[i].type == SENSOR_TYPE_ADDITIONAL_INFO) {
+                    // This is a flush complete sensor event. Call dispatchAdditionalInfoEvent
+                    // method.
+                    if (receiverObj.get()) {
+                        int type = buffer[i].additional_info.type;
+                        int serial = buffer[i].additional_info.serial;
+                        env->CallVoidMethod(receiverObj.get(),
+                                            gBaseEventQueueClassInfo.dispatchAdditionalInfoEvent,
+                                            buffer[i].sensor,
+                                            type, serial,
+                                            mFloatScratch,
+                                            mIntScratch,
+                                            buffer[i].timestamp);
+                    }
+                }else {
                     int8_t status;
                     switch (buffer[i].type) {
                     case SENSOR_TYPE_ORIENTATION:
@@ -269,7 +373,7 @@
                         env->CallVoidMethod(receiverObj.get(),
                                             gBaseEventQueueClassInfo.dispatchSensorEvent,
                                             buffer[i].sensor,
-                                            mScratch,
+                                            mFloatScratch,
                                             status,
                                             buffer[i].timestamp);
                     }
@@ -290,7 +394,7 @@
 };
 
 static jlong nativeInitSensorEventQueue(JNIEnv *env, jclass clazz, jlong sensorManager,
-        jobject eventQWeak, jobject msgQ, jfloatArray scratch, jstring packageName, jint mode) {
+        jobject eventQWeak, jobject msgQ, jstring packageName, jint mode) {
     SensorManager* mgr = reinterpret_cast<SensorManager*>(sensorManager);
     ScopedUtfChars packageUtf(env, packageName);
     String8 clientName(packageUtf.c_str());
@@ -302,7 +406,7 @@
         return 0;
     }
 
-    sp<Receiver> receiver = new Receiver(queue, messageQueue, eventQWeak, scratch);
+    sp<Receiver> receiver = new Receiver(queue, messageQueue, eventQWeak);
     receiver->incStrong((void*)nativeInitSensorEventQueue);
     return jlong(receiver.get());
 }
@@ -355,6 +459,10 @@
             "(JLandroid/hardware/Sensor;I)Z",
             (void*)nativeGetSensorAtIndex },
 
+    {"nativeGetDynamicSensors",
+            "(JLjava/util/List;)V",
+            (void*)nativeGetDynamicSensors },
+
     {"nativeIsDataInjectionEnabled",
             "(J)Z",
             (void*)nativeIsDataInjectionEnabled},
@@ -362,7 +470,7 @@
 
 static const JNINativeMethod gBaseEventQueueMethods[] = {
     {"nativeInitBaseEventQueue",
-             "(JLjava/lang/ref/WeakReference;Landroid/os/MessageQueue;[FLjava/lang/String;ILjava/lang/String;)J",
+             "(JLjava/lang/ref/WeakReference;Landroid/os/MessageQueue;Ljava/lang/String;ILjava/lang/String;)J",
              (void*)nativeInitSensorEventQueue },
 
     {"nativeEnableSensor",
@@ -407,5 +515,8 @@
     gBaseEventQueueClassInfo.dispatchFlushCompleteEvent = GetMethodIDOrDie(env,
             gBaseEventQueueClassInfo.clazz, "dispatchFlushCompleteEvent", "(I)V");
 
+    gBaseEventQueueClassInfo.dispatchAdditionalInfoEvent = GetMethodIDOrDie(env,
+            gBaseEventQueueClassInfo.clazz, "dispatchAdditionalInfoEvent", "(III[F[I)V");
+
     return 0;
 }
diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp
index 041e693..4194aa4 100644
--- a/core/jni/com_android_internal_os_Zygote.cpp
+++ b/core/jni/com_android_internal_os_Zygote.cpp
@@ -608,7 +608,7 @@
     jlong capabilities = 0;
 
     // Grant CAP_WAKE_ALARM to the Bluetooth process.
-    if (uid == AID_BLUETOOTH) {
+    if (multiuser_get_app_id(uid) == AID_BLUETOOTH) {
       capabilities |= (1LL << CAP_WAKE_ALARM);
     }
 
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 3aa7de5..1c3db10 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -420,6 +420,8 @@
     <protected-broadcast android:name="android.permission.GET_APP_GRANTED_URI_PERMISSIONS" />
     <protected-broadcast android:name="android.permission.CLEAR_APP_GRANTED_URI_PERMISSIONS" />
 
+    <protected-broadcast android:name="android.intent.action.DYNAMIC_SENSOR_CHANGED" />
+
     <!-- ====================================================================== -->
     <!--                          RUNTIME PERMISSIONS                           -->
     <!-- ====================================================================== -->
diff --git a/core/res/res/layout/app_error_dialog.xml b/core/res/res/layout/app_error_dialog.xml
index aaa2dbc..46a2b2a 100644
--- a/core/res/res/layout/app_error_dialog.xml
+++ b/core/res/res/layout/app_error_dialog.xml
@@ -26,7 +26,7 @@
 >
 
 
-    <TextView
+    <Button
             android:id="@+id/aerr_restart"
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
@@ -34,7 +34,7 @@
             style="@style/aerr_list_item"
     />
 
-    <TextView
+    <Button
             android:id="@+id/aerr_reset"
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
@@ -42,7 +42,7 @@
             style="@style/aerr_list_item"
     />
 
-    <TextView
+    <Button
             android:id="@+id/aerr_report"
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
@@ -50,7 +50,7 @@
             style="@style/aerr_list_item"
     />
 
-    <TextView
+    <Button
             android:id="@+id/aerr_close"
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
@@ -58,7 +58,7 @@
             style="@style/aerr_list_item"
     />
 
-    <TextView
+    <Button
             android:id="@+id/aerr_mute"
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 713038b..1f7206e 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -3727,7 +3727,7 @@
         <!-- Controls how the image should be resized or moved to match the size
              of this ImageView.  See {@link android.widget.ImageView.ScaleType} -->
         <attr name="scaleType">
-            <!-- Scale using the image matrix when drawing. See  
+            <!-- Scale using the image matrix when drawing. See
                  {@link android.widget.ImageView#setImageMatrix(Matrix)}. -->
             <enum name="matrix" value="0" />
             <!-- Scale the image using {@link android.graphics.Matrix.ScaleToFit#FILL}. -->
@@ -3740,7 +3740,7 @@
             <enum name="fitEnd" value="4" />
             <!-- Center the image in the view, but perform no scaling. -->
             <enum name="center" value="5" />
-            <!-- Scale the image uniformly (maintain the image's aspect ratio) so both dimensions 
+            <!-- Scale the image uniformly (maintain the image's aspect ratio) so both dimensions
                  (width and height) of the image will be equal to or larger than the corresponding
                  dimension of the view (minus padding). The image is then centered in the view. -->
             <enum name="centerCrop" value="6" />
@@ -8135,4 +8135,52 @@
         <attr name="entries" />
         <attr name="entryValues" />
     </declare-styleable>
+
+    <!-- Used to describe the gradient for fill or stroke in a path of VectorDrawable. -->
+    <declare-styleable name="GradientColor">
+        <!-- Start color of the gradient. -->
+        <attr name="startColor" />
+        <!-- Optional center color. -->
+        <attr name="centerColor" />
+        <!-- End color of the gradient. -->
+        <attr name="endColor" />
+        <!-- Type of gradient. The default type is linear. -->
+        <attr name="type" />
+
+        <!-- Only applied to RadialGradient-->
+        <!-- Radius of the gradient, used only with radial gradient. -->
+        <attr name="gradientRadius" />
+
+        <!-- Only applied to SweepGradient / RadialGradient-->
+        <!-- X coordinate of the center of the gradient within the path. -->
+        <attr name="centerX" />
+        <!-- Y coordinate of the center of the gradient within the path. -->
+        <attr name="centerY" />
+
+        <!-- LinearGradient specific -->
+        <!-- X coordinate of the start point origin of the gradient.
+             Defined in same coordinates as the path itself -->
+        <attr name="startX" format="float" />
+        <!-- Y coordinate of the start point of the gradient within the shape.
+             Defined in same coordinates as the path itself -->
+        <attr name="startY" format="float" />
+        <!-- X coordinate of the end point origin of the gradient.
+             Defined in same coordinates as the path itself -->
+        <attr name="endX" format="float" />
+        <!-- Y coordinate of the end point of the gradient within the shape.
+             Defined in same coordinates as the path itself -->
+        <attr name="endY" format="float" />
+
+    </declare-styleable>
+
+    <!-- Describes an item of a GradientColor. Minimally need 2 items to define the gradient
+         Colors defined in <item> override the simple color attributes such as
+         "startColor / centerColor / endColor" are ignored -->
+    <declare-styleable name="GradientColorItem">
+        <!-- The offset (or ratio) of this current color item inside the gradient.
+             The value is only meaningful when it is between 0 and 1. -->
+        <attr name="offset" format="float" />
+        <!-- The current color for the offset inside the gradient. -->
+        <attr name="color" />
+    </declare-styleable>
 </resources>
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 57132ea..a2ad09b 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -2691,6 +2691,11 @@
     <public type="attr" name="canPerformGestures" />
     <public type="attr" name="externalService" />
     <public type="attr" name="supportsLocalInteraction" />
+    <public type="attr" name="startX" />
+    <public type="attr" name="startY" />
+    <public type="attr" name="endX" />
+    <public type="attr" name="endY" />
+    <public type="attr" name="offset" />
 
     <public type="style" name="Theme.Material.Light.DialogWhenLarge.DarkActionBar" />
     <public type="style" name="Widget.Material.SeekBar.Discrete" />
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index a276854..26421fb 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -559,6 +559,9 @@
     <!-- Text shown in place of notification contents when the notification is hidden on a secure lockscreen -->
     <string name="notification_hidden_text">Contents hidden</string>
 
+    <!-- Text shown in place of notification contents when the notification is hidden by policy on a secure lockscreen -->
+    <string name="notification_hidden_by_policy_text">Contents hidden by policy</string>
+
     <!-- Displayed to the user to tell them that they have started up the phone in "safe mode" -->
     <string name="safeMode">Safe mode</string>
 
@@ -4149,14 +4152,10 @@
     <string name="importance_from_topic">You set the importance of these notifications.</string>
     <string name="importance_from_person">This is important because of the people involved.</string>
 
-    <!-- Message to user that app trying to create user is not allowed to due to restrictions. [CHAR LIMIT=none] -->
-    <string name="user_creation_cannot_add"><b><xliff:g id="app" example="Gmail">%1$s</xliff:g></b> is trying to add a new user, but is currently prohibited.</string>
-    <!-- Message to user that app trying to create user is not allowed to due to user limit being reached. [CHAR LIMIT=none] -->
-    <string name="user_creation_cannot_add_any_more"><b><xliff:g id="app" example="Gmail">%1$s</xliff:g></b> is trying to add a new user, but the user limit has been reached.</string>
     <!-- Message to user that app trying to create user for an account that already exists. [CHAR LIMIT=none] -->
-    <string name="user_creation_account_exists"><b><xliff:g id="app" example="Gmail">%1$s</xliff:g></b> is trying to add a new user, but the account <b><xliff:g id="account" example="foobar">%2$s</xliff:g></b> already exists on this device. Proceed anyway?</string>
+    <string name="user_creation_account_exists">Allow <xliff:g id="app" example="Gmail">%1$s</xliff:g> to create a new User with <xliff:g id="account" example="foobar@gmail.com">%2$s</xliff:g> ?</string>
     <!-- Message to user that app is trying to create user for a specified account. [CHAR LIMIT=none] -->
-    <string name="user_creation_adding"><b><xliff:g id="app" example="Gmail">%1$s</xliff:g></b> is trying to add a new user for the account <b><xliff:g id="account" example="foobar">%2$s</xliff:g></b>. Proceed?</string>
+    <string name="user_creation_adding">Allow <xliff:g id="app" example="Gmail">%1$s</xliff:g> to create a new User with <xliff:g id="account" example="foobar">%2$s</xliff:g> (a User with this account already exists) ?</string>
 
     <!-- Locale picker strings -->
 
@@ -4190,4 +4189,12 @@
     <string name="new_sms_notification_title">You have new messages</string>
     <!-- Notification content shown when new SMS/MMS is received while the device is locked [CHAR LIMIT=NONE] -->
     <string name="new_sms_notification_content">Open SMS app to view</string>
+
+    <!-- Notification title shown when user profile is credential encrypted and requires the user to unlock before some features are usable [CHAR LIMIT=30] -->
+    <string name="user_encrypted_title">Some functions might not be available</string>
+    <!-- Notification message shown when user profile is credential encrypted and requires the user to unlock before some features are usable [CHAR LIMIT=30] -->
+    <string name="user_encrypted_message">Touch to continue</string>
+    <!-- Notification detail shown when user profile is credential encrypted and requires the user to unlock before some features are usable [CHAR LIMIT=30] -->
+    <string name="user_encrypted_detail">User profile locked</string>
+
 </resources>
diff --git a/core/res/res/values/styles.xml b/core/res/res/values/styles.xml
index b660277..f0960c7 100644
--- a/core/res/res/values/styles.xml
+++ b/core/res/res/values/styles.xml
@@ -1398,7 +1398,7 @@
     </style>
 
     <!-- @hide -->
-    <style name="aerr_list_item" parent="Widget.Material.Light.TextView">
+    <style name="aerr_list_item" parent="Widget.Material.Light.Button.Borderless">
         <item name="minHeight">?attr/listPreferredItemHeightSmall</item>
         <item name="textAppearance">?attr/textAppearanceListItemSmall</item>
         <item name="textColor">?attr/textColorAlertDialogListItem</item>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 81705b4..aa44d7c 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -937,8 +937,6 @@
   <java-symbol type="string" name="time_picker_increment_minute_button" />
   <java-symbol type="string" name="time_picker_increment_set_pm_button" />
   <java-symbol type="string" name="upload_file" />
-  <java-symbol type="string" name="user_creation_cannot_add" />
-  <java-symbol type="string" name="user_creation_cannot_add_any_more" />
   <java-symbol type="string" name="user_creation_account_exists" />
   <java-symbol type="string" name="user_creation_adding" />
   <java-symbol type="string" name="user_switched" />
@@ -2401,6 +2399,7 @@
 
   <java-symbol type="string" name="notification_children_count_bracketed" />
   <java-symbol type="string" name="notification_hidden_text" />
+  <java-symbol type="string" name="notification_hidden_by_policy_text" />
   <java-symbol type="id" name="app_name_text" />
   <java-symbol type="id" name="number_of_children" />
   <java-symbol type="id" name="header_sub_text" />
@@ -2505,4 +2504,10 @@
   <java-symbol type="string" name="new_sms_notification_content" />
 
   <java-symbol type="bool" name="config_strongAuthRequiredOnBoot" />
+
+  <!-- Encryption notification while accounts are locked by credential encryption -->
+  <java-symbol type="string" name="user_encrypted_title" />
+  <java-symbol type="string" name="user_encrypted_message" />
+  <java-symbol type="string" name="user_encrypted_detail" />
+
 </resources>
diff --git a/core/res/res/xml/power_profile.xml b/core/res/res/xml/power_profile.xml
index ddd0ca2..76b5fe1 100644
--- a/core/res/res/xml/power_profile.xml
+++ b/core/res/res/xml/power_profile.xml
@@ -39,15 +39,27 @@
   <item name="dsp.video">0.1</item> <!-- ~50mA -->
   <item name="camera.flashlight">0.1</item> <!-- Avg. power for camera flash, ~160mA -->
   <item name="camera.avg">0.1</item> <!-- Avg. power use of camera in standard usecases, ~550mA -->
+  <item name="gps.on">0.1</item> <!-- ~50mA -->
+
+  <!-- Radio related values. For modems without energy reporting support in firmware, use
+       radio.active, radio.scanning, and radio.on. -->
   <item name="radio.active">0.1</item> <!-- ~200mA -->
   <item name="radio.scanning">0.1</item> <!-- cellular radio scanning for signal, ~10mA -->
-  <item name="gps.on">0.1</item> <!-- ~50mA -->
   <!-- Current consumed by the radio at different signal strengths, when paging -->
   <array name="radio.on"> <!-- Strength 0 to BINS-1 -->
       <value>0.2</value> <!-- ~2mA -->
       <value>0.1</value> <!-- ~1mA -->
   </array>
 
+
+  <!-- Radio related values. For modems WITH energy reporting support in firmware, use
+       modem.controller.idle, modem.controller.tx, modem.controller.rx, modem.controller.voltage.
+       -->
+  <item name="modem.controller.idle">0</item>
+  <item name="modem.controller.rx">0</item>
+  <item name="modem.controller.tx">0</item>
+  <item name="modem.controller.voltage">0</item>
+
   <!-- A list of heterogeneous CPU clusters, where the value for each cluster represents the
        number of CPU cores for that cluster.
 
diff --git a/data/etc/platform.xml b/data/etc/platform.xml
index 999d47b..d412d7c 100644
--- a/data/etc/platform.xml
+++ b/data/etc/platform.xml
@@ -4,9 +4,9 @@
      Licensed under the Apache License, Version 2.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.
@@ -136,6 +136,12 @@
     <assign-permission name="android.permission.UPDATE_DEVICE_STATS" uid="audioserver" />
     <assign-permission name="android.permission.UPDATE_APP_OPS_STATS" uid="audioserver" />
 
+    <assign-permission name="android.permission.MODIFY_AUDIO_SETTINGS" uid="cameraserver" />
+    <assign-permission name="android.permission.ACCESS_SURFACE_FLINGER" uid="cameraserver" />
+    <assign-permission name="android.permission.WAKE_LOCK" uid="cameraserver" />
+    <assign-permission name="android.permission.UPDATE_DEVICE_STATS" uid="cameraserver" />
+    <assign-permission name="android.permission.UPDATE_APP_OPS_STATS" uid="cameraserver" />
+
     <assign-permission name="android.permission.ACCESS_SURFACE_FLINGER" uid="graphics" />
 
     <!-- This is a list of all the libraries available for application
diff --git a/graphics/java/android/graphics/Shader.java b/graphics/java/android/graphics/Shader.java
index adb282f..94983b3 100644
--- a/graphics/java/android/graphics/Shader.java
+++ b/graphics/java/android/graphics/Shader.java
@@ -117,7 +117,10 @@
         }
     }
 
-    /* package */ long getNativeInstance() {
+    /**
+     * @hide
+     */
+    public long getNativeInstance() {
         return native_instance;
     }
 
diff --git a/graphics/java/android/graphics/drawable/VectorDrawable.java b/graphics/java/android/graphics/drawable/VectorDrawable.java
index 6526021..1fc1b83 100644
--- a/graphics/java/android/graphics/drawable/VectorDrawable.java
+++ b/graphics/java/android/graphics/drawable/VectorDrawable.java
@@ -17,6 +17,8 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.content.res.ColorStateList;
+import android.content.res.ComplexColor;
+import android.content.res.GradientColor;
 import android.content.res.Resources;
 import android.content.res.Resources.Theme;
 import android.content.res.TypedArray;
@@ -27,6 +29,7 @@
 import android.graphics.PorterDuffColorFilter;
 import android.graphics.Rect;
 import android.graphics.PorterDuff.Mode;
+import android.graphics.Shader;
 import android.util.ArrayMap;
 import android.util.AttributeSet;
 import android.util.DisplayMetrics;
@@ -121,9 +124,9 @@
  * <dd>Defines path data using exactly same format as "d" attribute
  * in the SVG's path data. This is defined in the viewport space.</dd>
  * <dt><code>android:fillColor</code></dt>
- * <dd>Specifies the color used to fill the path. May be a color or (SDK 24+ only) a color state
- * list. If this property is animated, any value set by the animation will override the original
- * value. No path fill is drawn if this property is not specified.</dd>
+ * <dd>Specifies the color used to fill the path. May be a color, also may be a color state list or
+ * a gradient color for SDK 24+. If this property is animated, any value set by the animation will
+ * override the original value. No path fill is drawn if this property is not specified.</dd>
  * <dt><code>android:strokeColor</code></dt>
  * <dd>Specifies the color used to draw the path outline. May be a color or (SDK 24+ only) a color
  * state list. If this property is animated, any value set by the animation will override the
@@ -1276,8 +1279,9 @@
         /////////////////////////////////////////////////////
         // Variables below need to be copied (deep copy if applicable) for mutation.
         private int[] mThemeAttrs;
-        ColorStateList mStrokeColors = null;
-        ColorStateList mFillColors = null;
+
+        ComplexColor mStrokeColors = null;
+        ComplexColor mFillColors = null;
         private long mNativePtr = 0;
 
         public VFullPath() {
@@ -1297,23 +1301,25 @@
         public boolean onStateChange(int[] stateSet) {
             boolean changed = false;
 
-            if (mStrokeColors != null) {
+            if (mStrokeColors != null && mStrokeColors instanceof ColorStateList) {
                 final int oldStrokeColor = getStrokeColor();
-                final int newStrokeColor = mStrokeColors.getColorForState(stateSet, oldStrokeColor);
+                final int newStrokeColor =
+                        ((ColorStateList) mStrokeColors).getColorForState(stateSet, oldStrokeColor);
                 changed |= oldStrokeColor != newStrokeColor;
                 if (oldStrokeColor != newStrokeColor) {
                     nSetStrokeColor(mNativePtr, newStrokeColor);
                 }
             }
 
-            if (mFillColors != null) {
+            if (mFillColors != null && mFillColors instanceof ColorStateList) {
                 final int oldFillColor = getFillColor();
-                final int newFillColor = mFillColors.getColorForState(stateSet, oldFillColor);
+                final int newFillColor = ((ColorStateList) mFillColors).getColorForState(stateSet, oldFillColor);
                 changed |= oldFillColor != newFillColor;
                 if (oldFillColor != newFillColor) {
                     nSetFillColor(mNativePtr, newFillColor);
                 }
             }
+
             return changed;
         }
 
@@ -1372,7 +1378,8 @@
             int strokeLineCap =  properties.getInt(STROKE_LINE_CAP_INDEX * 4);
             int strokeLineJoin = properties.getInt(STROKE_LINE_JOIN_INDEX * 4);
             float strokeMiterLimit = properties.getFloat(STROKE_MITER_LIMIT_INDEX * 4);
-
+            Shader fillGradient = null;
+            Shader strokeGradient = null;
             // Account for any configuration changes.
             mChangingConfigurations |= a.getChangingConfigurations();
 
@@ -1391,23 +1398,43 @@
                 nSetPathString(mNativePtr, pathString, pathString.length());
             }
 
-            final ColorStateList fillColors = a.getColorStateList(
+            final ComplexColor fillColors = a.getComplexColor(
                     R.styleable.VectorDrawablePath_fillColor);
             if (fillColors != null) {
-                // If the color state list isn't stateful, discard the state
-                // list and keep the default (e.g. the only) color.
-                mFillColors = fillColors.isStateful() ? fillColors : null;
+                // If the colors is a gradient color, or the color state list is stateful, keep the
+                // colors information. Otherwise, discard the colors and keep the default color.
+                if (fillColors instanceof  GradientColor) {
+                    mFillColors = fillColors;
+                    fillGradient = ((GradientColor) fillColors).getShader();
+                } else if (fillColors.isStateful()) {
+                    mFillColors = fillColors;
+                } else {
+                    mFillColors = null;
+                }
                 fillColor = fillColors.getDefaultColor();
             }
 
-            final ColorStateList strokeColors = a.getColorStateList(
+            final ComplexColor strokeColors = a.getComplexColor(
                     R.styleable.VectorDrawablePath_strokeColor);
             if (strokeColors != null) {
-                // If the color state list isn't stateful, discard the state
-                // list and keep the default (e.g. the only) color.
-                mStrokeColors = strokeColors.isStateful() ? strokeColors : null;
+                // If the colors is a gradient color, or the color state list is stateful, keep the
+                // colors information. Otherwise, discard the colors and keep the default color.
+                if (strokeColors instanceof GradientColor) {
+                    mStrokeColors = strokeColors;
+                    strokeGradient = ((GradientColor) strokeColors).getShader();
+                } else if (strokeColors.isStateful()) {
+                    mStrokeColors = strokeColors;
+                } else {
+                    mStrokeColors = null;
+                }
                 strokeColor = strokeColors.getDefaultColor();
             }
+            // Update the gradient info, even if the gradiet is null.
+            nUpdateFullPathFillGradient(mNativePtr,
+                    fillGradient != null ? fillGradient.getNativeInstance() : 0);
+            nUpdateFullPathStrokeGradient(mNativePtr,
+                    strokeGradient != null ? strokeGradient.getNativeInstance() : 0);
+
             fillAlpha = a.getFloat(R.styleable.VectorDrawablePath_fillAlpha, fillAlpha);
 
             strokeLineCap = a.getInt(
@@ -1434,18 +1461,44 @@
 
         @Override
         public boolean canApplyTheme() {
-            return mThemeAttrs != null;
+            if (mThemeAttrs != null) {
+                return true;
+            }
+            boolean fillCanApplyTheme = canGradientApplyTheme(mFillColors);
+            boolean strokeCanApplyTheme = canGradientApplyTheme(mStrokeColors);
+            if (fillCanApplyTheme || strokeCanApplyTheme) {
+                return true;
+            }
+            return false;
+
         }
 
         @Override
         public void applyTheme(Theme t) {
-            if (mThemeAttrs == null) {
-                return;
+            if (mThemeAttrs != null) {
+                final TypedArray a = t.resolveAttributes(mThemeAttrs, R.styleable.VectorDrawablePath);
+                updateStateFromTypedArray(a);
+                a.recycle();
             }
 
-            final TypedArray a = t.resolveAttributes(mThemeAttrs, R.styleable.VectorDrawablePath);
-            updateStateFromTypedArray(a);
-            a.recycle();
+            boolean fillCanApplyTheme = canGradientApplyTheme(mFillColors);
+            boolean strokeCanApplyTheme = canGradientApplyTheme(mStrokeColors);
+            if (fillCanApplyTheme) {
+                mFillColors = mFillColors.obtainForTheme(t);
+                nUpdateFullPathFillGradient(mNativePtr,
+                        ((GradientColor)mFillColors).getShader().getNativeInstance());
+            }
+
+            if (strokeCanApplyTheme) {
+                mStrokeColors = mStrokeColors.obtainForTheme(t);
+                nUpdateFullPathStrokeGradient(mNativePtr,
+                        ((GradientColor)mStrokeColors).getShader().getNativeInstance());
+            }
+        }
+
+        private boolean canGradientApplyTheme(ComplexColor complexColor) {
+            return complexColor != null && complexColor.canApplyTheme()
+                    && complexColor instanceof GradientColor;
         }
 
         /* Setters and Getters, used by animator from AnimatedVectorDrawable. */
@@ -1560,6 +1613,8 @@
             int strokeColor, float strokeAlpha, int fillColor, float fillAlpha, float trimPathStart,
             float trimPathEnd, float trimPathOffset, float strokeMiterLimit, int strokeLineCap,
             int strokeLineJoin);
+    private static native void nUpdateFullPathFillGradient(long pathPtr, long fillGradientPtr);
+    private static native void nUpdateFullPathStrokeGradient(long pathPtr, long strokeGradientPtr);
 
     private static native long nCreateClipPath();
     private static native long nCreateClipPath(long clipPathPtr);
diff --git a/libs/hwui/VectorDrawable.cpp b/libs/hwui/VectorDrawable.cpp
index 1d31c9e..1cf15ac 100644
--- a/libs/hwui/VectorDrawable.cpp
+++ b/libs/hwui/VectorDrawable.cpp
@@ -18,6 +18,7 @@
 
 #include "PathParser.h"
 #include "SkImageInfo.h"
+#include "SkShader.h"
 #include <utils/Log.h>
 #include "utils/Macros.h"
 #include "utils/VectorDrawableUtils.h"
@@ -49,7 +50,7 @@
 
     float minScale = fmin(scaleX, scaleY);
     float strokeScale = minScale * matrixScale;
-    drawPath(outCanvas, renderPath, strokeScale);
+    drawPath(outCanvas, renderPath, strokeScale, pathMatrix);
 }
 
 void Path::setPathData(const Data& data) {
@@ -148,6 +149,9 @@
     mStrokeMiterLimit = path.mStrokeMiterLimit;
     mStrokeLineCap = path.mStrokeLineCap;
     mStrokeLineJoin = path.mStrokeLineJoin;
+
+    SkRefCnt_SafeAssign(mStrokeGradient, path.mStrokeGradient);
+    SkRefCnt_SafeAssign(mFillGradient, path.mFillGradient);
 }
 
 const SkPath& FullPath::getUpdatedPath() {
@@ -186,22 +190,43 @@
     return SkColorSetA(color, alphaBytes * alpha);
 }
 
-void FullPath::drawPath(SkCanvas* outCanvas, const SkPath& renderPath, float strokeScale){
-    // Draw path's fill, if fill color isn't transparent.
-    if (mFillColor != SK_ColorTRANSPARENT) {
+void FullPath::drawPath(SkCanvas* outCanvas, const SkPath& renderPath, float strokeScale,
+                        const SkMatrix& matrix){
+    // Draw path's fill, if fill color or gradient is valid
+    bool needsFill = false;
+    if (mFillGradient != nullptr) {
+        mPaint.setColor(applyAlpha(SK_ColorBLACK, mFillAlpha));
+        SkShader* newShader = mFillGradient->newWithLocalMatrix(matrix);
+        mPaint.setShader(newShader);
+        needsFill = true;
+    } else if (mFillColor != SK_ColorTRANSPARENT) {
+        mPaint.setColor(applyAlpha(mFillColor, mFillAlpha));
+        needsFill = true;
+    }
+
+    if (needsFill) {
         mPaint.setStyle(SkPaint::Style::kFill_Style);
         mPaint.setAntiAlias(true);
-        mPaint.setColor(applyAlpha(mFillColor, mFillAlpha));
         outCanvas->drawPath(renderPath, mPaint);
     }
-    // Draw path's stroke, if stroke color isn't transparent
-    if (mStrokeColor != SK_ColorTRANSPARENT) {
+
+    // Draw path's stroke, if stroke color or gradient is valid
+    bool needsStroke = false;
+    if (mStrokeGradient != nullptr) {
+        mPaint.setColor(applyAlpha(SK_ColorBLACK, mStrokeAlpha));
+        SkShader* newShader = mStrokeGradient->newWithLocalMatrix(matrix);
+        mPaint.setShader(newShader);
+        needsStroke = true;
+    } else if (mStrokeColor != SK_ColorTRANSPARENT) {
+        mPaint.setColor(applyAlpha(mStrokeColor, mStrokeAlpha));
+        needsStroke = true;
+    }
+    if (needsStroke) {
         mPaint.setStyle(SkPaint::Style::kStroke_Style);
         mPaint.setAntiAlias(true);
         mPaint.setStrokeJoin(mStrokeLineJoin);
         mPaint.setStrokeCap(mStrokeLineCap);
         mPaint.setStrokeMiter(mStrokeMiterLimit);
-        mPaint.setColor(applyAlpha(mStrokeColor, mStrokeAlpha));
         mPaint.setStrokeWidth(mStrokeWidth * strokeScale);
         outCanvas->drawPath(renderPath, mPaint);
     }
@@ -288,7 +313,7 @@
 }
 
 void ClipPath::drawPath(SkCanvas* outCanvas, const SkPath& renderPath,
-        float strokeScale){
+        float strokeScale, const SkMatrix& matrix){
     outCanvas->clipPath(renderPath, SkRegion::kIntersect_Op);
 }
 
diff --git a/libs/hwui/VectorDrawable.h b/libs/hwui/VectorDrawable.h
index 5ae5f6a..09bdce5 100644
--- a/libs/hwui/VectorDrawable.h
+++ b/libs/hwui/VectorDrawable.h
@@ -26,6 +26,7 @@
 #include <SkPath.h>
 #include <SkPathMeasure.h>
 #include <SkRect.h>
+#include <SkShader.h>
 
 #include <cutils/compiler.h>
 #include <stddef.h>
@@ -95,7 +96,7 @@
 protected:
     virtual const SkPath& getUpdatedPath();
     virtual void drawPath(SkCanvas *outCanvas, const SkPath& renderPath,
-            float strokeScale) = 0;
+            float strokeScale, const SkMatrix& matrix) = 0;
     Data mData;
     SkPath mSkPath;
     bool mSkPathDirty = true;
@@ -108,6 +109,11 @@
     FullPath() : Path() {}
     FullPath(const Data& nodes) : Path(nodes) {}
 
+    ~FullPath() {
+        SkSafeUnref(mFillGradient);
+        SkSafeUnref(mStrokeGradient);
+    }
+
     void updateProperties(float strokeWidth, SkColor strokeColor,
             float strokeAlpha, SkColor fillColor, float fillAlpha,
             float trimPathStart, float trimPathEnd, float trimPathOffset,
@@ -162,10 +168,18 @@
     }
     bool getProperties(int8_t* outProperties, int length);
 
+    void setFillGradient(SkShader* fillGradient) {
+        SkRefCnt_SafeAssign(mFillGradient, fillGradient);
+    };
+    void setStrokeGradient(SkShader* strokeGradient) {
+        SkRefCnt_SafeAssign(mStrokeGradient, strokeGradient);
+    };
+
+
 protected:
     const SkPath& getUpdatedPath() override;
     void drawPath(SkCanvas* outCanvas, const SkPath& renderPath,
-            float strokeScale) override;
+            float strokeScale, const SkMatrix& matrix) override;
 
 private:
     // Applies trimming to the specified path.
@@ -174,6 +188,8 @@
     SkColor mStrokeColor = SK_ColorTRANSPARENT;
     float mStrokeAlpha = 1;
     SkColor mFillColor = SK_ColorTRANSPARENT;
+    SkShader* mStrokeGradient = nullptr;
+    SkShader* mFillGradient = nullptr;
     float mFillAlpha = 1;
     float mTrimPathStart = 0;
     float mTrimPathEnd = 1;
@@ -195,7 +211,7 @@
 
 protected:
     void drawPath(SkCanvas* outCanvas, const SkPath& renderPath,
-            float strokeScale) override;
+            float strokeScale, const SkMatrix& matrix) override;
 };
 
 class ANDROID_API Group: public Node {
diff --git a/location/java/android/location/GnssNmeaListener.java b/location/java/android/location/GnssNmeaListener.java
new file mode 100644
index 0000000..6c9b08a
--- /dev/null
+++ b/location/java/android/location/GnssNmeaListener.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.location;
+
+/**
+* Used for receiving NMEA sentences from the GNSS.
+* NMEA 0183 is a standard for communicating with marine electronic devices
+* and is a common method for receiving data from a GNSS, typically over a serial port.
+* See <a href="http://en.wikipedia.org/wiki/NMEA_0183">NMEA 0183</a> for more details.
+* You can implement this interface and call {@link LocationManager#addNmeaListener}
+* to receive NMEA data from the GNSS engine.
+*/
+public interface GnssNmeaListener {
+    /** Called when an NMEA message is received. */
+    void onNmeaReceived(long timestamp, String nmea);
+}
\ No newline at end of file
diff --git a/location/java/android/location/GnssStatus.java b/location/java/android/location/GnssStatus.java
new file mode 100644
index 0000000..77e8a5b
--- /dev/null
+++ b/location/java/android/location/GnssStatus.java
@@ -0,0 +1,140 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.location;
+
+/**
+ * This class represents the current state of the GNSS engine.
+ * This class is used in conjunction with the {@link GnssStatusCallback}.
+ */
+public final class GnssStatus {
+    /** Unknown constellation type. */
+    public static final int CONSTELLATION_UNKNOWN = 0;
+    /** Constellation type constant for GPS. */
+    public static final int CONSTELLATION_GPS = 1;
+    /** Constellation type constant for SBAS. */
+    public static final int CONSTELLATION_SBAS = 2;
+    /** Constellation type constant for Glonass. */
+    public static final int CONSTELLATION_GLONASS = 3;
+    /** Constellation type constant for QZSS. */
+    public static final int CONSTELLATION_QZSS = 4;
+    /** Constellation type constant for Beidou. */
+    public static final int CONSTELLATION_BEIDOU = 5;
+    /** Constellation type constant for Galileo. */
+    public static final int CONSTELLATION_GALILEO = 6;
+
+    // these must match the definitions in gps.h
+    /** @hide */
+    public static final int GNSS_SV_FLAGS_NONE = 0;
+    /** @hide */
+    public static final int GNSS_SV_FLAGS_HAS_EPHEMERIS_DATA = (1 << 0);
+    /** @hide */
+    public static final int GNSS_SV_FLAGS_HAS_ALMANAC_DATA = (1 << 1);
+    /** @hide */
+    public static final int GNSS_SV_FLAGS_USED_IN_FIX = (1 << 2);
+
+    /** @hide */
+    public static final int PRN_SHIFT_WIDTH = 3;
+
+    /* These package private values are modified by the LocationManager class */
+    /* package */ int[] mPrnWithFlags;
+    /* package */ float[] mSnrs;
+    /* package */ float[] mElevations;
+    /* package */ float[] mAzimuths;
+    /* package */ int[] mConstellationTypes;
+    /* package */ int mSvCount;
+
+    GnssStatus(int svCount, int[] prnWithFlags, float[] snrs, float[] elevations, float[] azimuths,
+            int[] constellationTypes) {
+        mSvCount = svCount;
+        mPrnWithFlags = prnWithFlags;
+        mSnrs = snrs;
+        mElevations = elevations;
+        mAzimuths = azimuths;
+        mConstellationTypes = constellationTypes;
+    }
+
+    /**
+     * Gets the total number of satellites in satellite list.
+     */
+    public int getNumSatellites() {
+        return mSvCount;
+    }
+
+    /**
+     * Retrieves the constellation type of the satellite at the specified position.
+     * @param satIndex the index of the satellite in the list.
+     */
+    public int getConstellationType(int satIndex) {
+        return mConstellationTypes[satIndex];
+    }
+
+    /**
+     * Retrieves the pseudo-random number of the satellite at the specified position.
+     * @param satIndex the index of the satellite in the list.
+     */
+    public int getPrn(int satIndex) {
+        return mPrnWithFlags[satIndex] >> PRN_SHIFT_WIDTH;
+    }
+
+    /**
+     * Retrieves the signal-noise ration of the satellite at the specified position.
+     * @param satIndex the index of the satellite in the list.
+     */
+    public float getSnr(int satIndex) {
+        return mSnrs[satIndex];
+    }
+
+    /**
+     * Retrieves the elevation of the satellite at the specified position.
+     * @param satIndex the index of the satellite in the list.
+     */
+    public float getElevation(int satIndex) {
+        return 0f;
+    }
+
+    /**
+     * Retrieves the azimuth the satellite at the specified position.
+     * @param satIndex the index of the satellite in the list.
+     */
+    public float getAzimuth(int satIndex) {
+        return mAzimuths[satIndex];
+    }
+
+    /**
+     * Detects whether the satellite at the specified position has ephemeris data.
+     * @param satIndex the index of the satellite in the list.
+     */
+    public boolean hasEphemeris(int satIndex) {
+        return (mPrnWithFlags[satIndex] & GNSS_SV_FLAGS_HAS_EPHEMERIS_DATA) != 0;
+    }
+
+    /**
+     * Detects whether the satellite at the specified position has almanac data.
+     * @param satIndex the index of the satellite in the list.
+     */
+    public boolean hasAlmanac(int satIndex) {
+        return (mPrnWithFlags[satIndex] & GNSS_SV_FLAGS_HAS_ALMANAC_DATA) != 0;
+    }
+
+    /**
+     * Detects whether the satellite at the specified position is used in fix.
+     * @param satIndex the index of the satellite in the list.
+     */
+    public boolean usedInFix(int satIndex) {
+        return (mPrnWithFlags[satIndex] & GNSS_SV_FLAGS_USED_IN_FIX) != 0;
+    }
+}
diff --git a/location/java/android/location/GnssStatusCallback.java b/location/java/android/location/GnssStatusCallback.java
new file mode 100644
index 0000000..b86171b
--- /dev/null
+++ b/location/java/android/location/GnssStatusCallback.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.location;
+
+/**
+ * Used for receiving notifications when GNSS events happen.
+ */
+public abstract class GnssStatusCallback {
+    /**
+     * Called when GNSS system has started.
+     */
+    public void onStarted() {}
+
+    /**
+     * Called when GNSS system has stopped.
+     */
+    public void onStopped() {}
+
+    /**
+     * Called when the GNSS system has received its first fix since starting.
+     * @param ttff the time from start to first fix.
+     */
+    public void onFirstFix(int ttff) {}
+
+    /**
+     * Called periodically to report GNSS satellite status.
+     * @param status the current status of all satellites.
+     */
+    public void onSatelliteStatusChanged(GnssStatus status) {}
+}
\ No newline at end of file
diff --git a/location/java/android/location/GpsClock.java b/location/java/android/location/GpsClock.java
index 4135a1c..719e56f 100644
--- a/location/java/android/location/GpsClock.java
+++ b/location/java/android/location/GpsClock.java
@@ -16,36 +16,41 @@
 
 package android.location;
 
-import android.annotation.SystemApi;
+import android.annotation.IntDef;
 import android.os.Parcel;
 import android.os.Parcelable;
 
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
 /**
  * A class containing a GPS clock timestamp.
  * It represents a measurement of the GPS receiver's clock.
- *
- * @hide
  */
-@SystemApi
-public class GpsClock implements Parcelable {
+public final class GpsClock implements Parcelable {
 
     // The following enumerations must be in sync with the values declared in gps.h
 
+    /** The type of the GPS Clock. */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef({CLOCK_TYPE_UNKNOWN, CLOCK_TYPE_LOCAL_HW_TIME, CLOCK_TYPE_GPS_TIME})
+    public @interface GpsClockType {}
+
     /**
      * The type of the time stored is not available or it is unknown.
      */
-    public static final byte TYPE_UNKNOWN = 0;
+    public static final byte CLOCK_TYPE_UNKNOWN = 0;
 
     /**
      * The source of the time value reported by this class is the 'Local Hardware Clock'.
      */
-    public static final byte TYPE_LOCAL_HW_TIME = 1;
+    public static final byte CLOCK_TYPE_LOCAL_HW_TIME = 1;
 
     /**
      * The source of the time value reported by this class is the 'GPS time' derived from
      * satellites (epoch = Jan 6, 1980).
      */
-    public static final byte TYPE_GPS_TIME = 2;
+    public static final byte CLOCK_TYPE_GPS_TIME = 2;
 
     private static final short HAS_NO_FLAGS = 0;
     private static final short HAS_LEAP_SECOND = (1<<0);
@@ -68,6 +73,7 @@
     private double mBiasUncertaintyInNs;
     private double mDriftInNsPerSec;
     private double mDriftUncertaintyInNsPerSec;
+    private long mTimeOfLastHwClockDiscontinuityInNs;
 
     GpsClock() {
         initialize();
@@ -87,6 +93,7 @@
         mBiasUncertaintyInNs = clock.mBiasUncertaintyInNs;
         mDriftInNsPerSec = clock.mDriftInNsPerSec;
         mDriftUncertaintyInNsPerSec = clock.mDriftUncertaintyInNsPerSec;
+        mTimeOfLastHwClockDiscontinuityInNs = clock.mTimeOfLastHwClockDiscontinuityInNs;
     }
 
     /**
@@ -99,6 +106,7 @@
     /**
      * Gets the type of time reported by {@link #getTimeInNs()}.
      */
+    @GpsClockType
     public byte getType() {
         return mType;
     }
@@ -106,7 +114,7 @@
     /**
      * Sets the type of time reported.
      */
-    public void setType(byte value) {
+    public void setType(@GpsClockType byte value) {
         mType = value;
     }
 
@@ -116,11 +124,11 @@
      */
     private String getTypeString() {
         switch (mType) {
-            case TYPE_UNKNOWN:
+            case CLOCK_TYPE_UNKNOWN:
                 return "Unknown";
-            case TYPE_GPS_TIME:
+            case CLOCK_TYPE_GPS_TIME:
                 return "GpsTime";
-            case TYPE_LOCAL_HW_TIME:
+            case CLOCK_TYPE_LOCAL_HW_TIME:
                 return "LocalHwClock";
             default:
                 return "<Invalid:" + mType + ">";
@@ -163,8 +171,8 @@
 
     /**
      * Gets the GPS receiver internal clock value in nanoseconds.
-     * This can be either the 'local hardware clock' value ({@link #TYPE_LOCAL_HW_TIME}), or the
-     * current GPS time derived inside GPS receiver ({@link #TYPE_GPS_TIME}).
+     * This can be either the 'local hardware clock' value ({@link #CLOCK_TYPE_LOCAL_HW_TIME}), or the
+     * current GPS time derived inside GPS receiver ({@link #CLOCK_TYPE_GPS_TIME}).
      * {@link #getType()} defines the time reported.
      *
      * For 'local hardware clock' this value is expected to be monotonically increasing during the
@@ -223,7 +231,7 @@
     }
 
     /**
-     * Returns true if {@link @getFullBiasInNs()} is available, false otherwise.
+     * Returns true if {@link #getFullBiasInNs()} is available, false otherwise.
      */
     public boolean hasFullBiasInNs() {
         return isFlagSet(HAS_FULL_BIAS);
@@ -233,7 +241,7 @@
      * Gets the difference between hardware clock ({@link #getTimeInNs()}) inside GPS receiver and
      * the true GPS time since 0000Z, January 6, 1980, in nanoseconds.
      *
-     * This value is available if {@link #TYPE_LOCAL_HW_TIME} is set, and GPS receiver has solved
+     * This value is available if {@link #CLOCK_TYPE_LOCAL_HW_TIME} is set, and GPS receiver has solved
      * the clock for GPS time.
      * {@link #getBiasUncertaintyInNs()} should be used for quality check.
      *
@@ -387,6 +395,20 @@
     }
 
     /**
+     * Gets time of last hardware clock discontinuity.
+     */
+    public long getTimeOfLastHwClockDiscontinuityInNs() {
+        return mTimeOfLastHwClockDiscontinuityInNs;
+    }
+
+    /**
+     * Sets time of last hardware clock discontinuity.
+     */
+    public void setTimeOfLastHwClockDiscontinuityInNs(long timeOfLastHwClockDiscontinuityInNs) {
+        mTimeOfLastHwClockDiscontinuityInNs = timeOfLastHwClockDiscontinuityInNs;
+    }
+
+    /**
      * Resets the clock's Drift Uncertainty (1-Sigma) in nanoseconds per second.
      */
     public void resetDriftUncertaintyInNsPerSec() {
@@ -409,6 +431,7 @@
             gpsClock.mBiasUncertaintyInNs = parcel.readDouble();
             gpsClock.mDriftInNsPerSec = parcel.readDouble();
             gpsClock.mDriftUncertaintyInNsPerSec = parcel.readDouble();
+            gpsClock.mTimeOfLastHwClockDiscontinuityInNs = parcel.readLong();
 
             return gpsClock;
         }
@@ -430,6 +453,7 @@
         parcel.writeDouble(mBiasUncertaintyInNs);
         parcel.writeDouble(mDriftInNsPerSec);
         parcel.writeDouble(mDriftUncertaintyInNsPerSec);
+        parcel.writeLong(mTimeOfLastHwClockDiscontinuityInNs);
     }
 
     @Override
@@ -473,13 +497,17 @@
                 "DriftUncertaintyInNsPerSec",
                 hasDriftUncertaintyInNsPerSec() ? mDriftUncertaintyInNsPerSec : null));
 
+        builder.append(String.format(format, "TimeOfLastHwClockDiscontinuityInNs",
+                getType() == CLOCK_TYPE_LOCAL_HW_TIME
+                        ? mTimeOfLastHwClockDiscontinuityInNs : null));
+
         return builder.toString();
     }
 
     private void initialize() {
         mFlags = HAS_NO_FLAGS;
         resetLeapSecond();
-        setType(TYPE_UNKNOWN);
+        setType(CLOCK_TYPE_UNKNOWN);
         setTimeInNs(Long.MIN_VALUE);
         resetTimeUncertaintyInNs();
         resetFullBiasInNs();
@@ -487,6 +515,7 @@
         resetBiasUncertaintyInNs();
         resetDriftInNsPerSec();
         resetDriftUncertaintyInNsPerSec();
+        setTimeOfLastHwClockDiscontinuityInNs(Long.MIN_VALUE);
     }
 
     private void setFlag(short flag) {
diff --git a/location/java/android/location/GpsMeasurement.java b/location/java/android/location/GpsMeasurement.java
index f13a440..366ad61 100644
--- a/location/java/android/location/GpsMeasurement.java
+++ b/location/java/android/location/GpsMeasurement.java
@@ -16,17 +16,17 @@
 
 package android.location;
 
-import android.annotation.SystemApi;
+import android.annotation.IntDef;
 import android.os.Parcel;
 import android.os.Parcelable;
 
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
 /**
  * A class representing a GPS satellite measurement, containing raw and computed information.
- *
- * @hide
  */
-@SystemApi
-public class GpsMeasurement implements Parcelable {
+public final class GpsMeasurement implements Parcelable {
     private int mFlags;
     private byte mPrn;
     private double mTimeOffsetInNs;
@@ -59,6 +59,8 @@
     private double mAzimuthInDeg;
     private double mAzimuthUncertaintyInDeg;
     private boolean mUsedInFix;
+    private double mPseudorangeRateCarrierInMetersPerSec;
+    private double mPseudorangeRateCarrierUncertaintyInMetersPerSec;
 
     // The following enumerations must be in sync with the values declared in gps.h
 
@@ -83,6 +85,11 @@
     private static final int HAS_USED_IN_FIX = (1<<17);
     private static final int GPS_MEASUREMENT_HAS_UNCORRECTED_PSEUDORANGE_RATE = (1<<18);
 
+    /** The status of 'loss of lock'. */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef({LOSS_OF_LOCK_UNKNOWN, LOSS_OF_LOCK_OK, LOSS_OF_LOCK_CYCLE_SLIP})
+    public @interface LossOfLockStatus {}
+
     /**
      * The indicator is not available or it is unknown.
      */
@@ -98,6 +105,12 @@
      */
     public static final byte LOSS_OF_LOCK_CYCLE_SLIP = 2;
 
+    /** The status of multipath. */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef({MULTIPATH_INDICATOR_UNKNOWN, MULTIPATH_INDICATOR_DETECTED,
+        MULTIPATH_INDICATOR_NOT_USED})
+    public @interface MultipathIndicator {}
+
     /**
      * The indicator is not available or it is unknown.
      */
@@ -218,6 +231,10 @@
         mAzimuthInDeg = measurement.mAzimuthInDeg;
         mAzimuthUncertaintyInDeg = measurement.mAzimuthUncertaintyInDeg;
         mUsedInFix = measurement.mUsedInFix;
+        mPseudorangeRateCarrierInMetersPerSec =
+                measurement.mPseudorangeRateCarrierInMetersPerSec;
+        mPseudorangeRateCarrierUncertaintyInMetersPerSec =
+                measurement.mPseudorangeRateCarrierUncertaintyInMetersPerSec;
     }
 
     /**
@@ -776,6 +793,7 @@
     /**
      * Gets a value indicating the 'loss of lock' state of the event.
      */
+    @LossOfLockStatus
     public byte getLossOfLock() {
         return mLossOfLock;
     }
@@ -783,7 +801,7 @@
     /**
      * Sets the 'loss of lock' status.
      */
-    public void setLossOfLock(byte value) {
+    public void setLossOfLock(@LossOfLockStatus byte value) {
         mLossOfLock = value;
     }
 
@@ -941,6 +959,7 @@
     /**
      * Gets a value indicating the 'multipath' state of the event.
      */
+    @MultipathIndicator
     public byte getMultipathIndicator() {
         return mMultipathIndicator;
     }
@@ -948,7 +967,7 @@
     /**
      * Sets the 'multi-path' indicator.
      */
-    public void setMultipathIndicator(byte value) {
+    public void setMultipathIndicator(@MultipathIndicator byte value) {
         mMultipathIndicator = value;
     }
 
@@ -1157,6 +1176,34 @@
         mUsedInFix = value;
     }
 
+    /**
+     * Gets pseudorange rate (based on carrier phase changes) at the timestamp in m/s.
+     */
+    public double getPseudorangeRateCarrierInMetersPerSec() {
+        return mPseudorangeRateCarrierInMetersPerSec;
+    }
+
+    /**
+     * Sets pseudorange rate (based on carrier phase changes) at the timestamp in m/s.
+     */
+    public void setPseudorangeRateCarrierInMetersPerSec(double value) {
+        mPseudorangeRateCarrierInMetersPerSec = value;
+    }
+
+    /**
+     * Gets 1-Sigma uncertainty of the pseudorange rate carrier.
+     */
+    public double getPseudorangeRateCarrierUncertaintyInMetersPerSec() {
+        return mPseudorangeRateCarrierUncertaintyInMetersPerSec;
+    }
+
+    /**
+     * Sets 1-Sigma uncertainty of the pseudorange rate carrier.
+     */
+    public void setPseudorangeRateCarrierUncertaintyInMetersPerSec(double value) {
+        mPseudorangeRateCarrierUncertaintyInMetersPerSec = value;
+    }
+
     public static final Creator<GpsMeasurement> CREATOR = new Creator<GpsMeasurement>() {
         @Override
         public GpsMeasurement createFromParcel(Parcel parcel) {
@@ -1194,6 +1241,8 @@
             gpsMeasurement.mAzimuthInDeg = parcel.readDouble();
             gpsMeasurement.mAzimuthUncertaintyInDeg = parcel.readDouble();
             gpsMeasurement.mUsedInFix = parcel.readInt() != 0;
+            gpsMeasurement.mPseudorangeRateCarrierInMetersPerSec = parcel.readDouble();
+            gpsMeasurement.mPseudorangeRateCarrierUncertaintyInMetersPerSec = parcel.readDouble();
 
             return gpsMeasurement;
         }
@@ -1237,6 +1286,8 @@
         parcel.writeDouble(mAzimuthInDeg);
         parcel.writeDouble(mAzimuthUncertaintyInDeg);
         parcel.writeInt(mUsedInFix ? 1 : 0);
+        parcel.writeDouble(mPseudorangeRateCarrierInMetersPerSec);
+        parcel.writeDouble(mPseudorangeRateCarrierUncertaintyInMetersPerSec);
     }
 
     @Override
@@ -1361,6 +1412,11 @@
 
         builder.append(String.format(format, "UsedInFix", mUsedInFix));
 
+        builder.append(String.format(format, "PseudorangeRateCarrierInMetersPerSec",
+                    mPseudorangeRateCarrierInMetersPerSec));
+        builder.append(String.format(format, "PseudorangeRateCarrierUncertaintyInMetersPerSec",
+                    mPseudorangeRateCarrierUncertaintyInMetersPerSec));
+
         return builder.toString();
     }
 
@@ -1397,6 +1453,8 @@
         resetAzimuthInDeg();
         resetAzimuthUncertaintyInDeg();
         setUsedInFix(false);
+        setPseudorangeRateCarrierInMetersPerSec(Double.MIN_VALUE);
+        setPseudorangeRateCarrierUncertaintyInMetersPerSec(Double.MIN_VALUE);
     }
 
     private void setFlag(int flag) {
diff --git a/location/java/android/location/GpsMeasurementListenerTransport.java b/location/java/android/location/GpsMeasurementCallbackTransport.java
similarity index 68%
rename from location/java/android/location/GpsMeasurementListenerTransport.java
rename to location/java/android/location/GpsMeasurementCallbackTransport.java
index 610da96..02d9026 100644
--- a/location/java/android/location/GpsMeasurementListenerTransport.java
+++ b/location/java/android/location/GpsMeasurementCallbackTransport.java
@@ -20,17 +20,17 @@
 import android.os.RemoteException;
 
 /**
- * A handler class to manage transport listeners for {@link GpsMeasurementsEvent.Listener}.
+ * A handler class to manage transport callbacks for {@link GpsMeasurementsEvent.Callback}.
  *
  * @hide
  */
-class GpsMeasurementListenerTransport
-        extends LocalListenerHelper<GpsMeasurementsEvent.Listener> {
+class GpsMeasurementCallbackTransport
+        extends LocalListenerHelper<GpsMeasurementsEvent.Callback> {
     private final ILocationManager mLocationManager;
 
     private final IGpsMeasurementsListener mListenerTransport = new ListenerTransport();
 
-    public GpsMeasurementListenerTransport(Context context, ILocationManager locationManager) {
+    public GpsMeasurementCallbackTransport(Context context, ILocationManager locationManager) {
         super(context, "GpsMeasurementListenerTransport");
         mLocationManager = locationManager;
     }
@@ -50,11 +50,11 @@
     private class ListenerTransport extends IGpsMeasurementsListener.Stub {
         @Override
         public void onGpsMeasurementsReceived(final GpsMeasurementsEvent event) {
-            ListenerOperation<GpsMeasurementsEvent.Listener> operation =
-                    new ListenerOperation<GpsMeasurementsEvent.Listener>() {
+            ListenerOperation<GpsMeasurementsEvent.Callback> operation =
+                    new ListenerOperation<GpsMeasurementsEvent.Callback>() {
                 @Override
-                public void execute(GpsMeasurementsEvent.Listener listener) throws RemoteException {
-                    listener.onGpsMeasurementsReceived(event);
+                public void execute(GpsMeasurementsEvent.Callback callback) throws RemoteException {
+                    callback.onGpsMeasurementsReceived(event);
                 }
             };
             foreach(operation);
@@ -62,11 +62,11 @@
 
         @Override
         public void onStatusChanged(final int status) {
-            ListenerOperation<GpsMeasurementsEvent.Listener> operation =
-                    new ListenerOperation<GpsMeasurementsEvent.Listener>() {
+            ListenerOperation<GpsMeasurementsEvent.Callback> operation =
+                    new ListenerOperation<GpsMeasurementsEvent.Callback>() {
                 @Override
-                public void execute(GpsMeasurementsEvent.Listener listener) throws RemoteException {
-                    listener.onStatusChanged(status);
+                public void execute(GpsMeasurementsEvent.Callback callback) throws RemoteException {
+                    callback.onStatusChanged(status);
                 }
             };
             foreach(operation);
diff --git a/location/java/android/location/GpsMeasurementsEvent.java b/location/java/android/location/GpsMeasurementsEvent.java
index 1366873..ef9edeb 100644
--- a/location/java/android/location/GpsMeasurementsEvent.java
+++ b/location/java/android/location/GpsMeasurementsEvent.java
@@ -16,11 +16,13 @@
 
 package android.location;
 
+import android.annotation.IntDef;
 import android.annotation.NonNull;
-import android.annotation.SystemApi;
 import android.os.Parcel;
 import android.os.Parcelable;
 
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 import java.security.InvalidParameterException;
 import java.util.Arrays;
 import java.util.Collection;
@@ -28,12 +30,13 @@
 
 /**
  * A class implementing a container for data associated with a measurement event.
- * Events are delivered to registered instances of {@link Listener}.
- *
- * @hide
+ * Events are delivered to registered instances of {@link Callback}.
  */
-@SystemApi
-public class GpsMeasurementsEvent implements Parcelable {
+public final class GpsMeasurementsEvent implements Parcelable {
+    /** The status of GPS measurements event. */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef({STATUS_NOT_SUPPORTED, STATUS_READY, STATUS_GPS_LOCATION_DISABLED})
+    public @interface GpsMeasurementsStatus {}
 
     /**
      * The system does not support tracking of GPS Measurements. This status will not change in the
@@ -58,22 +61,20 @@
     /**
      * Used for receiving GPS satellite measurements from the GPS engine.
      * Each measurement contains raw and computed data identifying a satellite.
-     * You can implement this interface and call {@link LocationManager#addGpsMeasurementListener}.
-     *
-     * @hide
+     * You can implement this interface and call
+     * {@link LocationManager#registerGpsMeasurementCallback}.
      */
-    @SystemApi
-    public interface Listener {
+    public static abstract class Callback {
 
         /**
          * Returns the latest collected GPS Measurements.
          */
-        void onGpsMeasurementsReceived(GpsMeasurementsEvent eventArgs);
+        public void onGpsMeasurementsReceived(GpsMeasurementsEvent eventArgs) {}
 
         /**
          * Returns the latest status of the GPS Measurements sub-system.
          */
-        void onStatusChanged(int status);
+        public void onStatusChanged(@GpsMeasurementsStatus int status) {}
     }
 
     public GpsMeasurementsEvent(GpsClock clock, GpsMeasurement[] measurements) {
diff --git a/location/java/android/location/GpsNavigationMessage.java b/location/java/android/location/GpsNavigationMessage.java
index 5c3c710..d799572 100644
--- a/location/java/android/location/GpsNavigationMessage.java
+++ b/location/java/android/location/GpsNavigationMessage.java
@@ -16,49 +16,54 @@
 
 package android.location;
 
+import android.annotation.IntDef;
 import android.annotation.NonNull;
-import android.annotation.SystemApi;
 import android.os.Parcel;
 import android.os.Parcelable;
 
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 import java.security.InvalidParameterException;
 
 /**
  * A class containing a GPS satellite Navigation Message.
- *
- * @hide
  */
-@SystemApi
-public class GpsNavigationMessage implements Parcelable {
+public final class GpsNavigationMessage implements Parcelable {
 
     private static final byte[] EMPTY_ARRAY = new byte[0];
 
+    /** The type of the GPS Clock. */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef({MESSAGE_TYPE_UNKNOWN, MESSAGE_TYPE_L1CA, MESSAGE_TYPE_L2CNAV, MESSAGE_TYPE_L5CNAV,
+            MESSAGE_TYPE_CNAV2})
+    public @interface GpsNavigationMessageType {}
+
     // The following enumerations must be in sync with the values declared in gps.h
 
     /**
      * The type of the navigation message is not available or unknown.
      */
-    public static final byte TYPE_UNKNOWN = 0;
+    public static final byte MESSAGE_TYPE_UNKNOWN = 0;
 
     /**
      * The Navigation Message is of type L1 C/A.
      */
-    public static final byte TYPE_L1CA = 1;
+    public static final byte MESSAGE_TYPE_L1CA = 1;
 
     /**
      * The Navigation Message is of type L1-CNAV.
      */
-    public static final byte TYPE_L2CNAV = 2;
+    public static final byte MESSAGE_TYPE_L2CNAV = 2;
 
     /**
      * The Navigation Message is of type L5-CNAV.
      */
-    public static final byte TYPE_L5CNAV = 3;
+    public static final byte MESSAGE_TYPE_L5CNAV = 3;
 
     /**
      * The Navigation Message is of type CNAV-2.
      */
-    public static final byte TYPE_CNAV2 = 4;
+    public static final byte MESSAGE_TYPE_CNAV2 = 4;
 
     /**
      * The Navigation Message Status is 'unknown'.
@@ -111,6 +116,7 @@
     /**
      * Gets the type of the navigation message contained in the object.
      */
+    @GpsNavigationMessageType
     public byte getType() {
         return mType;
     }
@@ -118,7 +124,7 @@
     /**
      * Sets the type of the navigation message.
      */
-    public void setType(byte value) {
+    public void setType(@GpsNavigationMessageType byte value) {
         mType = value;
     }
 
@@ -128,15 +134,15 @@
      */
     private String getTypeString() {
         switch (mType) {
-            case TYPE_UNKNOWN:
+            case MESSAGE_TYPE_UNKNOWN:
                 return "Unknown";
-            case TYPE_L1CA:
+            case MESSAGE_TYPE_L1CA:
                 return "L1 C/A";
-            case TYPE_L2CNAV:
+            case MESSAGE_TYPE_L2CNAV:
                 return "L2-CNAV";
-            case TYPE_L5CNAV:
+            case MESSAGE_TYPE_L5CNAV:
                 return "L5-CNAV";
-            case TYPE_CNAV2:
+            case MESSAGE_TYPE_CNAV2:
                 return "CNAV-2";
             default:
                 return "<Invalid:" + mType + ">";
@@ -314,7 +320,7 @@
     }
 
     private void initialize() {
-        mType = TYPE_UNKNOWN;
+        mType = MESSAGE_TYPE_UNKNOWN;
         mPrn = 0;
         mMessageId = -1;
         mSubmessageId = -1;
diff --git a/location/java/android/location/GpsNavigationMessageListenerTransport.java b/location/java/android/location/GpsNavigationMessageCallbackTransport.java
similarity index 73%
rename from location/java/android/location/GpsNavigationMessageListenerTransport.java
rename to location/java/android/location/GpsNavigationMessageCallbackTransport.java
index f6ba407..155d96d 100644
--- a/location/java/android/location/GpsNavigationMessageListenerTransport.java
+++ b/location/java/android/location/GpsNavigationMessageCallbackTransport.java
@@ -20,20 +20,20 @@
 import android.os.RemoteException;
 
 /**
- * A handler class to manage transport listeners for {@link GpsNavigationMessageEvent.Listener}.
+ * A handler class to manage transport callback for {@link GpsNavigationMessageEvent.Callback}.
  *
  * @hide
  */
-class GpsNavigationMessageListenerTransport
-        extends LocalListenerHelper<GpsNavigationMessageEvent.Listener> {
+class GpsNavigationMessageCallbackTransport
+        extends LocalListenerHelper<GpsNavigationMessageEvent.Callback> {
     private final ILocationManager mLocationManager;
 
     private final IGpsNavigationMessageListener mListenerTransport = new ListenerTransport();
 
-    public GpsNavigationMessageListenerTransport(
+    public GpsNavigationMessageCallbackTransport(
             Context context,
             ILocationManager locationManager) {
-        super(context, "GpsNavigationMessageListenerTransport");
+        super(context, "GpsNavigationMessageCallbackTransport");
         mLocationManager = locationManager;
     }
 
@@ -52,12 +52,12 @@
     private class ListenerTransport extends IGpsNavigationMessageListener.Stub {
         @Override
         public void onGpsNavigationMessageReceived(final GpsNavigationMessageEvent event) {
-            ListenerOperation<GpsNavigationMessageEvent.Listener> operation =
-                    new ListenerOperation<GpsNavigationMessageEvent.Listener>() {
+            ListenerOperation<GpsNavigationMessageEvent.Callback> operation =
+                    new ListenerOperation<GpsNavigationMessageEvent.Callback>() {
                 @Override
-                public void execute(GpsNavigationMessageEvent.Listener listener)
+                public void execute(GpsNavigationMessageEvent.Callback callback)
                         throws RemoteException {
-                    listener.onGpsNavigationMessageReceived(event);
+                    callback.onGpsNavigationMessageReceived(event);
                 }
             };
             foreach(operation);
@@ -65,12 +65,12 @@
 
         @Override
         public void onStatusChanged(final int status) {
-            ListenerOperation<GpsNavigationMessageEvent.Listener> operation =
-                    new ListenerOperation<GpsNavigationMessageEvent.Listener>() {
+            ListenerOperation<GpsNavigationMessageEvent.Callback> operation =
+                    new ListenerOperation<GpsNavigationMessageEvent.Callback>() {
                 @Override
-                public void execute(GpsNavigationMessageEvent.Listener listener)
+                public void execute(GpsNavigationMessageEvent.Callback callback)
                         throws RemoteException {
-                    listener.onStatusChanged(status);
+                    callback.onStatusChanged(status);
                 }
             };
             foreach(operation);
diff --git a/location/java/android/location/GpsNavigationMessageEvent.java b/location/java/android/location/GpsNavigationMessageEvent.java
index bd6921c..b16a485 100644
--- a/location/java/android/location/GpsNavigationMessageEvent.java
+++ b/location/java/android/location/GpsNavigationMessageEvent.java
@@ -16,60 +16,60 @@
 
 package android.location;
 
+import android.annotation.IntDef;
 import android.annotation.NonNull;
-import android.annotation.SystemApi;
 import android.os.Parcel;
 import android.os.Parcelable;
 
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 import java.security.InvalidParameterException;
 
 /**
  * A class implementing a container for data associated with a navigation message event.
- * Events are delivered to registered instances of {@link Listener}.
- *
- * @hide
+ * Events are delivered to registered instances of {@link Callback}.
  */
-@SystemApi
-public class GpsNavigationMessageEvent implements Parcelable {
+public final class GpsNavigationMessageEvent implements Parcelable {
+    /** The status of GPS measurements event. */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef({STATUS_NOT_SUPPORTED, STATUS_READY, STATUS_GPS_LOCATION_DISABLED})
+    public @interface GpsNavigationMessageStatus {}
 
     /**
      * The system does not support tracking of GPS Navigation Messages. This status will not change
      * in the future.
      */
-    public static int STATUS_NOT_SUPPORTED = 0;
+    public static final int STATUS_NOT_SUPPORTED = 0;
 
     /**
      * GPS Navigation Messages are successfully being tracked, it will receive updates once they are
      * available.
      */
-    public static int STATUS_READY = 1;
+    public static final int STATUS_READY = 1;
 
     /**
      * GPS provider or Location is disabled, updated will not be received until they are enabled.
      */
-    public static int STATUS_GPS_LOCATION_DISABLED = 2;
+    public static final int STATUS_GPS_LOCATION_DISABLED = 2;
 
     private final GpsNavigationMessage mNavigationMessage;
 
     /**
      * Used for receiving GPS satellite Navigation Messages from the GPS engine.
      * You can implement this interface and call
-     * {@link LocationManager#addGpsNavigationMessageListener}.
-     *
-     * @hide
+     * {@link LocationManager#registerGpsNavigationMessageCallback}.
      */
-    @SystemApi
-    public interface Listener {
+    public static abstract class Callback {
 
         /**
          * Returns the latest collected GPS Navigation Message.
          */
-        void onGpsNavigationMessageReceived(GpsNavigationMessageEvent event);
+        public void onGpsNavigationMessageReceived(GpsNavigationMessageEvent event) {}
 
         /**
          * Returns the latest status of the GPS Navigation Messages sub-system.
          */
-        void onStatusChanged(int status);
+        public void onStatusChanged(@GpsNavigationMessageStatus int status) {}
     }
 
     public GpsNavigationMessageEvent(GpsNavigationMessage message) {
diff --git a/location/java/android/location/GpsStatus.java b/location/java/android/location/GpsStatus.java
index 323f326..8d2f781 100644
--- a/location/java/android/location/GpsStatus.java
+++ b/location/java/android/location/GpsStatus.java
@@ -34,17 +34,15 @@
     private final SparseArray<GpsSatellite> mSatellites = new SparseArray<>();
 
     private final class SatelliteIterator implements Iterator<GpsSatellite> {
-
-        private final SparseArray<GpsSatellite> mSatellites;
         private final int mSatellitesCount;
 
         private int mIndex = 0;
 
-        SatelliteIterator(SparseArray<GpsSatellite> satellites) {
-            mSatellites = satellites;
-            mSatellitesCount = satellites.size();
+        SatelliteIterator() {
+            mSatellitesCount = mSatellites.size();
         }
 
+        @Override
         public boolean hasNext() {
             for (; mIndex < mSatellitesCount; ++mIndex) {
                 GpsSatellite satellite = mSatellites.valueAt(mIndex);
@@ -55,6 +53,7 @@
             return false;
         }
 
+        @Override
         public GpsSatellite next() {
             while (mIndex < mSatellitesCount) {
                 GpsSatellite satellite = mSatellites.valueAt(mIndex);
@@ -66,14 +65,16 @@
             throw new NoSuchElementException();
         }
 
+        @Override
         public void remove() {
             throw new UnsupportedOperationException();
         }
     }
 
     private Iterable<GpsSatellite> mSatelliteList = new Iterable<GpsSatellite>() {
+        @Override
         public Iterator<GpsSatellite> iterator() {
-            return new SatelliteIterator(mSatellites);
+            return new SatelliteIterator();
         }
     };
 
@@ -137,18 +138,15 @@
     // For API-compat a public ctor() is not available
     GpsStatus() {}
 
-    /**
-     * Used internally within {@link LocationManager} to copy GPS status
-     * data from the Location Manager Service to its cached GpsStatus instance.
-     * Is synchronized to ensure that GPS status updates are atomic.
-     */
-    synchronized void setStatus(int svCount, int[] prns, float[] snrs,
-            float[] elevations, float[] azimuths, int ephemerisMask,
-            int almanacMask, int usedInFixMask) {
+    private void setStatus(int svCount, int[] prnWithFlags, float[] snrs, float[] elevations,
+            float[] azimuths, int[] constellationTypes) { 
         clearSatellites();
         for (int i = 0; i < svCount; i++) {
-            int prn = prns[i];
-            int prnShift = (1 << (prn - 1));
+            // Skip all non-GPS satellites.
+            if (constellationTypes[i] != GnssStatus.CONSTELLATION_GPS) {
+                continue;
+            }
+            int prn = prnWithFlags[i] >> GnssStatus.PRN_SHIFT_WIDTH;
             if (prn > 0 && prn <= NUM_SATELLITES) {
                 GpsSatellite satellite = mSatellites.get(prn);
                 if (satellite == null) {
@@ -160,53 +158,26 @@
                 satellite.mSnr = snrs[i];
                 satellite.mElevation = elevations[i];
                 satellite.mAzimuth = azimuths[i];
-                satellite.mHasEphemeris = ((ephemerisMask & prnShift) != 0);
-                satellite.mHasAlmanac = ((almanacMask & prnShift) != 0);
-                satellite.mUsedInFix = ((usedInFixMask & prnShift) != 0);
+                satellite.mHasEphemeris =
+                        (prnWithFlags[i] & GnssStatus.GNSS_SV_FLAGS_HAS_EPHEMERIS_DATA) != 0;
+                satellite.mHasAlmanac =
+                        (prnWithFlags[i] & GnssStatus.GNSS_SV_FLAGS_HAS_ALMANAC_DATA) != 0;
+                satellite.mUsedInFix =
+                        (prnWithFlags[i] & GnssStatus.GNSS_SV_FLAGS_USED_IN_FIX) != 0;
             }
         }
     }
 
     /**
-     * Used by {@link LocationManager#getGpsStatus} to copy LocationManager's
-     * cached GpsStatus instance to the client's copy.
+     * Copies GPS satellites information from GnssStatus object.
      * Since this method is only used within {@link LocationManager#getGpsStatus},
      * it does not need to be synchronized.
+     * @hide
      */
-    void setStatus(GpsStatus status) {
-        mTimeToFirstFix = status.getTimeToFirstFix();
-        clearSatellites();
-
-        SparseArray<GpsSatellite> otherSatellites = status.mSatellites;
-        int otherSatellitesCount = otherSatellites.size();
-        int satelliteIndex = 0;
-        // merge both sparse arrays, note that we have already invalidated the elements in the
-        // receiver array
-        for (int i = 0; i < otherSatellitesCount; ++i) {
-            GpsSatellite otherSatellite = otherSatellites.valueAt(i);
-            int otherSatellitePrn = otherSatellite.getPrn();
-
-            int satellitesCount = mSatellites.size();
-            while (satelliteIndex < satellitesCount
-                    && mSatellites.valueAt(satelliteIndex).getPrn() < otherSatellitePrn) {
-                ++satelliteIndex;
-            }
-
-            if (satelliteIndex < mSatellites.size()) {
-                GpsSatellite satellite = mSatellites.valueAt(satelliteIndex);
-                if (satellite.getPrn() == otherSatellitePrn) {
-                    satellite.setStatus(otherSatellite);
-                } else {
-                    satellite = new GpsSatellite(otherSatellitePrn);
-                    satellite.setStatus(otherSatellite);
-                    mSatellites.put(otherSatellitePrn, satellite);
-                }
-            } else {
-                GpsSatellite satellite = new GpsSatellite(otherSatellitePrn);
-                satellite.setStatus(otherSatellite);
-                mSatellites.append(otherSatellitePrn, satellite);
-            }
-        }
+    void setStatus(GnssStatus status, int timeToFirstFix) {
+        mTimeToFirstFix = timeToFirstFix;
+        setStatus(status.mSvCount, status.mPrnWithFlags, status.mSnrs, status.mElevations,
+                status.mAzimuths, status.mConstellationTypes);
     }
 
     void setTimeToFirstFix(int ttff) {
diff --git a/location/java/android/location/IGpsStatusListener.aidl b/location/java/android/location/IGnssStatusListener.aidl
similarity index 73%
rename from location/java/android/location/IGpsStatusListener.aidl
rename to location/java/android/location/IGnssStatusListener.aidl
index 62b1c6b..d1c6a85 100644
--- a/location/java/android/location/IGpsStatusListener.aidl
+++ b/location/java/android/location/IGnssStatusListener.aidl
@@ -21,13 +21,12 @@
 /**
  * {@hide}
  */
-oneway interface IGpsStatusListener
+oneway interface IGnssStatusListener
 {
-    void onGpsStarted();
-    void onGpsStopped();
+    void onGnssStarted();
+    void onGnssStopped();
     void onFirstFix(int ttff);
-    void onSvStatusChanged(int svCount, in int[] prns, in float[] snrs, 
-            in float[] elevations, in float[] azimuths, 
-            int ephemerisMask, int almanacMask, int usedInFixMask);
+    void onSvStatusChanged(int svCount, in int[] prnWithFlags, in float[] snrs,
+            in float[] elevations, in float[] azimuths, in int[] constellationTypes);
     void onNmeaReceived(long timestamp, String nmea);
 }
diff --git a/location/java/android/location/IGpsStatusProvider.aidl b/location/java/android/location/IGnssStatusProvider.aidl
similarity index 70%
rename from location/java/android/location/IGpsStatusProvider.aidl
rename to location/java/android/location/IGnssStatusProvider.aidl
index cf277c8..006b5d3 100644
--- a/location/java/android/location/IGpsStatusProvider.aidl
+++ b/location/java/android/location/IGnssStatusProvider.aidl
@@ -16,14 +16,14 @@
 
 package android.location;
 
-import android.location.IGpsStatusListener;
+import android.location.IGnssStatusListener;
 
 /**
- * An interface for location providers that provide GPS status information.
+ * An interface for location providers that provide GNSS status information.
  *
  * {@hide}
  */
-interface IGpsStatusProvider {
-    void addGpsStatusListener(IGpsStatusListener listener);
-    void removeGpsStatusListener(IGpsStatusListener listener);
+interface IGnssStatusProvider {
+    void registerGnssStatusCallback(IGnssStatusListener callback);
+    void unregisterGnssStatusCallback(IGnssStatusListener callback);
 }
diff --git a/location/java/android/location/ILocationManager.aidl b/location/java/android/location/ILocationManager.aidl
index f3d755c..49d841f 100644
--- a/location/java/android/location/ILocationManager.aidl
+++ b/location/java/android/location/ILocationManager.aidl
@@ -21,9 +21,9 @@
 import android.location.Criteria;
 import android.location.GeocoderParams;
 import android.location.Geofence;
+import android.location.IGnssStatusListener;
 import android.location.IGpsMeasurementsListener;
 import android.location.IGpsNavigationMessageListener;
-import android.location.IGpsStatusListener;
 import android.location.ILocationListener;
 import android.location.Location;
 import android.location.LocationRequest;
@@ -48,8 +48,8 @@
 
     Location getLastLocation(in LocationRequest request, String packageName);
 
-    boolean addGpsStatusListener(IGpsStatusListener listener, String packageName);
-    void removeGpsStatusListener(IGpsStatusListener listener);
+    boolean registerGnssStatusCallback(IGnssStatusListener callback, String packageName);
+    void unregisterGnssStatusCallback(IGnssStatusListener callback);
 
     boolean geocoderIsPresent();
     String getFromLocation(double latitude, double longitude, int maxResults,
@@ -69,6 +69,8 @@
             in String packageName);
     void removeGpsNavigationMessageListener(in IGpsNavigationMessageListener listener);
 
+    int getGpsYearOfHardware();
+
     // --- deprecated ---
     List<String> getAllProviders();
     List<String> getProviders(in Criteria criteria, boolean enabledOnly);
diff --git a/location/java/android/location/LocalListenerHelper.java b/location/java/android/location/LocalListenerHelper.java
index 458c451..d7d2c513 100644
--- a/location/java/android/location/LocalListenerHelper.java
+++ b/location/java/android/location/LocalListenerHelper.java
@@ -20,12 +20,14 @@
 
 import android.annotation.NonNull;
 import android.content.Context;
+import android.os.Handler;
 import android.os.RemoteException;
 import android.util.Log;
 
 import java.util.ArrayList;
 import java.util.Collection;
-import java.util.HashSet;
+import java.util.HashMap;
+import java.util.Map;
 
 /**
  * A base handler class to manage transport and local listeners.
@@ -33,7 +35,7 @@
  * @hide
  */
 abstract class LocalListenerHelper<TListener> {
-    private final HashSet<TListener> mListeners = new HashSet<>();
+    private final HashMap<TListener, Handler> mListeners = new HashMap<>();
 
     private final String mTag;
     private final Context mContext;
@@ -44,7 +46,7 @@
         mTag = name;
     }
 
-    public boolean add(@NonNull TListener listener) {
+    public boolean add(@NonNull TListener listener, Handler handler) {
         Preconditions.checkNotNull(listener);
         synchronized (mListeners) {
             // we need to register with the service first, because we need to find out if the
@@ -62,17 +64,19 @@
                     return false;
                 }
             }
-            if (mListeners.contains(listener)) {
+            if (mListeners.containsKey(listener)) {
                 return true;
             }
-            return mListeners.add(listener);
+            mListeners.put(listener, handler);
+            return true;
         }
     }
 
     public void remove(@NonNull TListener listener) {
         Preconditions.checkNotNull(listener);
         synchronized (mListeners) {
-            boolean removed = mListeners.remove(listener);
+            boolean removed = mListeners.containsKey(listener);
+            mListeners.remove(listener);
             boolean isLastRemoved = removed && mListeners.isEmpty();
             if (isLastRemoved) {
                 try {
@@ -95,17 +99,30 @@
         return mContext;
     }
 
-    protected void foreach(ListenerOperation<TListener> operation) {
-        Collection<TListener> listeners;
-        synchronized (mListeners) {
-            listeners = new ArrayList<>(mListeners);
+    private void executeOperation(ListenerOperation<TListener> operation, TListener listener) {
+        try {
+            operation.execute(listener);
+        } catch (RemoteException e) {
+            Log.e(mTag, "Error in monitored listener.", e);
+            // don't return, give a fair chance to all listeners to receive the event
         }
-        for (TListener listener : listeners) {
-            try {
-                operation.execute(listener);
-            } catch (RemoteException e) {
-                Log.e(mTag, "Error in monitored listener.", e);
-                // don't return, give a fair chance to all listeners to receive the event
+    }
+
+    protected void foreach(final ListenerOperation<TListener> operation) {
+        Collection<Map.Entry<TListener, Handler>> listeners;
+        synchronized (mListeners) {
+            listeners = new ArrayList<>(mListeners.entrySet());
+        }
+        for (final Map.Entry<TListener, Handler> listener : listeners) {
+            if (listener.getValue() == null) {
+                executeOperation(operation, listener.getKey());
+            } else {
+                listener.getValue().post(new Runnable() {
+                    @Override
+                    public void run() {
+                        executeOperation(operation, listener.getKey());
+                    }
+                });
             }
         }
     }
diff --git a/location/java/android/location/LocationManager.java b/location/java/android/location/LocationManager.java
index 2c19324..5447bb1 100644
--- a/location/java/android/location/LocationManager.java
+++ b/location/java/android/location/LocationManager.java
@@ -20,6 +20,7 @@
 
 import android.annotation.RequiresPermission;
 import android.annotation.SystemApi;
+import android.annotation.TestApi;
 import android.app.PendingIntent;
 import android.content.Context;
 import android.content.Intent;
@@ -63,13 +64,18 @@
 
     private final Context mContext;
     private final ILocationManager mService;
-    private final GpsMeasurementListenerTransport mGpsMeasurementListenerTransport;
-    private final GpsNavigationMessageListenerTransport mGpsNavigationMessageListenerTransport;
-    private final HashMap<GpsStatus.Listener, GpsStatusListenerTransport> mGpsStatusListeners =
-            new HashMap<GpsStatus.Listener, GpsStatusListenerTransport>();
-    private final HashMap<GpsStatus.NmeaListener, GpsStatusListenerTransport> mNmeaListeners =
-            new HashMap<GpsStatus.NmeaListener, GpsStatusListenerTransport>();
-    private final GpsStatus mGpsStatus = new GpsStatus();
+    private final GpsMeasurementCallbackTransport mGpsMeasurementCallbackTransport;
+    private final GpsNavigationMessageCallbackTransport mGpsNavigationMessageCallbackTransport;
+    private final HashMap<GpsStatus.Listener, GnssStatusListenerTransport> mGpsStatusListeners =
+            new HashMap<>();
+    private final HashMap<GpsStatus.NmeaListener, GnssStatusListenerTransport> mGpsNmeaListeners =
+            new HashMap<>();
+    private final HashMap<GnssStatusCallback, GnssStatusListenerTransport> mGnssStatusListeners =
+            new HashMap<>();
+    private final HashMap<GnssNmeaListener, GnssStatusListenerTransport> mGnssNmeaListeners =
+            new HashMap<>();
+    private GnssStatus mGnssStatus;
+    private int mTimeToFirstFix;
 
     /**
      * Name of the network location provider.
@@ -315,9 +321,9 @@
     public LocationManager(Context context, ILocationManager service) {
         mService = service;
         mContext = context;
-        mGpsMeasurementListenerTransport = new GpsMeasurementListenerTransport(mContext, mService);
-        mGpsNavigationMessageListenerTransport =
-                new GpsNavigationMessageListenerTransport(mContext, mService);
+        mGpsMeasurementCallbackTransport = new GpsMeasurementCallbackTransport(mContext, mService);
+        mGpsNavigationMessageCallbackTransport =
+                new GpsNavigationMessageCallbackTransport(mContext, mService);
     }
 
     private LocationProvider createProvider(String name, ProviderProperties properties) {
@@ -1389,11 +1395,51 @@
 
     // --- GPS-specific support ---
 
-    // This class is used to send GPS status events to the client's main thread.
-    private class GpsStatusListenerTransport extends IGpsStatusListener.Stub {
+    // This class is used to send Gnss status events to the client's specific thread.
+    private class GnssStatusListenerTransport extends IGnssStatusListener.Stub {
 
-        private final GpsStatus.Listener mListener;
-        private final GpsStatus.NmeaListener mNmeaListener;
+        private final GpsStatus.Listener mGpsListener;
+        private final GpsStatus.NmeaListener mGpsNmeaListener;
+        private final GnssStatusCallback mGnssCallback;
+        private final GnssNmeaListener mGnssNmeaListener;
+
+        private class GnssHandler extends Handler {
+            public GnssHandler(Handler handler) {
+                super(handler != null ? handler.getLooper() : Looper.myLooper());
+            }
+
+            @Override
+            public void handleMessage(Message msg) {
+                switch (msg.what) {
+                    case NMEA_RECEIVED:
+                        synchronized (mNmeaBuffer) {
+                            int length = mNmeaBuffer.size();
+                            for (int i = 0; i < length; i++) {
+                                Nmea nmea = mNmeaBuffer.get(i);
+                                mGnssNmeaListener.onNmeaReceived(nmea.mTimestamp, nmea.mNmea);
+                            }
+                            mNmeaBuffer.clear();
+                        }
+                        break;
+                    case GpsStatus.GPS_EVENT_STARTED:
+                        mGnssCallback.onStarted();
+                        break;
+                    case GpsStatus.GPS_EVENT_STOPPED:
+                        mGnssCallback.onStopped();
+                        break;
+                    case GpsStatus.GPS_EVENT_FIRST_FIX:
+                        mGnssCallback.onFirstFix(mTimeToFirstFix);
+                        break;
+                    case GpsStatus.GPS_EVENT_SATELLITE_STATUS:
+                        mGnssCallback.onSatelliteStatusChanged(mGnssStatus);
+                        break;
+                    default:
+                        break;
+                }
+            }
+        }
+
+        private final Handler mGnssHandler;
 
         // This must not equal any of the GpsStatus event IDs
         private static final int NMEA_RECEIVED = 1000;
@@ -1407,97 +1453,141 @@
                 mNmea = nmea;
             }
         }
-        private ArrayList<Nmea> mNmeaBuffer;
+        private final ArrayList<Nmea> mNmeaBuffer;
 
-        GpsStatusListenerTransport(GpsStatus.Listener listener) {
-            mListener = listener;
-            mNmeaListener = null;
+        GnssStatusListenerTransport(GpsStatus.Listener listener) {
+            this(listener, null);
         }
 
-        GpsStatusListenerTransport(GpsStatus.NmeaListener listener) {
-            mNmeaListener = listener;
-            mListener = null;
+        GnssStatusListenerTransport(GpsStatus.Listener listener, Handler handler) {
+            mGpsListener = listener;
+            mGnssHandler = new GnssHandler(handler);
+            mGpsNmeaListener = null;
+            mNmeaBuffer = null;
+            mGnssCallback = new GnssStatusCallback() {
+                @Override
+                public void onStarted() {
+                    mGpsListener.onGpsStatusChanged(GpsStatus.GPS_EVENT_STARTED);
+                }
+
+                @Override
+                public void onStopped() {
+                    mGpsListener.onGpsStatusChanged(GpsStatus.GPS_EVENT_STOPPED);
+                }
+
+                @Override
+                public void onFirstFix(int ttff) {
+                    mGpsListener.onGpsStatusChanged(GpsStatus.GPS_EVENT_FIRST_FIX);
+                }
+
+                @Override
+                public void onSatelliteStatusChanged(GnssStatus status) {
+                    mGpsListener.onGpsStatusChanged(GpsStatus.GPS_EVENT_SATELLITE_STATUS);
+                }
+            };
+            mGnssNmeaListener = null;
+        }
+
+        GnssStatusListenerTransport(GpsStatus.NmeaListener listener) {
+            this(listener, null);
+        }
+
+        GnssStatusListenerTransport(GpsStatus.NmeaListener listener, Handler handler) {
+            mGpsListener = null;
+            mGnssHandler = new GnssHandler(handler);
+            mGpsNmeaListener = listener;
+            mNmeaBuffer = new ArrayList<Nmea>();
+            mGnssCallback = null;
+            mGnssNmeaListener = new GnssNmeaListener() {
+                @Override
+                public void onNmeaReceived(long timestamp, String nmea) {
+                    mGpsNmeaListener.onNmeaReceived(timestamp, nmea);
+                }
+            };
+        }
+
+        GnssStatusListenerTransport(GnssStatusCallback callback) {
+            this(callback, null);
+        }
+
+        GnssStatusListenerTransport(GnssStatusCallback callback, Handler handler) {
+            mGnssCallback = callback;
+            mGnssHandler = new GnssHandler(handler);
+            mGnssNmeaListener = null;
+            mNmeaBuffer = null;
+            mGpsListener = null;
+            mGpsNmeaListener = null;
+        }
+
+        GnssStatusListenerTransport(GnssNmeaListener listener) {
+            this(listener, null);
+        }
+
+        GnssStatusListenerTransport(GnssNmeaListener listener, Handler handler) {
+            mGnssCallback = null;
+            mGnssHandler = new GnssHandler(handler);
+            mGnssNmeaListener = listener;
+            mGpsListener = null;
+            mGpsNmeaListener = null;
             mNmeaBuffer = new ArrayList<Nmea>();
         }
 
         @Override
-        public void onGpsStarted() {
-            if (mListener != null) {
+        public void onGnssStarted() {
+            if (mGpsListener != null) {
                 Message msg = Message.obtain();
                 msg.what = GpsStatus.GPS_EVENT_STARTED;
-                mGpsHandler.sendMessage(msg);
+                mGnssHandler.sendMessage(msg);
             }
         }
 
         @Override
-        public void onGpsStopped() {
-            if (mListener != null) {
+        public void onGnssStopped() {
+            if (mGpsListener != null) {
                 Message msg = Message.obtain();
                 msg.what = GpsStatus.GPS_EVENT_STOPPED;
-                mGpsHandler.sendMessage(msg);
+                mGnssHandler.sendMessage(msg);
             }
         }
 
         @Override
         public void onFirstFix(int ttff) {
-            if (mListener != null) {
-                mGpsStatus.setTimeToFirstFix(ttff);
+            if (mGpsListener != null) {
+                mTimeToFirstFix = ttff;
                 Message msg = Message.obtain();
                 msg.what = GpsStatus.GPS_EVENT_FIRST_FIX;
-                mGpsHandler.sendMessage(msg);
+                mGnssHandler.sendMessage(msg);
             }
         }
 
         @Override
-        public void onSvStatusChanged(int svCount, int[] prns, float[] snrs,
-                float[] elevations, float[] azimuths, int ephemerisMask,
-                int almanacMask, int usedInFixMask) {
-            if (mListener != null) {
-                mGpsStatus.setStatus(svCount, prns, snrs, elevations, azimuths,
-                        ephemerisMask, almanacMask, usedInFixMask);
+        public void onSvStatusChanged(int svCount, int[] prnWithFlags,
+                float[] snrs, float[] elevations, float[] azimuths, int[] constellationTypes) {
+            if (mGnssCallback != null) {
+                mGnssStatus = new GnssStatus(svCount, prnWithFlags, snrs, elevations, azimuths,
+                        constellationTypes);
 
                 Message msg = Message.obtain();
                 msg.what = GpsStatus.GPS_EVENT_SATELLITE_STATUS;
                 // remove any SV status messages already in the queue
-                mGpsHandler.removeMessages(GpsStatus.GPS_EVENT_SATELLITE_STATUS);
-                mGpsHandler.sendMessage(msg);
+                mGnssHandler.removeMessages(GpsStatus.GPS_EVENT_SATELLITE_STATUS);
+                mGnssHandler.sendMessage(msg);
             }
         }
 
         @Override
         public void onNmeaReceived(long timestamp, String nmea) {
-            if (mNmeaListener != null) {
+            if (mGnssNmeaListener != null) {
                 synchronized (mNmeaBuffer) {
                     mNmeaBuffer.add(new Nmea(timestamp, nmea));
                 }
                 Message msg = Message.obtain();
                 msg.what = NMEA_RECEIVED;
                 // remove any NMEA_RECEIVED messages already in the queue
-                mGpsHandler.removeMessages(NMEA_RECEIVED);
-                mGpsHandler.sendMessage(msg);
+                mGnssHandler.removeMessages(NMEA_RECEIVED);
+                mGnssHandler.sendMessage(msg);
             }
         }
-
-        private final Handler mGpsHandler = new Handler() {
-            @Override
-            public void handleMessage(Message msg) {
-                if (msg.what == NMEA_RECEIVED) {
-                    synchronized (mNmeaBuffer) {
-                        int length = mNmeaBuffer.size();
-                        for (int i = 0; i < length; i++) {
-                            Nmea nmea = mNmeaBuffer.get(i);
-                            mNmeaListener.onNmeaReceived(nmea.mTimestamp, nmea.mNmea);
-                        }
-                        mNmeaBuffer.clear();
-                    }
-                } else {
-                    // synchronize on mGpsStatus to ensure the data is copied atomically.
-                    synchronized(mGpsStatus) {
-                        mListener.onGpsStatusChanged(msg.what);
-                    }
-                }
-            }
-        };
     }
 
     /**
@@ -1508,7 +1598,9 @@
      * @return true if the listener was successfully added
      *
      * @throws SecurityException if the ACCESS_FINE_LOCATION permission is not present
+     * @deprecated use {@link #registerGnssStatusCallback(GnssStatusCallback)} instead.
      */
+    @Deprecated
     @RequiresPermission(ACCESS_FINE_LOCATION)
     public boolean addGpsStatusListener(GpsStatus.Listener listener) {
         boolean result;
@@ -1518,8 +1610,8 @@
             return true;
         }
         try {
-            GpsStatusListenerTransport transport = new GpsStatusListenerTransport(listener);
-            result = mService.addGpsStatusListener(transport, mContext.getPackageName());
+            GnssStatusListenerTransport transport = new GnssStatusListenerTransport(listener);
+            result = mService.registerGnssStatusCallback(transport, mContext.getPackageName());
             if (result) {
                 mGpsStatusListeners.put(listener, transport);
             }
@@ -1536,17 +1628,81 @@
      *
      * @param listener GPS status listener object to remove
      */
+    @Deprecated
     public void removeGpsStatusListener(GpsStatus.Listener listener) {
         try {
-            GpsStatusListenerTransport transport = mGpsStatusListeners.remove(listener);
+            GnssStatusListenerTransport transport = mGpsStatusListeners.remove(listener);
             if (transport != null) {
-                mService.removeGpsStatusListener(transport);
+                mService.unregisterGnssStatusCallback(transport);
             }
         } catch (RemoteException e) {
             Log.e(TAG, "RemoteException in unregisterGpsStatusListener: ", e);
         }
     }
 
+
+    /**
+     * Registers a GNSS status listener.
+     *
+     * @param callback GNSS status listener object to register
+     *
+     * @return true if the listener was successfully added
+     *
+     * @throws SecurityException if the ACCESS_FINE_LOCATION permission is not present
+     */
+    @RequiresPermission(ACCESS_FINE_LOCATION)
+    public boolean registerGnssStatusCallback(GnssStatusCallback callback) {
+        return registerGnssStatusCallback(callback, null);
+    }
+
+    /**
+     * Registers a GNSS status listener.
+     *
+     * @param callback GNSS status listener object to register
+     * @param handler the handler that the callback runs on.
+     *
+     * @return true if the listener was successfully added
+     *
+     * @throws SecurityException if the ACCESS_FINE_LOCATION permission is not present
+     */
+    @RequiresPermission(ACCESS_FINE_LOCATION)
+    public boolean registerGnssStatusCallback(GnssStatusCallback callback, Handler handler) {
+        boolean result;
+        if (mGnssStatusListeners.get(callback) != null) {
+            // listener is already registered
+            return true;
+        }
+        try {
+            GnssStatusListenerTransport transport =
+                    new GnssStatusListenerTransport(callback, handler);
+            result = mService.registerGnssStatusCallback(transport, mContext.getPackageName());
+            if (result) {
+                mGnssStatusListeners.put(callback, transport);
+            }
+        } catch (RemoteException e) {
+            Log.e(TAG, "RemoteException in registerGnssStatusCallback: ", e);
+            result = false;
+        }
+
+        return result;
+    }
+
+    /**
+     * Removes a GNSS status listener.
+     *
+     * @param callback GNSS status listener object to remove
+     */
+    public void unregisterGnssStatusCallback(GnssStatusCallback callback) {
+        try {
+            GnssStatusListenerTransport transport = mGnssStatusListeners.remove(callback);
+            if (transport != null) {
+                mService.unregisterGnssStatusCallback(transport);
+            }
+        } catch (RemoteException e) {
+            Log.e(TAG, "RemoteException in unregisterGnssStatusCallback: ", e);
+        }
+    }
+
     /**
      * Adds an NMEA listener.
      *
@@ -1555,20 +1711,22 @@
      * @return true if the listener was successfully added
      *
      * @throws SecurityException if the ACCESS_FINE_LOCATION permission is not present
+     * @deprecated use {@link #addNmeaListener(GnssNmeaListener)} instead.
      */
+    @Deprecated
     @RequiresPermission(ACCESS_FINE_LOCATION)
     public boolean addNmeaListener(GpsStatus.NmeaListener listener) {
         boolean result;
 
-        if (mNmeaListeners.get(listener) != null) {
+        if (mGpsNmeaListeners.get(listener) != null) {
             // listener is already registered
             return true;
         }
         try {
-            GpsStatusListenerTransport transport = new GpsStatusListenerTransport(listener);
-            result = mService.addGpsStatusListener(transport, mContext.getPackageName());
+            GnssStatusListenerTransport transport = new GnssStatusListenerTransport(listener);
+            result = mService.registerGnssStatusCallback(transport, mContext.getPackageName());
             if (result) {
-                mNmeaListeners.put(listener, transport);
+                mGpsNmeaListeners.put(listener, transport);
             }
         } catch (RemoteException e) {
             Log.e(TAG, "RemoteException in registerGpsStatusListener: ", e);
@@ -1583,11 +1741,12 @@
      *
      * @param listener a {@link GpsStatus.NmeaListener} object to remove
      */
+    @Deprecated
     public void removeNmeaListener(GpsStatus.NmeaListener listener) {
         try {
-            GpsStatusListenerTransport transport = mNmeaListeners.remove(listener);
+            GnssStatusListenerTransport transport = mGpsNmeaListeners.remove(listener);
             if (transport != null) {
-                mService.removeGpsStatusListener(transport);
+                mService.unregisterGnssStatusCallback(transport);
             }
         } catch (RemoteException e) {
             Log.e(TAG, "RemoteException in unregisterGpsStatusListener: ", e);
@@ -1595,54 +1754,133 @@
     }
 
     /**
-     * Adds a GPS Measurement listener.
+     * Adds an NMEA listener.
      *
-     * @param listener a {@link GpsMeasurementsEvent.Listener} object to register.
-     * @return {@code true} if the listener was added successfully, {@code false} otherwise.
+     * @param listener a {@link GnssNmeaListener} object to register
      *
-     * @hide
+     * @return true if the listener was successfully added
+     *
+     * @throws SecurityException if the ACCESS_FINE_LOCATION permission is not present
      */
-    @SystemApi
-    public boolean addGpsMeasurementListener(GpsMeasurementsEvent.Listener listener) {
-        return mGpsMeasurementListenerTransport.add(listener);
+    @RequiresPermission(ACCESS_FINE_LOCATION)
+    public boolean addNmeaListener(GnssNmeaListener listener) {
+        return addNmeaListener(listener, null);
     }
 
     /**
-     * Removes a GPS Measurement listener.
+     * Adds an NMEA listener.
      *
-     * @param listener a {@link GpsMeasurementsEvent.Listener} object to remove.
+     * @param listener a {@link GnssNmeaListener} object to register
+     * @param handler the handler that the listener runs on.
      *
-     * @hide
+     * @return true if the listener was successfully added
+     *
+     * @throws SecurityException if the ACCESS_FINE_LOCATION permission is not present
      */
-    @SystemApi
-    public void removeGpsMeasurementListener(GpsMeasurementsEvent.Listener listener) {
-        mGpsMeasurementListenerTransport.remove(listener);
+    @RequiresPermission(ACCESS_FINE_LOCATION)
+    public boolean addNmeaListener(GnssNmeaListener listener, Handler handler) {
+        boolean result;
+
+        if (mGpsNmeaListeners.get(listener) != null) {
+            // listener is already registered
+            return true;
+        }
+        try {
+            GnssStatusListenerTransport transport =
+                    new GnssStatusListenerTransport(listener, handler);
+            result = mService.registerGnssStatusCallback(transport, mContext.getPackageName());
+            if (result) {
+                mGnssNmeaListeners.put(listener, transport);
+            }
+        } catch (RemoteException e) {
+            Log.e(TAG, "RemoteException in registerGnssStatusCallback: ", e);
+            result = false;
+        }
+
+        return result;
     }
 
     /**
-     * Adds a GPS Navigation Message listener.
+     * Removes an NMEA listener.
      *
-     * @param listener a {@link GpsNavigationMessageEvent.Listener} object to register.
-     * @return {@code true} if the listener was added successfully, {@code false} otherwise.
-     *
-     * @hide
+     * @param listener a {@link GnssNmeaListener} object to remove
      */
-    @SystemApi
-    public boolean addGpsNavigationMessageListener(GpsNavigationMessageEvent.Listener listener) {
-        return mGpsNavigationMessageListenerTransport.add(listener);
+    public void removeNmeaListener(GnssNmeaListener listener) {
+        try {
+            GnssStatusListenerTransport transport = mGnssNmeaListeners.remove(listener);
+            if (transport != null) {
+                mService.unregisterGnssStatusCallback(transport);
+            }
+        } catch (RemoteException e) {
+            Log.e(TAG, "RemoteException in unregisterGnssStatusCallback: ", e);
+        }
     }
 
     /**
-     * Removes a GPS Navigation Message listener.
+     * Registers a GPS Measurement callback.
      *
-     * @param listener a {@link GpsNavigationMessageEvent.Listener} object to remove.
-     *
-     * @hide
+     * @param callback a {@link GpsMeasurementsEvent.Callback} object to register.
+     * @return {@code true} if the callback was added successfully, {@code false} otherwise.
      */
-    @SystemApi
-    public void removeGpsNavigationMessageListener(
-            GpsNavigationMessageEvent.Listener listener) {
-        mGpsNavigationMessageListenerTransport.remove(listener);
+    @RequiresPermission(ACCESS_FINE_LOCATION)
+    public boolean registerGpsMeasurementCallback(GpsMeasurementsEvent.Callback callback) {
+        return registerGpsMeasurementCallback(callback, null);
+    }
+
+    /**
+     * Registers a GPS Measurement callback.
+     *
+     * @param callback a {@link GpsMeasurementsEvent.Callback} object to register.
+     * @param handler the handler that the callback runs on.
+     * @return {@code true} if the callback was added successfully, {@code false} otherwise.
+     */
+    @RequiresPermission(ACCESS_FINE_LOCATION)
+    public boolean registerGpsMeasurementCallback(GpsMeasurementsEvent.Callback callback,
+            Handler handler) {
+        return mGpsMeasurementCallbackTransport.add(callback, handler);
+    }
+
+    /**
+     * Unregisters a GPS Measurement callback.
+     *
+     * @param callback a {@link GpsMeasurementsEvent.Callback} object to remove.
+     */
+    public void unregisterGpsMeasurementCallback(GpsMeasurementsEvent.Callback callback) {
+        mGpsMeasurementCallbackTransport.remove(callback);
+    }
+
+    /**
+     * Registers a GPS Navigation Message callback.
+     *
+     * @param callback a {@link GpsNavigationMessageEvent.Callback} object to register.
+     * @return {@code true} if the callback was added successfully, {@code false} otherwise.
+     */
+    public boolean registerGpsNavigationMessageCallback(
+            GpsNavigationMessageEvent.Callback callback) {
+        return registerGpsNavigationMessageCallback(callback, null);
+    }
+
+    /**
+     * Registers a GPS Navigation Message callback.
+     *
+     * @param callback a {@link GpsNavigationMessageEvent.Callback} object to register.
+     * @param handler the handler that the callback runs on.
+     * @return {@code true} if the callback was added successfully, {@code false} otherwise.
+     */
+    @RequiresPermission(ACCESS_FINE_LOCATION)
+    public boolean registerGpsNavigationMessageCallback(
+            GpsNavigationMessageEvent.Callback callback, Handler handler) {
+        return mGpsNavigationMessageCallbackTransport.add(callback, handler);
+    }
+
+    /**
+     * Unregisters a GPS Navigation Message callback.
+     *
+     * @param callback a {@link GpsNavigationMessageEvent.Callback} object to remove.
+     */
+    public void unregisterGpsNavigationMessageCallback(
+            GpsNavigationMessageEvent.Callback callback) {
+        mGpsNavigationMessageCallbackTransport.remove(callback);
     }
 
      /**
@@ -1656,15 +1894,36 @@
      * @param status object containing GPS status details, or null.
      * @return status object containing updated GPS status.
      */
+    @Deprecated
+    @RequiresPermission(ACCESS_FINE_LOCATION)
     public GpsStatus getGpsStatus(GpsStatus status) {
         if (status == null) {
             status = new GpsStatus();
         }
-        status.setStatus(mGpsStatus);
+        // When mGnssStatus is null, that means that this method is called outside
+        // onGpsStatusChanged().  Return an empty status  to maintain backwards compatibility.
+        if (mGnssStatus != null) {
+            status.setStatus(mGnssStatus, mTimeToFirstFix);
+        }
         return status;
     }
 
     /**
+     * Returns the system information of the GPS hardware.
+     * May return 0 if GPS hardware is earlier than 2016.
+     * @hide
+     */
+    @TestApi
+    public int getGpsYearOfHardware() {
+        try {
+            return mService.getGpsYearOfHardware();
+        } catch (RemoteException e) {
+            Log.e(TAG, "RemoteException in getGpsSystemInfo: ", e);
+            return 0;
+        }
+    }
+
+    /**
      * Sends additional commands to a location provider.
      * Can be used to support provider specific extensions to the Location Manager API
      *
diff --git a/location/java/com/android/internal/location/GpsNetInitiatedHandler.java b/location/java/com/android/internal/location/GpsNetInitiatedHandler.java
index 260f380..93e86af 100644
--- a/location/java/com/android/internal/location/GpsNetInitiatedHandler.java
+++ b/location/java/com/android/internal/location/GpsNetInitiatedHandler.java
@@ -62,7 +62,7 @@
     public static final String NI_INTENT_KEY_TIMEOUT = "timeout";
     public static final String NI_INTENT_KEY_DEFAULT_RESPONSE = "default_resp";
 
-    // the extra command to send NI response to GpsLocationProvider
+    // the extra command to send NI response to GnssLocationProvider
     public static final String NI_RESPONSE_EXTRA_CMD = "send_ni_response";
 
     // the extra command parameter names in the Bundle
diff --git a/media/java/android/media/AudioRecordConfiguration.java b/media/java/android/media/AudioRecordConfiguration.java
index c7d219d..69df88f 100644
--- a/media/java/android/media/AudioRecordConfiguration.java
+++ b/media/java/android/media/AudioRecordConfiguration.java
@@ -23,7 +23,7 @@
 
 /**
  * The AudioRecordConfiguration class collects the information describing an audio recording
- * session. This information is returned through the 
+ * session. This information is returned through the
  * {@link AudioManager#getActiveRecordConfigurations()} method.
  *
  */
@@ -33,12 +33,32 @@
 
     private final int mClientSource;
 
+    private final AudioFormat mDeviceFormat;
+    private final AudioFormat mClientFormat;
+
+    private final AudioDeviceInfo mRecDevice;
+
     /**
      * @hide
      */
     public AudioRecordConfiguration(int session, int source) {
         mSessionId = session;
         mClientSource = source;
+        mDeviceFormat = new AudioFormat.Builder().build();
+        mClientFormat = new AudioFormat.Builder().build();
+        mRecDevice = null;
+    }
+
+    /**
+     * @hide
+     */
+    public AudioRecordConfiguration(int session, int source, AudioFormat devFormat,
+            AudioFormat clientFormat, AudioDeviceInfo device) {
+        mSessionId = session;
+        mClientSource = source;
+        mDeviceFormat = devFormat;
+        mClientFormat = clientFormat;
+        mRecDevice = device;
     }
 
     /**
@@ -57,7 +77,28 @@
      * Returns the session number of the recording, see {@link AudioRecord#getAudioSessionId()}.
      * @return the session number.
      */
-    public int getAudioSessionId() { return mSessionId; }
+    public int getClientAudioSessionId() { return mSessionId; }
+
+    /**
+     * Returns the audio format at which audio is recorded on this Android device.
+     * Note that it may differ from the client application recording format
+     * (see {@link #getClientFormat()}).
+     * @return the device recording format
+     */
+    public AudioFormat getFormat() { return mDeviceFormat; }
+
+    /**
+     * Returns the audio format at which the client application is recording audio.
+     * Note that it may differ from the actual recording format (see {@link #getFormat()}).
+     * @return the recording format
+     */
+    public AudioFormat getClientFormat() { return mClientFormat; }
+
+    /**
+     * Returns the audio input device used for this recording.
+     * @return the audio recording device
+     */
+    public AudioDeviceInfo getAudioDevice() { return mRecDevice; }
 
     public static final Parcelable.Creator<AudioRecordConfiguration> CREATOR
             = new Parcelable.Creator<AudioRecordConfiguration>() {
@@ -93,6 +134,9 @@
     private AudioRecordConfiguration(Parcel in) {
         mSessionId = in.readInt();
         mClientSource = in.readInt();
+        mDeviceFormat = new AudioFormat.Builder().build();
+        mClientFormat = new AudioFormat.Builder().build();
+        mRecDevice = null;
     }
 
     @Override
diff --git a/media/java/android/media/DrmInitData.java b/media/java/android/media/DrmInitData.java
new file mode 100644
index 0000000..06fe6ff
--- /dev/null
+++ b/media/java/android/media/DrmInitData.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.media;
+
+import android.media.MediaDrm;
+
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.UUID;
+
+/**
+ * Encapsulates initialization data required by a {@link MediaDrm} instance.
+ */
+public abstract class DrmInitData {
+
+    /**
+     * Retrieves initialization data for a given DRM scheme, specified by its UUID.
+     *
+     * @param schemeUuid The DRM scheme's UUID.
+     * @return The initialization data for the scheme, or null if the scheme is not supported.
+     */
+    public abstract SchemeInitData get(UUID schemeUuid);
+
+    /**
+     * Scheme initialization data.
+     */
+    public static final class SchemeInitData {
+
+        /**
+         * The mimeType of {@link #data}.
+         */
+        public final String mimeType;
+        /**
+         * The initialization data.
+         */
+        public final byte[] data;
+
+        /**
+         * @param mimeType The mimeType of the initialization data.
+         * @param data The initialization data.
+         *
+         * @hide
+         */
+        public SchemeInitData(String mimeType, byte[] data) {
+            this.mimeType = mimeType;
+            this.data = data;
+        }
+
+        @Override
+        public boolean equals(Object obj) {
+            if (!(obj instanceof SchemeInitData)) {
+                return false;
+            }
+            if (obj == this) {
+                return true;
+            }
+
+            SchemeInitData other = (SchemeInitData) obj;
+            return mimeType.equals(other.mimeType) && Arrays.equals(data, other.data);
+        }
+
+        @Override
+        public int hashCode() {
+            return mimeType.hashCode() + 31 * Arrays.hashCode(data);
+        }
+
+    }
+
+}
diff --git a/media/java/android/media/MediaExtractor.java b/media/java/android/media/MediaExtractor.java
index 0bf995f..b339925 100644
--- a/media/java/android/media/MediaExtractor.java
+++ b/media/java/android/media/MediaExtractor.java
@@ -34,7 +34,9 @@
 import java.lang.annotation.RetentionPolicy;
 import java.nio.ByteBuffer;
 import java.nio.ByteOrder;
+import java.util.Collections;
 import java.util.HashMap;
+import java.util.List;
 import java.util.Map;
 import java.util.UUID;
 
@@ -226,6 +228,46 @@
     public native final int getTrackCount();
 
     /**
+     * Extract DRM initialization data if it exists
+     *
+     * @return DRM initialization data in the content, or {@code null}
+     * if no recognizable DRM format is found;
+     * @see DrmInitData
+     */
+    public DrmInitData getDrmInitData() {
+        Map<String, Object> formatMap = getFileFormatNative();
+        if (formatMap == null) {
+            return null;
+        }
+        if (formatMap.containsKey("pssh")) {
+            Map<UUID, byte[]> psshMap = getPsshInfo();
+            final Map<UUID, DrmInitData.SchemeInitData> initDataMap =
+                new HashMap<UUID, DrmInitData.SchemeInitData>();
+            for (Map.Entry<UUID, byte[]> e: psshMap.entrySet()) {
+                UUID uuid = e.getKey();
+                byte[] data = e.getValue();
+                initDataMap.put(uuid, new DrmInitData.SchemeInitData("cenc", data));
+            }
+            return new DrmInitData() {
+                public SchemeInitData get(UUID schemeUuid) {
+                    return initDataMap.get(schemeUuid);
+                }
+            };
+        } else if (formatMap.containsKey("crypto-key")) {
+            ByteBuffer buf = (ByteBuffer) formatMap.get("crypto-key");
+            buf.rewind();
+            final byte[] data = new byte[buf.remaining()];
+            buf.get(data);
+            return new DrmInitData() {
+                public SchemeInitData get(UUID schemeUuid) {
+                    return new DrmInitData.SchemeInitData("webm", data);
+                }
+            };
+        }
+        return null;
+    }
+
+    /**
      * Get the PSSH info if present.
      * @return a map of uuid-to-bytes, with the uuid specifying
      * the crypto scheme, and the bytes being the data specific to that scheme.
diff --git a/media/java/android/media/MediaFormat.java b/media/java/android/media/MediaFormat.java
index 930d8b8..d06da97 100644
--- a/media/java/android/media/MediaFormat.java
+++ b/media/java/android/media/MediaFormat.java
@@ -563,6 +563,66 @@
     /** @hide */
     public static final String KEY_IS_TIMED_TEXT = "is-timed-text";
 
+    // The following color aspect values must be in sync with the ones in HardwareAPI.h.
+    /*
+     * An optional key describing the color primaries, white point and
+     * luminance factors for video content.
+     *
+     * The associated value is an integer: 0 if unspecified, or one of the
+     * COLOR_STANDARD_ values.
+     */
+    public static final String KEY_COLOR_STANDARD = "color-standard";
+
+    /** BT.709 color chromacity coordinates with KR = 0.2126, KB = 0.0722. */
+    public static final int COLOR_STANDARD_BT709 = 1;
+
+    /** BT.601 625 color chromacity coordinates with KR = 0.299, KB = 0.114. */
+    public static final int COLOR_STANDARD_BT601_PAL = 2;
+
+    /** BT.601 525 color chromacity coordinates with KR = 0.299, KB = 0.114. */
+    public static final int COLOR_STANDARD_BT601_NTSC = 4;
+
+    /** BT.2020 color chromacity coordinates with KR = 0.2627, KB = 0.0593. */
+    public static final int COLOR_STANDARD_BT2020 = 6;
+
+    /**
+     * An optional key describing the opto-electronic transfer function used
+     * for the video content.
+     *
+     * The associated value is an integer: 0 if unspecified, or one of the
+     * COLOR_TRANSFER_ values.
+     */
+    public static final String KEY_COLOR_TRANSFER = "color-transfer";
+
+    /** Linear transfer characteristic curve. */
+    public static final int COLOR_TRANSFER_LINEAR = 1;
+
+    /** SMPTE 170M transfer characteristic curve used by BT.601/BT.709/BT.2020. This is the curve
+     *  used by most non-HDR video content. */
+    public static final int COLOR_TRANSFER_SDR_VIDEO = 3;
+
+    /** SMPTE ST 2084 transfer function. This is used by some HDR video content. */
+    public static final int COLOR_TRANSFER_ST2084 = 6;
+
+    /** ARIB STD-B67 hybrid-log-gamma transfer function. This is used by some HDR video content. */
+    public static final int COLOR_TRANSFER_HLG = 7;
+
+    /**
+     * An optional key describing the range of the component values of the video content.
+     *
+     * The associated value is an integer: 0 if unspecified, or one of the
+     * COLOR_RANGE_ values.
+     */
+    public static final String KEY_COLOR_RANGE = "color-range";
+
+    /** Limited range. Y component values range from 16 to 235 for 8-bit content.
+     *  Cr, Cy values range from 16 to 240 for 8-bit content.
+     *  This is the default for video content. */
+    public static final int COLOR_RANGE_LIMITED = 2;
+
+    /** Full range. Y, Cr and Cb component values range from 0 to 255 for 8-bit content. */
+    public static final int COLOR_RANGE_FULL = 1;
+
     /* package private */ MediaFormat(Map<String, Object> map) {
         mMap = map;
     }
diff --git a/media/java/android/media/audiopolicy/AudioMixingRule.java b/media/java/android/media/audiopolicy/AudioMixingRule.java
index 02b03d2..b9fcba2 100644
--- a/media/java/android/media/audiopolicy/AudioMixingRule.java
+++ b/media/java/android/media/audiopolicy/AudioMixingRule.java
@@ -19,6 +19,7 @@
 import android.annotation.SystemApi;
 import android.media.AudioAttributes;
 import android.os.Parcel;
+import android.util.Log;
 
 import java.util.ArrayList;
 import java.util.Iterator;
@@ -48,14 +49,27 @@
 
     /**
      * A rule requiring the usage information of the {@link AudioAttributes} to match.
+     * This mixing rule can be added with {@link Builder#addRule(AudioAttributes, int)} or
+     * {@link Builder#addMixRule(int, Object)} where the Object parameter is an instance of
+     * {@link AudioAttributes}.
      */
     @SystemApi
     public static final int RULE_MATCH_ATTRIBUTE_USAGE = 0x1;
     /**
      * A rule requiring the capture preset information of the {@link AudioAttributes} to match.
+     * This mixing rule can be added with {@link Builder#addRule(AudioAttributes, int)} or
+     * {@link Builder#addMixRule(int, Object)} where the Object parameter is an instance of
+     * {@link AudioAttributes}.
      */
     @SystemApi
     public static final int RULE_MATCH_ATTRIBUTE_CAPTURE_PRESET = 0x1 << 1;
+    /**
+     * A rule requiring the UID of the audio stream to match that specified.
+     * This mixing rule can be added with {@link Builder#addMixRule(int, Object)} where the Object
+     * parameter is an instance of {@link java.lang.Integer}.
+     */
+    @SystemApi
+    public static final int RULE_MATCH_UID = 0x1 << 2;
 
     private final static int RULE_EXCLUSION_MASK = 0x8000;
     /**
@@ -70,29 +84,53 @@
      */
     public static final int RULE_EXCLUDE_ATTRIBUTE_CAPTURE_PRESET =
             RULE_EXCLUSION_MASK | RULE_MATCH_ATTRIBUTE_CAPTURE_PRESET;
+    /**
+     * @hide
+     * A rule requiring the UID information to differ.
+     */
+    public static final int RULE_EXCLUDE_UID =
+            RULE_EXCLUSION_MASK | RULE_MATCH_UID;
 
     static final class AttributeMatchCriterion {
-        AudioAttributes mAttr;
-        int mRule;
+        final AudioAttributes mAttr;
+        final Integer mIntProp;
+        final int mRule;
 
         /** input parameters must be valid */
         AttributeMatchCriterion(AudioAttributes attributes, int rule) {
             mAttr = attributes;
+            mIntProp = null;
+            mRule = rule;
+        }
+        /** input parameters must be valid */
+        AttributeMatchCriterion(Integer intProp, int rule) {
+            mAttr = null;
+            mIntProp = intProp;
             mRule = rule;
         }
 
         @Override
         public int hashCode() {
-            return Objects.hash(mAttr, mRule);
+            return Objects.hash(mAttr, mIntProp, mRule);
         }
 
         void writeToParcel(Parcel dest) {
             dest.writeInt(mRule);
-            if ((mRule == RULE_MATCH_ATTRIBUTE_USAGE) || (mRule == RULE_EXCLUDE_ATTRIBUTE_USAGE)) {
+            final int match_rule = mRule & ~RULE_EXCLUSION_MASK;
+            switch (match_rule) {
+            case RULE_MATCH_ATTRIBUTE_USAGE:
                 dest.writeInt(mAttr.getUsage());
-            } else {
-                // capture preset rule
+                break;
+            case RULE_MATCH_ATTRIBUTE_CAPTURE_PRESET:
                 dest.writeInt(mAttr.getCapturePreset());
+                break;
+            case RULE_MATCH_UID:
+                dest.writeInt(mIntProp.intValue());
+                break;
+            default:
+                Log.e("AttributeMatchCriterion", "Unknown match rule" + match_rule
+                        + " when writing to Parcel");
+                dest.writeInt(-1);
             }
         }
     }
@@ -111,18 +149,31 @@
         switch(rule) {
             case RULE_MATCH_ATTRIBUTE_USAGE:
             case RULE_MATCH_ATTRIBUTE_CAPTURE_PRESET:
+            case RULE_MATCH_UID:
                 return true;
             default:
                 return false;
         }
     }
 
-    private static boolean isValidIntRule(int rule) {
+    private static boolean isValidAttributesSystemApiRule(int rule) {
+        switch(rule) {
+            case RULE_MATCH_ATTRIBUTE_USAGE:
+            case RULE_MATCH_ATTRIBUTE_CAPTURE_PRESET:
+                return true;
+            default:
+                return false;
+        }
+    }
+
+    private static boolean isValidRule(int rule) {
         switch(rule) {
             case RULE_MATCH_ATTRIBUTE_USAGE:
             case RULE_EXCLUDE_ATTRIBUTE_USAGE:
             case RULE_MATCH_ATTRIBUTE_CAPTURE_PRESET:
             case RULE_EXCLUDE_ATTRIBUTE_CAPTURE_PRESET:
+            case RULE_MATCH_UID:
+            case RULE_EXCLUDE_UID:
                 return true;
             default:
                 return false;
@@ -130,8 +181,24 @@
     }
 
     private static boolean isPlayerRule(int rule) {
-        return ((rule == RULE_MATCH_ATTRIBUTE_USAGE)
-                || (rule == RULE_EXCLUDE_ATTRIBUTE_USAGE));
+        final int match_rule = rule & ~RULE_EXCLUSION_MASK;
+        switch (match_rule) {
+        case RULE_MATCH_ATTRIBUTE_USAGE:
+        case RULE_MATCH_UID:
+            return true;
+        default:
+            return false;
+        }
+    }
+
+    private static boolean isAudioAttributeRule(int match_rule) {
+        switch(match_rule) {
+            case RULE_MATCH_ATTRIBUTE_USAGE:
+            case RULE_MATCH_ATTRIBUTE_CAPTURE_PRESET:
+                return true;
+            default:
+                return false;
+        }
     }
 
     /**
@@ -158,14 +225,15 @@
          *     {@link AudioMixingRule#RULE_MATCH_ATTRIBUTE_CAPTURE_PRESET}.
          * @return the same Builder instance.
          * @throws IllegalArgumentException
+         * @see {@link #excludeRule(AudioAttributes, int)}
          */
         @SystemApi
         public Builder addRule(AudioAttributes attrToMatch, int rule)
                 throws IllegalArgumentException {
-            if (!isValidSystemApiRule(rule)) {
+            if (!isValidAttributesSystemApiRule(rule)) {
                 throw new IllegalArgumentException("Illegal rule value " + rule);
             }
-            return addRuleInt(attrToMatch, rule);
+            return checkAddRuleObjInternal(rule, attrToMatch);
         }
 
         /**
@@ -186,33 +254,82 @@
          *     {@link AudioMixingRule#RULE_MATCH_ATTRIBUTE_CAPTURE_PRESET}.
          * @return the same Builder instance.
          * @throws IllegalArgumentException
+         * @see {@link #addRule(AudioAttributes, int)}
          */
         @SystemApi
         public Builder excludeRule(AudioAttributes attrToMatch, int rule)
                 throws IllegalArgumentException {
+            if (!isValidAttributesSystemApiRule(rule)) {
+                throw new IllegalArgumentException("Illegal rule value " + rule);
+            }
+            return checkAddRuleObjInternal(rule | RULE_EXCLUSION_MASK, attrToMatch);
+        }
+
+        /**
+         * Add a rule for the selection of which streams are mixed together.
+         * The rule defines what the matching will be made on. It also determines the type of the
+         * property to match against.
+         * @param rule one of {@link AudioMixingRule#RULE_MATCH_ATTRIBUTE_USAGE},
+         *     {@link AudioMixingRule#RULE_MATCH_ATTRIBUTE_CAPTURE_PRESET} or
+         *     {@link AudioMixingRule#RULE_MATCH_UID}.
+         * @param property see the definition of each rule for the type to use (either an
+         *     {@link AudioAttributes} or an {@link java.lang.Integer}).
+         * @return the same Builder instance.
+         * @throws IllegalArgumentException
+         * @see {@link #excludeMixRule(int, Object)}
+         */
+        @SystemApi
+        public Builder addMixRule(int rule, Object property) throws IllegalArgumentException {
             if (!isValidSystemApiRule(rule)) {
                 throw new IllegalArgumentException("Illegal rule value " + rule);
             }
-            return addRuleInt(attrToMatch, rule | RULE_EXCLUSION_MASK);
+            return checkAddRuleObjInternal(rule, property);
+        }
+
+        /**
+         * Add a rule by exclusion for the selection of which streams are mixed together.
+         * <br>For instance the following code
+         * <br><pre>
+         * AudioAttributes mediaAttr = new AudioAttributes.Builder()
+         *         .setUsage(AudioAttributes.USAGE_MEDIA)
+         *         .build();
+         * AudioMixingRule noMediaRule = new AudioMixingRule.Builder()
+         *         .addMixRule(AudioMixingRule.RULE_MATCH_ATTRIBUTE_USAGE, mediaAttr)
+         *         .excludeMixRule(AudioMixingRule.RULE_MATCH_UID, new Integer(uidToExclude)
+         *         .build();
+         * </pre>
+         * <br>will create a rule which maps to usage USAGE_MEDIA, but excludes any stream
+         * coming from the specified UID.
+         * @param rule one of {@link AudioMixingRule#RULE_MATCH_ATTRIBUTE_USAGE},
+         *     {@link AudioMixingRule#RULE_MATCH_ATTRIBUTE_CAPTURE_PRESET} or
+         *     {@link AudioMixingRule#RULE_MATCH_UID}.
+         * @param property see the definition of each rule for the type to use (either an
+         *     {@link AudioAttributes} or an {@link java.lang.Integer}).
+         * @return the same Builder instance.
+         * @throws IllegalArgumentException
+         */
+        @SystemApi
+        public Builder excludeMixRule(int rule, Object property) throws IllegalArgumentException {
+            if (!isValidSystemApiRule(rule)) {
+                throw new IllegalArgumentException("Illegal rule value " + rule);
+            }
+            return checkAddRuleObjInternal(rule | RULE_EXCLUSION_MASK, property);
         }
 
         /**
          * Add or exclude a rule for the selection of which streams are mixed together.
-         * @param attrToMatch a non-null AudioAttributes instance for which a contradictory
-         *     rule hasn't been set yet.
-         * @param rule one of {@link AudioMixingRule#RULE_EXCLUDE_ATTRIBUTE_USAGE},
-         *     {@link AudioMixingRule#RULE_MATCH_ATTRIBUTE_USAGE},
-         *     {@link AudioMixingRule#RULE_MATCH_ATTRIBUTE_CAPTURE_PRESET} or
-         *     {@link AudioMixingRule#RULE_EXCLUDE_ATTRIBUTE_CAPTURE_PRESET}.
+         * Does error checking on the parameters.
+         * @param rule
+         * @param property
          * @return the same Builder instance.
          * @throws IllegalArgumentException
          */
-        Builder addRuleInt(AudioAttributes attrToMatch, int rule)
+        private Builder checkAddRuleObjInternal(int rule, Object property)
                 throws IllegalArgumentException {
-            if (attrToMatch == null) {
-                throw new IllegalArgumentException("Illegal null AudioAttributes argument");
+            if (property == null) {
+                throw new IllegalArgumentException("Illegal null Object argument");
             }
-            if (!isValidIntRule(rule)) {
+            if (!isValidRule(rule)) {
                 throw new IllegalArgumentException("Illegal rule value " + rule);
             } else {
                 // as rules are added to the Builder, we verify they are consistent with the type
@@ -229,36 +346,87 @@
                     throw new IllegalArgumentException("Incompatible rule for mix");
                 }
             }
+            final int match_rule = rule & ~RULE_EXCLUSION_MASK;
+            if (isAudioAttributeRule(match_rule)) {
+                if (!(property instanceof AudioAttributes)) {
+                    throw new IllegalArgumentException("Invalid AudioAttributes argument");
+                }
+                return addRuleInternal((AudioAttributes) property, null, rule);
+            } else {
+                // implies integer match rule
+                if (!(property instanceof Integer)) {
+                    throw new IllegalArgumentException("Invalid Integer argument");
+                }
+                return addRuleInternal(null, (Integer) property, rule);
+            }
+        }
+
+        /**
+         * Add or exclude a rule on AudioAttributes or integer property for the selection of which
+         * streams are mixed together.
+         * No rule-to-parameter type check, all done in {@link #checkAddRuleObjInternal(int, Object)}.
+         * Exceptions are thrown only when incompatible rules are added.
+         * @param attrToMatch a non-null AudioAttributes instance for which a contradictory
+         *     rule hasn't been set yet, null if not used.
+         * @param intProp an integer property to match or exclude, null if not used.
+         * @param rule one of {@link AudioMixingRule#RULE_EXCLUDE_ATTRIBUTE_USAGE},
+         *     {@link AudioMixingRule#RULE_MATCH_ATTRIBUTE_USAGE},
+         *     {@link AudioMixingRule#RULE_MATCH_ATTRIBUTE_CAPTURE_PRESET} or
+         *     {@link AudioMixingRule#RULE_EXCLUDE_ATTRIBUTE_CAPTURE_PRESET},
+         *     {@link AudioMixingRule#RULE_MATCH_UID}, {@link AudioMixingRule#RULE_EXCLUDE_UID}.
+         * @return the same Builder instance.
+         * @throws IllegalArgumentException
+         */
+        private Builder addRuleInternal(AudioAttributes attrToMatch, Integer intProp, int rule)
+                throws IllegalArgumentException {
             synchronized (mCriteria) {
                 Iterator<AttributeMatchCriterion> crIterator = mCriteria.iterator();
+                final int match_rule = rule & ~RULE_EXCLUSION_MASK;
                 while (crIterator.hasNext()) {
                     final AttributeMatchCriterion criterion = crIterator.next();
-                    if ((rule == RULE_MATCH_ATTRIBUTE_USAGE)
-                            || (rule == RULE_EXCLUDE_ATTRIBUTE_USAGE)) {
-                        // "usage"-based rule
-                        if (criterion.mAttr.getUsage() == attrToMatch.getUsage()) {
-                            if (criterion.mRule == rule) {
-                                // rule already exists, we're done
-                                return this;
-                            } else {
-                                // criterion already exists with a another rule, it is incompatible
-                                throw new IllegalArgumentException("Contradictory rule exists for "
-                                        + attrToMatch);
+                    switch (match_rule) {
+                        case RULE_MATCH_ATTRIBUTE_USAGE:
+                            // "usage"-based rule
+                            if (criterion.mAttr.getUsage() == attrToMatch.getUsage()) {
+                                if (criterion.mRule == rule) {
+                                    // rule already exists, we're done
+                                    return this;
+                                } else {
+                                    // criterion already exists with a another rule,
+                                    // it is incompatible
+                                    throw new IllegalArgumentException("Contradictory rule exists"
+                                            + " for " + attrToMatch);
+                                }
                             }
-                        }
-                    } else if ((rule == RULE_MATCH_ATTRIBUTE_CAPTURE_PRESET)
-                            || (rule == RULE_EXCLUDE_ATTRIBUTE_CAPTURE_PRESET)) {
-                        // "capture preset"-base rule
-                        if (criterion.mAttr.getCapturePreset() == attrToMatch.getCapturePreset()) {
-                            if (criterion.mRule == rule) {
-                             // rule already exists, we're done
-                                return this;
-                            } else {
-                                // criterion already exists with a another rule, it is incompatible
-                                throw new IllegalArgumentException("Contradictory rule exists for "
-                                        + attrToMatch);
+                            break;
+                        case RULE_MATCH_ATTRIBUTE_CAPTURE_PRESET:
+                            // "capture preset"-base rule
+                            if (criterion.mAttr.getCapturePreset() == attrToMatch.getCapturePreset()) {
+                                if (criterion.mRule == rule) {
+                                    // rule already exists, we're done
+                                    return this;
+                                } else {
+                                    // criterion already exists with a another rule,
+                                    // it is incompatible
+                                    throw new IllegalArgumentException("Contradictory rule exists"
+                                            + " for " + attrToMatch);
+                                }
                             }
-                        }
+                            break;
+                        case RULE_MATCH_UID:
+                            // "usage"-based rule
+                            if (criterion.mIntProp.intValue() == intProp.intValue()) {
+                                if (criterion.mRule == rule) {
+                                    // rule already exists, we're done
+                                    return this;
+                                } else {
+                                    // criterion already exists with a another rule,
+                                    // it is incompatible
+                                    throw new IllegalArgumentException("Contradictory rule exists"
+                                            + " for UID " + intProp);
+                                }
+                            }
+                            break;
                     }
                 }
                 // rule didn't exist, add it
@@ -268,22 +436,30 @@
         }
 
         Builder addRuleFromParcel(Parcel in) throws IllegalArgumentException {
-            int rule = in.readInt();
-            AudioAttributes attr;
-            if ((rule == RULE_MATCH_ATTRIBUTE_USAGE) || (rule == RULE_EXCLUDE_ATTRIBUTE_USAGE)) {
-                int usage = in.readInt();
-                attr = new AudioAttributes.Builder()
-                        .setUsage(usage).build();
-            } else if ((rule == RULE_MATCH_ATTRIBUTE_CAPTURE_PRESET)
-                    || (rule == RULE_EXCLUDE_ATTRIBUTE_CAPTURE_PRESET)) {
-                int preset = in.readInt();
-                attr = new AudioAttributes.Builder()
-                        .setInternalCapturePreset(preset).build();
-            } else {
-                in.readInt(); // assume there was in int value to read as for now they come in pair
-                throw new IllegalArgumentException("Illegal rule value " + rule + " in parcel");
+            final int rule = in.readInt();
+            final int match_rule = rule & ~RULE_EXCLUSION_MASK;
+            AudioAttributes attr = null;
+            Integer intProp = null;
+            switch (match_rule) {
+                case RULE_MATCH_ATTRIBUTE_USAGE:
+                    int usage = in.readInt();
+                    attr = new AudioAttributes.Builder()
+                            .setUsage(usage).build();
+                    break;
+                case RULE_MATCH_ATTRIBUTE_CAPTURE_PRESET:
+                    int preset = in.readInt();
+                    attr = new AudioAttributes.Builder()
+                            .setInternalCapturePreset(preset).build();
+                    break;
+                case RULE_MATCH_UID:
+                    intProp = new Integer(in.readInt());
+                    break;
+                default:
+                    // assume there was in int value to read as for now they come in pair
+                    in.readInt();
+                    throw new IllegalArgumentException("Illegal rule value " + rule + " in parcel");
             }
-            return addRuleInt(attr, rule);
+            return addRuleInternal(attr, intProp, rule);
         }
 
         /**
diff --git a/media/java/android/media/audiopolicy/AudioPolicyConfig.java b/media/java/android/media/audiopolicy/AudioPolicyConfig.java
index 252f5f4..5ad6126 100644
--- a/media/java/android/media/audiopolicy/AudioPolicyConfig.java
+++ b/media/java/android/media/audiopolicy/AudioPolicyConfig.java
@@ -169,6 +169,14 @@
                         textDump += "  match capture preset ";
                         textDump += criterion.mAttr.getCapturePreset();
                         break;
+                    case AudioMixingRule.RULE_MATCH_UID:
+                        textDump += "  match UID ";
+                        textDump += criterion.mIntProp.toString();
+                        break;
+                    case AudioMixingRule.RULE_EXCLUDE_UID:
+                        textDump += "  exclude UID ";
+                        textDump += criterion.mIntProp.toString();
+                        break;
                     default:
                         textDump += "invalid rule!";
                 }
diff --git a/media/java/android/media/session/MediaController.java b/media/java/android/media/session/MediaController.java
index b1a51a56..3d9b60d 100644
--- a/media/java/android/media/session/MediaController.java
+++ b/media/java/android/media/session/MediaController.java
@@ -605,6 +605,7 @@
         /**
          * Request that the player start playback for a specific media id.
          *
+         * @see PlaybackState#EXTRA_PREPARE_ONLY
          * @param mediaId The id of the requested media.
          * @param extras Optional extras that can include extra information about the media item
          *               to be played.
@@ -626,6 +627,7 @@
          * An empty or null query should be treated as a request to play any
          * music.
          *
+         * @see PlaybackState#EXTRA_PREPARE_ONLY
          * @param query The search query.
          * @param extras Optional extras that can include extra information
          *            about the query.
@@ -646,6 +648,7 @@
         /**
          * Request that the player start playback for a specific {@link Uri}.
          *
+         * @see PlaybackState#EXTRA_PREPARE_ONLY
          * @param uri  The URI of the requested media.
          * @param extras Optional extras that can include extra information about the media item
          *               to be played.
diff --git a/media/java/android/media/session/MediaSession.java b/media/java/android/media/session/MediaSession.java
index e1e9b79..8c5b19c 100644
--- a/media/java/android/media/session/MediaSession.java
+++ b/media/java/android/media/session/MediaSession.java
@@ -87,6 +87,12 @@
     public static final int FLAG_HANDLES_TRANSPORT_CONTROLS = 1 << 1;
 
     /**
+     * Set this flag on the session to indicate that it can handle
+     * the {@link PlaybackState#EXTRA_PREPARE_ONLY} field.
+     */
+    public static final int FLAG_HANDLES_PREPARE_ONLY = 1 << 2;
+
+    /**
      * System only flag for a session that needs to have priority over all other
      * sessions. This flag ensures this session will receive media button events
      * regardless of the current ordering in the system.
@@ -100,6 +106,7 @@
     @IntDef(flag = true, value = {
             FLAG_HANDLES_MEDIA_BUTTONS,
             FLAG_HANDLES_TRANSPORT_CONTROLS,
+            FLAG_HANDLES_PREPARE_ONLY,
             FLAG_EXCLUSIVE_GLOBAL_PRIORITY })
     public @interface SessionFlags { }
 
diff --git a/media/java/android/media/session/PlaybackState.java b/media/java/android/media/session/PlaybackState.java
index bbe04b5..1079a1f 100644
--- a/media/java/android/media/session/PlaybackState.java
+++ b/media/java/android/media/session/PlaybackState.java
@@ -133,6 +133,21 @@
     public static final long ACTION_PLAY_FROM_URI = 1 << 13;
 
     /**
+     * Used as an optional boolean extra field in
+     * {@link MediaController.TransportControls#playFromMediaId},
+     * {@link MediaController.TransportControls#playFromSearch}, and
+     * {@link MediaController.TransportControls#playFromUri}. Value of {@code true} overrides
+     * the default behavior of starting the playback after preparing. Check
+     * {@link MediaSession#FLAG_HANDLES_PREPARE_ONLY} to see if the media session supports this.
+     *
+     * @see MediaSession#FLAG_HANDLES_PREPARE_ONLY
+     * @see MediaController.TransportControls#playFromMediaId
+     * @see MediaController.TransportControls#playFromSearch
+     * @see MediaController.TransportControls#playFromUri
+     */
+    public static final String EXTRA_PREPARE_ONLY = "android.media.session.extra.PREPARE_ONLY";
+
+    /**
      * This is the default playback state and indicates that no media has been
      * added yet, or the performer has been reset and has no content to play.
      *
diff --git a/media/java/android/media/soundtrigger/SoundTriggerDetector.java b/media/java/android/media/soundtrigger/SoundTriggerDetector.java
index ebba343..707db06 100644
--- a/media/java/android/media/soundtrigger/SoundTriggerDetector.java
+++ b/media/java/android/media/soundtrigger/SoundTriggerDetector.java
@@ -40,6 +40,7 @@
  *
  * @hide
  */
+@SystemApi
 public final class SoundTriggerDetector {
     private static final boolean DBG = false;
     private static final String TAG = "SoundTriggerDetector";
@@ -132,6 +133,9 @@
         return true;
     }
 
+    /**
+     * @hide
+     */
     public void dump(String prefix, PrintWriter pw) {
         synchronized (mLock) {
             // TODO: Dump useful debug information.
diff --git a/media/java/android/media/soundtrigger/SoundTriggerManager.java b/media/java/android/media/soundtrigger/SoundTriggerManager.java
index 4ae8e72..4fd3310 100644
--- a/media/java/android/media/soundtrigger/SoundTriggerManager.java
+++ b/media/java/android/media/soundtrigger/SoundTriggerManager.java
@@ -37,8 +37,8 @@
  * OEMs to write apps that can manage non-voice based sound trigger models.
  *
  * @hide
- * TODO: Mark this as a SystemApi and get approval.
  */
+@SystemApi
 public final class SoundTriggerManager {
     private static final boolean DBG = false;
     private static final String TAG = "SoundTriggerManager";
diff --git a/media/java/android/media/tv/ITvInputManager.aidl b/media/java/android/media/tv/ITvInputManager.aidl
index 0febc16..d189333 100644
--- a/media/java/android/media/tv/ITvInputManager.aidl
+++ b/media/java/android/media/tv/ITvInputManager.aidl
@@ -41,6 +41,7 @@
 interface ITvInputManager {
     List<TvInputInfo> getTvInputList(int userId);
     TvInputInfo getTvInputInfo(in String inputId, int userId);
+    void setTvInputInfo(in TvInputInfo inputInfo, int userId);
     int getTvInputState(in String inputId, int userId);
 
     List<TvContentRatingSystemInfo> getTvContentRatingSystemList(int userId);
diff --git a/media/java/android/media/tv/ITvInputManagerCallback.aidl b/media/java/android/media/tv/ITvInputManagerCallback.aidl
index 3bf415b..395c9f3 100644
--- a/media/java/android/media/tv/ITvInputManagerCallback.aidl
+++ b/media/java/android/media/tv/ITvInputManagerCallback.aidl
@@ -29,5 +29,5 @@
 
     void onInputStateChanged(in String inputId, int state);
 
-    void onTvInputInfoChanged(in String inputId, in TvInputInfo TvInputInfo);
+    void onTvInputInfoChanged(in TvInputInfo TvInputInfo);
 }
diff --git a/media/java/android/media/tv/ITvInputServiceCallback.aidl b/media/java/android/media/tv/ITvInputServiceCallback.aidl
index 9f13882..74ab562 100644
--- a/media/java/android/media/tv/ITvInputServiceCallback.aidl
+++ b/media/java/android/media/tv/ITvInputServiceCallback.aidl
@@ -27,6 +27,4 @@
     void addHardwareTvInput(in int deviceId, in TvInputInfo inputInfo);
     void addHdmiTvInput(in int id, in TvInputInfo inputInfo);
     void removeTvInput(in String inputId);
-
-    void setTvInputInfo(in String inputId, in TvInputInfo inputInfo);
 }
diff --git a/media/java/android/media/tv/TvInputInfo.java b/media/java/android/media/tv/TvInputInfo.java
index 671a86f..20491e4 100644
--- a/media/java/android/media/tv/TvInputInfo.java
+++ b/media/java/android/media/tv/TvInputInfo.java
@@ -107,15 +107,6 @@
      */
     public static final String EXTRA_INPUT_ID = "android.media.tv.extra.INPUT_ID";
 
-    private static final SparseIntArray sHardwareTypeToTvInputType = new SparseIntArray();
-
-    private static final String XML_START_TAG_NAME = "tv-input";
-    private static final String DELIMITER_INFO_IN_ID = "/";
-    private static final String PREFIX_HDMI_DEVICE = "HDMI";
-    private static final String PREFIX_HARDWARE_DEVICE = "HW";
-    private static final int LENGTH_HDMI_PHYSICAL_ADDRESS = 4;
-    private static final int LENGTH_HDMI_DEVICE_ID = 2;
-
     private final ResolveInfo mService;
     private final String mId;
     private final String mParentId;
@@ -137,21 +128,6 @@
     private Uri mIconUri;
     private boolean mIsConnectedToHdmiSwitch;
 
-    static {
-        sHardwareTypeToTvInputType.put(TvInputHardwareInfo.TV_INPUT_TYPE_OTHER_HARDWARE,
-                TYPE_OTHER);
-        sHardwareTypeToTvInputType.put(TvInputHardwareInfo.TV_INPUT_TYPE_TUNER, TYPE_TUNER);
-        sHardwareTypeToTvInputType.put(TvInputHardwareInfo.TV_INPUT_TYPE_COMPOSITE, TYPE_COMPOSITE);
-        sHardwareTypeToTvInputType.put(TvInputHardwareInfo.TV_INPUT_TYPE_SVIDEO, TYPE_SVIDEO);
-        sHardwareTypeToTvInputType.put(TvInputHardwareInfo.TV_INPUT_TYPE_SCART, TYPE_SCART);
-        sHardwareTypeToTvInputType.put(TvInputHardwareInfo.TV_INPUT_TYPE_COMPONENT, TYPE_COMPONENT);
-        sHardwareTypeToTvInputType.put(TvInputHardwareInfo.TV_INPUT_TYPE_VGA, TYPE_VGA);
-        sHardwareTypeToTvInputType.put(TvInputHardwareInfo.TV_INPUT_TYPE_DVI, TYPE_DVI);
-        sHardwareTypeToTvInputType.put(TvInputHardwareInfo.TV_INPUT_TYPE_HDMI, TYPE_HDMI);
-        sHardwareTypeToTvInputType.put(TvInputHardwareInfo.TV_INPUT_TYPE_DISPLAY_PORT,
-                TYPE_DISPLAY_PORT);
-    }
-
     /**
      * Create a new instance of the TvInputInfo class, instantiating it from the given Context,
      * ResolveInfo, and HdmiDeviceInfo.
@@ -260,93 +236,6 @@
                 .build();
     }
 
-    static TvInputInfo createTvInputInfo(Context context, ResolveInfo resolveInfo, Icon icon,
-            int labelResId, int tunerCount, boolean canRecord, HdmiDeviceInfo hdmiDeviceInfo,
-            String parentId, TvInputHardwareInfo tvInputHardwareInfo)
-            throws IOException, XmlPullParserException {
-        ComponentName componentName = new ComponentName(resolveInfo.serviceInfo.packageName,
-                resolveInfo.serviceInfo.name);
-        String id;
-        int type;
-        boolean isHardwareInput = false;
-        boolean isConnectedToHdmiSwitch = false;
-
-        if (hdmiDeviceInfo != null) {
-            id = generateInputIdForHdmiDevice(componentName, hdmiDeviceInfo);
-            type = TYPE_HDMI;
-            isHardwareInput = true;
-            isConnectedToHdmiSwitch = (hdmiDeviceInfo.getPhysicalAddress() & 0x0FFF) != 0;
-            tunerCount = 0;
-        } else if (tvInputHardwareInfo != null) {
-            id = generateInputIdForHardware(componentName, tvInputHardwareInfo);
-            type = sHardwareTypeToTvInputType.get(tvInputHardwareInfo.getType(), TYPE_TUNER);
-            isHardwareInput = true;
-            tunerCount = 0;
-        } else {
-            id = generateInputIdForComponentName(componentName);
-            type = TYPE_TUNER;
-        }
-
-        TvInputInfo info = new TvInputInfo(resolveInfo, id, parentId, type, isHardwareInput,
-                isConnectedToHdmiSwitch, tunerCount, canRecord);
-        return parseServiceMetadata(context, resolveInfo, type, info);
-    }
-
-    private static TvInputInfo parseServiceMetadata(
-            Context context, ResolveInfo service, int inputType, TvInputInfo input)
-            throws XmlPullParserException, IOException {
-        ServiceInfo si = service.serviceInfo;
-        PackageManager pm = context.getPackageManager();
-        XmlResourceParser parser = null;
-        try {
-            parser = si.loadXmlMetaData(pm, TvInputService.SERVICE_META_DATA);
-            if (parser == null) {
-                throw new XmlPullParserException("No " + TvInputService.SERVICE_META_DATA
-                        + " meta-data for " + si.name);
-            }
-
-            Resources res = pm.getResourcesForApplication(si.applicationInfo);
-            AttributeSet attrs = Xml.asAttributeSet(parser);
-
-            int type;
-            while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
-                    && type != XmlPullParser.START_TAG) {
-            }
-
-            String nodeName = parser.getName();
-            if (!XML_START_TAG_NAME.equals(nodeName)) {
-                throw new XmlPullParserException(
-                        "Meta-data does not start with tv-input-service tag in " + si.name);
-            }
-
-            TypedArray sa = res.obtainAttributes(attrs,
-                    com.android.internal.R.styleable.TvInputService);
-            input.mSetupActivity = sa.getString(
-                    com.android.internal.R.styleable.TvInputService_setupActivity);
-            if (DEBUG) {
-                Log.d(TAG, "Setup activity loaded. [" + input.mSetupActivity + "] for " + si.name);
-            }
-            if (inputType == TYPE_TUNER && TextUtils.isEmpty(input.mSetupActivity)) {
-                throw new XmlPullParserException("Setup activity not found in " + si.name);
-            }
-            input.mSettingsActivity = sa.getString(
-                    com.android.internal.R.styleable.TvInputService_settingsActivity);
-            if (DEBUG) {
-                Log.d(TAG, "Settings activity loaded. [" + input.mSettingsActivity + "] for "
-                        + si.name);
-            }
-            sa.recycle();
-
-        } catch (NameNotFoundException e) {
-            throw new XmlPullParserException("Unable to create context for: " + si.packageName);
-        } finally {
-            if (parser != null) {
-                parser.close();
-            }
-        }
-        return input;
-    }
-
     /**
      * Constructor.
      *
@@ -645,46 +534,6 @@
         return mService.serviceInfo.loadIcon(context.getPackageManager());
     }
 
-    /**
-     * Used to generate an input id from a ComponentName.
-     *
-     * @param name the component name for generating an input id.
-     * @return the generated input id for the given {@code name}.
-     */
-    private static String generateInputIdForComponentName(ComponentName name) {
-        return name.flattenToShortString();
-    }
-
-    /**
-     * Used to generate an input id from a ComponentName and HdmiDeviceInfo.
-     *
-     * @param name the component name for generating an input id.
-     * @param deviceInfo HdmiDeviceInfo describing this TV input.
-     * @return the generated input id for the given {@code name} and {@code deviceInfo}.
-     */
-    private static String generateInputIdForHdmiDevice(
-            ComponentName name, HdmiDeviceInfo deviceInfo) {
-        // Example of the format : "/HDMI%04X%02X"
-        String format = DELIMITER_INFO_IN_ID + PREFIX_HDMI_DEVICE
-                + "%0" + LENGTH_HDMI_PHYSICAL_ADDRESS + "X"
-                + "%0" + LENGTH_HDMI_DEVICE_ID + "X";
-        return name.flattenToShortString() + String.format(Locale.ENGLISH, format,
-                deviceInfo.getPhysicalAddress(), deviceInfo.getId());
-    }
-
-    /**
-     * Used to generate an input id from a ComponentName and TvInputHardwareInfo
-     *
-     * @param name the component name for generating an input id.
-     * @param hardwareInfo TvInputHardwareInfo describing this TV input.
-     * @return the generated input id for the given {@code name} and {@code hardwareInfo}.
-     */
-    private static String generateInputIdForHardware(
-            ComponentName name, TvInputHardwareInfo hardwareInfo) {
-        return name.flattenToShortString() + DELIMITER_INFO_IN_ID + PREFIX_HARDWARE_DEVICE
-                + hardwareInfo.getDeviceId();
-    }
-
     public static final Parcelable.Creator<TvInputInfo> CREATOR =
             new Parcelable.Creator<TvInputInfo>() {
         @Override
@@ -720,6 +569,32 @@
      * A convenience builder for creating {@link TvInputInfo} objects.
      */
     public static final class Builder {
+        private static final int LENGTH_HDMI_PHYSICAL_ADDRESS = 4;
+        private static final int LENGTH_HDMI_DEVICE_ID = 2;
+
+        private static final String XML_START_TAG_NAME = "tv-input";
+        private static final String DELIMITER_INFO_IN_ID = "/";
+        private static final String PREFIX_HDMI_DEVICE = "HDMI";
+        private static final String PREFIX_HARDWARE_DEVICE = "HW";
+
+        private static final SparseIntArray sHardwareTypeToTvInputType = new SparseIntArray();
+        static {
+            sHardwareTypeToTvInputType.put(TvInputHardwareInfo.TV_INPUT_TYPE_OTHER_HARDWARE,
+                    TYPE_OTHER);
+            sHardwareTypeToTvInputType.put(TvInputHardwareInfo.TV_INPUT_TYPE_TUNER, TYPE_TUNER);
+            sHardwareTypeToTvInputType.put(TvInputHardwareInfo.TV_INPUT_TYPE_COMPOSITE,
+                    TYPE_COMPOSITE);
+            sHardwareTypeToTvInputType.put(TvInputHardwareInfo.TV_INPUT_TYPE_SVIDEO, TYPE_SVIDEO);
+            sHardwareTypeToTvInputType.put(TvInputHardwareInfo.TV_INPUT_TYPE_SCART, TYPE_SCART);
+            sHardwareTypeToTvInputType.put(TvInputHardwareInfo.TV_INPUT_TYPE_COMPONENT,
+                    TYPE_COMPONENT);
+            sHardwareTypeToTvInputType.put(TvInputHardwareInfo.TV_INPUT_TYPE_VGA, TYPE_VGA);
+            sHardwareTypeToTvInputType.put(TvInputHardwareInfo.TV_INPUT_TYPE_DVI, TYPE_DVI);
+            sHardwareTypeToTvInputType.put(TvInputHardwareInfo.TV_INPUT_TYPE_HDMI, TYPE_HDMI);
+            sHardwareTypeToTvInputType.put(TvInputHardwareInfo.TV_INPUT_TYPE_DISPLAY_PORT,
+                    TYPE_DISPLAY_PORT);
+        }
+
         private final Context mContext;
         private final ResolveInfo mResolveInfo;
         private Icon mIcon;
@@ -864,11 +739,102 @@
          * @return TvInputInfo containing information about this TV input.
          * @throws IOException If there was an I/O error.
          * @throws XmlPullParserException If there was an XML parsing error.
-         *
          */
         public TvInputInfo build() throws IOException, XmlPullParserException {
-            return createTvInputInfo(mContext, mResolveInfo, mIcon, mLabelResId, mTunerCount,
-                    mCanRecord, mHdmiDeviceInfo, mParentId, mTvInputHardwareInfo);
+            ComponentName componentName = new ComponentName(mResolveInfo.serviceInfo.packageName,
+                    mResolveInfo.serviceInfo.name);
+            String id;
+            int type;
+            boolean isHardwareInput = false;
+            boolean isConnectedToHdmiSwitch = false;
+
+            if (mHdmiDeviceInfo != null) {
+                id = generateInputId(componentName, mHdmiDeviceInfo);
+                type = TYPE_HDMI;
+                isHardwareInput = true;
+                isConnectedToHdmiSwitch = (mHdmiDeviceInfo.getPhysicalAddress() & 0x0FFF) != 0;
+                mTunerCount = 0;
+            } else if (mTvInputHardwareInfo != null) {
+                id = generateInputId(componentName, mTvInputHardwareInfo);
+                type = sHardwareTypeToTvInputType.get(mTvInputHardwareInfo.getType(), TYPE_TUNER);
+                isHardwareInput = true;
+                mTunerCount = 0;
+            } else {
+                id = generateInputId(componentName);
+                type = TYPE_TUNER;
+            }
+
+            TvInputInfo info = new TvInputInfo(mResolveInfo, id, mParentId, type, isHardwareInput,
+                    isConnectedToHdmiSwitch, mTunerCount, mCanRecord);
+            return parseServiceMetadata(type, info);
+        }
+
+        private static String generateInputId(ComponentName name) {
+            return name.flattenToShortString();
+        }
+
+        private static String generateInputId(ComponentName name, HdmiDeviceInfo hdmiDeviceInfo) {
+            // Example of the format : "/HDMI%04X%02X"
+            String format = DELIMITER_INFO_IN_ID + PREFIX_HDMI_DEVICE
+                    + "%0" + LENGTH_HDMI_PHYSICAL_ADDRESS + "X"
+                    + "%0" + LENGTH_HDMI_DEVICE_ID + "X";
+            return name.flattenToShortString() + String.format(Locale.ENGLISH, format,
+                    hdmiDeviceInfo.getPhysicalAddress(), hdmiDeviceInfo.getId());
+        }
+
+        private static String generateInputId(ComponentName name,
+                TvInputHardwareInfo tvInputHardwareInfo) {
+            return name.flattenToShortString() + DELIMITER_INFO_IN_ID + PREFIX_HARDWARE_DEVICE
+                    + tvInputHardwareInfo.getDeviceId();
+        }
+
+        private TvInputInfo parseServiceMetadata(int inputType, TvInputInfo info)
+                throws XmlPullParserException, IOException {
+            ServiceInfo si = mResolveInfo.serviceInfo;
+            PackageManager pm = mContext.getPackageManager();
+            try (XmlResourceParser parser =
+                         si.loadXmlMetaData(pm, TvInputService.SERVICE_META_DATA)) {
+                if (parser == null) {
+                    throw new XmlPullParserException("No " + TvInputService.SERVICE_META_DATA
+                            + " meta-data for " + si.name);
+                }
+
+                Resources res = pm.getResourcesForApplication(si.applicationInfo);
+                AttributeSet attrs = Xml.asAttributeSet(parser);
+
+                int type;
+                while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+                        && type != XmlPullParser.START_TAG) {
+                }
+
+                String nodeName = parser.getName();
+                if (!XML_START_TAG_NAME.equals(nodeName)) {
+                    throw new XmlPullParserException(
+                            "Meta-data does not start with tv-input-service tag in " + si.name);
+                }
+
+                TypedArray sa = res.obtainAttributes(attrs,
+                        com.android.internal.R.styleable.TvInputService);
+                info.mSetupActivity = sa.getString(
+                        com.android.internal.R.styleable.TvInputService_setupActivity);
+                if (DEBUG) {
+                    Log.d(TAG, "Setup activity loaded. [" + info.mSetupActivity + "] for "
+                            + si.name);
+                }
+                if (inputType == TYPE_TUNER && TextUtils.isEmpty(info.mSetupActivity)) {
+                    throw new XmlPullParserException("Setup activity not found in " + si.name);
+                }
+                info.mSettingsActivity = sa.getString(
+                        com.android.internal.R.styleable.TvInputService_settingsActivity);
+                if (DEBUG) {
+                    Log.d(TAG, "Settings activity loaded. [" + info.mSettingsActivity + "] for "
+                            + si.name);
+                }
+                sa.recycle();
+            } catch (NameNotFoundException e) {
+                throw new XmlPullParserException("Unable to create context for: " + si.packageName);
+            }
+            return info;
         }
     }
 
diff --git a/media/java/android/media/tv/TvInputManager.java b/media/java/android/media/tv/TvInputManager.java
index f1de8fd..86bded9 100644
--- a/media/java/android/media/tv/TvInputManager.java
+++ b/media/java/android/media/tv/TvInputManager.java
@@ -726,12 +726,11 @@
         }
 
         /**
-         * This is called when the information about a given TV input is changed.
+         * This is called when the information about a given TV input has been changed.
          *
-         * @param inputId The ID of the TV input.
          * @param inputInfo TvInputInfo object that contains the information about the TV input.
          */
-        public void onTvInputInfoChanged(String inputId, TvInputInfo inputInfo) {
+        public void onTvInputInfoChanged(TvInputInfo inputInfo) {
         }
     }
 
@@ -784,11 +783,11 @@
             });
         }
 
-        public void postTvInputInfoChanged(final String inputId, final TvInputInfo inputInfo) {
+        public void postTvInputInfoChanged(final TvInputInfo inputInfo) {
             mHandler.post(new Runnable() {
                 @Override
                 public void run() {
-                    mCallback.onTvInputInfoChanged(inputId, inputInfo);
+                    mCallback.onTvInputInfoChanged(inputInfo);
                 }
             });
         }
@@ -1089,10 +1088,10 @@
             }
 
             @Override
-            public void onTvInputInfoChanged(String inputId, TvInputInfo inputInfo) {
+            public void onTvInputInfoChanged(TvInputInfo inputInfo) {
                 synchronized (mLock) {
                     for (TvInputCallbackRecord record : mCallbackRecords) {
-                        record.postTvInputInfoChanged(inputId, inputInfo);
+                        record.postTvInputInfoChanged(inputInfo);
                     }
                 }
             }
@@ -1143,6 +1142,23 @@
     }
 
     /**
+     * Sets a new TvInputInfo object for a given input.
+     *
+     * <p>This is called internally only by {@link TvInputService}.
+     *
+     * @param inputInfo The TvInputInfo object to set.
+     * @throws IllegalArgumentException if the argument is {@code null}.
+     */
+    void setTvInputInfo(@NonNull TvInputInfo inputInfo) {
+        Preconditions.checkNotNull(inputInfo);
+        try {
+            mService.setTvInputInfo(inputInfo, mUserId);
+        } catch (RemoteException e) {
+            throw new RuntimeException("Error trying to set " + inputInfo, e);
+        }
+    }
+
+    /**
      * Returns the state of a given TV input.
      *
      * <p>The state is one of the following:
diff --git a/media/java/android/media/tv/TvInputService.java b/media/java/android/media/tv/TvInputService.java
index f74ae66..a2b6346 100644
--- a/media/java/android/media/tv/TvInputService.java
+++ b/media/java/android/media/tv/TvInputService.java
@@ -262,16 +262,16 @@
      * <p>The system service automatically creates the TvInputInfo for each TV input based on
      * information collected from the AndroidManifest.xml, thus it is not necessary to call this
      * method unless the TV input has additional information to pass such as ability to record and
-     * tuner count.
+     * tuner count. Attempting to change information about a TV input that the calling package does
+     * not own does nothing.
      *
-     * @param inputId The ID of the TV input.
+     * @param context The application context.
      * @param inputInfo The TvInputInfo object that contains that new information.
      */
-    public final void setTvInputInfo(String inputId, TvInputInfo inputInfo) {
-        SomeArgs args = SomeArgs.obtain();
-        args.arg1 = inputId;
-        args.arg2 = inputInfo;
-        mServiceHandler.obtainMessage(ServiceHandler.DO_SET_TV_INPUT_INFO, args).sendToTarget();
+    public static final void setTvInputInfo(Context context, TvInputInfo inputInfo) {
+        TvInputManager manager = (TvInputManager) context.getSystemService(
+                Context.TV_INPUT_SERVICE);
+        manager.setTvInputInfo(inputInfo);
     }
 
     private boolean isPassthroughInput(String inputId) {
@@ -1938,7 +1938,6 @@
         private static final int DO_REMOVE_HARDWARE_TV_INPUT = 5;
         private static final int DO_ADD_HDMI_TV_INPUT = 6;
         private static final int DO_REMOVE_HDMI_TV_INPUT = 7;
-        private static final int DO_SET_TV_INPUT_INFO = 8;
 
         private void broadcastAddHardwareTvInput(int deviceId, TvInputInfo inputInfo) {
             int n = mCallbacks.beginBroadcast();
@@ -1976,18 +1975,6 @@
             mCallbacks.finishBroadcast();
         }
 
-        private void broadcastSetTvInputInfo(String inputId, TvInputInfo inputInfo) {
-            int n = mCallbacks.beginBroadcast();
-            for (int i = 0; i < n; ++i) {
-                try {
-                    mCallbacks.getBroadcastItem(i).setTvInputInfo(inputId, inputInfo);
-                } catch (RemoteException e) {
-                    Log.e(TAG, "error in broadcastSetTvInputInfo", e);
-                }
-            }
-            mCallbacks.finishBroadcast();
-        }
-
         @Override
         public final void handleMessage(Message msg) {
             switch (msg.what) {
@@ -2120,16 +2107,6 @@
                     }
                     return;
                 }
-                case DO_SET_TV_INPUT_INFO: {
-                    SomeArgs args = (SomeArgs) msg.obj;
-                    String inputId = (String) args.arg1;
-                    TvInputInfo inputInfo = (TvInputInfo) args.arg2;
-                    if (inputInfo != null) {
-                        broadcastSetTvInputInfo(inputId, inputInfo);
-                    }
-                    args.recycle();
-                    return;
-                }
                 default: {
                     Log.w(TAG, "Unhandled message code: " + msg.what);
                     return;
diff --git a/media/java/android/mtp/MtpConstants.java b/media/java/android/mtp/MtpConstants.java
index ef2cf2b..0dcc718 100644
--- a/media/java/android/mtp/MtpConstants.java
+++ b/media/java/android/mtp/MtpConstants.java
@@ -690,4 +690,6 @@
     public static final int OPERATION_SET_OBJECT_REFERENCES = 0x9811;
     /** Operation code for Skip */
     public static final int OPERATION_SKIP = 0x9820;
+    /** Operation code for GetPartialObject64 */
+    public static final int OPERATION_GET_PARTIAL_OBJECT_64 = 0x95C1;
 }
diff --git a/media/java/android/mtp/MtpDevice.java b/media/java/android/mtp/MtpDevice.java
index 4379a99..0e7013c 100644
--- a/media/java/android/mtp/MtpDevice.java
+++ b/media/java/android/mtp/MtpDevice.java
@@ -153,10 +153,11 @@
      *
      * @param objectHandle handle of the object to read
      * @param objectSize the size of the object (this should match
-     *      {@link MtpObjectInfo#getCompressedSize}
+     *      {@link MtpObjectInfo#getCompressedSize})
      * @return the object's data, or null if reading fails
      */
     public byte[] getObject(int objectHandle, int objectSize) {
+        Preconditions.checkArgumentNonnegative(objectSize, "objectSize should not be negative");
         return native_get_object(objectHandle, objectSize);
     }
 
@@ -179,6 +180,27 @@
     }
 
     /**
+     * Obtains object bytes in the specified range and writes it to an array.
+     * This call may block for an arbitrary amount of time depending on the size
+     * of the data and speed of the devices.
+     *
+     * This is a vender-extended operation supported by Android that enables us to pass
+     * unsigned 64-bit offset. Check if the MTP device supports the operation by using
+     * {@link MtpDeviceInfo#getOperationsSupported()}.
+     *
+     * @param objectHandle handle of the object to read
+     * @param offset Start index of reading range. It must be a non-negative value.
+     * @param size Size of reading range. It must be a non-negative value at most 0xffffffff.
+     * @param buffer Array to write data.
+     * @return Size of bytes that are actually read.
+     * @see MtpConstants#OPERATION_GET_PARTIAL_OBJECT_64
+     */
+    public long getPartialObject64(int objectHandle, long offset, long size, byte[] buffer)
+            throws IOException {
+        return native_get_partial_object_64(objectHandle, offset, size, buffer);
+    }
+
+    /**
      * Returns the thumbnail data for an object as a byte array.
      * The size and format of the thumbnail data can be determined via
      * {@link MtpObjectInfo#getThumbCompressedSize} and
@@ -284,7 +306,7 @@
      * @param descriptor file descriptor to read the data from.
      * @return true if the file transfer succeeds
      */
-    public boolean sendObject(int objectHandle, int size, ParcelFileDescriptor descriptor) {
+    public boolean sendObject(int objectHandle, long size, ParcelFileDescriptor descriptor) {
         return native_send_object(objectHandle, size, descriptor.getFd());
     }
 
@@ -343,16 +365,18 @@
     private native MtpStorageInfo native_get_storage_info(int storageId);
     private native int[] native_get_object_handles(int storageId, int format, int objectHandle);
     private native MtpObjectInfo native_get_object_info(int objectHandle);
-    private native byte[] native_get_object(int objectHandle, int objectSize);
+    private native byte[] native_get_object(int objectHandle, long objectSize);
     private native long native_get_partial_object(
             int objectHandle, long offset, long objectSize, byte[] buffer) throws IOException;
+    private native int native_get_partial_object_64(
+            int objectHandle, long offset, long objectSize, byte[] buffer) throws IOException;
     private native byte[] native_get_thumbnail(int objectHandle);
     private native boolean native_delete_object(int objectHandle);
-    private native long native_get_parent(int objectHandle);
-    private native long native_get_storage_id(int objectHandle);
+    private native int native_get_parent(int objectHandle);
+    private native int native_get_storage_id(int objectHandle);
     private native boolean native_import_file(int objectHandle, String destPath);
     private native boolean native_import_file(int objectHandle, int fd);
-    private native boolean native_send_object(int objectHandle, int size, int fd);
+    private native boolean native_send_object(int objectHandle, long size, int fd);
     private native MtpObjectInfo native_send_object_info(MtpObjectInfo info);
     private native int native_submit_event_request();
     private native MtpEvent native_reap_event_request(int handle);
diff --git a/media/java/android/mtp/MtpObjectInfo.java b/media/java/android/mtp/MtpObjectInfo.java
index 64aa997..02092b1 100644
--- a/media/java/android/mtp/MtpObjectInfo.java
+++ b/media/java/android/mtp/MtpObjectInfo.java
@@ -16,6 +16,8 @@
 
 package android.mtp;
 
+import com.android.internal.util.Preconditions;
+
 /**
  * This class encapsulates information about an object on an MTP device.
  * This corresponds to the ObjectInfo Dataset described in
@@ -96,10 +98,20 @@
      * @return the object size
      */
     public final int getCompressedSize() {
+        Preconditions.checkState(mCompressedSize >= 0);
         return mCompressedSize;
     }
 
     /**
+     * Returns the size of the MTP object
+     *
+     * @return the object size
+     */
+    public final long getCompressedSizeLong() {
+        return uint32ToLong(mCompressedSize);
+    }
+
+    /**
      * Returns the format code for the MTP object's thumbnail
      * Will be zero for objects with no thumbnail
      *
@@ -116,60 +128,126 @@
      * @return the thumbnail size
      */
     public final int getThumbCompressedSize() {
+        Preconditions.checkState(mThumbCompressedSize >= 0);
         return mThumbCompressedSize;
     }
 
     /**
+     * Returns the size of the MTP object's thumbnail
+     * Will be zero for objects with no thumbnail
+     *
+     * @return the thumbnail size
+     */
+    public final long getThumbCompressedSizeLong() {
+        return uint32ToLong(mThumbCompressedSize);
+    }
+
+    /**
      * Returns the width of the MTP object's thumbnail in pixels
      * Will be zero for objects with no thumbnail
      *
      * @return the thumbnail width
      */
     public final int getThumbPixWidth() {
+        Preconditions.checkState(mThumbPixWidth >= 0);
         return mThumbPixWidth;
     }
 
     /**
+     * Returns the width of the MTP object's thumbnail in pixels
+     * Will be zero for objects with no thumbnail
+     *
+     * @return the thumbnail width
+     */
+    public final long getThumbPixWidthLong() {
+        return uint32ToLong(mThumbPixWidth);
+    }
+
+    /**
      * Returns the height of the MTP object's thumbnail in pixels
      * Will be zero for objects with no thumbnail
      *
      * @return the thumbnail height
      */
     public final int getThumbPixHeight() {
+        Preconditions.checkState(mThumbPixHeight >= 0);
         return mThumbPixHeight;
     }
 
     /**
+     * Returns the height of the MTP object's thumbnail in pixels
+     * Will be zero for objects with no thumbnail
+     *
+     * @return the thumbnail height
+     */
+    public final long getThumbPixHeightLong() {
+        return uint32ToLong(mThumbPixHeight);
+    }
+
+    /**
      * Returns the width of the MTP object in pixels
      * Will be zero for non-image objects
      *
      * @return the image width
      */
     public final int getImagePixWidth() {
+        Preconditions.checkState(mImagePixWidth >= 0);
         return mImagePixWidth;
     }
 
     /**
+     * Returns the width of the MTP object in pixels
+     * Will be zero for non-image objects
+     *
+     * @return the image width
+     */
+    public final long getImagePixWidthLong() {
+        return uint32ToLong(mImagePixWidth);
+    }
+
+    /**
      * Returns the height of the MTP object in pixels
      * Will be zero for non-image objects
      *
      * @return the image height
      */
     public final int getImagePixHeight() {
+        Preconditions.checkState(mImagePixHeight >= 0);
         return mImagePixHeight;
     }
 
     /**
+     * Returns the height of the MTP object in pixels
+     * Will be zero for non-image objects
+     *
+     * @return the image height
+     */
+    public final long getImagePixHeightLong() {
+        return uint32ToLong(mImagePixHeight);
+    }
+
+    /**
      * Returns the depth of the MTP object in bits per pixel
      * Will be zero for non-image objects
      *
      * @return the image depth
      */
     public final int getImagePixDepth() {
+        Preconditions.checkState(mImagePixDepth >= 0);
         return mImagePixDepth;
     }
 
     /**
+     * Returns the depth of the MTP object in bits per pixel
+     * Will be zero for non-image objects
+     *
+     * @return the image depth
+     */
+    public final long getImagePixDepthLong() {
+        return uint32ToLong(mImagePixDepth);
+    }
+
+    /**
      * Returns the object handle for the object's parent
      * Will be zero for the root directory of a storage unit
      *
@@ -203,7 +281,7 @@
         return mAssociationDesc;
     }
 
-   /**
+    /**
      * Returns the sequence number for the MTP object
      * This field is typically not used for MTP devices,
      * but is sometimes used to define a sequence of photos
@@ -212,9 +290,22 @@
      * @return the object's sequence number
      */
     public final int getSequenceNumber() {
+        Preconditions.checkState(mSequenceNumber >= 0);
         return mSequenceNumber;
     }
 
+    /**
+     * Returns the sequence number for the MTP object
+     * This field is typically not used for MTP devices,
+     * but is sometimes used to define a sequence of photos
+     * on PTP cameras.
+     *
+     * @return the object's sequence number
+     */
+    public final long getSequenceNumberLong() {
+        return uint32ToLong(mSequenceNumber);
+    }
+
    /**
      * Returns the name of the MTP object
      *
@@ -309,8 +400,8 @@
             return this;
         }
 
-        public Builder setCompressedSize(int value) {
-            mObjectInfo.mCompressedSize = value;
+        public Builder setCompressedSize(long value) {
+            mObjectInfo.mCompressedSize = longToUint32(value, "value");
             return this;
         }
 
@@ -329,18 +420,18 @@
             return this;
         }
 
-        public Builder setImagePixDepth(int value) {
-            mObjectInfo.mImagePixDepth = value;
+        public Builder setImagePixDepth(long value) {
+            mObjectInfo.mImagePixDepth = longToUint32(value, "value");
             return this;
         }
 
-        public Builder setImagePixHeight(int value) {
-            mObjectInfo.mImagePixHeight = value;
+        public Builder setImagePixHeight(long value) {
+            mObjectInfo.mImagePixHeight = longToUint32(value, "value");
             return this;
         }
 
-        public Builder setImagePixWidth(int value) {
-            mObjectInfo.mImagePixWidth = value;
+        public Builder setImagePixWidth(long value) {
+            mObjectInfo.mImagePixWidth = longToUint32(value, "value");
             return this;
         }
 
@@ -364,8 +455,8 @@
             return this;
         }
 
-        public Builder setSequenceNumber(int value) {
-            mObjectInfo.mSequenceNumber = value;
+        public Builder setSequenceNumber(long value) {
+            mObjectInfo.mSequenceNumber = longToUint32(value, "value");
             return this;
         }
 
@@ -374,8 +465,8 @@
             return this;
         }
 
-        public Builder setThumbCompressedSize(int value) {
-            mObjectInfo.mThumbCompressedSize = value;
+        public Builder setThumbCompressedSize(long value) {
+            mObjectInfo.mThumbCompressedSize = longToUint32(value, "value");
             return this;
         }
 
@@ -384,13 +475,13 @@
             return this;
         }
 
-        public Builder setThumbPixHeight(int value) {
-            mObjectInfo.mThumbPixHeight = value;
+        public Builder setThumbPixHeight(long value) {
+            mObjectInfo.mThumbPixHeight = longToUint32(value, "value");
             return this;
         }
 
-        public Builder setThumbPixWidth(int value) {
-            mObjectInfo.mThumbPixWidth = value;
+        public Builder setThumbPixWidth(long value) {
+            mObjectInfo.mThumbPixWidth = longToUint32(value, "value");
             return this;
         }
 
@@ -407,4 +498,13 @@
             return result;
         }
     }
+
+    private static long uint32ToLong(int value) {
+        return value < 0 ? 0x100000000L + value : value;
+    }
+
+    private static int longToUint32(long value, String valueName) {
+        Preconditions.checkArgumentInRange(value, 0, 0xffffffffL, valueName);
+        return (int) value;
+    }
 }
diff --git a/media/jni/android_media_ExifInterface.cpp b/media/jni/android_media_ExifInterface.cpp
index 42deab4..ba38569 100644
--- a/media/jni/android_media_ExifInterface.cpp
+++ b/media/jni/android_media_ExifInterface.cpp
@@ -238,7 +238,7 @@
             map.add(
                     String8("GPSTimeStamp"),
                     String8::format(
-                            "%2u:%2u:%2u",
+                            "%02u:%02u:%02u",
                             image_data.gps.time_stamp[0].numerator
                             / image_data.gps.time_stamp[0].denominator,
                             image_data.gps.time_stamp[1].numerator
diff --git a/media/jni/android_media_MediaDataSource.cpp b/media/jni/android_media_MediaDataSource.cpp
index 025133f..3b892cb 100644
--- a/media/jni/android_media_MediaDataSource.cpp
+++ b/media/jni/android_media_MediaDataSource.cpp
@@ -150,4 +150,8 @@
     mJavaObjStatus = UNKNOWN_ERROR;
 }
 
+uint32_t JMediaDataSource::getFlags() {
+    return 0;
+}
+
 }  // namespace android
diff --git a/media/jni/android_media_MediaDataSource.h b/media/jni/android_media_MediaDataSource.h
index 2bc237e..34624eb 100644
--- a/media/jni/android_media_MediaDataSource.h
+++ b/media/jni/android_media_MediaDataSource.h
@@ -45,6 +45,7 @@
     virtual ssize_t readAt(off64_t offset, size_t size);
     virtual status_t getSize(off64_t* size);
     virtual void close();
+    virtual uint32_t getFlags();
 
 private:
     // Protect all member variables with mLock because this object will be
diff --git a/media/jni/android_media_Utils.cpp b/media/jni/android_media_Utils.cpp
index 5108eb5..c08a5e3 100644
--- a/media/jni/android_media_Utils.cpp
+++ b/media/jni/android_media_Utils.cpp
@@ -81,7 +81,8 @@
 
 bool GetExifFromRawImage(
         FileStream* stream, const String8& filename, piex::PreviewImageData& image_data) {
-    memset(&image_data, 0, sizeof(image_data));
+    // Reset the PreviewImageData to its default.
+    image_data = piex::PreviewImageData();
 
     if (!stream->exists()) {
         // File is not exists.
diff --git a/media/jni/android_mtp_MtpDevice.cpp b/media/jni/android_mtp_MtpDevice.cpp
index 8b7a926..0ecb750 100644
--- a/media/jni/android_mtp_MtpDevice.cpp
+++ b/media/jni/android_mtp_MtpDevice.cpp
@@ -26,6 +26,7 @@
 #include <fcntl.h>
 
 #include <memory>
+#include <string>
 
 #include "jni.h"
 #include "JNIHelp.h"
@@ -370,9 +371,26 @@
     return info;
 }
 
+bool check_uint32_arg(JNIEnv *env, const char* name, jlong value, uint32_t* out) {
+    if (value < 0 || 0xffffffff < value) {
+        jniThrowException(
+                env,
+                "java/lang/IllegalArgumentException",
+                (std::string("argument must be a 32-bit unsigned integer: ") + name).c_str());
+        return false;
+    }
+    *out = static_cast<uint32_t>(value);
+    return true;
+}
+
 static jbyteArray
-android_mtp_MtpDevice_get_object(JNIEnv *env, jobject thiz, jint objectID, jint objectSize)
+android_mtp_MtpDevice_get_object(JNIEnv *env, jobject thiz, jint objectID, jlong objectSizeLong)
 {
+    uint32_t objectSize;
+    if (!check_uint32_arg(env, "objectSize", objectSizeLong, &objectSize)) {
+        return nullptr;
+    }
+
     MtpDevice* device = get_device_from_object(env, thiz);
     if (!device) {
         return nullptr;
@@ -396,8 +414,8 @@
 android_mtp_MtpDevice_get_partial_object(JNIEnv *env,
                                          jobject thiz,
                                          jint objectID,
-                                         jlong offset,
-                                         jlong size,
+                                         jlong offsetLong,
+                                         jlong sizeLong,
                                          jbyteArray array)
 {
     if (!array) {
@@ -405,19 +423,10 @@
         return -1;
     }
 
-    if (offset < 0 || 0xffffffffL < offset) {
-        jniThrowException(
-                env,
-                "java/lang/IllegalArgumentException",
-                "Offset argument must be a 32-bit unsigned integer.");
-        return -1;
-    }
-
-    if (size < 0 || 0xffffffffL < size) {
-        jniThrowException(
-                env,
-                "java/lang/IllegalArgumentException",
-                "Size argument must be a 32-bit unsigned integer.");
+    uint32_t offset;
+    uint32_t size;
+    if (!check_uint32_arg(env, "offset", offsetLong, &offset) ||
+            !check_uint32_arg(env, "size", sizeLong, &size)) {
         return -1;
     }
 
@@ -438,6 +447,60 @@
     return static_cast<jlong>(written_size);
 }
 
+static jint
+android_mtp_MtpDevice_get_partial_object_64(JNIEnv *env,
+                                            jobject thiz,
+                                            jint objectID,
+                                            jlong offset,
+                                            jlong size,
+                                            jbyteArray array) {
+    if (!array) {
+        jniThrowException(env, "java/lang/IllegalArgumentException", "Array must not be null.");
+        return -1;
+    }
+
+    if (offset < 0) {
+        jniThrowException(
+                env,
+                "java/lang/IllegalArgumentException",
+                "Offset argument must not be a negative value.");
+        return -1;
+    }
+
+    if (size < 0 || 0xffffffffL < size) {
+        jniThrowException(
+                env,
+                "java/lang/IllegalArgumentException",
+                "Size argument must be a 32-bit unsigned integer.");
+        return -1;
+    }
+
+    MtpDevice* const device = get_device_from_object(env, thiz);
+    if (!device) {
+        jniThrowException(env, "java/io/IOException", "Failed to obtain MtpDevice.");
+        return -1;
+    }
+
+    const uint32_t native_object_handle = static_cast<uint32_t>(objectID);
+    const uint64_t native_offset = static_cast<uint64_t>(offset);
+    const uint32_t native_size = static_cast<uint32_t>(size);
+
+    JavaArrayWriter writer(env, array);
+    uint32_t written_size;
+    const bool success = device->readPartialObject64(
+            native_object_handle,
+            native_offset,
+            native_size,
+            &written_size,
+            JavaArrayWriter::writeTo,
+            &writer);
+    if (!success) {
+        jniThrowException(env, "java/io/IOException", "Failed to read data.");
+        return -1;
+    }
+    return static_cast<jint>(written_size);
+}
+
 static jbyteArray
 android_mtp_MtpDevice_get_thumbnail(JNIEnv *env, jobject thiz, jint objectID)
 {
@@ -467,22 +530,22 @@
     }
 }
 
-static jlong
+static jint
 android_mtp_MtpDevice_get_parent(JNIEnv *env, jobject thiz, jint object_id)
 {
     MtpDevice* device = get_device_from_object(env, thiz);
     if (device)
-        return (jlong)device->getParent(object_id);
+        return static_cast<jint>(device->getParent(object_id));
     else
         return -1;
 }
 
-static jlong
+static jint
 android_mtp_MtpDevice_get_storage_id(JNIEnv *env, jobject thiz, jint object_id)
 {
     MtpDevice* device = get_device_from_object(env, thiz);
     if (device)
-        return (jlong)device->getStorageID(object_id);
+        return static_cast<jint>(device->getStorageID(object_id));
     else
         return -1;
 }
@@ -516,8 +579,13 @@
 }
 
 static jboolean
-android_mtp_MtpDevice_send_object(JNIEnv *env, jobject thiz, jint object_id, jint size, jint fd)
+android_mtp_MtpDevice_send_object(
+        JNIEnv *env, jobject thiz, jint object_id, jlong sizeLong, jint fd)
 {
+    uint32_t size;
+    if (!check_uint32_arg(env, "size", sizeLong, &size))
+        return JNI_FALSE;
+
     MtpDevice* device = get_device_from_object(env, thiz);
     if (!device)
         return JNI_FALSE;
@@ -647,16 +715,18 @@
                                         (void *)android_mtp_MtpDevice_get_object_handles},
     {"native_get_object_info",  "(I)Landroid/mtp/MtpObjectInfo;",
                                         (void *)android_mtp_MtpDevice_get_object_info},
-    {"native_get_object",       "(II)[B",(void *)android_mtp_MtpDevice_get_object},
+    {"native_get_object",       "(IJ)[B",(void *)android_mtp_MtpDevice_get_object},
     {"native_get_partial_object", "(IJJ[B)J", (void *)android_mtp_MtpDevice_get_partial_object},
+    {"native_get_partial_object_64", "(IJJ[B)I",
+                                        (void *)android_mtp_MtpDevice_get_partial_object_64},
     {"native_get_thumbnail",    "(I)[B",(void *)android_mtp_MtpDevice_get_thumbnail},
     {"native_delete_object",    "(I)Z", (void *)android_mtp_MtpDevice_delete_object},
-    {"native_get_parent",       "(I)J", (void *)android_mtp_MtpDevice_get_parent},
-    {"native_get_storage_id",   "(I)J", (void *)android_mtp_MtpDevice_get_storage_id},
+    {"native_get_parent",       "(I)I", (void *)android_mtp_MtpDevice_get_parent},
+    {"native_get_storage_id",   "(I)I", (void *)android_mtp_MtpDevice_get_storage_id},
     {"native_import_file",      "(ILjava/lang/String;)Z",
                                         (void *)android_mtp_MtpDevice_import_file},
     {"native_import_file",      "(II)Z",(void *)android_mtp_MtpDevice_import_file_to_fd},
-    {"native_send_object",      "(III)Z",(void *)android_mtp_MtpDevice_send_object},
+    {"native_send_object",      "(IJI)Z",(void *)android_mtp_MtpDevice_send_object},
     {"native_send_object_info", "(Landroid/mtp/MtpObjectInfo;)Landroid/mtp/MtpObjectInfo;",
                                         (void *)android_mtp_MtpDevice_send_object_info},
     {"native_submit_event_request",  "()I", (void *)android_mtp_MtpDevice_submit_event_request},
diff --git a/packages/DocumentsUI/AndroidManifest.xml b/packages/DocumentsUI/AndroidManifest.xml
index c3452d5..fa9ff01 100644
--- a/packages/DocumentsUI/AndroidManifest.xml
+++ b/packages/DocumentsUI/AndroidManifest.xml
@@ -54,8 +54,7 @@
             android:name=".LauncherActivity"
             android:theme="@android:style/Theme.NoDisplay"
             android:icon="@drawable/ic_files_app"
-            android:label="@string/files_label"
-            android:enabled="@bool/productivity_device">
+            android:label="@string/files_label">
             <intent-filter>
                 <action android:name="android.intent.action.MAIN" />
                 <category android:name="android.intent.category.LAUNCHER" />
diff --git a/packages/DocumentsUI/res/values/config.xml b/packages/DocumentsUI/res/values/config.xml
index ff28e15..e8d8c8eb 100644
--- a/packages/DocumentsUI/res/values/config.xml
+++ b/packages/DocumentsUI/res/values/config.xml
@@ -18,7 +18,6 @@
     <!-- Allow Advanced Devices default value to be customised -->
     <bool name="config_defaultAdvancedDevices">false</bool>
 
-    <bool name="productivity_device">true</bool>
     <!-- Intentionally unset. Vendors should set this in an overlay. -->
     <string name="trusted_quick_viewer_package"></string>
 </resources>
diff --git a/packages/DocumentsUI/res/values/strings.xml b/packages/DocumentsUI/res/values/strings.xml
index aefdc67..afe9336 100644
--- a/packages/DocumentsUI/res/values/strings.xml
+++ b/packages/DocumentsUI/res/values/strings.xml
@@ -181,6 +181,8 @@
     <string name="copy_failure_alert_content">These files weren\'t copied: <xliff:g id="list">%1$s</xliff:g></string>
     <!-- Contents of the moving failure alert dialog. [CHAR LIMIT=48] -->
     <string name="move_failure_alert_content">These files weren\'t moved: <xliff:g id="list">%1$s</xliff:g></string>
+    <!-- Contents of the copying warning dialog due to converted files. [CHAR LIMIT=64] -->
+    <string name="copy_converted_warning_content">These files were converted to another format: <xliff:g id="list" example="Document.pdf, Photo.jpg, Song.ogg">%1$s</xliff:g></string>
     <!-- Toast shown when a user copies files to clipboard. -->
     <plurals name="clipboard_files_clipped">
         <item quantity="one">Copied <xliff:g id="count" example="1">%1$d</xliff:g> file to clipboard.</item>
@@ -192,6 +194,9 @@
     <string name="menu_rename">Rename</string>
     <!-- Toast shown when renaming document failed with an error [CHAR LIMIT=48] -->
     <string name="rename_error">Failed to rename document</string>
+    <!-- First line for notifications saying that some files were converted to a different format
+         during a copy. [CHAR LIMIT=48] -->
+    <string name="notification_copy_files_converted_title">Some files were converted</string>
 
     <!--  DO NOT TRANSLATE - final phrase has not been decided yet (b/26750152) -->
     <string name="open_external_dialog_request">Grant <xliff:g id="appName" example="System Settings"><b>^1</b></xliff:g>
diff --git a/packages/DocumentsUI/src/com/android/documentsui/BaseActivity.java b/packages/DocumentsUI/src/com/android/documentsui/BaseActivity.java
index ad8b0d1..9309693 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/BaseActivity.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/BaseActivity.java
@@ -17,11 +17,13 @@
 package com.android.documentsui;
 
 import static com.android.documentsui.Shared.DEBUG;
+import static com.android.documentsui.State.MODE_GRID;
 import static com.android.documentsui.dirlist.DirectoryFragment.ANIM_DOWN;
 import static com.android.documentsui.dirlist.DirectoryFragment.ANIM_NONE;
 import static com.android.documentsui.dirlist.DirectoryFragment.ANIM_SIDE;
 import static com.android.documentsui.dirlist.DirectoryFragment.ANIM_UP;
 import static com.android.internal.util.Preconditions.checkArgument;
+import static com.android.internal.util.Preconditions.checkState;
 
 import android.app.Activity;
 import android.app.Fragment;
@@ -51,8 +53,8 @@
 import android.widget.TextView;
 
 import com.android.documentsui.RecentsProvider.ResumeColumns;
-import com.android.documentsui.SearchManager;
 import com.android.documentsui.SearchManager.SearchManagerListener;
+import com.android.documentsui.State.ViewMode;
 import com.android.documentsui.dirlist.DirectoryFragment;
 import com.android.documentsui.model.DocumentInfo;
 import com.android.documentsui.model.DocumentStack;
@@ -102,7 +104,6 @@
     public void onCreate(Bundle icicle) {
         super.onCreate(icicle);
 
-        mProductivityDevice = getResources().getBoolean(R.bool.productivity_device);
         mState = (icicle != null)
                 ? icicle.<State>getParcelable(EXTRA_STATE)
                         : buildState();
@@ -204,6 +205,8 @@
     void onStackRestored(boolean restored, boolean external) {}
 
     void onRootPicked(RootInfo root) {
+        mState.derivedMode = LocalPreferences.getViewMode(this, root, MODE_GRID);
+
         // Clear entire backstack and start in new root
         mState.onRootChanged(root);
         mSearchManager.update(root);
@@ -260,11 +263,11 @@
                 return true;
 
             case R.id.menu_grid:
-                setUserMode(State.MODE_GRID);
+                setViewMode(State.MODE_GRID);
                 return true;
 
             case R.id.menu_list:
-                setUserMode(State.MODE_LIST);
+                setViewMode(State.MODE_LIST);
                 return true;
 
             case R.id.menu_paste_from_clipboard:
@@ -419,24 +422,27 @@
         invalidateOptionsMenu();
     }
 
-    public void onStateChanged() {
-        invalidateOptionsMenu();
-    }
-
     /**
      * Set state sort order based on explicit user action.
      */
     void setUserSortOrder(int sortOrder) {
         mState.userSortOrder = sortOrder;
-        DirectoryFragment.get(getFragmentManager()).onUserSortOrderChanged();
+        DirectoryFragment.get(getFragmentManager()).onSortOrderChanged();
     }
 
     /**
-     * Set state mode based on explicit user action.
+     * Set mode based on explicit user action.
      */
-    void setUserMode(int mode) {
-        mState.userMode = mode;
-        DirectoryFragment.get(getFragmentManager()).onUserModeChanged();
+    void setViewMode(@ViewMode int mode) {
+        checkState(mState.stack.root != null);
+        LocalPreferences.setViewMode(this, mState.stack.root, mode);
+        mState.derivedMode = mode;
+
+        // view icon needs to be updated, but we *could* do it
+        // in onOptionsItemSelected, and not do the full invalidation
+        // But! That's a larger refactoring we'll save for another day.
+        invalidateOptionsMenu();
+        DirectoryFragment.get(getFragmentManager()).onViewModeChanged();
     }
 
     public void setPending(boolean pending) {
diff --git a/packages/DocumentsUI/src/com/android/documentsui/DirectoryLoader.java b/packages/DocumentsUI/src/com/android/documentsui/DirectoryLoader.java
index 6719b23..b0542b9 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/DirectoryLoader.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/DirectoryLoader.java
@@ -16,11 +16,11 @@
 
 package com.android.documentsui;
 
+import static com.android.documentsui.Shared.DEBUG;
 import static com.android.documentsui.Shared.TAG;
 import static com.android.documentsui.State.SORT_ORDER_DISPLAY_NAME;
 import static com.android.documentsui.State.SORT_ORDER_LAST_MODIFIED;
 import static com.android.documentsui.State.SORT_ORDER_SIZE;
-import static com.android.documentsui.model.DocumentInfo.getCursorInt;
 
 import android.content.AsyncTaskLoader;
 import android.content.ContentProviderClient;
@@ -35,7 +35,6 @@
 import android.provider.DocumentsContract.Document;
 import android.util.Log;
 
-import com.android.documentsui.RecentsProvider.StateColumns;
 import com.android.documentsui.dirlist.DirectoryFragment;
 import com.android.documentsui.model.DocumentInfo;
 import com.android.documentsui.model.RootInfo;
@@ -52,10 +51,10 @@
 
     private final int mType;
     private final RootInfo mRoot;
-    private DocumentInfo mDoc;
     private final Uri mUri;
     private final int mUserSortOrder;
 
+    private DocumentInfo mDoc;
     private CancellationSignal mSignal;
     private DirectoryResult mResult;
 
@@ -64,9 +63,9 @@
         super(context, ProviderExecutor.forAuthority(root.authority));
         mType = type;
         mRoot = root;
-        mDoc = doc;
         mUri = uri;
         mUserSortOrder = userSortOrder;
+        mDoc = doc;
     }
 
     @Override
@@ -83,8 +82,6 @@
 
         final DirectoryResult result = new DirectoryResult();
 
-        int userMode = State.MODE_UNKNOWN;
-
         // Use default document when searching
         if (mType == DirectoryFragment.TYPE_SEARCH) {
             final Uri docUri = DocumentsContract.buildDocumentUri(
@@ -98,25 +95,6 @@
             }
         }
 
-        // Pick up any custom modes requested by user
-        Cursor cursor = null;
-        try {
-            final Uri stateUri = RecentsProvider.buildState(
-                    mRoot.authority, mRoot.rootId, mDoc.documentId);
-            cursor = resolver.query(stateUri, null, null, null, null);
-            if (cursor.moveToFirst()) {
-                userMode = getCursorInt(cursor, StateColumns.MODE);
-            }
-        } finally {
-            IoUtils.closeQuietly(cursor);
-        }
-
-        if (userMode != State.MODE_UNKNOWN) {
-            result.mode = userMode;
-        } else {
-            result.mode = State.MODE_GRID;
-        }
-
         if (mUserSortOrder != State.SORT_ORDER_UNKNOWN) {
             result.sortOrder = mUserSortOrder;
         } else {
@@ -132,13 +110,13 @@
             result.sortOrder = State.SORT_ORDER_UNKNOWN;
         }
 
-        Log.d(TAG, "userMode=" + userMode + ", userSortOrder=" + mUserSortOrder + " --> mode="
-                + result.mode + ", sortOrder=" + result.sortOrder);
+        if (DEBUG)
+                Log.d(TAG, "userSortOrder=" + mUserSortOrder + ", sortOrder=" + result.sortOrder);
 
         ContentProviderClient client = null;
+        Cursor cursor = null;
         try {
             client = DocumentsApplication.acquireUnstableProviderOrThrow(resolver, authority);
-
             cursor = client.query(
                     mUri, null, null, null, getQuerySortOrder(result.sortOrder), mSignal);
             if (cursor == null) {
diff --git a/packages/DocumentsUI/src/com/android/documentsui/DirectoryResult.java b/packages/DocumentsUI/src/com/android/documentsui/DirectoryResult.java
index e7e4f73..22e438a 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/DirectoryResult.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/DirectoryResult.java
@@ -29,7 +29,6 @@
     public Cursor cursor;
     public Exception exception;
 
-    public int mode = MODE_UNKNOWN;
     public int sortOrder = SORT_ORDER_UNKNOWN;
 
     @Override
diff --git a/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java b/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java
index 6dcd472..c3395ae 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java
@@ -346,11 +346,13 @@
             } else {
                 DirectoryFragment.showRecentsOpen(fm, anim);
 
-                // Start recents in grid when requesting visual things
-                final boolean visualMimes = MimePredicate.mimeMatches(
+                // In recents we pick layout mode based on the mimetype,
+                // picking GRID for visual types. We intentionally don't
+                // consult a user's saved preferences here since they are
+                // set per root (not per root and per mimetype).
+                boolean visualMimes = MimePredicate.mimeMatches(
                         MimePredicate.VISUAL_MIMES, mState.acceptMimes);
-                mState.userMode = visualMimes ? State.MODE_GRID : State.MODE_LIST;
-                mState.derivedMode = mState.userMode;
+                mState.derivedMode = visualMimes ? State.MODE_GRID : State.MODE_LIST;
             }
         } else {
             if (mSearchManager.isSearching()) {
diff --git a/packages/DocumentsUI/src/com/android/documentsui/FailureDialogFragment.java b/packages/DocumentsUI/src/com/android/documentsui/FailureDialogFragment.java
deleted file mode 100644
index b8ef5ac..0000000
--- a/packages/DocumentsUI/src/com/android/documentsui/FailureDialogFragment.java
+++ /dev/null
@@ -1,102 +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.documentsui;
-
-import static com.android.internal.util.Preconditions.checkArgument;
-
-import android.app.AlertDialog;
-import android.app.Dialog;
-import android.app.DialogFragment;
-import android.app.FragmentManager;
-import android.app.FragmentTransaction;
-import android.content.DialogInterface;
-import android.os.Bundle;
-import android.text.Html;
-
-import com.android.documentsui.model.DocumentInfo;
-import com.android.documentsui.model.DocumentStack;
-import com.android.documentsui.services.FileOperationService;
-import com.android.documentsui.services.FileOperations;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * Alert dialog for failed operations.
- */
-public class FailureDialogFragment extends DialogFragment {
-    private static final String TAG = "FailureDialogFragment";
-
-    public static void show(FragmentManager fm, int failure,
-            ArrayList<DocumentInfo> failedSrcList, DocumentStack dstStack, int operationType) {
-        // TODO: Add support for other failures than copy.
-        if (failure != FileOperationService.FAILURE_COPY) {
-            return;
-        }
-
-        final Bundle args = new Bundle();
-        args.putInt(FileOperationService.EXTRA_FAILURE, failure);
-        args.putInt(FileOperationService.EXTRA_OPERATION, operationType);
-        args.putParcelableArrayList(FileOperationService.EXTRA_SRC_LIST, failedSrcList);
-
-        final FragmentTransaction ft = fm.beginTransaction();
-        final FailureDialogFragment fragment = new FailureDialogFragment();
-        fragment.setArguments(args);
-
-        ft.add(fragment, TAG);
-        ft.commitAllowingStateLoss();
-    }
-
-    @Override
-    public Dialog onCreateDialog(Bundle inState) {
-        super.onCreate(inState);
-
-        final int operationType = getArguments().getInt(FileOperationService.EXTRA_OPERATION);
-        final List<DocumentInfo> failedSrcList = getArguments().getParcelableArrayList(
-                FileOperationService.EXTRA_SRC_LIST);
-
-        final StringBuilder list = new StringBuilder("<p>");
-        for (DocumentInfo documentInfo : failedSrcList) {
-            list.append(String.format("&#8226; %s<br>", documentInfo.displayName));
-        }
-        list.append("</p>");
-
-        // TODO: Add support for other file operations.
-        checkArgument(
-                operationType == FileOperationService.OPERATION_COPY
-                || operationType == FileOperationService.OPERATION_MOVE);
-
-        int messageId = operationType == FileOperationService.OPERATION_COPY
-                ? R.string.copy_failure_alert_content
-                : R.string.move_failure_alert_content;
-
-        final String messageFormat = getString(
-                messageId);
-
-        final String message = String.format(messageFormat, list.toString());
-
-        return new AlertDialog.Builder(getActivity())
-                .setMessage(Html.fromHtml(message))
-                .setPositiveButton(R.string.close, new DialogInterface.OnClickListener() {
-                    @Override
-                    public void onClick(DialogInterface dialog, int id) {
-                        dialog.dismiss();
-                    }
-                })
-                .create();
-    }
-}
diff --git a/packages/DocumentsUI/src/com/android/documentsui/FilesActivity.java b/packages/DocumentsUI/src/com/android/documentsui/FilesActivity.java
index 5abe7f6..f51f689 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/FilesActivity.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/FilesActivity.java
@@ -16,6 +16,7 @@
 
 package com.android.documentsui;
 
+import static com.android.documentsui.OperationDialogFragment.DIALOG_TYPE_UNKNOWN;
 import static com.android.documentsui.Shared.DEBUG;
 import static com.android.documentsui.dirlist.DirectoryFragment.ANIM_NONE;
 import static com.android.internal.util.Preconditions.checkArgument;
@@ -43,6 +44,7 @@
 import android.widget.Spinner;
 import android.widget.Toolbar;
 
+import com.android.documentsui.OperationDialogFragment.DialogType;
 import com.android.documentsui.RecentsProvider.ResumeColumns;
 import com.android.documentsui.dirlist.DirectoryFragment;
 import com.android.documentsui.model.DocumentInfo;
@@ -121,20 +123,20 @@
                     ProviderExecutor.forAuthority(homeUri.getAuthority()));
         }
 
-        final int failure = intent.getIntExtra(FileOperationService.EXTRA_FAILURE, 0);
-        final int opType = intent.getIntExtra(
-                FileOperationService.EXTRA_OPERATION,
-                FileOperationService.OPERATION_COPY);
-
+        final @DialogType int dialogType = intent.getIntExtra(
+                FileOperationService.EXTRA_DIALOG_TYPE, DIALOG_TYPE_UNKNOWN);
         // DialogFragment takes care of restoring the dialog on configuration change.
         // Only show it manually for the first time (icicle is null).
-        if (icicle == null && failure != 0) {
-            final ArrayList<DocumentInfo> failedSrcList =
+        if (icicle == null && dialogType != DIALOG_TYPE_UNKNOWN) {
+            final int opType = intent.getIntExtra(
+                    FileOperationService.EXTRA_OPERATION,
+                    FileOperationService.OPERATION_COPY);
+            final ArrayList<DocumentInfo> srcList =
                     intent.getParcelableArrayListExtra(FileOperationService.EXTRA_SRC_LIST);
-            FailureDialogFragment.show(
+            OperationDialogFragment.show(
                     getFragmentManager(),
-                    failure,
-                    failedSrcList,
+                    dialogType,
+                    srcList,
                     mState.stack,
                     opType);
         }
@@ -254,9 +256,6 @@
 
         pasteFromCb.setEnabled(mClipper.hasItemsToPaste());
 
-        newWindow.setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER);
-        newWindow.setVisible(mProductivityDevice);
-
         Menus.disableHiddenItems(menu, pasteFromCb);
         return true;
     }
@@ -285,6 +284,14 @@
         Metrics.logMultiWindow(this);
         Intent intent = LauncherActivity.createLaunchIntent(this);
         intent.putExtra(Shared.EXTRA_STACK, (Parcelable) mState.stack);
+
+        // With new multi-window mode we have to pick how we are launched.
+        // By default we'd be launched in-place above the existing app.
+        // By setting launch-to-side ActivityManager will open us to side.
+        if (inMultiWindowMode()) {
+            intent.addFlags(Intent.FLAG_ACTIVITY_LAUNCH_TO_SIDE);
+        }
+
         startActivity(intent);
     }
 
@@ -296,12 +303,6 @@
 
         if (cwd == null) {
             DirectoryFragment.showRecentsOpen(fm, anim);
-
-            // Start recents in grid when requesting visual things
-            final boolean visualMimes = MimePredicate.mimeMatches(
-                    MimePredicate.VISUAL_MIMES, mState.acceptMimes);
-            mState.userMode = visualMimes ? State.MODE_GRID : State.MODE_LIST;
-            mState.derivedMode = mState.userMode;
         } else {
             if (mState.currentSearch != null) {
                 // Ongoing search
diff --git a/packages/DocumentsUI/src/com/android/documentsui/LauncherActivity.java b/packages/DocumentsUI/src/com/android/documentsui/LauncherActivity.java
index b3d0cf3..7930c28 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/LauncherActivity.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/LauncherActivity.java
@@ -74,6 +74,9 @@
 
     private void startTask() {
         Intent intent = createLaunchIntent(this);
+
+        // Forward any flags from the original intent.
+        intent.setFlags(getIntent().getFlags());
         if (DEBUG) Log.d(TAG, "Starting new task > " + intent.getData());
         startActivity(intent);
     }
@@ -84,7 +87,7 @@
         startActivity(intent);
     }
 
-    static Intent createLaunchIntent(Context context) {
+    static final Intent createLaunchIntent(Context context) {
         Intent intent = new Intent(context, FilesActivity.class);
         intent.setData(buildLaunchUri());
         return intent;
diff --git a/packages/DocumentsUI/src/com/android/documentsui/LocalPreferences.java b/packages/DocumentsUI/src/com/android/documentsui/LocalPreferences.java
index 113e9d7..4bffc49 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/LocalPreferences.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/LocalPreferences.java
@@ -16,12 +16,19 @@
 
 package com.android.documentsui;
 
+import static com.android.documentsui.State.MODE_UNKNOWN;
+import static com.android.internal.util.Preconditions.checkArgument;
+
 import android.content.Context;
 import android.preference.PreferenceManager;
 
+import com.android.documentsui.State.ViewMode;
+import com.android.documentsui.model.RootInfo;
+
 public class LocalPreferences {
     private static final String KEY_ADVANCED_DEVICES = "advancedDevices";
     private static final String KEY_FILE_SIZE = "fileSize";
+    private static final String ROOT_VIEW_MODE_PREFIX = "rootViewMode-";
 
     public static boolean getDisplayAdvancedDevices(Context context) {
         boolean defaultAdvanced = context.getResources()
@@ -35,6 +42,12 @@
                 .getBoolean(KEY_FILE_SIZE, false);
     }
 
+    public static @ViewMode int getViewMode(
+            Context context, RootInfo root, @ViewMode int fallback) {
+        return PreferenceManager.getDefaultSharedPreferences(context)
+                .getInt(createKey(root), fallback);
+    }
+
     public static void setDisplayAdvancedDevices(Context context, boolean display) {
         PreferenceManager.getDefaultSharedPreferences(context).edit()
                 .putBoolean(KEY_ADVANCED_DEVICES, display).apply();
@@ -44,4 +57,14 @@
         PreferenceManager.getDefaultSharedPreferences(context).edit()
                 .putBoolean(KEY_FILE_SIZE, display).apply();
     }
+
+    public static void setViewMode(Context context, RootInfo root, @ViewMode int viewMode) {
+        checkArgument(viewMode != MODE_UNKNOWN);
+        PreferenceManager.getDefaultSharedPreferences(context).edit()
+                .putInt(createKey(root), viewMode).apply();
+    }
+
+    private static String createKey(RootInfo root) {
+        return ROOT_VIEW_MODE_PREFIX + root.authority + root.rootId;
+    }
 }
diff --git a/packages/DocumentsUI/src/com/android/documentsui/OperationDialogFragment.java b/packages/DocumentsUI/src/com/android/documentsui/OperationDialogFragment.java
new file mode 100644
index 0000000..85cc12b
--- /dev/null
+++ b/packages/DocumentsUI/src/com/android/documentsui/OperationDialogFragment.java
@@ -0,0 +1,132 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.documentsui;
+
+import static com.android.documentsui.services.FileOperationService.OpType;
+import static com.android.internal.util.Preconditions.checkArgument;
+
+import android.annotation.IntDef;
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.app.DialogFragment;
+import android.app.FragmentManager;
+import android.app.FragmentTransaction;
+import android.content.DialogInterface;
+import android.os.Bundle;
+import android.text.Html;
+
+import com.android.documentsui.model.DocumentInfo;
+import com.android.documentsui.model.DocumentStack;
+import com.android.documentsui.services.FileOperationService;
+import com.android.documentsui.services.FileOperations;
+import com.android.documentsui.services.Job;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Alert dialog for operation dialogs.
+ */
+public class OperationDialogFragment extends DialogFragment {
+
+    public static final int DIALOG_TYPE_UNKNOWN = 0;
+    public static final int DIALOG_TYPE_FAILURE = 1;
+    public static final int DIALOG_TYPE_CONVERTED = 2;
+
+    @IntDef(flag = true, value = {
+        DIALOG_TYPE_UNKNOWN,
+        DIALOG_TYPE_FAILURE,
+        DIALOG_TYPE_CONVERTED
+    })
+
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface DialogType {}
+
+    private static final String TAG = "OperationDialogFragment";
+
+    public static void show(FragmentManager fm, @DialogType int dialogType,
+            ArrayList<DocumentInfo> failedSrcList, DocumentStack dstStack,
+            @OpType int operationType) {
+        final Bundle args = new Bundle();
+        args.putInt(FileOperationService.EXTRA_DIALOG_TYPE, dialogType);
+        args.putInt(FileOperationService.EXTRA_OPERATION, operationType);
+        args.putParcelableArrayList(FileOperationService.EXTRA_SRC_LIST, failedSrcList);
+
+        final FragmentTransaction ft = fm.beginTransaction();
+        final OperationDialogFragment fragment = new OperationDialogFragment();
+        fragment.setArguments(args);
+
+        ft.add(fragment, TAG);
+        ft.commitAllowingStateLoss();
+    }
+
+    @Override
+    public Dialog onCreateDialog(Bundle inState) {
+        super.onCreate(inState);
+
+        final @DialogType int dialogType =
+              getArguments().getInt(FileOperationService.EXTRA_DIALOG_TYPE);
+        final @OpType int operationType =
+              getArguments().getInt(FileOperationService.EXTRA_OPERATION);
+        final ArrayList<DocumentInfo> srcList = getArguments().getParcelableArrayList(
+                FileOperationService.EXTRA_SRC_LIST);
+
+        final AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
+        String messageFormat;
+
+        switch (dialogType) {
+            case DIALOG_TYPE_CONVERTED:
+                messageFormat = getString(R.string.copy_converted_warning_content);
+                break;
+
+            case DIALOG_TYPE_FAILURE:
+                switch (operationType) {
+                    case FileOperationService.OPERATION_COPY:
+                        messageFormat = getString(R.string.copy_failure_alert_content);
+                        break;
+                    case FileOperationService.OPERATION_MOVE:
+                        messageFormat = getString(R.string.move_failure_alert_content);
+                        break;
+                    default:
+                        throw new UnsupportedOperationException();
+                }
+                break;
+
+            default:
+                throw new UnsupportedOperationException();
+        }
+
+        final StringBuilder list = new StringBuilder("<p>");
+        for (DocumentInfo documentInfo : srcList) {
+            list.append(String.format("&#8226; %s<br>", documentInfo.displayName));
+        }
+        list.append("</p>");
+        builder.setMessage(Html.fromHtml(String.format(messageFormat, list.toString())));
+        builder.setPositiveButton(
+                R.string.close,
+                new DialogInterface.OnClickListener() {
+                    @Override
+                    public void onClick(DialogInterface dialog, int id) {
+                        dialog.dismiss();
+                    }
+                });
+
+        return builder.create();
+    }
+}
diff --git a/packages/DocumentsUI/src/com/android/documentsui/RecentsProvider.java b/packages/DocumentsUI/src/com/android/documentsui/RecentsProvider.java
index 82eb732..92ffb93 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/RecentsProvider.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/RecentsProvider.java
@@ -85,6 +85,8 @@
         public static final String AUTHORITY = "authority";
         public static final String ROOT_ID = Root.COLUMN_ROOT_ID;
         public static final String DOCUMENT_ID = Document.COLUMN_DOCUMENT_ID;
+
+        @Deprecated  // mode is tracked in local preferences now...by root only
         public static final String MODE = "mode";
         public static final String SORT_ORDER = "sortOrder";
     }
diff --git a/packages/DocumentsUI/src/com/android/documentsui/State.java b/packages/DocumentsUI/src/com/android/documentsui/State.java
index 2f0224f..28f7432 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/State.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/State.java
@@ -16,6 +16,7 @@
 
 package com.android.documentsui;
 
+import android.annotation.IntDef;
 import android.content.Intent;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -26,18 +27,43 @@
 import com.android.documentsui.model.DurableUtils;
 import com.android.documentsui.model.RootInfo;
 
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.List;
 
 public class State implements android.os.Parcelable {
+
+    public static final int ACTION_OPEN = 1;
+    public static final int ACTION_CREATE = 2;
+    public static final int ACTION_GET_CONTENT = 3;
+    public static final int ACTION_OPEN_TREE = 4;
+    public static final int ACTION_MANAGE = 5;
+    public static final int ACTION_BROWSE = 6;
+    public static final int ACTION_PICK_COPY_DESTINATION = 8;
+
+    @IntDef(flag = true, value = {
+            MODE_UNKNOWN,
+            MODE_LIST,
+            MODE_GRID
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface ViewMode {}
+    public static final int MODE_UNKNOWN = 0;
+    public static final int MODE_LIST = 1;
+    public static final int MODE_GRID = 2;
+
+    public static final int SORT_ORDER_UNKNOWN = 0;
+    public static final int SORT_ORDER_DISPLAY_NAME = 1;
+    public static final int SORT_ORDER_LAST_MODIFIED = 2;
+    public static final int SORT_ORDER_SIZE = 3;
+
     public int action;
     public String[] acceptMimes;
 
-    /** Explicit user choice */
-    public int userMode = MODE_UNKNOWN;
-    /** Derived after loader */
-    public int derivedMode = MODE_GRID;
+    /** Derived from local preferences */
+    public @ViewMode int derivedMode = MODE_GRID;
 
     /** Explicit user choice */
     public int userSortOrder = SORT_ORDER_UNKNOWN;
@@ -58,6 +84,8 @@
 
     /** Current user navigation stack; empty implies recents. */
     public DocumentStack stack = new DocumentStack();
+    private boolean mStackTouched;
+
     /** Currently active search, overriding any stack. */
     public String currentSearch;
 
@@ -70,25 +98,6 @@
     /** Name of the package that started DocsUI */
     public List<String> excludedAuthorities = new ArrayList<>();
 
-    public static final int ACTION_OPEN = 1;
-    public static final int ACTION_CREATE = 2;
-    public static final int ACTION_GET_CONTENT = 3;
-    public static final int ACTION_OPEN_TREE = 4;
-    public static final int ACTION_MANAGE = 5;
-    public static final int ACTION_BROWSE = 6;
-    public static final int ACTION_PICK_COPY_DESTINATION = 8;
-
-    public static final int MODE_UNKNOWN = 0;
-    public static final int MODE_LIST = 1;
-    public static final int MODE_GRID = 2;
-
-    public static final int SORT_ORDER_UNKNOWN = 0;
-    public static final int SORT_ORDER_DISPLAY_NAME = 1;
-    public static final int SORT_ORDER_LAST_MODIFIED = 2;
-    public static final int SORT_ORDER_SIZE = 3;
-
-    private boolean mStackTouched;
-
     public void initAcceptMimes(Intent intent) {
         if (intent.hasExtra(Intent.EXTRA_MIME_TYPES)) {
             acceptMimes = intent.getStringArrayExtra(Intent.EXTRA_MIME_TYPES);
@@ -131,7 +140,6 @@
     @Override
     public void writeToParcel(Parcel out, int flags) {
         out.writeInt(action);
-        out.writeInt(userMode);
         out.writeStringArray(acceptMimes);
         out.writeInt(userSortOrder);
         out.writeInt(allowMultiple ? 1 : 0);
@@ -155,7 +163,6 @@
         public State createFromParcel(Parcel in) {
             final State state = new State();
             state.action = in.readInt();
-            state.userMode = in.readInt();
             state.acceptMimes = in.readStringArray();
             state.userSortOrder = in.readInt();
             state.allowMultiple = in.readInt() != 0;
diff --git a/packages/DocumentsUI/src/com/android/documentsui/dirlist/DirectoryFragment.java b/packages/DocumentsUI/src/com/android/documentsui/dirlist/DirectoryFragment.java
index 74fa8d0..f57aa4b 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/dirlist/DirectoryFragment.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/dirlist/DirectoryFragment.java
@@ -20,7 +20,6 @@
 import static com.android.documentsui.State.ACTION_MANAGE;
 import static com.android.documentsui.State.MODE_GRID;
 import static com.android.documentsui.State.MODE_LIST;
-import static com.android.documentsui.State.MODE_UNKNOWN;
 import static com.android.documentsui.State.SORT_ORDER_UNKNOWN;
 import static com.android.documentsui.model.DocumentInfo.getCursorInt;
 import static com.android.documentsui.model.DocumentInfo.getCursorString;
@@ -37,7 +36,6 @@
 import android.app.LoaderManager.LoaderCallbacks;
 import android.content.ClipData;
 import android.content.ContentResolver;
-import android.content.ContentValues;
 import android.content.Context;
 import android.content.Intent;
 import android.content.Loader;
@@ -91,9 +89,6 @@
 import com.android.documentsui.MimePredicate;
 import com.android.documentsui.R;
 import com.android.documentsui.RecentLoader;
-import com.android.documentsui.RecentsProvider;
-import com.android.documentsui.RecentsProvider.StateColumns;
-import com.android.documentsui.dirlist.RenameDocumentFragment;
 import com.android.documentsui.RootsCache;
 import com.android.documentsui.Shared;
 import com.android.documentsui.Snackbars;
@@ -155,7 +150,6 @@
     private int mType = TYPE_NORMAL;
     private String mStateKey;
 
-    private int mLastMode = MODE_UNKNOWN;
     private int mLastSortOrder = SORT_ORDER_UNKNOWN;
     private boolean mLastShowSize;
     private DocumentsAdapter mAdapter;
@@ -240,7 +234,7 @@
         final RootInfo root = getArguments().getParcelable(EXTRA_ROOT);
         final DocumentInfo doc = getArguments().getParcelable(EXTRA_DOC);
 
-        mIconHelper = new IconHelper(context, state.derivedMode);
+        mIconHelper = new IconHelper(context, MODE_GRID);
 
         mAdapter = new SectionBreakDocumentsAdapterWrapper(
                 this, new ModelBackedDocumentsAdapter(this, mIconHelper));
@@ -323,12 +317,6 @@
                 if (!isAdded()) return;
 
                 mModel.update(result);
-
-                // Push latest state up to UI
-                // TODO: if mode change was racing with us, don't overwrite it
-                if (result.mode != MODE_UNKNOWN) {
-                    state.derivedMode = result.mode;
-                }
                 state.derivedSortOrder = result.sortOrder;
 
                 updateDisplayState();
@@ -361,8 +349,6 @@
 
         // Kick off loader at least once
         getLoaderManager().restartLoader(LOADER_ID, null, mCallbacks);
-
-        updateDisplayState();
     }
 
     @Override
@@ -425,61 +411,25 @@
         state.dirState.put(mStateKey, container);
     }
 
-    @Override
-    public void onResume() {
-        super.onResume();
-        updateDisplayState();
-    }
-
     public void onDisplayStateChanged() {
         updateDisplayState();
     }
 
-    public void onUserSortOrderChanged() {
-        // Sort order change always triggers reload; we'll trigger state change
-        // on the flip side.
+    public void onSortOrderChanged() {
+        // Sort order is implemented as a sorting wrapper around directory
+        // results. So when sort order changes, we force a reload of the directory.
         getLoaderManager().restartLoader(LOADER_ID, null, mCallbacks);
     }
 
-    public void onUserModeChanged() {
-        final ContentResolver resolver = getActivity().getContentResolver();
-        final State state = getDisplayState();
-
-        final RootInfo root = getArguments().getParcelable(EXTRA_ROOT);
-        final DocumentInfo doc = getArguments().getParcelable(EXTRA_DOC);
-
-        if (root != null && doc != null) {
-            final Uri stateUri = RecentsProvider.buildState(
-                    root.authority, root.rootId, doc.documentId);
-            final ContentValues values = new ContentValues();
-            values.put(StateColumns.MODE, state.userMode);
-
-            new AsyncTask<Void, Void, Void>() {
-                @Override
-                protected Void doInBackground(Void... params) {
-                    resolver.insert(stateUri, values);
-                    return null;
-                }
-            }.execute();
-        }
-
-        // Mode change is just visual change; no need to kick loader, and
-        // deliver change event immediately.
-        state.derivedMode = state.userMode;
-        ((BaseActivity) getActivity()).onStateChanged();
-
+    public void onViewModeChanged() {
+        // Mode change is just visual change; no need to kick loader.
         updateDisplayState();
     }
 
     private void updateDisplayState() {
-        final State state = getDisplayState();
-
-        if (mLastMode == state.derivedMode && mLastShowSize == state.showSize) return;
-        mLastMode = state.derivedMode;
+        State state = getDisplayState();
         mLastShowSize = state.showSize;
-
         updateLayout(state.derivedMode);
-
         mRecView.setAdapter(mAdapter);
     }
 
@@ -506,7 +456,6 @@
                 }
                 layout = mListLayout;
                 break;
-            case MODE_UNKNOWN:
             default:
                 throw new IllegalArgumentException("Unsupported layout mode: " + mode);
         }
@@ -516,7 +465,7 @@
         // setting layout manager automatically invalidates existing ViewHolders.
         mRecView.setLayoutManager(layout);
         mSelectionManager.handleLayoutChanged();  // RecyclerView doesn't do this for us
-        mIconHelper.setMode(mode);
+        mIconHelper.setViewMode(mode);
     }
 
     private int calculateColumnCount() {
@@ -539,7 +488,6 @@
             case MODE_LIST:
                 return getResources().getDimensionPixelSize(
                         R.dimen.list_container_padding);
-            case MODE_UNKNOWN:
             default:
                 throw new IllegalArgumentException("Unsupported layout mode: " + mode);
         }
diff --git a/packages/DocumentsUI/src/com/android/documentsui/dirlist/IconHelper.java b/packages/DocumentsUI/src/com/android/documentsui/dirlist/IconHelper.java
index 0314077..c97c446 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/dirlist/IconHelper.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/dirlist/IconHelper.java
@@ -42,6 +42,8 @@
 import com.android.documentsui.ProviderExecutor;
 import com.android.documentsui.ProviderExecutor.Preemptable;
 import com.android.documentsui.R;
+import com.android.documentsui.State;
+import com.android.documentsui.State.ViewMode;
 import com.android.documentsui.ThumbnailCache;
 
 /**
@@ -64,7 +66,7 @@
      */
     public IconHelper(Context context, int mode) {
         mContext = context;
-        setMode(mode);
+        setViewMode(mode);
         mCache = DocumentsApplication.getThumbnailsCache(context, mThumbSize);
     }
 
@@ -82,7 +84,7 @@
      * Sets the current display mode.  This affects the thumbnail sizes that are loaded.
      * @param mode See {@link State.MODE_LIST} and {@link State.MODE_GRID}.
      */
-    public void setMode(int mode) {
+    public void setViewMode(@ViewMode int mode) {
         // TODO: Instead of exposing setMode, make the mode final, and make separate instances for
         // grid/list.
         int thumbSize;
@@ -94,7 +96,6 @@
                 thumbSize = mContext.getResources().getDimensionPixelSize(
                         R.dimen.list_item_thumbnail_size);
                 break;
-            case MODE_UNKNOWN:
             default:
                 throw new IllegalArgumentException("Unsupported layout mode: " + mode);
         }
diff --git a/packages/DocumentsUI/src/com/android/documentsui/dirlist/ModelBackedDocumentsAdapter.java b/packages/DocumentsUI/src/com/android/documentsui/dirlist/ModelBackedDocumentsAdapter.java
index 68756a3..880da9c0 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/dirlist/ModelBackedDocumentsAdapter.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/dirlist/ModelBackedDocumentsAdapter.java
@@ -19,7 +19,6 @@
 import static com.android.documentsui.Shared.DEBUG;
 import static com.android.documentsui.State.MODE_GRID;
 import static com.android.documentsui.State.MODE_LIST;
-import static com.android.documentsui.State.MODE_UNKNOWN;
 import static com.android.documentsui.model.DocumentInfo.getCursorInt;
 import static com.android.documentsui.model.DocumentInfo.getCursorString;
 
@@ -92,7 +91,6 @@
             case MODE_LIST:
                 holder = new ListDocumentHolder(mEnv.getContext(), parent, mIconHelper);
                 break;
-            case MODE_UNKNOWN:
             default:
                 throw new IllegalStateException("Unsupported layout mode.");
         }
diff --git a/packages/DocumentsUI/src/com/android/documentsui/services/CopyJob.java b/packages/DocumentsUI/src/com/android/documentsui/services/CopyJob.java
index 1d8eba5..e5e66f8 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/services/CopyJob.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/services/CopyJob.java
@@ -21,17 +21,23 @@
 import static android.provider.DocumentsContract.buildDocumentUri;
 import static android.provider.DocumentsContract.getDocumentId;
 import static android.provider.DocumentsContract.isChildDocument;
+import static com.android.documentsui.OperationDialogFragment.DIALOG_TYPE_CONVERTED;
 import static com.android.documentsui.Shared.DEBUG;
 import static com.android.documentsui.model.DocumentInfo.getCursorLong;
 import static com.android.documentsui.model.DocumentInfo.getCursorString;
+import static com.android.documentsui.services.FileOperationService.EXTRA_DIALOG_TYPE;
+import static com.android.documentsui.services.FileOperationService.EXTRA_OPERATION;
+import static com.android.documentsui.services.FileOperationService.EXTRA_SRC_LIST;
 import static com.android.documentsui.services.FileOperationService.OPERATION_COPY;
 import static com.google.common.base.Preconditions.checkArgument;
 
 import android.annotation.StringRes;
 import android.app.Notification;
 import android.app.Notification.Builder;
+import android.app.PendingIntent;
 import android.content.ContentProviderClient;
 import android.content.Context;
+import android.content.Intent;
 import android.content.res.AssetFileDescriptor;
 import android.database.Cursor;
 import android.net.Uri;
@@ -56,12 +62,14 @@
 import java.io.InputStream;
 import java.io.OutputStream;
 import java.text.NumberFormat;
+import java.util.ArrayList;
 import java.util.List;
 
 class CopyJob extends Job {
     private static final String TAG = "CopyJob";
     private static final int PROGRESS_INTERVAL_MILLIS = 1000;
     final List<DocumentInfo> mSrcs;
+    final ArrayList<DocumentInfo> convertedFiles = new ArrayList<>();
 
     private long mStartTime = -1;
     private long mBatchSize;
@@ -176,6 +184,29 @@
     }
 
     @Override
+    Notification getWarningNotification() {
+        final Intent navigateIntent = buildNavigateIntent();
+        navigateIntent.putExtra(EXTRA_DIALOG_TYPE, DIALOG_TYPE_CONVERTED);
+        navigateIntent.putExtra(EXTRA_OPERATION, operationType);
+
+        navigateIntent.putParcelableArrayListExtra(EXTRA_SRC_LIST, convertedFiles);
+
+        // TODO: Consider adding a dialog on tapping the notification with a list of
+        // converted files.
+        final Notification.Builder warningBuilder = new Notification.Builder(service)
+                .setContentTitle(service.getResources().getString(
+                        R.string.notification_copy_files_converted_title))
+                .setContentText(service.getString(
+                        R.string.notification_touch_for_details))
+                .setContentIntent(PendingIntent.getActivity(appContext, 0, navigateIntent,
+                        PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_ONE_SHOT))
+                .setCategory(Notification.CATEGORY_ERROR)
+                .setSmallIcon(R.drawable.ic_menu_copy)
+                .setAutoCancel(true);
+        return warningBuilder.build();
+    }
+
+    @Override
     void start() throws RemoteException {
         mStartTime = elapsedRealtime();
 
@@ -203,6 +234,11 @@
         }
     }
 
+    @Override
+    boolean hasWarnings() {
+        return !convertedFiles.isEmpty();
+    }
+
     /**
      * Logs progress on the current copy operation. Displays/Updates the progress notification.
      *
@@ -425,6 +461,10 @@
             }
         }
 
+        if (src.isVirtualDocument() && success) {
+           convertedFiles.add(src);
+        }
+
         return success;
     }
 
diff --git a/packages/DocumentsUI/src/com/android/documentsui/services/DeleteJob.java b/packages/DocumentsUI/src/com/android/documentsui/services/DeleteJob.java
index eef696a..24eb987 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/services/DeleteJob.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/services/DeleteJob.java
@@ -75,6 +75,11 @@
     }
 
     @Override
+    Notification getWarningNotification() {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
     void start() throws RemoteException {
         for (DocumentInfo doc : mSrcs) {
             if (DEBUG) Log.d(TAG, "Deleting document @ " + doc.derivedUri);
diff --git a/packages/DocumentsUI/src/com/android/documentsui/services/FileOperationService.java b/packages/DocumentsUI/src/com/android/documentsui/services/FileOperationService.java
index 5d74cdc..3a025c2 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/services/FileOperationService.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/services/FileOperationService.java
@@ -55,6 +55,7 @@
     private static final int POOL_SIZE = 2;  // "pool size", not *max* "pool size".
     private static final int NOTIFICATION_ID_PROGRESS = 0;
     private static final int NOTIFICATION_ID_FAILURE = 1;
+    private static final int NOTIFICATION_ID_WARNING = 2;
 
     public static final String TAG = "FileOperationService";
 
@@ -63,7 +64,7 @@
     public static final String EXTRA_OPERATION = "com.android.documentsui.OPERATION";
     public static final String EXTRA_CANCEL = "com.android.documentsui.CANCEL";
     public static final String EXTRA_SRC_LIST = "com.android.documentsui.SRC_LIST";
-    public static final String EXTRA_FAILURE = "com.android.documentsui.FAILURE";
+    public static final String EXTRA_DIALOG_TYPE = "com.android.documentsui.DIALOG_TYPE";
 
     // This extra is used only for moving and deleting. Currently it's not the case,
     // but in the future those files may be from multiple different parents. In
@@ -306,6 +307,18 @@
         // Dismiss the ongoing copy notification when the copy is done.
         mNotificationManager.cancel(job.id, NOTIFICATION_ID_PROGRESS);
 
+        if (job.hasFailures()) {
+            Log.e(TAG, "Job failed on files: " + job.failedFiles.size() + ".");
+            mNotificationManager.notify(
+                job.id, NOTIFICATION_ID_FAILURE, job.getFailureNotification());
+        }
+
+        if (job.hasWarnings()) {
+            if (DEBUG) Log.d(TAG, "Job finished with warnings.");
+            mNotificationManager.notify(
+                    job.id, NOTIFICATION_ID_WARNING, job.getWarningNotification());
+        }
+
         synchronized (mRunning) {
             deleteJob(job);
         }
@@ -318,15 +331,6 @@
                 job.id, NOTIFICATION_ID_PROGRESS, job.getProgressNotification());
     }
 
-    @Override
-    public void onFailed(Job job) {
-        if (DEBUG) Log.d(TAG, "onFailed: " + job.id);
-        checkArgument(job.failed());
-        Log.e(TAG, "Job failed on files: " + job.failedFiles.size() + ".");
-        mNotificationManager.notify(job.id, NOTIFICATION_ID_FAILURE, job.getFailureNotification());
-        onFinished(job);  // Failed jobs don't call finished, so we do.
-    }
-
     private static final class JobRecord {
         private final Job job;
         private final ScheduledFuture<?> future;
diff --git a/packages/DocumentsUI/src/com/android/documentsui/services/Job.java b/packages/DocumentsUI/src/com/android/documentsui/services/Job.java
index 9534d6c..7b8011a 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/services/Job.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/services/Job.java
@@ -18,11 +18,10 @@
 
 import static com.android.documentsui.DocumentsApplication.acquireUnstableProviderOrThrow;
 import static com.android.documentsui.services.FileOperationService.EXTRA_CANCEL;
-import static com.android.documentsui.services.FileOperationService.EXTRA_FAILURE;
+import static com.android.documentsui.services.FileOperationService.EXTRA_DIALOG_TYPE;
 import static com.android.documentsui.services.FileOperationService.EXTRA_JOB_ID;
 import static com.android.documentsui.services.FileOperationService.EXTRA_OPERATION;
 import static com.android.documentsui.services.FileOperationService.EXTRA_SRC_LIST;
-import static com.android.documentsui.services.FileOperationService.FAILURE_COPY;
 import static com.android.documentsui.services.FileOperationService.OPERATION_UNKNOWN;
 import static com.android.internal.util.Preconditions.checkArgument;
 import static com.android.internal.util.Preconditions.checkNotNull;
@@ -42,6 +41,7 @@
 import android.util.Log;
 
 import com.android.documentsui.FilesActivity;
+import com.android.documentsui.OperationDialogFragment;
 import com.android.documentsui.R;
 import com.android.documentsui.Shared;
 import com.android.documentsui.model.DocumentInfo;
@@ -57,8 +57,7 @@
  * A mashup of work item and ui progress update factory. Used by {@link FileOperationService}
  * to do work and show progress relating to this work.
  */
-abstract class Job implements Runnable {
-
+abstract public class Job implements Runnable {
     private static final String TAG = "Job";
     final Context service;
     final Context appContext;
@@ -114,13 +113,9 @@
             // to resolve business in an orderly fashion. That'll
             // ensure the service is shut down and notifications
             // shown/closed.
-            listener.onFailed(this);
+            Log.e(TAG, "Operation failed due to an exception.", e);
         } finally {
-            if (failed()) {
-                listener.onFailed(this);
-            } else {
-                listener.onFinished(this);
-            }
+            listener.onFinished(this);
         }
     }
 
@@ -131,6 +126,8 @@
     // abstract Notification getProgressNotification(long bytesCopied);
     abstract Notification getFailureNotification();
 
+    abstract Notification getWarningNotification();
+
     ContentProviderClient getClient(DocumentInfo doc) throws RemoteException {
         ContentProviderClient client = mClients.get(doc.authority);
         if (client == null) {
@@ -167,10 +164,14 @@
         failedFiles.add(file);
     }
 
-    final boolean failed() {
+    final boolean hasFailures() {
         return !failedFiles.isEmpty();
     }
 
+    boolean hasWarnings() {
+        return false;
+    }
+
     final boolean deleteDocument(DocumentInfo doc) {
         try {
             DocumentsContract.deleteDocument(getClient(doc), doc.derivedUri);
@@ -190,7 +191,7 @@
 
     Notification getFailureNotification(@PluralsRes int titleId, @DrawableRes int icon) {
         final Intent navigateIntent = buildNavigateIntent();
-        navigateIntent.putExtra(EXTRA_FAILURE, FAILURE_COPY);
+        navigateIntent.putExtra(EXTRA_DIALOG_TYPE, OperationDialogFragment.DIALOG_TYPE_FAILURE);
         navigateIntent.putExtra(EXTRA_OPERATION, operationType);
 
         navigateIntent.putParcelableArrayListExtra(EXTRA_SRC_LIST, failedFiles);
@@ -291,7 +292,6 @@
      */
     interface Listener {
         void onStart(Job job);
-        void onFailed(Job job);
         void onFinished(Job job);
         void onProgress(CopyJob job);
     }
diff --git a/packages/DocumentsUI/tests/src/com/android/documentsui/services/TestJob.java b/packages/DocumentsUI/tests/src/com/android/documentsui/services/TestJob.java
index 9c58780..a1c6dab 100644
--- a/packages/DocumentsUI/tests/src/com/android/documentsui/services/TestJob.java
+++ b/packages/DocumentsUI/tests/src/com/android/documentsui/services/TestJob.java
@@ -63,6 +63,11 @@
     }
 
     @Override
+    Notification getWarningNotification() {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
     Builder createProgressBuilder() {
         // the "copy" stuff was just convenient and available :)
         return super.createProgressBuilder(
diff --git a/packages/DocumentsUI/tests/src/com/android/documentsui/services/TestJobListener.java b/packages/DocumentsUI/tests/src/com/android/documentsui/services/TestJobListener.java
index 0110197..46b093d 100644
--- a/packages/DocumentsUI/tests/src/com/android/documentsui/services/TestJobListener.java
+++ b/packages/DocumentsUI/tests/src/com/android/documentsui/services/TestJobListener.java
@@ -33,7 +33,6 @@
     private final CountDownLatch latch = new CountDownLatch(1);
     private final List<Job> progress = new ArrayList<>();
     @Nullable private Job started;
-    @Nullable private Job failed;
     @Nullable private Job finished;
 
     @Override
@@ -42,11 +41,6 @@
     }
 
     @Override
-    public void onFailed(Job job) {
-        failed = job;
-    }
-
-    @Override
     public void onFinished(Job job) {
         this.finished = job;
         latch.countDown();
@@ -70,28 +64,28 @@
     }
 
     public void assertFailed() {
-        if (failed == null) {
+        if (finished == null || !finished.hasFailures()) {
             fail("Job didn't fail. onFailed never called.");
         }
     }
 
     public void assertFilesFailed(ArrayList<String> names) {
-        if (failed == null) {
+        if (finished == null || !finished.hasFailures()) {
             fail("Can't test failed documetns. Job didn't fail.");
         }
 
-        assertEquals(failed.failedFiles.size(), names.size());
+        assertEquals(finished.failedFiles.size(), names.size());
         for (String name : names) {
             assertFileFailed(name);
         }
     }
 
     public void assertFileFailed(String name) {
-        if (failed == null) {
+        if (finished == null || !finished.hasFailures()) {
             fail("Can't test failed documetns. Job didn't fail.");
         }
 
-        for (DocumentInfo failed : failed.failedFiles) {
+        for (DocumentInfo failed : finished.failedFiles) {
             if (name.equals(failed.displayName)) {
                 return;
             }
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardSecurityContainer.java b/packages/Keyguard/src/com/android/keyguard/KeyguardSecurityContainer.java
index c7d17dc..409f6a7 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardSecurityContainer.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardSecurityContainer.java
@@ -18,7 +18,6 @@
 import android.app.Activity;
 import android.app.AlertDialog;
 import android.app.admin.DevicePolicyManager;
-import android.auditing.SecurityLog;
 import android.content.Context;
 import android.os.UserHandle;
 import android.util.AttributeSet;
@@ -424,11 +423,6 @@
         }
 
         public void reportUnlockAttempt(int userId, boolean success, int timeoutMs) {
-            if (SecurityLog.isLoggingEnabled()) {
-                SecurityLog.writeEvent(SecurityLog.TAG_DEVICE_UNLOCK_ATTEMPT,
-                        (success ? 1 : 0),
-                        mCurrentSecuritySelection.name());
-            }
             KeyguardUpdateMonitor monitor = KeyguardUpdateMonitor.getInstance(mContext);
             if (success) {
                 monitor.clearFailedUnlockAttempts();
diff --git a/packages/MtpDocumentsProvider/jni/com_android_mtp_AppFuse.cpp b/packages/MtpDocumentsProvider/jni/com_android_mtp_AppFuse.cpp
index 7b57d89..2b44d51 100644
--- a/packages/MtpDocumentsProvider/jni/com_android_mtp_AppFuse.cpp
+++ b/packages/MtpDocumentsProvider/jni/com_android_mtp_AppFuse.cpp
@@ -50,7 +50,8 @@
 
 static jclass app_fuse_class;
 static jmethodID app_fuse_get_file_size;
-static jmethodID app_fuse_get_object_bytes;
+static jmethodID app_fuse_read_object_bytes;
+static jfieldID app_fuse_buffer;
 
 // NOTE:
 // FuseRequest and FuseResponse shares the same buffer to save memory usage, so the handlers must
@@ -282,7 +283,7 @@
         out->prepare_buffer(0);
         const int64_t result = get_object_bytes(it->second, offset, size, out->data());
         if (result < 0) {
-            return -EIO;
+            return result;
         }
         out->set_size(result);
         return 0;
@@ -332,20 +333,24 @@
             uint64_t offset,
             uint32_t size,
             void* buf) {
-        ScopedLocalRef<jbyteArray> array(env_, static_cast<jbyteArray>(env_->CallObjectMethod(
+        const jlong read_size = env_->CallLongMethod(
                 self_,
-                app_fuse_get_object_bytes,
-                inode,
-                offset,
-                size)));
+                app_fuse_read_object_bytes,
+                static_cast<jint>(inode),
+                static_cast<jlong>(offset),
+                static_cast<jlong>(size));
+        if (read_size <= 0) {
+            return read_size;
+        }
+        ScopedLocalRef<jbyteArray> array(
+                env_, static_cast<jbyteArray>(env_->GetObjectField(self_, app_fuse_buffer)));
         if (array.get() == nullptr) {
-            return -1;
+            return -EFAULT;
         }
         ScopedByteArrayRO bytes(env_, array.get());
         if (bytes.get() == nullptr) {
-            return -1;
+            return -ENOMEM;
         }
-        const uint32_t read_size = std::min(static_cast<uint32_t>(bytes.size()), size);
         memcpy(buf, bytes.get(), read_size);
         return read_size;
     }
@@ -451,10 +456,16 @@
         return -1;
     }
 
-    app_fuse_get_object_bytes = env->GetMethodID(
-            app_fuse_class, "getObjectBytes", "(IJI)[B");
-    if (app_fuse_get_object_bytes == nullptr) {
-        ALOGE("Can't find getObjectBytes");
+    app_fuse_read_object_bytes = env->GetMethodID(
+            app_fuse_class, "readObjectBytes", "(IJJ)J");
+    if (app_fuse_read_object_bytes == nullptr) {
+        ALOGE("Can't find readObjectBytes");
+        return -1;
+    }
+
+    app_fuse_buffer = env->GetFieldID(app_fuse_class, "mBuffer", "[B");
+    if (app_fuse_buffer == nullptr) {
+        ALOGE("Can't find mBuffer");
         return -1;
     }
 
diff --git a/packages/MtpDocumentsProvider/src/com/android/mtp/AppFuse.java b/packages/MtpDocumentsProvider/src/com/android/mtp/AppFuse.java
index 1300c47..6a98405 100644
--- a/packages/MtpDocumentsProvider/src/com/android/mtp/AppFuse.java
+++ b/packages/MtpDocumentsProvider/src/com/android/mtp/AppFuse.java
@@ -16,9 +16,11 @@
 
 package com.android.mtp;
 
+import android.annotation.WorkerThread;
 import android.os.ParcelFileDescriptor;
 import android.os.Process;
 import android.os.storage.StorageManager;
+import android.system.OsConstants;
 import android.util.Log;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.Preconditions;
@@ -40,6 +42,13 @@
 
     private final String mName;
     private final Callback mCallback;
+
+    /**
+     * Buffer for read bytes request.
+     * Don't use the buffer from the out of AppFuseMessageThread.
+     */
+    private byte[] mBuffer = new byte[MAX_READ];
+
     private Thread mMessageThread;
     private ParcelFileDescriptor mDeviceFd;
 
@@ -82,28 +91,48 @@
     }
 
     static interface Callback {
+        /**
+         * Returns file size for the given inode.
+         * @param inode
+         * @return File size. Must not be negative.
+         * @throws FileNotFoundException
+         */
         long getFileSize(int inode) throws FileNotFoundException;
-        byte[] getObjectBytes(int inode, long offset, int size) throws IOException;
+
+        /**
+         * Returns flie bytes for the give inode.
+         * @param inode
+         * @param offset Offset for file bytes.
+         * @param size Size for file bytes.
+         * @param bytes Buffer to store file bytes.
+         * @return Number of read bytes. Must not be negative.
+         * @throws IOException
+         */
+        long readObjectBytes(int inode, long offset, long size, byte[] bytes) throws IOException;
     }
 
     @UsedByNative("com_android_mtp_AppFuse.cpp")
+    @WorkerThread
     private long getFileSize(int inode) {
         try {
             return mCallback.getFileSize(inode);
-        } catch (IOException e) {
-            return -1;
+        } catch (FileNotFoundException e) {
+            return -OsConstants.ENOENT;
         }
     }
 
     @UsedByNative("com_android_mtp_AppFuse.cpp")
-    private byte[] getObjectBytes(int inode, long offset, int size) {
+    @WorkerThread
+    private long readObjectBytes(int inode, long offset, long size) {
         if (offset < 0 || size < 0 || size > MAX_READ) {
-            return null;
+            return -OsConstants.EINVAL;
         }
         try {
-            return mCallback.getObjectBytes(inode, offset, size);
+            // It's OK to share the same mBuffer among requests because the requests are processed
+            // by AppFuseMessageThread sequentially.
+            return mCallback.readObjectBytes(inode, offset, size, mBuffer);
         } catch (IOException e) {
-            return null;
+            return -OsConstants.EIO;
         }
     }
 
diff --git a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDocumentsProvider.java b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDocumentsProvider.java
index 3ac1b3d..6b5f16a 100644
--- a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDocumentsProvider.java
+++ b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDocumentsProvider.java
@@ -16,8 +16,6 @@
 
 package com.android.mtp;
 
-import static com.android.internal.util.Preconditions.checkArgument;
-
 import android.content.ContentResolver;
 import android.content.res.AssetFileDescriptor;
 import android.content.res.Resources;
@@ -40,7 +38,6 @@
 
 import java.io.FileNotFoundException;
 import java.io.IOException;
-import java.util.Arrays;
 import java.util.HashMap;
 import java.util.Map;
 
@@ -391,16 +388,12 @@
     }
 
     private class AppFuseCallback implements AppFuse.Callback {
-        final byte[] mBytes = new byte[AppFuse.MAX_READ];
-
         @Override
-        public byte[] getObjectBytes(int inode, long offset, int size) throws IOException {
+        public long readObjectBytes(
+                int inode, long offset, long size, byte[] buffer) throws IOException {
             final Identifier identifier = mDatabase.createIdentifier(Integer.toString(inode));
-            final long readSize = mMtpManager.getPartialObject(
-                    identifier.mDeviceId, identifier.mObjectHandle, offset, size, mBytes);
-            // TODO: Change signature so that getObjectBytes can return read size without copying
-            // bytes.
-            return Arrays.copyOf(mBytes, (int) readSize);
+            return mMtpManager.getPartialObject(
+                    identifier.mDeviceId, identifier.mObjectHandle, offset, size, buffer);
         }
 
         @Override
diff --git a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/AppFuseTest.java b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/AppFuseTest.java
index 6354880..c0973bd 100644
--- a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/AppFuseTest.java
+++ b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/AppFuseTest.java
@@ -77,30 +77,35 @@
 
     public void testReadFile() throws IOException {
         final StorageManager storageManager = getContext().getSystemService(StorageManager.class);
-        final int INODE = 10;
-        final byte[] BYTES = new byte[] { 'a', 'b', 'c', 'd', 'e' };
+        final int fileInode = 10;
+        final byte[] fileBytes = new byte[] { 'a', 'b', 'c', 'd', 'e' };
         final AppFuse appFuse = new AppFuse(
                 "test",
                 new TestCallback() {
                     @Override
                     public long getFileSize(int inode) throws FileNotFoundException {
-                        if (inode == INODE) {
-                            return BYTES.length;
+                        if (inode == fileInode) {
+                            return fileBytes.length;
                         }
                         return super.getFileSize(inode);
                     }
 
                     @Override
-                    public byte[] getObjectBytes(int inode, long offset, int size)
+                    public long readObjectBytes(int inode, long offset, long size, byte[] bytes)
                             throws IOException {
-                        if (inode == INODE) {
-                            return Arrays.copyOfRange(BYTES, (int) offset, (int) offset + size);
+                        if (inode == fileInode) {
+                            int i = 0;
+                            while (i < size && i + offset < fileBytes.length)  {
+                                bytes[i] = fileBytes[(int) (i + offset)];
+                                i++;
+                            }
+                            return i;
                         }
-                        return super.getObjectBytes(inode, offset, size);
+                        return super.readObjectBytes(inode, offset, size, bytes);
                     }
                 });
         appFuse.mount(storageManager);
-        final ParcelFileDescriptor fd = appFuse.openFile(INODE);
+        final ParcelFileDescriptor fd = appFuse.openFile(fileInode);
         try (final ParcelFileDescriptor.AutoCloseInputStream stream =
                 new ParcelFileDescriptor.AutoCloseInputStream(fd)) {
             final byte[] buffer = new byte[1024];
@@ -117,7 +122,7 @@
         }
 
         @Override
-        public byte[] getObjectBytes(int inode, long offset, int size)
+        public long readObjectBytes(int inode, long offset, long size, byte[] bytes)
                 throws IOException {
             throw new IOException();
         }
diff --git a/packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtils.java b/packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtils.java
index d3c1364..210682f 100644
--- a/packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtils.java
@@ -21,6 +21,7 @@
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
+import android.content.pm.ApplicationInfo;
 import android.content.pm.IPackageManager;
 import android.content.pm.UserInfo;
 import android.graphics.Color;
@@ -178,7 +179,7 @@
                     if (userInfo.isManagedProfile()) {
                         // If userInfo.id is a managed profile, we also need to look at
                         // the policies set on the parent.
-                        DevicePolicyManager parentDpm = dpm.getParentProfileInstance(admin);
+                        DevicePolicyManager parentDpm = dpm.getParentProfileInstance(userInfo);
                         if ((parentDpm.getKeyguardDisabledFeatures(admin, userInfo.id)
                                 & keyguardFeatures) != 0) {
                             if (enforcedAdmin == null) {
@@ -227,6 +228,26 @@
     }
 
     /**
+     * Check if an application is suspended.
+     *
+     * @return EnforcedAdmin Object containing the enforced admin component and admin user details,
+     * or {@code null} if the application is not suspended.
+     */
+    public static EnforcedAdmin checkIfApplicationIsSuspended(Context context, String packageName,
+            int userId) {
+        IPackageManager ipm = AppGlobals.getPackageManager();
+        try {
+            ApplicationInfo ai = ipm.getApplicationInfo(packageName, 0, userId);
+            if (ai != null && ((ai.flags & ApplicationInfo.FLAG_SUSPENDED) != 0)) {
+                return getProfileOrDeviceOwnerOnCallingUser(context);
+            }
+        } catch (RemoteException e) {
+            // Nothing to do
+        }
+        return null;
+    }
+
+    /**
      * Check if account management for a specific type of account is disabled by admin.
      * Only a profile or device owner can disable account management. So, we check if account
      * management is disabled and return profile or device owner on the calling user.
@@ -375,7 +396,7 @@
                     if (userInfo.isManagedProfile()) {
                         // If userInfo.id is a managed profile, we also need to look at
                         // the policies set on the parent.
-                        DevicePolicyManager parentDpm = dpm.getParentProfileInstance(admin);
+                        DevicePolicyManager parentDpm = dpm.getParentProfileInstance(userInfo);
                         if (parentDpm.getMaximumTimeToLock(admin, userInfo.id) > 0) {
                             if (enforcedAdmin == null) {
                                 enforcedAdmin = new EnforcedAdmin(admin, userInfo.id);
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
index 2ee4b12..5b8ed28 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
@@ -37,6 +37,7 @@
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.os.UserHandle;
+import android.support.annotation.NonNull;
 import android.text.Spannable;
 import android.text.SpannableString;
 import android.text.TextUtils;
@@ -104,7 +105,8 @@
     private static final int PSK_WPA2 = 2;
     private static final int PSK_WPA_WPA2 = 3;
 
-    private static final int VISIBILITY_OUTDATED_AGE_IN_MILLI = 20000;
+    public static final int SIGNAL_LEVELS = 4;
+
     private final Context mContext;
 
     private String ssid;
@@ -167,7 +169,7 @@
     }
 
     @Override
-    public int compareTo(AccessPoint other) {
+    public int compareTo(@NonNull AccessPoint other) {
         // Active one goes first.
         if (isActive() && !other.isActive()) return -1;
         if (!isActive() && other.isActive()) return 1;
@@ -182,8 +184,9 @@
         if (networkId == WifiConfiguration.INVALID_NETWORK_ID
                 && other.networkId != WifiConfiguration.INVALID_NETWORK_ID) return 1;
 
-        // Sort by signal strength.
-        int difference = WifiManager.compareSignalLevel(other.mRssi, mRssi);
+        // Sort by signal strength, bucketed by level
+        int difference = WifiManager.calculateSignalLevel(other.mRssi, SIGNAL_LEVELS)
+                - WifiManager.calculateSignalLevel(mRssi, SIGNAL_LEVELS);
         if (difference != 0) {
             return difference;
         }
@@ -260,7 +263,7 @@
         if (mRssi == Integer.MAX_VALUE) {
             return -1;
         }
-        return WifiManager.calculateSignalLevel(mRssi, 4);
+        return WifiManager.calculateSignalLevel(mRssi, SIGNAL_LEVELS);
     }
 
     public int getRssi() {
diff --git a/packages/SystemUI/Android.mk b/packages/SystemUI/Android.mk
index e4d0fec..88313bb 100644
--- a/packages/SystemUI/Android.mk
+++ b/packages/SystemUI/Android.mk
@@ -12,6 +12,7 @@
     android-support-v7-preference \
     android-support-v7-appcompat \
     android-support-v14-preference \
+    android-support-v17-leanback \
     framework-protos
 
 LOCAL_JAVA_LIBRARIES := telephony-common
@@ -30,10 +31,12 @@
     frameworks/support/v7/preference/res \
     frameworks/support/v14/preference/res \
     frameworks/support/v7/appcompat/res \
-    frameworks/support/v7/recyclerview/res
+    frameworks/support/v7/recyclerview/res \
+    frameworks/support/v17/leanback/res
 
 LOCAL_AAPT_FLAGS := --auto-add-overlay \
-    --extra-packages com.android.keyguard:android.support.v7.recyclerview:android.support.v7.preference:android.support.v14.preference:android.support.v7.appcompat
+    --extra-packages com.android.keyguard:android.support.v7.recyclerview:android.support.v7.preference:android.support.v14.preference:android.support.v7.appcompat \
+    --extra-packages android.support.v17.leanback
 
 ifneq ($(SYSTEM_UI_INCREMENTAL_BUILDS),)
     LOCAL_PROGUARD_ENABLED := disabled
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index 21abb90..2713fb5 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -236,6 +236,20 @@
             </intent-filter>
         </activity>
 
+        <activity android:name=".recents.tv.RecentsTvActivity"
+                  android:label="@string/accessibility_desc_recent_apps"
+                  android:exported="false"
+                  android:launchMode="singleInstance"
+                  android:excludeFromRecents="true"
+                  android:stateNotNeeded="true"
+                  android:resumeWhilePausing="true"
+                  android:screenOrientation="behind"
+                  android:theme="@style/RecentsTheme.Wallpaper">
+            <intent-filter>
+                <action android:name="com.android.systemui.recents.TOGGLE_RECENTS" />
+            </intent-filter>
+        </activity>
+
         <!-- Callback for dismissing screenshot notification after a share target is picked -->
         <receiver android:name=".screenshot.GlobalScreenshot$TargetChosenReceiver"
                   android:process=":screenshot"
diff --git a/packages/SystemUI/proguard.flags b/packages/SystemUI/proguard.flags
index 2ea8c9c..d0c2d29 100644
--- a/packages/SystemUI/proguard.flags
+++ b/packages/SystemUI/proguard.flags
@@ -35,3 +35,4 @@
 }
 
 -keep class ** extends android.support.v14.preference.PreferenceFragment
+-keep class com.android.systemui.tuner.*
diff --git a/packages/SystemUI/res/drawable/clipboard_empty.xml b/packages/SystemUI/res/drawable/clipboard_empty.xml
new file mode 100644
index 0000000..14a5ad2
--- /dev/null
+++ b/packages/SystemUI/res/drawable/clipboard_empty.xml
@@ -0,0 +1,25 @@
+<!--
+    Copyright (C) 2016 The Android Open Source Project
+
+    Licensed under the Apache License, Version 2.0 (the License);
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an AS IS BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24.0dp"
+        android:height="24.0dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FFFFFFFF"
+        android:pathData="M19.0,2.0l-4.18,0.0C14.0,0.84 13.3,0.0 12.0,0.0c-1.3,0.0 -2.0,0.84 -2.82,2.0L5.0,2.0c-1.1,0.0 -2.0,0.9 -2.0,2.0l0.0,16.0c0.0,1.0 0.9,2.0 2.0,2.0l14.0,0.0c1.1,0.0 2.0,-0.9 2.0,-2.0L21.0,4.0c0.0,-1.1 -0.9,-2.0 -2.0,-2.0zm-7.0,0.0c0.55,0.0 1.0,0.45 1.0,1.0s-0.45,1.0 -1.0,1.0 -1.0,-0.45 -1.0,-1.0 0.45,-1.0 1.0,-1.0zm7.0,18.0L5.0,20.0L5.0,4.0l2.0,0.0l0.0,3.0l10.0,0.0L17.0,4.0l2.0,0.0l0.0,16.0z" />
+</vector>
+
diff --git a/packages/SystemUI/res/drawable/clipboard_full.xml b/packages/SystemUI/res/drawable/clipboard_full.xml
new file mode 100644
index 0000000..2d46870
--- /dev/null
+++ b/packages/SystemUI/res/drawable/clipboard_full.xml
@@ -0,0 +1,25 @@
+<!--
+    Copyright (C) 2016 The Android Open Source Project
+
+    Licensed under the Apache License, Version 2.0 (the License);
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an AS IS BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24.0dp"
+    android:height="24.0dp"
+    android:viewportWidth="24.0"
+    android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FFFFFFFF"
+        android:pathData="M19.0,2.0l-4.18,0.0C14.0,0.84 13.3,0.0 12.0,0.0c-1.3,0.0 -2.0,0.84 -2.82,2.0L5.0,2.0c-1.1,0.0 -2.0,0.9 -2.0,2.0l0.0,16.0c0.0,1.0 0.9,2.0 2.0,2.0l14.0,0.0c1.1,0.0 2.0,-0.9 2.0,-2.0L21.0,4.0c0.0,-1.1 -0.9,-2.0 -2.0,-2.0zm-7.0,0.0c0.55,0.0 1.0,0.45 1.0,1.0s-0.45,1.0 -1.0,1.0 -1.0,-0.45 -1.0,-1.0 0.45,-1.0 1.0,-1.0zm7.0,18.0L5.0,20.0L5.0,4.0l2.0,0.0l0.0,3.0l10.0,0.0L17.0,4.0l2.0,0.0l0.0,16.0z M 6,8 l 12,0 l 0,11 l -12,0 l 0,-11z" />
+</vector>
+
diff --git a/packages/SystemUI/res/drawable/ic_drag_handle.xml b/packages/SystemUI/res/drawable/ic_drag_handle.xml
new file mode 100644
index 0000000..9b319f1
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_drag_handle.xml
@@ -0,0 +1,24 @@
+<!--
+    Copyright (C) 2016 The Android Open Source Project
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24.0dp"
+        android:height="24.0dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FFFFFFFF"
+        android:pathData="M20.0,9.0L4.0,9.0l0.0,2.0l16.0,0.0L20.0,9.0zM4.0,15.0l16.0,0.0l0.0,-2.0L4.0,13.0l0.0,2.0z"/>
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_history.xml b/packages/SystemUI/res/drawable/ic_history.xml
new file mode 100644
index 0000000..e936864
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_history.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2016 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24"
+    android:viewportHeight="24">
+
+    <path
+        android:fillColor="#FFFFFF"
+        android:pathData="M13 3c-4.97 0-9 4.03-9 9H1l3.89 3.89 .07 .14L9 12H6c0-3.87 3.13-7 7-7s7 3.13 7
+7-3.13 7-7 7c-1.93 0-3.68-.79-4.94-2.06l-1.42 1.42C8.27 19.99 10.51 21 13
+21c4.97 0 9-4.03 9-9s-4.03-9-9-9zm-1 5v5l4.28 2.54 .72 -1.21-3.5-2.08V8H12z" />
+</vector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/ic_width.xml b/packages/SystemUI/res/drawable/ic_width.xml
new file mode 100644
index 0000000..a302c81
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_width.xml
@@ -0,0 +1,28 @@
+<!--
+    Copyright (C) 2016 The Android Open Source Project
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24.0dp"
+        android:height="24.0dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M7.77,6.76L6.23,5.4 0.82,12.0l5.41,6.52 1.54,-1.28L3.42,12.0l4.35,-5.24z
+        M17.77,5.48l-1.54,1.28L20.58,12.0l-4.35,5.24 1.54,1.28L23.18,12.0l-5.41,-6.52z"/>
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M2.0,13.0l20.0,0.0l0.0,-2.0l-20.0,0.0l0.0,2.0z" />
+</vector>
diff --git a/packages/SystemUI/res/drawable/recents_dismiss_all_history.xml b/packages/SystemUI/res/drawable/recents_dismiss_all_history.xml
new file mode 100644
index 0000000..6a417e6
--- /dev/null
+++ b/packages/SystemUI/res/drawable/recents_dismiss_all_history.xml
@@ -0,0 +1,54 @@
+<!--
+Copyright (C) 2016 The Android Open Source Project
+
+   Licensed under the Apache License, Version 2 (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="16dp"
+    android:width="28dp"
+    android:viewportHeight="48"
+    android:viewportWidth="72" >
+    <group
+        android:name="dismiss_all"
+        android:translateX="48"
+        android:translateY="6" >
+        <group
+            android:name="3"
+            android:translateX="-24"
+            android:translateY="36" >
+            <path
+                android:name="rectangle_path_1_2"
+                android:pathData="M -24.0,-6.0 l 48.0,0 l 0,12.0 l -48.0,0 Z"
+                android:fillColor="#FFFFFFFF"
+                android:fillAlpha="1" />
+        </group>
+        <group
+            android:name="2"
+            android:translateX="-12"
+            android:translateY="18" >
+            <path
+                android:name="rectangle_path_1_1"
+                android:pathData="M -24.0,-6.0 l 48.0,0 l 0,12.0 l -48.0,0 Z"
+                android:fillColor="#FFFFFFFF"
+                android:fillAlpha="1" />
+        </group>
+        <group
+            android:name="1" >
+            <path
+                android:name="rectangle_path_1"
+                android:pathData="M -24.0,-6.0 l 48.0,0 l 0,12.0 l -48.0,0 Z"
+                android:fillColor="#FFFFFFFF"
+                android:fillAlpha="1" />
+        </group>
+    </group>
+</vector>
diff --git a/packages/SystemUI/res/drawable/switchbar_background.xml b/packages/SystemUI/res/drawable/switchbar_background.xml
new file mode 100644
index 0000000..8d97c46
--- /dev/null
+++ b/packages/SystemUI/res/drawable/switchbar_background.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2016 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<ripple xmlns:android="http://schemas.android.com/apk/res/android"
+    android:color="?android:attr/colorControlHighlight">
+    <item android:drawable="@color/switch_bar_background" />
+</ripple>
+
diff --git a/packages/SystemUI/res/layout-land/nav_bar_tuner.xml b/packages/SystemUI/res/layout-land/nav_bar_tuner.xml
new file mode 100644
index 0000000..a430b73
--- /dev/null
+++ b/packages/SystemUI/res/layout-land/nav_bar_tuner.xml
@@ -0,0 +1,56 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2016 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:systemui="http://schemas.android.com/apk/res-auto"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:orientation="horizontal">
+
+    <LinearLayout
+        android:layout_width="wrap_content"
+        android:layout_height="match_parent"
+        android:elevation="4dp"
+        android:paddingTop="12dp"
+        android:paddingBottom="12dp"
+        android:paddingStart="8dp"
+        android:paddingEnd="8dp"
+        android:background="@android:color/white"
+        android:gravity="center"
+        android:orientation="vertical">
+
+        <TextView
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="@string/preview"
+            android:paddingStart="8dp"
+            android:paddingEnd="8dp"
+            android:textColor="?android:attr/colorAccent"
+            android:textAppearance="?android:attr/textAppearanceMedium" />
+
+        <FrameLayout
+            android:id="@+id/nav_preview_frame"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content" />
+
+    </LinearLayout>
+
+    <android.support.v7.widget.RecyclerView
+        android:id="@android:id/list"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent" />
+
+</LinearLayout>
diff --git a/packages/SystemUI/res/layout-sw600dp-land/nav_bar_tuner.xml b/packages/SystemUI/res/layout-sw600dp-land/nav_bar_tuner.xml
new file mode 100644
index 0000000..5479157
--- /dev/null
+++ b/packages/SystemUI/res/layout-sw600dp-land/nav_bar_tuner.xml
@@ -0,0 +1,57 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2016 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:systemui="http://schemas.android.com/apk/res-auto"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:orientation="vertical">
+
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:elevation="4dp"
+        android:paddingTop="8dp"
+        android:paddingBottom="8dp"
+        android:paddingStart="12dp"
+        android:paddingEnd="12dp"
+        android:layout_gravity="bottom"
+        android:background="@android:color/white"
+        android:gravity="center"
+        android:orientation="vertical">
+
+        <TextView
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:text="@string/preview"
+            android:paddingTop="8dp"
+            android:paddingBottom="8dp"
+            android:textColor="?android:attr/colorAccent"
+            android:textAppearance="?android:attr/textAppearanceLarge" />
+
+        <FrameLayout
+            android:id="@+id/nav_preview_frame"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content" />
+
+    </LinearLayout>
+
+    <android.support.v7.widget.RecyclerView
+        android:id="@android:id/list"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent" />
+
+</LinearLayout>
diff --git a/packages/SystemUI/res/layout/clipboard.xml b/packages/SystemUI/res/layout/clipboard.xml
new file mode 100644
index 0000000..441d6737
--- /dev/null
+++ b/packages/SystemUI/res/layout/clipboard.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2016 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<com.android.systemui.tuner.ClipboardView
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:systemui="http://schemas.android.com/apk/res-auto"
+    android:layout_width="@dimen/navigation_side_padding"
+    android:layout_height="match_parent"
+    android:layout_weight="0"
+    android:scaleType="center"
+    android:contentDescription="@string/clipboard"
+    />
+
diff --git a/packages/SystemUI/res/layout/color_matrix_settings.xml b/packages/SystemUI/res/layout/color_matrix_settings.xml
new file mode 100644
index 0000000..3725e78
--- /dev/null
+++ b/packages/SystemUI/res/layout/color_matrix_settings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2016 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:orientation="vertical">
+
+    <include layout="@layout/switch_bar" />
+
+</LinearLayout>
diff --git a/packages/SystemUI/res/layout/custom_key.xml b/packages/SystemUI/res/layout/custom_key.xml
new file mode 100644
index 0000000..0b5cb72
--- /dev/null
+++ b/packages/SystemUI/res/layout/custom_key.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2016 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<com.android.systemui.statusbar.policy.KeyButtonView
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:systemui="http://schemas.android.com/apk/res-auto"
+    android:layout_width="@dimen/navigation_side_padding"
+    android:layout_height="match_parent"
+    android:layout_weight="0"
+    android:scaleType="center"
+    android:tint="@android:color/white"
+    android:contentDescription="@string/accessibility_key" />
+
diff --git a/packages/SystemUI/res/layout/keyboard_shortcut_app_item.xml b/packages/SystemUI/res/layout/keyboard_shortcut_app_item.xml
new file mode 100644
index 0000000..5a6553f
--- /dev/null
+++ b/packages/SystemUI/res/layout/keyboard_shortcut_app_item.xml
@@ -0,0 +1,55 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2016 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License
+  -->
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+        android:id="@+id/keyboard_shortcuts_keyword_wrapper"
+        android:orientation="horizontal"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:paddingStart="24dp"
+        android:paddingEnd="24dp"
+        android:paddingBottom="8dp">
+    <TextView
+            android:id="@+id/keyboard_shortcuts_keyword"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:paddingEnd="12dp"
+            android:background="@android:color/white"
+            android:textColor="#D9000000"
+            android:textSize="16sp"
+            android:maxLines="5"
+            android:singleLine="false"
+            android:scrollHorizontally="false"
+            android:layout_alignParentStart="true"
+            android:minWidth="100dp"
+            android:maxWidth="260dp"/>
+    <!--TODO: introduce and use a layout that allows wrapping and right align -->
+    <LinearLayout
+            android:id="@+id/keyboard_shortcuts_item_container"
+            android:layout_toEndOf="@+id/keyboard_shortcuts_keyword"
+            android:orientation="horizontal"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:background="@android:color/white"
+            android:layout_alignParentEnd="true"
+            android:gravity="end"
+            android:textSize="14sp"
+            android:paddingStart="0dp"
+            android:paddingEnd="0dp"
+            android:scrollHorizontally="false"
+            android:minWidth="100dp"
+            android:maxWidth="260dp"/>
+</RelativeLayout>
diff --git a/packages/SystemUI/res/layout/keyboard_shortcuts_category_title.xml b/packages/SystemUI/res/layout/keyboard_shortcuts_category_title.xml
new file mode 100644
index 0000000..80a478a
--- /dev/null
+++ b/packages/SystemUI/res/layout/keyboard_shortcuts_category_title.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+  ~ Copyright (C) 2016 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License
+  -->
+<TextView xmlns:android="http://schemas.android.com/apk/res/android"
+          android:layout_width="match_parent"
+          android:layout_height="match_parent"
+          android:textSize="14sp"
+          android:paddingStart="24dp"
+          android:paddingTop="20dp"
+          android:paddingEnd="24dp"
+          android:paddingBottom="13dp" />
diff --git a/packages/SystemUI/res/layout/keyboard_shortcuts_container.xml b/packages/SystemUI/res/layout/keyboard_shortcuts_container.xml
new file mode 100644
index 0000000..fa07eb1
--- /dev/null
+++ b/packages/SystemUI/res/layout/keyboard_shortcuts_container.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2016 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License
+  -->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+              android:orientation="horizontal"
+              android:layout_width="match_parent"
+              android:layout_height="wrap_content">
+</LinearLayout>
diff --git a/packages/SystemUI/res/layout/keyboard_shortcuts_key_view.xml b/packages/SystemUI/res/layout/keyboard_shortcuts_key_view.xml
new file mode 100644
index 0000000..5002c12
--- /dev/null
+++ b/packages/SystemUI/res/layout/keyboard_shortcuts_key_view.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2016 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License
+  -->
+<TextView xmlns:android="http://schemas.android.com/apk/res/android"
+          android:layout_width="wrap_content"
+          android:layout_height="wrap_content"
+          android:layout_marginStart="4dp"
+          android:padding="4dp"
+          android:background="#EEEEEE"
+          android:textColor="#8C000000"
+          android:singleLine="true"
+          android:textSize="14sp"/>
diff --git a/packages/SystemUI/res/layout/keyboard_shortcuts_view.xml b/packages/SystemUI/res/layout/keyboard_shortcuts_view.xml
index 460433e..77b1264 100644
--- a/packages/SystemUI/res/layout/keyboard_shortcuts_view.xml
+++ b/packages/SystemUI/res/layout/keyboard_shortcuts_view.xml
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
 <!--
-  ~ Copyright (C) 2015 The Android Open Source Project
+  ~ Copyright (C) 2016 The Android Open Source Project
   ~
   ~ Licensed under the Apache License, Version 2.0 (the "License");
   ~ you may not use this file except in compliance with the License.
@@ -14,11 +14,25 @@
   ~ See the License for the specific language governing permissions and
   ~ limitations under the License
   -->
-<RelativeLayout
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:id="@+id/keyboard_shortcuts_wrapper"
-    android:layout_width="match_parent"
-    android:layout_height="match_parent"
-    android:layout_marginTop="40dp"
-    android:focusable="true">
-</RelativeLayout>
+<LinearLayout
+        xmlns:android="http://schemas.android.com/apk/res/android"
+        android:id="@+id/keyboard_shortcuts_wrapper"
+        android:layout_width="488dp"
+        android:layout_height="wrap_content"
+        android:focusable="true">
+    <ScrollView
+            android:id="@+id/keyboard_shortcuts_scroll_view"
+            android:layout_width="0dp"
+            android:layout_height="0dp"
+            android:layout_weight="1">
+        <LinearLayout
+                android:id="@+id/keyboard_shortcuts_container"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:orientation="vertical"/>
+    </ScrollView>
+    <View
+            android:layout_width="match_parent"
+            android:layout_height="1dp"
+            android:background="?android:attr/listDivider"/>
+</LinearLayout>
diff --git a/packages/SystemUI/res/layout/keyboard_shortcuts_wrapper.xml b/packages/SystemUI/res/layout/keyboard_shortcuts_wrapper.xml
new file mode 100644
index 0000000..802acfe
--- /dev/null
+++ b/packages/SystemUI/res/layout/keyboard_shortcuts_wrapper.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2016 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License
+  -->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+              android:orientation="vertical"
+              android:layout_width="match_parent"
+              android:layout_height="wrap_content">
+</LinearLayout>
diff --git a/packages/SystemUI/res/layout/nav_bar_tuner.xml b/packages/SystemUI/res/layout/nav_bar_tuner.xml
new file mode 100644
index 0000000..5479157
--- /dev/null
+++ b/packages/SystemUI/res/layout/nav_bar_tuner.xml
@@ -0,0 +1,57 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2016 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:systemui="http://schemas.android.com/apk/res-auto"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:orientation="vertical">
+
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:elevation="4dp"
+        android:paddingTop="8dp"
+        android:paddingBottom="8dp"
+        android:paddingStart="12dp"
+        android:paddingEnd="12dp"
+        android:layout_gravity="bottom"
+        android:background="@android:color/white"
+        android:gravity="center"
+        android:orientation="vertical">
+
+        <TextView
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:text="@string/preview"
+            android:paddingTop="8dp"
+            android:paddingBottom="8dp"
+            android:textColor="?android:attr/colorAccent"
+            android:textAppearance="?android:attr/textAppearanceLarge" />
+
+        <FrameLayout
+            android:id="@+id/nav_preview_frame"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content" />
+
+    </LinearLayout>
+
+    <android.support.v7.widget.RecyclerView
+        android:id="@android:id/list"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent" />
+
+</LinearLayout>
diff --git a/packages/SystemUI/res/layout/nav_bar_tuner_inflater.xml b/packages/SystemUI/res/layout/nav_bar_tuner_inflater.xml
new file mode 100644
index 0000000..b237633
--- /dev/null
+++ b/packages/SystemUI/res/layout/nav_bar_tuner_inflater.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2016 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<com.android.systemui.tuner.PreviewNavInflater
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:systemui="http://schemas.android.com/apk/res-auto"
+    android:background="@android:color/black"
+    android:layout_width="match_parent"
+    android:layout_height="@dimen/navigation_bar_size">
+
+    <include android:id="@+id/rot0" layout="@layout/navigation_layout" />
+
+    <include android:id="@+id/rot90" layout="@layout/navigation_layout_rot90" />
+
+</com.android.systemui.tuner.PreviewNavInflater>
diff --git a/packages/SystemUI/res/layout/nav_control_widget.xml b/packages/SystemUI/res/layout/nav_control_widget.xml
new file mode 100644
index 0000000..51dd68f
--- /dev/null
+++ b/packages/SystemUI/res/layout/nav_control_widget.xml
@@ -0,0 +1,62 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+    Copyright (C) 2016 The Android Open Source Project
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+-->
+
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:systemui="http://schemas.android.com/apk/res-auto"
+    android:layout_width="wrap_content"
+    android:layout_height="match_parent"
+    android:paddingTop="8dp"
+    android:paddingBottom="8dp"
+    android:gravity="center"
+    android:orientation="horizontal">
+
+    <ImageView
+        android:id="@+id/width"
+        android:layout_width="48dp"
+        android:layout_height="wrap_content"
+        android:src="@drawable/ic_width"
+        android:clickable="true"
+        android:tint="?android:attr/textColorPrimary" />
+
+    <View
+        android:layout_width="1dp"
+        android:layout_height="match_parent"
+        android:background="?android:attr/listDivider" />
+
+    <ImageView
+        android:id="@+id/close"
+        android:layout_width="48dp"
+        android:layout_height="wrap_content"
+        android:src="@drawable/ic_close"
+        android:clickable="true"
+        android:tint="?android:attr/textColorPrimary" />
+
+    <View
+        android:layout_width="1dp"
+        android:layout_height="match_parent"
+        android:background="?android:attr/listDivider" />
+
+    <ImageView
+        android:id="@+id/drag"
+        android:layout_width="48dp"
+        android:layout_height="wrap_content"
+        android:src="@drawable/ic_drag_handle"
+        android:clickable="true"
+        android:tint="?android:attr/textColorPrimary" />
+
+</LinearLayout>
diff --git a/packages/SystemUI/res/layout/nav_width_view.xml b/packages/SystemUI/res/layout/nav_width_view.xml
new file mode 100644
index 0000000..6a72faf
--- /dev/null
+++ b/packages/SystemUI/res/layout/nav_width_view.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+**
+** Copyright 2016, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<SeekBar
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/seekbar"
+    android:layout_height="wrap_content"
+    android:layout_width="match_parent"
+    android:paddingTop="12dp"
+    android:paddingBottom="4dp" />
diff --git a/packages/SystemUI/res/layout/preference_matrix.xml b/packages/SystemUI/res/layout/preference_matrix.xml
index ebf486f..1f6066e 100644
--- a/packages/SystemUI/res/layout/preference_matrix.xml
+++ b/packages/SystemUI/res/layout/preference_matrix.xml
@@ -19,82 +19,86 @@
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
-    android:orientation="horizontal">
-    <Space
-        android:layout_width="0dp"
-        android:layout_height="wrap_content"
-        android:layout_weight="1" />
-    <GridLayout
-        android:id="@+id/edit_group"
-        android:layout_height="wrap_content"
-        android:layout_width="wrap_content"
-        android:rowCount="5"
-        android:columnCount="5">
+    android:paddingStart="?android:attr/listPreferredItemPaddingStart"
+    android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
+    android:paddingTop="8dp"
+    android:paddingBottom="8dp"
+    android:orientation="vertical">
 
-        <Space android:layout_width="40dp" />
+    <LinearLayout
+        android:id="@+id/r_group"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:paddingTop="4dp"
+        android:paddingBottom="4dp"
+        android:orientation="horizontal">
 
         <TextView
-            android:layout_width="40dp"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
             android:text="@string/color_modification_r"
             android:gravity="center"
             android:textAppearance="?android:attr/textAppearanceMedium" />
 
+        <SeekBar android:id="@*android:id/seekbar"
+            android:layout_marginStart="16dp"
+            android:layout_gravity="center_vertical"
+            android:layout_width="0dp"
+            android:layout_height="wrap_content"
+            android:layout_weight="1" />
+    </LinearLayout>
+
+    <LinearLayout
+        android:id="@+id/g_group"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:paddingTop="4dp"
+        android:paddingBottom="4dp"
+        android:orientation="horizontal">
+
         <TextView
-            android:layout_width="40dp"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
             android:text="@string/color_modification_g"
             android:gravity="center"
             android:textAppearance="?android:attr/textAppearanceMedium" />
 
+        <SeekBar android:id="@*android:id/seekbar"
+            android:layout_marginStart="16dp"
+            android:layout_gravity="center_vertical"
+            android:layout_width="0dp"
+            android:layout_height="wrap_content"
+            android:layout_weight="1" />
+    </LinearLayout>
+
+    <LinearLayout
+        android:id="@+id/b_group"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:paddingTop="4dp"
+        android:paddingBottom="4dp"
+        android:orientation="horizontal">
+
         <TextView
-            android:layout_width="40dp"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
             android:text="@string/color_modification_b"
             android:gravity="center"
             android:textAppearance="?android:attr/textAppearanceMedium" />
 
-        <Space android:layout_width="40dp" />
+        <SeekBar android:id="@*android:id/seekbar"
+            android:layout_marginStart="16dp"
+            android:layout_gravity="center_vertical"
+            android:layout_width="0dp"
+            android:layout_height="wrap_content"
+            android:layout_weight="1" />
+    </LinearLayout>
 
-        <TextView
-            android:layout_width="40dp"
-            android:text="@string/color_modification_r"
-            android:gravity="center"
-            android:textAppearance="?android:attr/textAppearanceMedium" />
-        <EditText android:inputType="numberDecimal" android:layout_width="40dp" />
-        <EditText android:inputType="numberDecimal" android:layout_width="40dp" />
-        <EditText android:inputType="numberDecimal" android:layout_width="40dp" />
-        <EditText android:inputType="numberDecimal" android:layout_width="40dp" />
-
-        <TextView
-            android:layout_width="40dp"
-            android:text="@string/color_modification_g"
-            android:gravity="center"
-            android:textAppearance="?android:attr/textAppearanceMedium" />
-        <EditText android:inputType="numberDecimal" android:layout_width="40dp" />
-        <EditText android:inputType="numberDecimal" android:layout_width="40dp" />
-        <EditText android:inputType="numberDecimal" android:layout_width="40dp" />
-        <EditText android:inputType="numberDecimal" android:layout_width="40dp" />
-
-        <TextView
-            android:layout_width="40dp"
-            android:text="@string/color_modification_b"
-            android:gravity="center"
-            android:textAppearance="?android:attr/textAppearanceMedium" />
-        <EditText android:inputType="numberDecimal" android:layout_width="40dp" />
-        <EditText android:inputType="numberDecimal" android:layout_width="40dp" />
-        <EditText android:inputType="numberDecimal" android:layout_width="40dp" />
-        <EditText android:inputType="numberDecimal" android:layout_width="40dp" />
-
-        <Space android:layout_width="40dp" />
-        <EditText android:inputType="numberDecimal" android:layout_width="40dp" />
-        <EditText android:inputType="numberDecimal" android:layout_width="40dp" />
-        <EditText android:inputType="numberDecimal" android:layout_width="40dp" />
-        <EditText android:inputType="numberDecimal" android:layout_width="40dp" />
-
-    </GridLayout>
     <Button
         android:id="@+id/apply"
-        android:layout_width="0dp"
+        android:layout_width="wrap_content"
         android:layout_height="wrap_content"
-        android:layout_weight="1"
-        android:layout_gravity="bottom"
+        android:layout_gravity="end"
         android:text="@string/color_apply" />
+
 </LinearLayout>
diff --git a/packages/SystemUI/res/layout/recents.xml b/packages/SystemUI/res/layout/recents.xml
index 16ff14c..186aaf6 100644
--- a/packages/SystemUI/res/layout/recents.xml
+++ b/packages/SystemUI/res/layout/recents.xml
@@ -17,14 +17,6 @@
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="match_parent"
     android:layout_height="match_parent">
-    <!-- Status Bar Scrim View -->
-    <ImageView
-        android:id="@+id/status_bar_scrim"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:layout_gravity="center_horizontal|top"
-        android:scaleType="fitXY"
-        android:src="@drawable/recents_status_gradient" />
 
     <!-- Recents View -->
     <com.android.systemui.recents.views.RecentsView
@@ -33,12 +25,6 @@
         android:layout_height="match_parent">
     </com.android.systemui.recents.views.RecentsView>
 
-    <!-- History View -->
-    <ViewStub android:id="@+id/history_view_stub"
-           android:layout="@layout/recents_history"
-           android:layout_width="match_parent"
-           android:layout_height="match_parent" />
-
     <!-- Nav Bar Scrim View -->
     <ImageView
         android:id="@+id/nav_bar_scrim"
diff --git a/packages/SystemUI/res/layout/recents_empty.xml b/packages/SystemUI/res/layout/recents_empty.xml
index 4b68e77..b2c0331 100644
--- a/packages/SystemUI/res/layout/recents_empty.xml
+++ b/packages/SystemUI/res/layout/recents_empty.xml
@@ -24,5 +24,4 @@
     android:textColor="#ffffffff"
     android:text="@string/recents_empty_message"
     android:fontFamily="sans-serif"
-    android:background="#80000000"
     android:visibility="gone" />
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/recents_history.xml b/packages/SystemUI/res/layout/recents_history.xml
index b65a5c5..dc2da72 100644
--- a/packages/SystemUI/res/layout/recents_history.xml
+++ b/packages/SystemUI/res/layout/recents_history.xml
@@ -17,7 +17,6 @@
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
-    android:background="#99000000"
     android:orientation="vertical">
     <android.support.v7.widget.RecyclerView
         android:id="@+id/list"
diff --git a/packages/SystemUI/res/layout/recents_history_button.xml b/packages/SystemUI/res/layout/recents_history_button.xml
index 8c96fc6..538bad1 100644
--- a/packages/SystemUI/res/layout/recents_history_button.xml
+++ b/packages/SystemUI/res/layout/recents_history_button.xml
@@ -15,15 +15,24 @@
 -->
 <TextView
     xmlns:android="http://schemas.android.com/apk/res/android"
-    android:layout_width="match_parent"
-    android:layout_height="match_parent"
+    android:id="@+id/button"
+    android:layout_width="wrap_content"
+    android:layout_height="wrap_content"
     android:gravity="start|center_vertical"
-    android:text="@string/recents_show_history_button_label"
+    android:paddingStart="14dp"
+    android:paddingEnd="14dp"
+    android:paddingTop="12dp"
+    android:paddingBottom="12dp"
+    android:text="@string/recents_history_button_label"
     android:textSize="14sp"
     android:textColor="#FFFFFF"
     android:textAllCaps="true"
+    android:drawableStart="@drawable/ic_history"
+    android:drawablePadding="6dp"
     android:shadowColor="#99000000"
     android:shadowDx="0"
     android:shadowDy="2"
     android:shadowRadius="5"
-    android:fontFamily="sans-serif-medium" />
\ No newline at end of file
+    android:fontFamily="sans-serif-medium"
+    android:background="?android:selectableItemBackground"
+    android:visibility="invisible" />
diff --git a/packages/SystemUI/res/layout/recents_history_clear_all_button.xml b/packages/SystemUI/res/layout/recents_history_clear_all_button.xml
new file mode 100644
index 0000000..05f0979
--- /dev/null
+++ b/packages/SystemUI/res/layout/recents_history_clear_all_button.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2016 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<TextView
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/button"
+    android:layout_width="wrap_content"
+    android:layout_height="wrap_content"
+    android:gravity="start|center_vertical"
+    android:paddingStart="16dp"
+    android:paddingEnd="16dp"
+    android:paddingTop="14dp"
+    android:paddingBottom="14dp"
+    android:drawableStart="@drawable/recents_dismiss_all_history"
+    android:contentDescription="@string/recents_history_clear_all_button_label"
+    android:textSize="14sp"
+    android:textColor="#FFFFFF"
+    android:textAllCaps="true"
+    android:shadowColor="#99000000"
+    android:shadowDx="0"
+    android:shadowDy="2"
+    android:shadowRadius="5"
+    android:fontFamily="sans-serif-medium"
+    android:background="?android:selectableItemBackground"
+    android:visibility="invisible" />
diff --git a/packages/SystemUI/res/layout/recents_history_date.xml b/packages/SystemUI/res/layout/recents_history_date.xml
index 6d6a9ee..13c7dbe 100644
--- a/packages/SystemUI/res/layout/recents_history_date.xml
+++ b/packages/SystemUI/res/layout/recents_history_date.xml
@@ -15,10 +15,12 @@
 -->
 <TextView
     xmlns:android="http://schemas.android.com/apk/res/android"
-    android:theme="@android:style/Theme.Material"
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
-    android:padding="12dp"
+    android:paddingStart="4dp"
+    android:paddingEnd="4dp"
+    android:paddingTop="12dp"
+    android:paddingBottom="12dp"
     android:gravity="start"
     android:textSize="14sp"
     android:textColor="#009688"
diff --git a/packages/SystemUI/res/layout/recents_history_task.xml b/packages/SystemUI/res/layout/recents_history_task.xml
index ae11006..e92c24a 100644
--- a/packages/SystemUI/res/layout/recents_history_task.xml
+++ b/packages/SystemUI/res/layout/recents_history_task.xml
@@ -15,7 +15,6 @@
 -->
 <LinearLayout
     xmlns:android="http://schemas.android.com/apk/res/android"
-    android:theme="@android:style/Theme.Material"
     android:layout_width="match_parent"
     android:layout_height="48dp"
     android:orientation="horizontal"
@@ -27,7 +26,8 @@
         android:layout_width="32dp"
         android:layout_height="32dp"
         android:layout_gravity="center"
-        android:layout_marginStart="16dp" />
+        android:layout_marginStart="4dp"
+        android:layout_marginEnd="12dp" />
     <TextView
         android:id="@+id/description"
         android:layout_width="0dp"
diff --git a/packages/SystemUI/res/layout/recents_on_tv.xml b/packages/SystemUI/res/layout/recents_on_tv.xml
new file mode 100644
index 0000000..b4543bd
--- /dev/null
+++ b/packages/SystemUI/res/layout/recents_on_tv.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2016 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<com.android.systemui.recents.tv.views.RecentsTvView
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/recents_view"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:clipChildren="false"
+    android:clipToPadding="false" >
+
+    <com.android.systemui.recents.tv.views.TaskStackHorizontalGridView
+        android:id="@+id/task_list"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:clipChildren="false"
+        android:clipToPadding="false"
+        android:descendantFocusability="beforeDescendants"
+        android:gravity="center"
+        android:paddingStart="@dimen/recents_tv_grid_row_padding"
+        android:paddingEnd="@dimen/recents_tv_grid_row_padding"
+        android:focusable="true"/>
+
+</com.android.systemui.recents.tv.views.RecentsTvView>
+
diff --git a/packages/SystemUI/res/layout/recents_task_card_view.xml b/packages/SystemUI/res/layout/recents_task_card_view.xml
new file mode 100644
index 0000000..fa1daad
--- /dev/null
+++ b/packages/SystemUI/res/layout/recents_task_card_view.xml
@@ -0,0 +1,77 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2016 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<com.android.systemui.recents.tv.views.TaskCardView
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="wrap_content"
+    android:layout_height="wrap_content"
+    android:focusable="true"
+    android:focusableInTouchMode="true"
+    android:layout_gravity="center"
+    android:layout_centerInParent="true">
+
+    <RelativeLayout
+            android:layout_width="@dimen/recents_tv_card_width"
+            android:layout_height="wrap_content"
+            android:layout_centerInParent="true"
+            android:layout_gravity="center">
+        <ImageView
+                android:id="@+id/card_view_thumbnail"
+                android:layout_width="match_parent"
+                android:layout_height="@dimen/recents_tv_card_height"
+                android:scaleType="centerCrop"
+                android:gravity="center"
+                android:layout_alignParentTop="true"
+                android:layout_centerHorizontal="true"/>
+
+        <RelativeLayout
+                android:id="@+id/card_info_field"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:layout_below="@id/card_view_thumbnail"
+                android:background="@color/recents_tv_card_background_color" >
+            <TextView
+                    android:id="@+id/card_title_text"
+                    android:layout_width="match_parent"
+                    android:layout_height="wrap_content"
+                    android:layout_alignParentTop="false"
+                    android:includeFontPadding="true"
+                    android:minLines="1"
+                    android:maxLines="2"
+                    android:textColor="@color/recents_tv_card_title_text_color"
+                    android:ellipsize="end" />
+            <TextView
+                    android:id="@+id/card_content_text"
+                    android:layout_width="match_parent"
+                    android:layout_height="wrap_content"
+                    android:layout_alignParentStart="true"
+                    android:layout_below="@id/card_title_text"
+                    android:includeFontPadding="true"
+                    android:minLines="1"
+                    android:maxLines="2"
+                    android:textColor="@color/recents_tv_card_content_text_color"
+                    android:ellipsize="end" />
+            <ImageView
+                    android:id="@+id/card_extra_badge"
+                    android:layout_width="@dimen/recents_tv_card_extra_badge_size"
+                    android:layout_height="@dimen/recents_tv_card_extra_badge_size"
+                    android:scaleType="fitCenter"
+                    android:background="@android:color/transparent"
+                    android:contentDescription="@null"
+                    android:layout_centerVertical="true"
+                    android:layout_alignParentRight="true"/>
+        </RelativeLayout>
+    </RelativeLayout>
+</com.android.systemui.recents.tv.views.TaskCardView>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/switch_bar.xml b/packages/SystemUI/res/layout/switch_bar.xml
new file mode 100644
index 0000000..f98de96
--- /dev/null
+++ b/packages/SystemUI/res/layout/switch_bar.xml
@@ -0,0 +1,48 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2016 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/switch_bar"
+    android:layout_width="match_parent"
+    android:layout_height="?android:attr/actionBarSize"
+    android:background="@drawable/switchbar_background"
+    android:paddingStart="16dp"
+    android:paddingEnd="16dp"
+    android:clickable="true"
+    android:gravity="center">
+
+    <TextView android:id="@+id/switch_text"
+        android:layout_height="wrap_content"
+        android:layout_width="0dp"
+        android:layout_weight="1"
+        android:layout_gravity="center_vertical"
+        android:paddingStart="48dp"
+        android:maxLines="2"
+        android:ellipsize="end"
+        android:textAppearance="@android:style/TextAppearance.Material.Title"
+        android:textColor="?android:attr/textColorPrimaryInverse"
+        android:textAlignment="viewStart"
+        android:text="@string/switch_bar_on" />
+
+    <Switch
+        android:id="@android:id/switch_widget"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_gravity="center_vertical"
+        android:background="@null"
+        android:theme="@style/ThemeOverlay.SwitchBar" />
+
+</LinearLayout>
diff --git a/packages/SystemUI/res/values/attrs.xml b/packages/SystemUI/res/values/attrs.xml
index 955efb5..19bc755 100644
--- a/packages/SystemUI/res/values/attrs.xml
+++ b/packages/SystemUI/res/values/attrs.xml
@@ -28,9 +28,6 @@
     <declare-styleable name="NotificationLinearLayout">
         <attr name="insetLeft" format="dimension" />
     </declare-styleable>
-    <declare-styleable name="NotificationRowLayout">
-        <attr name="rowHeight" format="dimension" />
-    </declare-styleable>
     <declare-styleable name="RecentsPanelView">
         <attr name="recentItemLayout" format="reference" />
     </declare-styleable>
diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml
index 5e25d2c..9bb6dc6 100644
--- a/packages/SystemUI/res/values/colors.xml
+++ b/packages/SystemUI/res/values/colors.xml
@@ -151,4 +151,12 @@
     <color name="qs_tile_tint_unavailable">#40ffffff</color>
     <color name="qs_tile_tint_inactive">#4dffffff</color>
     <color name="qs_tile_tint_active">#ffffffff</color>
+
+    <color name="switch_bar_background">#ff37474f</color>
+    <color name="switch_accent_color">#ff7fcac3</color>
+
+    <!-- Keyboard shortcuts colors -->
+    <color name="ksh_system_group_color">#ff00bcd4</color>
+    <color name="ksh_application_group_color">#fff44336</color>
+    <color name="ksh_dialog_background_color">#ffffffff</color>
 </resources>
diff --git a/packages/SystemUI/res/values/colors_tv.xml b/packages/SystemUI/res/values/colors_tv.xml
new file mode 100644
index 0000000..6f4c983
--- /dev/null
+++ b/packages/SystemUI/res/values/colors_tv.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+ * Copyright 2016, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+-->
+<resources>
+    <color name="recents_tv_card_background_color">#FF37474F</color>
+    <color name="recents_tv_card_title_text_color">#FFEEEEEE</color>
+    <color name="recents_tv_card_content_text_color">#99EEEEEE</color>
+    <color name="recents_tv_card_source_text_color">#99EEEEEE</color>
+</resources>
\ No newline at end of file
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 8b0350a..32d09e8 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -606,5 +606,4 @@
 
     <dimen name="docked_divider_handle_width">16dp</dimen>
     <dimen name="docked_divider_handle_height">2dp</dimen>
-
 </resources>
diff --git a/packages/SystemUI/res/values/dimens_tv.xml b/packages/SystemUI/res/values/dimens_tv.xml
new file mode 100644
index 0000000..77605bd
--- /dev/null
+++ b/packages/SystemUI/res/values/dimens_tv.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+ * Copyright 2016, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+-->
+<resources>
+    <!-- Dimens for recents card in the recents view on tv -->
+    <dimen name="recents_tv_card_width">150dip</dimen>
+    <dimen name="recents_tv_card_height">85dip</dimen>
+    <dimen name="recents_tv_card_extra_badge_size">16dip</dimen>
+
+    <!-- Padding for grid view in recents view on tv -->
+    <dimen name="recents_tv_grid_row_padding">56dip</dimen>
+    <dimen name="recents_tv_gird_row_top_padding">57dip</dimen>
+    <dimen name="recents_tv_grid_max_row_height">200dip</dimen>
+    <dimen name="recents_tv_gird_card_spacing">8dip</dimen>
+
+    <!-- Values for focus animation -->
+    <dimen name="recents_tv_unselected_item_z">6dp</dimen>
+    <dimen name="recents_tv_selected_item_z_delta">10dp</dimen>
+</resources>
\ No newline at end of file
diff --git a/packages/SystemUI/res/values/integers_tv.xml b/packages/SystemUI/res/values/integers_tv.xml
new file mode 100644
index 0000000..bfd8f8b
--- /dev/null
+++ b/packages/SystemUI/res/values/integers_tv.xml
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2016 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<resources>
+    <integer name="item_scale_anim_duration">150</integer>
+</resources>
\ No newline at end of file
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index a490635..32da3c6 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -715,9 +715,9 @@
     <!-- Recents: Launch error string. [CHAR LIMIT=NONE] -->
     <string name="recents_launch_error_message">Could not start <xliff:g id="app" example="Calendar">%s</xliff:g>.</string>
     <!-- Recents: Show history string. [CHAR LIMIT=NONE] -->
-    <string name="recents_show_history_button_label">More</string>
-    <!-- Recents: A format string to set the number of availabe historical tasks in recents. [CHAR LIMIT=NONE] -->
-    <string name="recents_history_label_format"><xliff:g id="number">%d</xliff:g> More</string>
+    <string name="recents_history_button_label">History</string>
+    <!-- Recents: History clear all string. [CHAR LIMIT=NONE] -->
+    <string name="recents_history_clear_all_button_label">Clear</string>
 
     <!-- Recents: MultiStack add stack split horizontal radio button. [CHAR LIMIT=NONE] -->
     <string name="recents_multistack_add_stack_dialog_split_horizontal">Split Horizontal</string>
@@ -1251,6 +1251,9 @@
     <!-- Label for custom color transform [CHAR LIMIT=30] -->
     <string name="color_matrix_custom">Custom colors</string>
 
+    <!-- Label for auto color transforms [CHAR LIMIT=30] -->
+    <string name="color_matrix_auto">Auto</string>
+
     <!-- Label for unknown color transform [CHAR LIMIT=30] -->
     <string name="color_matrix_unknown">Unknown colors</string>
 
@@ -1340,4 +1343,61 @@
     <!-- Accessibility description for data saver being off [CHAR LIMIT=NONE] -->
     <string name="accessibility_data_saver_off">Data Saver is off</string>
 
+    <!-- Label for feature switch [CHAR LIMIT=30] -->
+    <string name="switch_bar_on">On</string>
+
+    <!-- SysUI Tuner: Button that leads to the navigation bar customization screen [CHAR LIMIT=60] -->
+    <string name="nav_bar">Navigation bar</string>
+
+    <!-- SysUI Tuner: Group of buttons that show on the start of the screen [CHAR LIMIT=30] -->
+    <string name="start">Start</string>
+    <!-- SysUI Tuner: Group of buttons that show on the center of the screen [CHAR LIMIT=30] -->
+    <string name="center">Center</string>
+    <!-- SysUI Tuner: Group of buttons that show on the end of the screen [CHAR LIMIT=30] -->
+    <string name="end">End</string>
+    <!-- SysUI Tuner: Name of space used in custom navigation bar layouts [CHAR LIMIT=30] -->
+    <string name="space">Spacer</string>
+    <!-- SysUI Tuner: Name of Combination Menu / Keyboard Switcher button [CHAR LIMIT=30] -->
+    <string name="menu_ime">Menu / Keyboard Switcher</string>
+    <!-- SysUI Tuner: Title for dialog to add a button [CHAR LIMIT=30] -->
+    <string name="select_button">Select button to add</string>
+    <!-- SysUI Tuner: Button to add a button [CHAR LIMIT=30] -->
+    <string name="add_button">Add button</string>
+    <!-- SysUI Tuner: Save the current settings [CHAR LIMIT=30] -->
+    <string name="save">Save</string>
+    <!-- SysUI Tuner: Reset to default settings [CHAR LIMIT=30] -->
+    <string name="reset">Reset</string>
+
+    <!-- SysUI Tuner: Title of no home warning dialog [CHAR LIMIT=30] -->
+    <string name="no_home_title">No home button found</string>
+    <!-- SysUI Tuner: Message of no home warning dialog [CHAR LIMIT=NONE] -->
+    <string name="no_home_message">A home button is required to be able to navigate this device. Please add a home button before saving.</string>
+
+    <!-- SysUI Tuner: Adjust button width dialog title [CHAR LIMIT=60] -->
+    <string name="adjust_button_width">Adjust button width</string>
+
+    <!-- SysUI Tuner: Nav bar button that holds the clipboard [CHAR LIMIT=30] -->
+    <string name="clipboard">Clipboard</string>
+
+    <!-- SysUI Tuner: Description of nav bar button that holds the clipboard [CHAR LIMIT=NONE] -->
+    <string name="clipboard_description">The Clipboard allows items to be dragged directly to the clipboard. Items can also be dragged directly out of the clipboard when present.</string>
+
+    <!-- SysUI Tuner: Accessibility description for custom nav key [CHAR LIMIT=NONE] -->
+    <string name="accessibility_key">Custom navigation button</string>
+
+    <!-- SysUI Tuner: Nav bar button that emulates a keycode [CHAR LIMIT=30] -->
+    <string name="keycode">Keycode</string>
+
+    <!-- SysUI Tuner: Description of nav bar button that emulates a keycode [CHAR LIMIT=NONE] -->
+    <string name="keycode_description">Keycode buttons allow keyboard keys to
+        be added to the Navigation Bar. When pressed they emulate the selected
+        keyboard key. First the key must be selected for the button, followed
+        by an image to be shown on the button.</string>
+
+    <!-- SysUI Tuner: Title of dialog to select which key to emulate [CHAR LIMIT=60] -->
+    <string name="select_keycode">Select Keyboard Button</string>
+
+    <!-- SysUI Tuner: Label for preview area in navigation bar tuner [CHAR LIMIT=NONE] -->
+    <string name="preview">Preview</string>
+
 </resources>
diff --git a/packages/SystemUI/res/values/strings_tv.xml b/packages/SystemUI/res/values/strings_tv.xml
index d432a62..c64327d 100644
--- a/packages/SystemUI/res/values/strings_tv.xml
+++ b/packages/SystemUI/res/values/strings_tv.xml
@@ -18,15 +18,15 @@
 -->
 <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <!-- Button to close PIP on PIP UI -->
-    <string name="pip_exit">Close PIP</string>
+    <string name="pip_exit" translatable="false">Close PIP</string>
     <!-- Button to move PIP screen to the fullscreen on PIP UI -->
-    <string name="pip_fullscreen">Full screen</string>
+    <string name="pip_fullscreen" translatable="false">Full screen</string>
     <!-- Button to play the current media on PIP UI -->
-    <string name="pip_play">Play</string>
+    <string name="pip_play" translatable="false">Play</string>
     <!-- Button to pause the current media on PIP UI -->
-    <string name="pip_pause">Pause</string>
+    <string name="pip_pause" translatable="false">Pause</string>
     <!-- Button to close PIP overlay menu on PIP UI -->
-    <string name="pip_cancel">Cancel</string>
+    <string name="pip_cancel" translatable="false">Cancel</string>
     <!-- Overlay text on PIP -->
-    <string name="pip_hold_home">Hold HOME to control PIP</string>
+    <string name="pip_hold_home" translatable="false">Hold HOME to control PIP</string>
 </resources>
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index 4329f78..9931ab9 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -20,7 +20,7 @@
         <item name="android:windowAnimationStyle">@style/Animation.RecentsActivity</item>
     </style>
 
-    <style name="RecentsTheme" parent="@android:style/Theme.Material.Light">
+    <style name="RecentsTheme" parent="@android:style/Theme.Material">
         <!-- NoTitle -->
         <item name="android:windowNoTitle">true</item>
         <!-- Misc -->
@@ -310,6 +310,7 @@
     </style>
 
     <style name="TunerSettings" parent="@android:style/Theme.Material.Settings">
+        <item name="android:windowActionBar">false</item>
         <item name="preferenceTheme">@style/TunerPreferenceTheme</item>
     </style>
 
@@ -345,4 +346,8 @@
         <item name="android:textColor">@*android:color/material_deep_teal_500</item>
     </style>
 
+    <style name="ThemeOverlay.SwitchBar" parent="@android:style/ThemeOverlay">
+        <item name="android:colorAccent">@color/switch_accent_color</item>
+    </style>
+
 </resources>
diff --git a/packages/SystemUI/res/values/values_tv.xml b/packages/SystemUI/res/values/values_tv.xml
new file mode 100644
index 0000000..45cdc07
--- /dev/null
+++ b/packages/SystemUI/res/values/values_tv.xml
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2016 The Android Open Source Project
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android">
+    <item format="float" type="raw" name="unselected_scale">1.0</item>
+</resources>
\ No newline at end of file
diff --git a/packages/SystemUI/res/xml/tuner_prefs.xml b/packages/SystemUI/res/xml/tuner_prefs.xml
index 67a7a23..febe518 100644
--- a/packages/SystemUI/res/xml/tuner_prefs.xml
+++ b/packages/SystemUI/res/xml/tuner_prefs.xml
@@ -166,6 +166,11 @@
 
     </PreferenceScreen>
 
+    <Preference
+        android:key="nav_bar"
+        android:title="@string/nav_bar"
+        android:fragment="com.android.systemui.tuner.NavBarTuner" />
+
     <!-- Warning, this goes last. -->
     <Preference
         android:summary="@string/tuner_persistent_warning"
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/HumanInteractionClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/HumanInteractionClassifier.java
index 27d4c0e..c7e5ea4 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/HumanInteractionClassifier.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/HumanInteractionClassifier.java
@@ -35,6 +35,10 @@
 public class HumanInteractionClassifier extends Classifier {
     private static final String HIC_ENABLE = "HIC_enable";
     private static final float FINGER_DISTANCE = 0.1f;
+
+    /** Default value for the HIC_ENABLE setting: 1 - enabled, 0 - disabled */
+    private static final int HIC_ENABLE_DEFAULT = 1;
+
     private static HumanInteractionClassifier sInstance = null;
 
     private final Handler mHandler = new Handler();
@@ -101,9 +105,9 @@
     }
 
     private void updateConfiguration() {
-        mEnableClassifier = Build.IS_DEBUGGABLE && 0 != Settings.Global.getInt(
+        mEnableClassifier = 0 != Settings.Global.getInt(
                 mContext.getContentResolver(),
-                HIC_ENABLE, 0);
+                HIC_ENABLE, HIC_ENABLE_DEFAULT);
     }
 
     public void setType(int type) {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index 0475c72..958572f 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -24,7 +24,6 @@
 import android.app.SearchManager;
 import android.app.StatusBarManager;
 import android.app.trust.TrustManager;
-import android.auditing.SecurityLog;
 import android.content.BroadcastReceiver;
 import android.content.ContentResolver;
 import android.content.Context;
@@ -1371,10 +1370,8 @@
      * @see #KEYGUARD_DONE
      */
     private void handleKeyguardDone(boolean authenticated) {
-        if (SecurityLog.isLoggingEnabled()
-                && mLockPatternUtils.isSecure(KeyguardUpdateMonitor.getCurrentUser())) {
-            SecurityLog.writeEvent(SecurityLog.TAG_DEVICE_UNLOCK_ATTEMPT,
-                    (authenticated ? 1 : 0), "Unknown");
+        if (mLockPatternUtils.isSecure(KeyguardUpdateMonitor.getCurrentUser())) {
+            mLockPatternUtils.getDevicePolicyManager().reportKeyguardDismissed();
         }
         if (DEBUG) Log.d(TAG, "handleKeyguardDone");
         synchronized (this) {
@@ -1487,9 +1484,8 @@
      * @see #SHOW
      */
     private void handleShow(Bundle options) {
-        if (SecurityLog.isLoggingEnabled()
-                && mLockPatternUtils.isSecure(KeyguardUpdateMonitor.getCurrentUser())) {
-            SecurityLog.writeEvent(SecurityLog.TAG_DEVICE_LOCKED, "");
+        if (mLockPatternUtils.isSecure(KeyguardUpdateMonitor.getCurrentUser())) {
+            mLockPatternUtils.getDevicePolicyManager().reportKeyguardSecured();
         }
         synchronized (KeyguardViewMediator.this) {
             if (!mSystemReady) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSTile.java b/packages/SystemUI/src/com/android/systemui/qs/QSTile.java
index 20e2fa4..35000d3 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSTile.java
@@ -16,6 +16,7 @@
 
 package com.android.systemui.qs;
 
+import android.app.ActivityManager;
 import android.app.PendingIntent;
 import android.content.Context;
 import android.content.Intent;
@@ -23,7 +24,6 @@
 import android.os.Handler;
 import android.os.Looper;
 import android.os.Message;
-import android.os.UserHandle;
 import android.util.Log;
 import android.util.SparseArray;
 import android.view.View;
@@ -35,6 +35,7 @@
 import com.android.systemui.statusbar.policy.BatteryController;
 import com.android.systemui.statusbar.policy.BluetoothController;
 import com.android.systemui.statusbar.policy.CastController;
+import com.android.systemui.statusbar.policy.DisplayController;
 import com.android.systemui.statusbar.policy.FlashlightController;
 import com.android.systemui.statusbar.policy.HotspotController;
 import com.android.systemui.statusbar.policy.KeyguardMonitor;
@@ -270,7 +271,7 @@
 
     protected void checkIfRestrictionEnforced(State state, String userRestriction) {
         EnforcedAdmin admin = RestrictedLockUtils.checkIfRestrictionEnforced(mContext,
-                userRestriction, UserHandle.myUserId());
+                userRestriction, ActivityManager.getCurrentUser());
         if (admin != null) {
             state.disabledByPolicy = true;
             state.enforcedAdmin = admin;
@@ -389,6 +390,7 @@
         UserInfoController getUserInfoController();
         BatteryController getBatteryController();
         TileServices getTileServices();
+        DisplayController getDisplayController();
         void removeTile(String tileSpec);
 
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java b/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java
index 8ae2d7b..3748a30 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java
@@ -72,8 +72,9 @@
             }
             mCurrentTiles = tileSpecs;
             final TileGroup group = new TileGroup("com.android.settings", mContext);
+            boolean hasColorMod = host.getDisplayController().isEnabled();
             String possible = mContext.getString(R.string.quick_settings_tiles_default)
-                    + ",hotspot,inversion,saver";
+                    + ",hotspot,inversion,saver" + (hasColorMod ? ",colors" : "");
             String[] possibleTiles = possible.split(",");
             for (int i = 0; i < possibleTiles.length; i++) {
                 final String spec = possibleTiles[i];
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java
index 6a07a07..dbb7423 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java
@@ -78,6 +78,10 @@
 
     @Override
     protected void handleClick() {
+        if (!mController.canConfigBluetooth()) {
+            mHost.startActivityDismissingKeyguard(new Intent(Settings.ACTION_BLUETOOTH_SETTINGS));
+            return;
+        }
         if (!mState.value) {
             mState.value = true;
             mController.setBluetoothEnabled(true);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java
index db86047..2f37943 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java
@@ -16,6 +16,8 @@
 
 package com.android.systemui.qs.tiles;
 
+import android.os.UserManager;
+
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.MetricsProto.MetricsEvent;
 import com.android.systemui.R;
@@ -66,6 +68,8 @@
     protected void handleUpdateState(BooleanState state, Object arg) {
         state.label = mContext.getString(R.string.quick_settings_hotspot_label);
 
+        state.disabledByPolicy = mController.isTetheringAllowed();
+        checkIfRestrictionEnforced(state, UserManager.DISALLOW_CONFIG_TETHERING);
         if (arg instanceof Boolean) {
             state.value = (boolean) arg;
         } else {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/LocationTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/LocationTile.java
index 724659c..8328897 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/LocationTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/LocationTile.java
@@ -16,6 +16,8 @@
 
 package com.android.systemui.qs.tiles;
 
+import android.os.UserManager;
+
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.MetricsProto.MetricsEvent;
 import com.android.systemui.R;
@@ -85,6 +87,8 @@
         // bug is fixed, this should be reverted to only hiding it on secure lock screens:
         // state.visible = !(mKeyguard.isSecure() && mKeyguard.isShowing());
         state.value = locationEnabled;
+        state.disabledByPolicy = mController.isUserLocationRestricted();
+        checkIfRestrictionEnforced(state, UserManager.DISALLOW_SHARE_LOCATION);
         if (locationEnabled) {
             state.icon = mEnable;
             state.label = mContext.getString(R.string.quick_settings_location_label);
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
index fa30eed..c3b794d 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
@@ -34,7 +34,6 @@
 import android.util.Log;
 import android.view.KeyEvent;
 import android.view.View;
-import android.view.ViewStub;
 import android.view.ViewTreeObserver;
 import android.view.WindowManager;
 
@@ -70,7 +69,6 @@
 import com.android.systemui.recents.events.ui.focus.DismissFocusedTaskViewEvent;
 import com.android.systemui.recents.events.ui.focus.FocusNextTaskViewEvent;
 import com.android.systemui.recents.events.ui.focus.FocusPreviousTaskViewEvent;
-import com.android.systemui.recents.history.RecentsHistoryView;
 import com.android.systemui.recents.misc.DozeTrigger;
 import com.android.systemui.recents.misc.SystemServicesProxy;
 import com.android.systemui.recents.model.RecentsPackageMonitor;
@@ -82,8 +80,6 @@
 import com.android.systemui.recents.views.SystemBarScrimViews;
 import com.android.systemui.statusbar.BaseStatusBar;
 
-import java.util.ArrayList;
-
 /**
  * The main Recents activity that is started from AlternateRecentsComponent.
  */
@@ -105,8 +101,6 @@
     // Top level views
     private RecentsView mRecentsView;
     private SystemBarScrimViews mScrimViews;
-    private ViewStub mHistoryViewStub;
-    private RecentsHistoryView mHistoryView;
 
     // Search AppWidget
     private AppWidgetProviderInfo mSearchWidgetInfo;
@@ -127,12 +121,14 @@
      * just finishing the activity since we don't know what is behind Recents in the task stack.
      */
     class FinishRecentsRunnable implements Runnable {
+
         Intent mLaunchIntent;
+        ActivityOptions mOpts;
 
         /**
          * Creates a finish runnable that starts the specified intent.
          */
-        public FinishRecentsRunnable(Intent launchIntent) {
+        public FinishRecentsRunnable(Intent launchIntent, ActivityOptions opts) {
             mLaunchIntent = launchIntent;
         }
 
@@ -141,13 +137,16 @@
             try {
                 RecentsActivityLaunchState launchState =
                         Recents.getConfiguration().getLaunchState();
-                ActivityOptions opts = ActivityOptions.makeCustomAnimation(RecentsActivity.this,
-                        launchState.launchedFromSearchHome ?
-                                R.anim.recents_to_search_launcher_enter :
-                                R.anim.recents_to_launcher_enter,
-                        launchState.launchedFromSearchHome ?
-                                R.anim.recents_to_search_launcher_exit :
-                                R.anim.recents_to_launcher_exit);
+                ActivityOptions opts = mOpts;
+                if (opts == null) {
+                    opts = ActivityOptions.makeCustomAnimation(RecentsActivity.this,
+                            launchState.launchedFromSearchHome ?
+                                    R.anim.recents_to_search_launcher_enter :
+                                    R.anim.recents_to_launcher_enter,
+                            launchState.launchedFromSearchHome ?
+                                    R.anim.recents_to_search_launcher_exit :
+                                    R.anim.recents_to_launcher_exit);
+                }
                 startActivityAsUser(mLaunchIntent, opts.toBundle(), UserHandle.CURRENT);
             } catch (Exception e) {
                 Log.e(TAG, getString(R.string.recents_launch_error_message, "Home"), e);
@@ -205,12 +204,9 @@
         int launchTaskIndexInStack = launchTarget != null
                 ? stack.indexOfStackTask(launchTarget)
                 : 0;
-        boolean hasStatusBarScrim = taskCount > 0;
-        boolean animateStatusBarScrim = launchState.launchedFromHome;
         boolean hasNavBarScrim = (taskCount > 0) && !config.hasTransposedNavBar;
         boolean animateNavBarScrim = true;
-        mScrimViews.prepareEnterRecentsAnimation(hasStatusBarScrim, animateStatusBarScrim,
-                hasNavBarScrim, animateNavBarScrim);
+        mScrimViews.prepareEnterRecentsAnimation(hasNavBarScrim, animateNavBarScrim);
 
         // Keep track of whether we launched from the nav bar button or via alt-tab
         if (launchState.launchedWithAltTab) {
@@ -234,8 +230,7 @@
      * Dismisses the history view back into the stack view.
      */
     boolean dismissHistory() {
-        // Try and hide the history view first
-        if (mHistoryView != null && mHistoryView.isVisible()) {
+        if (mRecentsView.isHistoryVisible()) {
             EventBus.getDefault().send(new HideHistoryEvent(true /* animate */));
             return true;
         }
@@ -287,9 +282,24 @@
      * Dismisses Recents directly to Home without checking whether it is currently visible.
      */
     void dismissRecentsToHome(boolean animateTaskViews) {
+        dismissRecentsToHome(animateTaskViews, null);
+    }
+
+    /**
+     * Dismisses Recents directly to Home without checking whether it is currently visible.
+     *
+     * @param overrideAnimation If not null, will override the default animation that is based on
+     *                          how Recents was launched.
+     */
+    void dismissRecentsToHome(boolean animateTaskViews, ActivityOptions overrideAnimation) {
         DismissRecentsToHomeAnimationStarted dismissEvent =
                 new DismissRecentsToHomeAnimationStarted(animateTaskViews);
-        dismissEvent.addPostAnimationCallback(mFinishLaunchHomeRunnable);
+        if (overrideAnimation != null) {
+            dismissEvent.addPostAnimationCallback(new FinishRecentsRunnable(
+                    mFinishLaunchHomeRunnable.mLaunchIntent, overrideAnimation));
+        } else {
+            dismissEvent.addPostAnimationCallback(mFinishLaunchHomeRunnable);
+        }
         dismissEvent.addPostAnimationCallback(new Runnable() {
             @Override
             public void run() {
@@ -342,7 +352,6 @@
         mRecentsView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE |
                 View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN |
                 View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION);
-        mHistoryViewStub = (ViewStub) findViewById(R.id.history_view_stub);
         mScrimViews = new SystemBarScrimViews(this);
         getWindow().getAttributes().privateFlags |=
                 WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_DECOR_VIEW_VISIBILITY;
@@ -360,7 +369,7 @@
         homeIntent.addCategory(Intent.CATEGORY_HOME);
         homeIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK |
                 Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
-        mFinishLaunchHomeRunnable = new FinishRecentsRunnable(homeIntent);
+        mFinishLaunchHomeRunnable = new FinishRecentsRunnable(homeIntent, null);
 
         // Bind the search app widget when we first start up
         if (RecentsDebugFlags.Static.EnableSearchBar) {
@@ -441,7 +450,7 @@
 
         // Reset some states
         mIgnoreAltTabRelease = false;
-        if (mHistoryView != null) {
+        if (mRecentsView.isHistoryVisible()) {
             EventBus.getDefault().send(new HideHistoryEvent(false /* animate */));
         }
 
@@ -503,8 +512,7 @@
     @Override
     protected void onSaveInstanceState(Bundle outState) {
         super.onSaveInstanceState(outState);
-        outState.putBoolean(KEY_SAVED_STATE_HISTORY_VISIBLE,
-                (mHistoryView != null) && mHistoryView.isVisible());
+        outState.putBoolean(KEY_SAVED_STATE_HISTORY_VISIBLE, mRecentsView.isHistoryVisible());
     }
 
     @Override
@@ -651,15 +659,12 @@
             }
         } else if (event.triggeredFromHomeKey) {
             // Otherwise, dismiss Recents to Home
-            if (mHistoryView != null && mHistoryView.isVisible()) {
-                HideHistoryEvent hideEvent = new HideHistoryEvent(true /* animate */);
-                hideEvent.addPostAnimationCallback(new Runnable() {
-                    @Override
-                    public void run() {
-                        dismissRecentsToHome(true /* animateTaskViews */);
-                    }
-                });
-                EventBus.getDefault().send(hideEvent);
+            if (mRecentsView.isHistoryVisible()) {
+                // If the history view is visible, then just cross-fade home
+                ActivityOptions opts = ActivityOptions.makeCustomAnimation(RecentsActivity.this,
+                                R.anim.recents_to_launcher_enter,
+                                R.anim.recents_to_launcher_exit);
+                dismissRecentsToHome(false /* animate */, opts);
 
             } else {
                 dismissRecentsToHome(true /* animateTaskViews */);
@@ -795,21 +800,6 @@
         mIgnoreAltTabRelease = true;
     }
 
-    public final void onBusEvent(ShowHistoryEvent event) {
-        if (mHistoryView == null) {
-            mHistoryView = (RecentsHistoryView) mHistoryViewStub.inflate();
-            // Since this history view is inflated by a view stub after the insets have already
-            // been applied, we have to set them ourselves initial from the insets that were last
-            // provided.
-            mHistoryView.setSystemInsets(mRecentsView.getSystemInsets());
-        }
-        mHistoryView.show(mRecentsView.getTaskStack(), event.getAnimationTrigger());
-    }
-
-    public final void onBusEvent(HideHistoryEvent event) {
-        mHistoryView.hide(event.animate, event.getAnimationTrigger());
-    }
-
     private void refreshSearchWidgetView() {
         if (mSearchWidgetInfo != null) {
             SystemServicesProxy ssp = Recents.getSystemServices();
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java
index b78fd22..d1301cf 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java
@@ -19,10 +19,12 @@
 import android.app.ActivityManager;
 import android.app.ActivityOptions;
 import android.app.ITaskStackListener;
+import android.app.UiModeManager;
 import android.appwidget.AppWidgetProviderInfo;
 import android.content.ActivityNotFoundException;
 import android.content.Context;
 import android.content.Intent;
+import android.content.res.Configuration;
 import android.content.res.Resources;
 import android.graphics.Bitmap;
 import android.graphics.Canvas;
@@ -87,7 +89,10 @@
 
     public final static String RECENTS_PACKAGE = "com.android.systemui";
     public final static String RECENTS_ACTIVITY = "com.android.systemui.recents.RecentsActivity";
+    public final static String RECENTS_TV_ACTIVITY = "com.android.systemui.recents.tv.RecentsTvActivity";
 
+    //Used to store tv or non-tv activty for use in creating intents.
+    private final String mRecentsIntentActivityName;
     /**
      * An implementation of ITaskStackListener, that allows us to listen for changes to the system
      * task stacks and update recents accordingly.
@@ -210,6 +215,14 @@
         launchOpts.numVisibleTaskThumbnails = loader.getThumbnailCacheSize();
         launchOpts.onlyLoadForCache = true;
         loader.loadTasks(mContext, plan, launchOpts);
+
+        //Manager used to determine if we are running on tv or not
+        UiModeManager uiModeManager = (UiModeManager) mContext.getSystemService(Context.UI_MODE_SERVICE);
+        if (uiModeManager.getCurrentModeType() == Configuration.UI_MODE_TYPE_TELEVISION) {
+            mRecentsIntentActivityName = RECENTS_TV_ACTIVITY;
+        } else {
+            mRecentsIntentActivityName = RECENTS_ACTIVITY;
+        }
     }
 
     public void onBootCompleted() {
@@ -906,10 +919,11 @@
         launchState.launchedViaDragGesture = mDraggingInRecents;
 
         Intent intent = new Intent();
-        intent.setClassName(RECENTS_PACKAGE, RECENTS_ACTIVITY);
+        intent.setClassName(RECENTS_PACKAGE, mRecentsIntentActivityName);
         intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
                 | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS
                 | Intent.FLAG_ACTIVITY_TASK_ON_HOME);
+
         if (opts != null) {
             mContext.startActivityAsUser(intent, opts.toBundle(), UserHandle.CURRENT);
         } else {
diff --git a/location/java/android/location/IGpsStatusProvider.aidl b/packages/SystemUI/src/com/android/systemui/recents/events/activity/ClearHistoryEvent.java
similarity index 60%
copy from location/java/android/location/IGpsStatusProvider.aidl
copy to packages/SystemUI/src/com/android/systemui/recents/events/activity/ClearHistoryEvent.java
index cf277c8..98c0a69 100644
--- a/location/java/android/location/IGpsStatusProvider.aidl
+++ b/packages/SystemUI/src/com/android/systemui/recents/events/activity/ClearHistoryEvent.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2009 The Android Open Source Project
+ * Copyright (C) 2016 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,16 +14,13 @@
  * limitations under the License.
  */
 
-package android.location;
+package com.android.systemui.recents.events.activity;
 
-import android.location.IGpsStatusListener;
+import com.android.systemui.recents.events.EventBus;
 
 /**
- * An interface for location providers that provide GPS status information.
- *
- * {@hide}
+ * This is sent when the history is to be cleared
  */
-interface IGpsStatusProvider {
-    void addGpsStatusListener(IGpsStatusListener listener);
-    void removeGpsStatusListener(IGpsStatusListener listener);
+public class ClearHistoryEvent extends EventBus.AnimatedEvent {
+    // Simple event
 }
diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/activity/HideHistoryEvent.java b/packages/SystemUI/src/com/android/systemui/recents/events/activity/HideHistoryEvent.java
index af3eeb0..bacf3bd 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/events/activity/HideHistoryEvent.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/events/activity/HideHistoryEvent.java
@@ -21,7 +21,7 @@
 /**
  * This is sent when the history view will be closed.
  */
-public class HideHistoryEvent extends EventBus.AnimatedEvent {
+public class HideHistoryEvent extends EventBus.Event {
 
     public final boolean animate;
 
diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/activity/ShowHistoryButtonEvent.java b/packages/SystemUI/src/com/android/systemui/recents/events/activity/ShowHistoryButtonEvent.java
index 7042537..ae803ea 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/events/activity/ShowHistoryButtonEvent.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/events/activity/ShowHistoryButtonEvent.java
@@ -22,5 +22,11 @@
  * This is sent when the history view button should be shown.
  */
 public class ShowHistoryButtonEvent extends EventBus.Event {
-    // Simple event
+
+    // Whether or not to translate the history button when showing it
+    public final boolean translate;
+
+    public ShowHistoryButtonEvent(boolean translate) {
+        this.translate = translate;
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/activity/ShowHistoryEvent.java b/packages/SystemUI/src/com/android/systemui/recents/events/activity/ShowHistoryEvent.java
index b39d645..469f336 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/events/activity/ShowHistoryEvent.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/events/activity/ShowHistoryEvent.java
@@ -21,7 +21,7 @@
 /**
  * This is sent when the history view button is clicked.
  */
-public class ShowHistoryEvent extends EventBus.AnimatedEvent {
+public class ShowHistoryEvent extends EventBus.Event {
 
     // Simple event
 
diff --git a/location/java/android/location/IGpsStatusProvider.aidl b/packages/SystemUI/src/com/android/systemui/recents/events/activity/ToggleHistoryEvent.java
similarity index 60%
copy from location/java/android/location/IGpsStatusProvider.aidl
copy to packages/SystemUI/src/com/android/systemui/recents/events/activity/ToggleHistoryEvent.java
index cf277c8..aaf77af 100644
--- a/location/java/android/location/IGpsStatusProvider.aidl
+++ b/packages/SystemUI/src/com/android/systemui/recents/events/activity/ToggleHistoryEvent.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2009 The Android Open Source Project
+ * Copyright (C) 2016 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,16 +14,15 @@
  * limitations under the License.
  */
 
-package android.location;
+package com.android.systemui.recents.events.activity;
 
-import android.location.IGpsStatusListener;
+import com.android.systemui.recents.events.EventBus;
 
 /**
- * An interface for location providers that provide GPS status information.
- *
- * {@hide}
+ * This is sent when the history view button is clicked.
  */
-interface IGpsStatusProvider {
-    void addGpsStatusListener(IGpsStatusListener listener);
-    void removeGpsStatusListener(IGpsStatusListener listener);
+public class ToggleHistoryEvent extends EventBus.AnimatedEvent {
+
+    // Simple event
+
 }
diff --git a/location/java/android/location/IGpsStatusProvider.aidl b/packages/SystemUI/src/com/android/systemui/recents/events/ui/ResetBackgroundScrimEvent.java
similarity index 60%
copy from location/java/android/location/IGpsStatusProvider.aidl
copy to packages/SystemUI/src/com/android/systemui/recents/events/ui/ResetBackgroundScrimEvent.java
index cf277c8..863f40b 100644
--- a/location/java/android/location/IGpsStatusProvider.aidl
+++ b/packages/SystemUI/src/com/android/systemui/recents/events/ui/ResetBackgroundScrimEvent.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2009 The Android Open Source Project
+ * Copyright (C) 2016 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,16 +14,13 @@
  * limitations under the License.
  */
 
-package android.location;
+package com.android.systemui.recents.events.ui;
 
-import android.location.IGpsStatusListener;
+import com.android.systemui.recents.events.EventBus;
 
 /**
- * An interface for location providers that provide GPS status information.
- *
- * {@hide}
+ * This is sent to reset the background scrim back to the initial state.
  */
-interface IGpsStatusProvider {
-    void addGpsStatusListener(IGpsStatusListener listener);
-    void removeGpsStatusListener(IGpsStatusListener listener);
+public class ResetBackgroundScrimEvent extends EventBus.Event {
+    // Simple event
 }
diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/ui/UpdateBackgroundScrimEvent.java b/packages/SystemUI/src/com/android/systemui/recents/events/ui/UpdateBackgroundScrimEvent.java
new file mode 100644
index 0000000..fdd4c67
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/recents/events/ui/UpdateBackgroundScrimEvent.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.recents.events.ui;
+
+import com.android.systemui.recents.events.EventBus;
+
+/**
+ * This is sent to request an update to the background scrim.
+ */
+public class UpdateBackgroundScrimEvent extends EventBus.Event {
+
+    public final float alpha;
+
+    public UpdateBackgroundScrimEvent(float alpha) {
+        this.alpha = alpha;
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/history/RecentsHistoryAdapter.java b/packages/SystemUI/src/com/android/systemui/recents/history/RecentsHistoryAdapter.java
index 4000991..e288878 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/history/RecentsHistoryAdapter.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/history/RecentsHistoryAdapter.java
@@ -34,6 +34,7 @@
 import com.android.systemui.recents.events.EventBus;
 import com.android.systemui.recents.events.activity.HideHistoryButtonEvent;
 import com.android.systemui.recents.events.activity.HideHistoryEvent;
+import com.android.systemui.recents.events.ui.DeleteTaskDataEvent;
 import com.android.systemui.recents.misc.SystemServicesProxy;
 import com.android.systemui.recents.model.RecentsTaskLoader;
 import com.android.systemui.recents.model.Task;
@@ -76,6 +77,10 @@
             // This callback is only made for TaskRow view holders
             ImageView iv = (ImageView) content.findViewById(R.id.icon);
             iv.setImageDrawable(task.icon);
+            iv.animate()
+                    .alpha(1f)
+                    .setDuration(100)
+                    .start();
         }
 
         @Override
@@ -83,6 +88,7 @@
             // This callback is only made for TaskRow view holders
             ImageView iv = (ImageView) content.findViewById(R.id.icon);
             iv.setImageBitmap(null);
+            iv.animate().cancel();
         }
 
         @Override
@@ -166,14 +172,16 @@
         final List<Task> tasksMostRecent = new ArrayList<>(stack.getHistoricalTasks());
         Collections.reverse(tasksMostRecent);
         int prevDateKey = -1;
+        int taskCount = tasksMostRecent.size();
         mRows.clear();
         mTaskRowCount.clear();
-        for (Task task : tasksMostRecent) {
+        Calendar cal = Calendar.getInstance(l);
+        for (int i = 0; i < taskCount; i++) {
+            Task task = tasksMostRecent.get(i);
             if (task.isFreeformTask()) {
                 continue;
             }
 
-            Calendar cal = Calendar.getInstance(l);
             cal.setTimeInMillis(task.key.lastActiveTime);
             int dateKey = Objects.hash(cal.get(Calendar.YEAR), cal.get(Calendar.DAY_OF_YEAR));
             if (dateKey != prevDateKey) {
@@ -208,6 +216,23 @@
     }
 
     /**
+     * Removes all historical tasks.
+     */
+    public void removeAllTasks() {
+        for (int i = mRows.size() - 1; i >= 0; i--) {
+            Row row = mRows.get(i);
+            if (row.getViewType() == TASK_ROW_VIEW_TYPE) {
+                TaskRow taskRow = (TaskRow) row;
+                Task task = taskRow.task;
+                mStack.removeTask(task, TaskViewAnimation.IMMEDIATE);
+                EventBus.getDefault().send(new DeleteTaskDataEvent(task));
+                i = removeTaskRow(i);
+            }
+        }
+        dismissHistory();
+    }
+
+    /**
      * Returns the row at the given {@param position}.
      */
     public Row getRow(int position) {
@@ -245,8 +270,10 @@
                 taskRow.task.addCallback(holder);
                 TextView tv = (TextView) holder.content.findViewById(R.id.description);
                 tv.setText(taskRow.task.title);
+                ImageView iv = (ImageView) holder.content.findViewById(R.id.icon);
+                iv.setAlpha(0f);
                 holder.content.setOnClickListener(taskRow);
-                loader.loadTaskData(taskRow.task);
+                loader.loadTaskData(taskRow.task, false /* fetchAndInvalidateThumbnails */);
                 break;
             }
         }
@@ -255,19 +282,25 @@
     @Override
     public void onViewRecycled(ViewHolder holder) {
         RecentsTaskLoader loader = Recents.getTaskLoader();
-
         int position = holder.getAdapterPosition();
         if (position != RecyclerView.NO_POSITION) {
             Row row = mRows.get(position);
             int viewType = row.getViewType();
             if (viewType == TASK_ROW_VIEW_TYPE) {
                 TaskRow taskRow = (TaskRow) row;
-                taskRow.task.removeCallback(holder);
                 loader.unloadTaskData(taskRow.task);
+                taskRow.task.removeCallback(holder);
             }
         }
     }
 
+    @Override
+    public boolean onFailedToRecycleView(ViewHolder holder) {
+        // Always recycle views, even if it is animating
+        onViewRecycled(holder);
+        return true;
+    }
+
     public void onTaskRemoved(Task task, int position) {
         // Since this is removed from the history, we need to update the stack as well to ensure
         // that the model is correct. Since the stack is hidden, we can update it immediately.
diff --git a/packages/SystemUI/src/com/android/systemui/recents/history/RecentsHistoryView.java b/packages/SystemUI/src/com/android/systemui/recents/history/RecentsHistoryView.java
index b9921b6..96b5cac 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/history/RecentsHistoryView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/history/RecentsHistoryView.java
@@ -16,6 +16,7 @@
 
 package com.android.systemui.recents.history;
 
+import android.animation.ValueAnimator;
 import android.content.Context;
 import android.content.res.Resources;
 import android.graphics.Rect;
@@ -36,23 +37,29 @@
 import com.android.systemui.recents.RecentsActivity;
 import com.android.systemui.recents.RecentsConfiguration;
 import com.android.systemui.recents.events.EventBus;
+import com.android.systemui.recents.events.activity.ClearHistoryEvent;
 import com.android.systemui.recents.events.activity.PackagesChangedEvent;
-import com.android.systemui.recents.misc.ReferenceCountedTrigger;
+import com.android.systemui.recents.events.ui.ResetBackgroundScrimEvent;
+import com.android.systemui.recents.events.ui.UpdateBackgroundScrimEvent;
 import com.android.systemui.recents.model.TaskStack;
+import com.android.systemui.recents.views.AnimateableViewBounds;
 
 /**
  * A list of the recent tasks that are not in the stack.
  */
-public class RecentsHistoryView extends LinearLayout {
+public class RecentsHistoryView extends LinearLayout
+        implements ValueAnimator.AnimatorUpdateListener {
 
-    private static final String TAG = "RecentsHistoryView";
-    private static final boolean DEBUG = false;
+    private static final float TRANSLATION_Y_PCT = 0.25f;
+    private static final float BG_SCRIM_ALPHA = 0.625f;
 
     private RecyclerView mRecyclerView;
     private RecentsHistoryAdapter mAdapter;
     private RecentsHistoryItemTouchCallbacks mItemTouchHandler;
+    private AnimateableViewBounds mViewBounds;
     private boolean mIsVisible;
     private Rect mSystemInsets = new Rect();
+    private int mHeaderHeight;
 
     private Interpolator mFastOutSlowInInterpolator;
     private Interpolator mFastOutLinearInInterpolator;
@@ -81,27 +88,35 @@
                 com.android.internal.R.interpolator.fast_out_slow_in);
         mFastOutLinearInInterpolator = AnimationUtils.loadInterpolator(context,
                 com.android.internal.R.interpolator.fast_out_linear_in);
+        mViewBounds = new AnimateableViewBounds(this, 0);
+        setOutlineProvider(mViewBounds);
     }
 
     /**
      * Updates this history view with the recent tasks, and then shows it.
      */
-    public void show(TaskStack stack, ReferenceCountedTrigger postHideAnimationTrigger) {
+    public void show(TaskStack stack, int stackHeight, View clearAllButton) {
         setVisibility(View.VISIBLE);
         setAlpha(0f);
-        postHideAnimationTrigger.addLastDecrementRunnable(new Runnable() {
-            @Override
-            public void run() {
-                animate()
-                        .alpha(1f)
-                        .setDuration(mHistoryTransitionDuration)
-                        .setInterpolator(mFastOutSlowInInterpolator)
-                        .withLayer()
-                        .start();
-            }
-        });
+        setTranslationY(-stackHeight * TRANSLATION_Y_PCT);
+        animate()
+                .alpha(1f)
+                .translationY(0f)
+                .setDuration(mHistoryTransitionDuration)
+                .setInterpolator(mFastOutSlowInInterpolator)
+                .setUpdateListener(this)
+                .start();
+        clearAllButton.setVisibility(View.VISIBLE);
+        clearAllButton.setAlpha(0f);
+        clearAllButton.animate()
+                .alpha(1f)
+                .setDuration(mHistoryTransitionDuration)
+                .setInterpolator(mFastOutSlowInInterpolator)
+                .withLayer()
+                .start();
         mAdapter.updateTasks(getContext(), stack);
         mIsVisible = true;
+        EventBus.getDefault().send(new UpdateBackgroundScrimEvent(BG_SCRIM_ALPHA));
 
         MetricsLogger.visible(mRecyclerView.getContext(), MetricsEvent.OVERVIEW_HISTORY);
     }
@@ -109,31 +124,42 @@
     /**
      * Hides this history view.
      */
-    public void hide(boolean animate, final ReferenceCountedTrigger postAnimationTrigger) {
+    public void hide(boolean animate, int stackHeight, final View clearAllButton) {
         if (animate) {
             animate()
                     .alpha(0f)
+                    .translationY(-stackHeight * TRANSLATION_Y_PCT)
                     .setDuration(mHistoryTransitionDuration)
                     .setInterpolator(mFastOutLinearInInterpolator)
+                    .setUpdateListener(this)
                     .withEndAction(new Runnable() {
                         @Override
                         public void run() {
                             setVisibility(View.INVISIBLE);
-                            if (postAnimationTrigger != null) {
-                                postAnimationTrigger.decrement();
-                            }
+                        }
+                    })
+                    .start();
+            clearAllButton.animate()
+                    .alpha(0f)
+                    .translationY(0f)
+                    .setDuration(mHistoryTransitionDuration)
+                    .setInterpolator(mFastOutSlowInInterpolator)
+                    .withEndAction(new Runnable() {
+                        @Override
+                        public void run() {
+                            clearAllButton.setVisibility(View.INVISIBLE);
                         }
                     })
                     .withLayer()
                     .start();
-            if (postAnimationTrigger != null) {
-                postAnimationTrigger.increment();
-            }
         } else {
             setAlpha(0f);
             setVisibility(View.INVISIBLE);
+            clearAllButton.setAlpha(0f);
+            clearAllButton.setVisibility(View.INVISIBLE);
         }
         mIsVisible = false;
+        EventBus.getDefault().send(new ResetBackgroundScrimEvent());
 
         MetricsLogger.hidden(mRecyclerView.getContext(), MetricsEvent.OVERVIEW_HISTORY);
     }
@@ -142,7 +168,15 @@
      * Updates the system insets of this history view to the provided values.
      */
     public void setSystemInsets(Rect systemInsets) {
-        mSystemInsets.set(systemInsets.left, systemInsets.top, systemInsets.right, systemInsets.bottom);
+        mSystemInsets.set(systemInsets);
+        requestLayout();
+    }
+
+    /**
+     * Updates the header height to account for the history button bar.
+     */
+    public void setHeaderHeight(int height) {
+        mHeaderHeight = height;
         requestLayout();
     }
 
@@ -177,7 +211,7 @@
         int stackHeightPadding = mContext.getResources().getDimensionPixelSize(
                 R.dimen.recents_stack_top_padding);
         mRecyclerView.setPadding(stackWidthPadding + mSystemInsets.left,
-                stackHeightPadding + mSystemInsets.top,
+                stackHeightPadding + mSystemInsets.top + mHeaderHeight,
                 stackWidthPadding + mSystemInsets.right, mSystemInsets.bottom);
 
         super.onMeasure(widthMeasureSpec, heightMeasureSpec);
@@ -201,9 +235,25 @@
         return insets;
     }
 
+    @Override
+    public void onAnimationUpdate(ValueAnimator animation) {
+        // Clip the top of the view by the header bar height
+        int top = Math.max(0, (int) -getTranslationY()) + mSystemInsets.top + mHeaderHeight;
+        mViewBounds.setClipTop(top);
+    }
+
+    @Override
+    public boolean hasOverlappingRendering() {
+        return false;
+    }
+
     /**** EventBus Events ****/
 
     public final void onBusEvent(PackagesChangedEvent event) {
         mAdapter.removeTasks(event.packageName, event.userId);
     }
+
+    public final void onBusEvent(ClearHistoryEvent event) {
+        mAdapter.removeAllTasks();
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
index 2882cec..aa006d1 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
@@ -81,6 +81,7 @@
 import static android.app.ActivityManager.StackId.DOCKED_STACK_ID;
 import static android.app.ActivityManager.StackId.FREEFORM_WORKSPACE_STACK_ID;
 import static android.app.ActivityManager.StackId.HOME_STACK_ID;
+import static android.app.ActivityManager.StackId.PINNED_STACK_ID;
 import static android.provider.Settings.Global.DEVELOPMENT_ENABLE_FREEFORM_WINDOWS_SUPPORT;
 
 /**
@@ -266,8 +267,9 @@
             ComponentName topActivity = topTask.topActivity;
 
             // Check if the front most activity is recents
-            if (topActivity.getPackageName().equals(RecentsImpl.RECENTS_PACKAGE) &&
-                    topActivity.getClassName().equals(RecentsImpl.RECENTS_ACTIVITY)) {
+            if ((topActivity.getPackageName().equals(RecentsImpl.RECENTS_PACKAGE) &&
+                    (topActivity.getClassName().equals(RecentsImpl.RECENTS_ACTIVITY) ||
+                    topActivity.getClassName().equals(RecentsImpl.RECENTS_TV_ACTIVITY)))) {
                 if (isHomeTopMost != null) {
                     isHomeTopMost.value = false;
                 }
@@ -356,6 +358,13 @@
     }
 
     /**
+     * Returns whether the given stack id is the pinned stack id.
+     */
+    public static boolean isPinnedStack(int stackId){
+        return stackId == PINNED_STACK_ID;
+    }
+
+    /**
      * Returns whether the given stack id is the docked stack id.
      */
     public static boolean isDockedStack(int stackId) {
@@ -968,4 +977,20 @@
     public void requestKeyboardShortcuts(Context context, KeyboardShortcutsReceiver receiver) {
         mWm.requestAppKeyboardShortcuts(receiver);
     }
+
+    public void focusPinnedStack() {
+        try {
+            mIam.setFocusedStack(PINNED_STACK_ID);
+        } catch (RemoteException e) {
+            e.printStackTrace();
+        }
+    }
+
+    public void focusHomeStack() {
+        try {
+            mIam.setFocusedStack(HOME_STACK_ID);
+        } catch (RemoteException e) {
+            e.printStackTrace();
+        }
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoadPlan.java b/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoadPlan.java
index d15828a..3029acf 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoadPlan.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoadPlan.java
@@ -37,6 +37,7 @@
 
 import java.util.ArrayList;
 import java.util.Collections;
+import java.util.Formatter;
 import java.util.List;
 
 
@@ -130,6 +131,7 @@
         SparseIntArray affiliatedTaskCounts = new SparseIntArray();
         String dismissDescFormat = mContext.getString(
                 R.string.accessibility_recents_item_will_be_dismissed);
+        Formatter dismissDescFormatter = new Formatter();
         long lastStackActiveTime = Prefs.getLong(mContext,
                 Prefs.Key.OVERVIEW_LAST_STACK_TASK_ACTIVE_TIME, 0);
         if (RecentsDebugFlags.Static.EnableMockTasks) {
@@ -168,7 +170,8 @@
             // Load the title, icon, and color
             String title = loader.getAndUpdateActivityTitle(taskKey, t.taskDescription);
             String contentDescription = loader.getAndUpdateContentDescription(taskKey, res);
-            String dismissDescription = String.format(dismissDescFormat, contentDescription);
+            String dismissDescription = dismissDescFormatter.format(dismissDescFormat,
+                    contentDescription).toString();
             Drawable icon = isStackTask
                     ? loader.getAndUpdateActivityIcon(taskKey, t.taskDescription, res, false)
                     : null;
diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoader.java b/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoader.java
index 44ad239..5c77d8a 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoader.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoader.java
@@ -334,10 +334,19 @@
         }
     }
 
-    /** Acquires the task resource data directly from the pool. */
-    public void loadTaskData(Task t) {
+    /**
+     * Acquires the task resource data directly from the cache, loading if necessary.
+     *
+     * @param fetchAndInvalidateThumbnails If set, will try loading thumbnails, invalidating them
+     *                                     in the cache and loading if necessary. Otherwise, do not
+     *                                     load the thumbnail unless the icon also has to be loaded.
+     */
+    public void loadTaskData(Task t, boolean fetchAndInvalidateThumbnails) {
         Drawable icon = mIconCache.getAndInvalidateIfModified(t.key);
-        Bitmap thumbnail = mThumbnailCache.getAndInvalidateIfModified(t.key);
+        Bitmap thumbnail = mDefaultThumbnail;
+        if (fetchAndInvalidateThumbnails) {
+            thumbnail = mThumbnailCache.getAndInvalidateIfModified(t.key);
+        }
 
         // Grab the thumbnail/icon from the cache, if either don't exist, then trigger a reload and
         // use the default assets in their place until they load
@@ -360,6 +369,8 @@
         mLoadQueue.removeTask(t);
         mThumbnailCache.remove(t.key);
         mIconCache.remove(t.key);
+        mActivityLabelCache.remove(t.key);
+        mContentDescriptionCache.remove(t.key);
         mActivityInfoCache.remove(t.key.getComponent());
         if (notifyTaskDataUnloaded) {
             t.notifyTaskDataUnloaded(null, mDefaultIcon);
diff --git a/packages/SystemUI/src/com/android/systemui/recents/tv/RecentsTvActivity.java b/packages/SystemUI/src/com/android/systemui/recents/tv/RecentsTvActivity.java
new file mode 100644
index 0000000..6593169
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/recents/tv/RecentsTvActivity.java
@@ -0,0 +1,451 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.systemui.recents.tv;
+
+import android.app.Activity;
+import android.app.ActivityOptions;
+import android.content.Intent;
+import android.os.Bundle;
+import android.os.UserHandle;
+
+import android.util.Log;
+import android.view.KeyEvent;
+import android.view.View;
+import android.view.ViewTreeObserver.OnPreDrawListener;
+import android.view.WindowManager;
+import com.android.systemui.R;
+import com.android.systemui.recents.*;
+import com.android.systemui.recents.events.EventBus;
+import com.android.systemui.recents.events.activity.CancelEnterRecentsWindowAnimationEvent;
+import com.android.systemui.recents.events.activity.DismissRecentsToHomeAnimationStarted;
+import com.android.systemui.recents.events.activity.EnterRecentsWindowAnimationCompletedEvent;
+import com.android.systemui.recents.events.activity.EnterRecentsWindowLastAnimationFrameEvent;
+import com.android.systemui.recents.events.activity.HideRecentsEvent;
+import com.android.systemui.recents.events.activity.LaunchTaskFailedEvent;
+import com.android.systemui.recents.events.activity.TaskStackUpdatedEvent;
+import com.android.systemui.recents.events.activity.ToggleRecentsEvent;
+import com.android.systemui.recents.events.component.RecentsVisibilityChangedEvent;
+import com.android.systemui.recents.events.ui.AllTaskViewsDismissedEvent;
+import com.android.systemui.recents.events.ui.DeleteTaskDataEvent;
+import com.android.systemui.recents.events.ui.UpdateFreeformTaskViewVisibilityEvent;
+import com.android.systemui.recents.events.ui.UserInteractionEvent;
+import com.android.systemui.recents.events.ui.focus.DismissFocusedTaskViewEvent;
+import com.android.systemui.recents.misc.SystemServicesProxy;
+import com.android.systemui.recents.model.RecentsPackageMonitor;
+import com.android.systemui.recents.model.RecentsTaskLoadPlan;
+import com.android.systemui.recents.model.RecentsTaskLoader;
+import com.android.systemui.recents.model.Task;
+import com.android.systemui.recents.model.TaskStack;
+import com.android.systemui.recents.tv.views.RecentsTvView;
+import com.android.systemui.recents.tv.views.TaskStackHorizontalViewAdapter;
+import com.android.systemui.statusbar.BaseStatusBar;
+import com.android.systemui.tv.pip.PipManager;
+
+import java.util.ArrayList;
+/**
+ * The main TV recents activity started by the RecentsImpl.
+ */
+public class RecentsTvActivity extends Activity implements OnPreDrawListener {
+    private final static String TAG = "RecentsTvActivity";
+    private final static boolean DEBUG = false;
+
+    public final static int EVENT_BUS_PRIORITY = Recents.EVENT_BUS_PRIORITY + 1;
+
+    private boolean mFinishedOnStartup;
+    private RecentsPackageMonitor mPackageMonitor;
+    private long mLastTabKeyEventTime;
+    private boolean mIgnoreAltTabRelease;
+
+    private RecentsTvView mRecentsView;
+    private TaskStackHorizontalViewAdapter mTaskStackViewAdapter;
+    private FinishRecentsRunnable mFinishLaunchHomeRunnable;
+
+
+    /**
+     * A common Runnable to finish Recents by launching Home with an animation depending on the
+     * last activity launch state.  Generally we always launch home when we exit Recents rather than
+     * just finishing the activity since we don't know what is behind Recents in the task stack.
+     */
+    class FinishRecentsRunnable implements Runnable {
+        Intent mLaunchIntent;
+
+        /**
+         * Creates a finish runnable that starts the specified intent.
+         */
+        public FinishRecentsRunnable(Intent launchIntent) {
+            mLaunchIntent = launchIntent;
+        }
+
+        @Override
+        public void run() {
+            try {
+                RecentsActivityLaunchState launchState =
+                        Recents.getConfiguration().getLaunchState();
+                ActivityOptions opts = ActivityOptions.makeCustomAnimation(RecentsTvActivity.this,
+                        launchState.launchedFromSearchHome ?
+                                R.anim.recents_to_search_launcher_enter :
+                                R.anim.recents_to_launcher_enter,
+                        launchState.launchedFromSearchHome ?
+                                R.anim.recents_to_search_launcher_exit :
+                                R.anim.recents_to_launcher_exit);
+                startActivityAsUser(mLaunchIntent, opts.toBundle(), UserHandle.CURRENT);
+            } catch (Exception e) {
+                Log.e(TAG, getString(R.string.recents_launch_error_message, "Home"), e);
+            }
+        }
+    }
+
+    private void updateRecentsTasks() {
+        RecentsTaskLoader loader = Recents.getTaskLoader();
+        RecentsTaskLoadPlan plan = RecentsImpl.consumeInstanceLoadPlan();
+        if (plan == null) {
+            plan = loader.createLoadPlan(this);
+        }
+
+        RecentsConfiguration config = Recents.getConfiguration();
+        RecentsActivityLaunchState launchState = config.getLaunchState();
+        if (!plan.hasTasks()) {
+            loader.preloadTasks(plan, -1, launchState.launchedFromHome);
+        }
+        TaskStack stack = plan.getTaskStack();
+        RecentsTaskLoadPlan.Options loadOpts = new RecentsTaskLoadPlan.Options();
+        loadOpts.runningTaskId = launchState.launchedToTaskId;
+        loadOpts.numVisibleTasks = stack.getStackTaskCount();
+        loadOpts.numVisibleTaskThumbnails = stack.getStackTaskCount();
+        loader.loadTasks(this, plan, loadOpts);
+
+
+        mRecentsView.setTaskStack(stack);
+        if (mTaskStackViewAdapter == null) {
+            mTaskStackViewAdapter = new TaskStackHorizontalViewAdapter(stack.getStackTasks());
+            mRecentsView.setTaskStackViewAdapter(mTaskStackViewAdapter);
+        } else {
+            mTaskStackViewAdapter.setNewStackTasks(stack.getStackTasks());
+        }
+
+        if (launchState.launchedToTaskId != -1) {
+            ArrayList<Task> tasks = stack.getStackTasks();
+            int taskCount = tasks.size();
+            for (int i = 0; i < taskCount; i++) {
+                Task t = tasks.get(i);
+                if (t.key.id == launchState.launchedToTaskId) {
+                    t.isLaunchTarget = true;
+                    break;
+                }
+            }
+        }
+    }
+
+    boolean dismissRecentsToLaunchTargetTaskOrHome() {
+        SystemServicesProxy ssp = Recents.getSystemServices();
+        if (ssp.isRecentsTopMost(ssp.getTopMostTask(), null)) {
+            // If we have a focused Task, launch that Task now
+            if (mRecentsView.launchPreviousTask()) return true;
+            // If none of the other cases apply, then just go Home
+            dismissRecentsToHome(true /* animateTaskViews */);
+        }
+        return false;
+    }
+
+    boolean dismissRecentsToFocusedTaskOrHome() {
+        SystemServicesProxy ssp = Recents.getSystemServices();
+        if (ssp.isRecentsTopMost(ssp.getTopMostTask(), null)) {
+            // If we have a focused Task, launch that Task now
+            if (mRecentsView.launchFocusedTask()) return true;
+            // If none of the other cases apply, then just go Home
+            dismissRecentsToHome(true /* animateTaskViews */);
+            return true;
+        }
+        return false;
+    }
+
+    void dismissRecentsToHome(boolean animateTaskViews) {
+        DismissRecentsToHomeAnimationStarted dismissEvent =
+                new DismissRecentsToHomeAnimationStarted(animateTaskViews);
+        dismissEvent.addPostAnimationCallback(mFinishLaunchHomeRunnable);
+        dismissEvent.addPostAnimationCallback(new Runnable() {
+            @Override
+            public void run() {
+                Recents.getSystemServices().sendCloseSystemWindows(
+                        BaseStatusBar.SYSTEM_DIALOG_REASON_HOME_KEY);
+            }
+        });
+        EventBus.getDefault().send(dismissEvent);
+    }
+
+    boolean dismissRecentsToHomeIfVisible(boolean animated) {
+        SystemServicesProxy ssp = Recents.getSystemServices();
+        if (ssp.isRecentsTopMost(ssp.getTopMostTask(), null)) {
+            // Return to Home
+            dismissRecentsToHome(animated);
+            return true;
+        }
+        return false;
+    }
+
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        mFinishedOnStartup = false;
+
+        // In the case that the activity starts up before the Recents component has initialized
+        // (usually when debugging/pushing the SysUI apk), just finish this activity.
+        SystemServicesProxy ssp = Recents.getSystemServices();
+        if (ssp == null) {
+            mFinishedOnStartup = true;
+            finish();
+            return;
+        }
+
+        // Register this activity with the event bus
+        EventBus.getDefault().register(this, EVENT_BUS_PRIORITY);
+
+        mPackageMonitor = new RecentsPackageMonitor();
+        mPackageMonitor.register(this);
+
+        // Set the Recents layout
+        setContentView(R.layout.recents_on_tv);
+
+        mRecentsView = (RecentsTvView) findViewById(R.id.recents_view);
+        mRecentsView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE |
+                View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN |
+                View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION);
+
+        getWindow().getAttributes().privateFlags |=
+                WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_DECOR_VIEW_VISIBILITY;
+
+        // Create the home intent runnable
+        Intent homeIntent = new Intent(Intent.ACTION_MAIN, null);
+        homeIntent.addCategory(Intent.CATEGORY_HOME);
+        homeIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK |
+                Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
+        mFinishLaunchHomeRunnable = new FinishRecentsRunnable(homeIntent);
+    }
+
+    @Override
+    protected void onNewIntent(Intent intent) {
+        super.onNewIntent(intent);
+        setIntent(intent);
+    }
+
+    @Override
+    protected void onStart() {
+        super.onStart();
+
+        // Update the recent tasks
+        updateRecentsTasks();
+
+        // If this is a new instance from a configuration change, then we have to manually trigger
+        // the enter animation state, or if recents was relaunched by AM, without going through
+        // the normal mechanisms
+        RecentsConfiguration config = Recents.getConfiguration();
+        RecentsActivityLaunchState launchState = config.getLaunchState();
+        boolean wasLaunchedByAm = !launchState.launchedFromHome &&
+                !launchState.launchedFromAppWithThumbnail;
+        if (launchState.launchedHasConfigurationChanged || wasLaunchedByAm) {
+            EventBus.getDefault().send(new EnterRecentsWindowAnimationCompletedEvent());
+        }
+
+        // Notify that recents is now visible
+        SystemServicesProxy ssp = Recents.getSystemServices();
+        EventBus.getDefault().send(new RecentsVisibilityChangedEvent(this, ssp, true));
+    }
+
+    @Override
+    public void onEnterAnimationComplete() {
+        super.onEnterAnimationComplete();
+        EventBus.getDefault().send(new EnterRecentsWindowAnimationCompletedEvent());
+    }
+
+    @Override
+    protected void onStop() {
+        super.onStop();
+
+        mIgnoreAltTabRelease = false;
+        // Notify that recents is now hidden
+        SystemServicesProxy ssp = Recents.getSystemServices();
+        EventBus.getDefault().send(new RecentsVisibilityChangedEvent(this, ssp, false));
+
+        // Workaround for b/22542869, if the RecentsActivity is started again, but without going
+        // through SystemUI, we need to reset the config launch flags to ensure that we do not
+        // wait on the system to send a signal that was never queued.
+        RecentsConfiguration config = Recents.getConfiguration();
+        RecentsActivityLaunchState launchState = config.getLaunchState();
+        launchState.launchedFromHome = false;
+        launchState.launchedFromSearchHome = false;
+        launchState.launchedFromAppWithThumbnail = false;
+        launchState.launchedToTaskId = -1;
+        launchState.launchedWithAltTab = false;
+        launchState.launchedHasConfigurationChanged = false;
+        launchState.launchedViaDragGesture = false;
+    }
+
+    @Override
+    protected void onDestroy() {
+        super.onDestroy();
+
+        // In the case that the activity finished on startup, just skip the unregistration below
+        if (mFinishedOnStartup) {
+            return;
+        }
+
+        // Unregister any broadcast receivers for the task loader
+        mPackageMonitor.unregister();
+
+        EventBus.getDefault().unregister(this);
+    }
+
+    @Override
+    public void onTrimMemory(int level) {
+        RecentsTaskLoader loader = Recents.getTaskLoader();
+        if (loader != null) {
+            loader.onTrimMemory(level);
+        }
+    }
+
+    @Override
+    public void onMultiWindowModeChanged(boolean multiWindowMode) {
+        super.onMultiWindowModeChanged(multiWindowMode);
+        if (!multiWindowMode) {
+            RecentsTaskLoader loader = Recents.getTaskLoader();
+            RecentsTaskLoadPlan.Options launchOpts = new RecentsTaskLoadPlan.Options();
+            launchOpts.loadIcons = false;
+            launchOpts.loadThumbnails = false;
+            launchOpts.onlyLoadForCache = true;
+            RecentsTaskLoadPlan loadPlan = loader.createLoadPlan(this);
+            loader.preloadTasks(loadPlan, -1, false);
+            loader.loadTasks(this, loadPlan, launchOpts);
+            EventBus.getDefault().send(new TaskStackUpdatedEvent(loadPlan.getTaskStack()));
+        }
+    }
+
+    @Override
+    public boolean onKeyDown(int keyCode, KeyEvent event) {
+        switch (keyCode) {
+            case KeyEvent.KEYCODE_DPAD_UP: {
+                SystemServicesProxy ssp = Recents.getSystemServices();
+                PipManager.getInstance().showPipMenu();
+                ssp.focusPinnedStack();
+                return true;
+            }
+            case KeyEvent.KEYCODE_DPAD_DOWN: {
+                SystemServicesProxy ssp = Recents.getSystemServices();
+                PipManager.getInstance().showPipOverlay(false);
+                ssp.focusHomeStack();
+                return true;
+            }
+            case KeyEvent.KEYCODE_DEL:
+            case KeyEvent.KEYCODE_FORWARD_DEL: {
+                EventBus.getDefault().send(new DismissFocusedTaskViewEvent());
+                return true;
+            }
+            default:
+                break;
+        }
+        return super.onKeyDown(keyCode, event);
+    }
+
+    @Override
+    public void onUserInteraction() {
+        EventBus.getDefault().send(new UserInteractionEvent());
+    }
+
+    @Override
+    public void onBackPressed() {
+        // Back behaves like the recents button so just trigger a toggle event
+        EventBus.getDefault().send(new ToggleRecentsEvent());
+    }
+
+    /**** EventBus events ****/
+
+    public final void onBusEvent(ToggleRecentsEvent event) {
+        RecentsActivityLaunchState launchState = Recents.getConfiguration().getLaunchState();
+        if (launchState.launchedFromHome) {
+            dismissRecentsToHome(true /* animateTaskViews */);
+        } else {
+            dismissRecentsToLaunchTargetTaskOrHome();
+        }
+    }
+
+    public final void onBusEvent(HideRecentsEvent event) {
+        if (event.triggeredFromAltTab) {
+            // If we are hiding from releasing Alt-Tab, dismiss Recents to the focused app
+            if (!mIgnoreAltTabRelease) {
+                dismissRecentsToFocusedTaskOrHome();
+            }
+        } else if (event.triggeredFromHomeKey) {
+                dismissRecentsToHome(true /* animateTaskViews */);
+        } else {
+            // Do nothing
+        }
+    }
+
+    public final void onBusEvent(EnterRecentsWindowLastAnimationFrameEvent event) {
+        EventBus.getDefault().send(new UpdateFreeformTaskViewVisibilityEvent(true));
+        mRecentsView.getViewTreeObserver().addOnPreDrawListener(this);
+        mRecentsView.invalidate();
+    }
+
+    public final void onBusEvent(CancelEnterRecentsWindowAnimationEvent event) {
+        RecentsActivityLaunchState launchState = Recents.getConfiguration().getLaunchState();
+        int launchToTaskId = launchState.launchedToTaskId;
+        if (launchToTaskId != -1 &&
+                (event.launchTask == null || launchToTaskId != event.launchTask.key.id)) {
+            SystemServicesProxy ssp = Recents.getSystemServices();
+            ssp.cancelWindowTransition(launchState.launchedToTaskId);
+            ssp.cancelThumbnailTransition(getTaskId());
+        }
+    }
+
+    public final void onBusEvent(DeleteTaskDataEvent event) {
+        // Remove any stored data from the loader
+        RecentsTaskLoader loader = Recents.getTaskLoader();
+        loader.deleteTaskData(event.task, false);
+
+        // Remove the task from activity manager
+        SystemServicesProxy ssp = Recents.getSystemServices();
+        ssp.removeTask(event.task.key.id);
+    }
+
+    public final void onBusEvent(AllTaskViewsDismissedEvent event) {
+        SystemServicesProxy ssp = Recents.getSystemServices();
+        if (ssp.hasDockedTask()) {
+            mRecentsView.showEmptyView();
+        } else {
+            // Just go straight home (no animation necessary because there are no more task views)
+            dismissRecentsToHome(false /* animateTaskViews */);
+        }
+    }
+
+    public final void onBusEvent(LaunchTaskFailedEvent event) {
+        // Return to Home
+        dismissRecentsToHome(true /* animateTaskViews */);
+    }
+
+    @Override
+    public boolean onPreDraw() {
+        mRecentsView.getViewTreeObserver().removeOnPreDrawListener(this);
+        // We post to make sure that this information is delivered after this traversals is
+        // finished.
+        mRecentsView.post(new Runnable() {
+            @Override
+            public void run() {
+                Recents.getSystemServices().endProlongedAnimations();
+            }
+        });
+        return true;
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/tv/animations/ViewFocusAnimator.java b/packages/SystemUI/src/com/android/systemui/recents/tv/animations/ViewFocusAnimator.java
new file mode 100644
index 0000000..8212c73
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/recents/tv/animations/ViewFocusAnimator.java
@@ -0,0 +1,143 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.systemui.recents.tv.animations;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ObjectAnimator;
+import android.content.res.Resources;
+import android.util.TypedValue;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.animation.AccelerateDecelerateInterpolator;
+import android.view.animation.Interpolator;
+import com.android.systemui.R;
+
+public class ViewFocusAnimator implements View.OnFocusChangeListener {
+    private final float mUnselectedScale;
+    private final float mSelectedScaleDelta;
+    private final float mUnselectedZ;
+    private final float mSelectedZDelta;
+    private final int mAnimDuration;
+    private final Interpolator mFocusInterpolator;
+
+    protected View mTargetView;
+    private float mFocusProgress;
+
+    ObjectAnimator mFocusAnimation;
+
+    public ViewFocusAnimator(View view) {
+        mTargetView = view;
+        final Resources res = view.getResources();
+
+        mTargetView.setOnFocusChangeListener(this);
+
+        TypedValue out = new TypedValue();
+        res.getValue(R.raw.unselected_scale, out, true);
+        mUnselectedScale = out.getFloat();
+        mSelectedScaleDelta = res.getFraction(R.fraction.lb_focus_zoom_factor_medium, 1, 1) -
+                mUnselectedScale;
+
+        mUnselectedZ = res.getDimensionPixelOffset(R.dimen.recents_tv_unselected_item_z);
+        mSelectedZDelta = res.getDimensionPixelOffset(R.dimen.recents_tv_selected_item_z_delta);
+
+        mAnimDuration = res.getInteger(R.integer.item_scale_anim_duration);
+
+        mFocusInterpolator = new AccelerateDecelerateInterpolator();
+
+        mFocusAnimation = ObjectAnimator.ofFloat(this, "focusProgress", 0.0f);
+        mFocusAnimation.setDuration(mAnimDuration);
+        mFocusAnimation.setInterpolator(mFocusInterpolator);
+
+        setFocusProgress(0.0f);
+
+        mFocusAnimation.addListener(new AnimatorListenerAdapter() {
+            @Override
+            public void onAnimationStart(Animator animation) {
+                mTargetView.setHasTransientState(true);
+            }
+
+            @Override
+            public void onAnimationEnd(Animator animation) {
+                mTargetView.setHasTransientState(false);
+            }
+        });
+    }
+
+    public void setFocusProgress(float level) {
+        mFocusProgress = level;
+
+        float scale = mUnselectedScale + (level * mSelectedScaleDelta);
+        float z = mUnselectedZ + (level * mSelectedZDelta);
+
+        mTargetView.setScaleX(scale);
+        mTargetView.setScaleY(scale);
+        mTargetView.setZ(z);
+    }
+
+    public float getFocusProgress() {
+        return mFocusProgress;
+    }
+
+    public void animateFocus(boolean focused) {
+        if (mFocusAnimation.isStarted()) {
+            mFocusAnimation.cancel();
+        }
+
+        float target = focused ? 1.0f : 0.0f;
+
+        if (getFocusProgress() != target) {
+            mFocusAnimation.setFloatValues(getFocusProgress(), target);
+            mFocusAnimation.start();
+        }
+    }
+
+    public void setFocusImmediate(boolean focused) {
+        if (mFocusAnimation.isStarted()) {
+            mFocusAnimation.cancel();
+        }
+
+        float target = focused ? 1.0f : 0.0f;
+
+        setFocusProgress(target);
+    }
+
+    @Override
+    public void onFocusChange(View v, boolean hasFocus) {
+        if (v != mTargetView) {
+            return;
+        }
+        changeSize(hasFocus);
+    }
+
+    protected void changeSize(boolean hasFocus) {
+        ViewGroup.LayoutParams lp = mTargetView.getLayoutParams();
+        int width = lp.width;
+        int height = lp.height;
+
+        if (width < 0 && height < 0) {
+            mTargetView.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED);
+            height = mTargetView.getMeasuredHeight();
+        }
+
+        if (mTargetView.isAttachedToWindow() && mTargetView.hasWindowFocus() &&
+                mTargetView.getVisibility() == View.VISIBLE) {
+            animateFocus(hasFocus);
+        } else {
+            setFocusImmediate(hasFocus);
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/tv/views/RecentsTvView.java b/packages/SystemUI/src/com/android/systemui/recents/tv/views/RecentsTvView.java
new file mode 100644
index 0000000..c4d5b2b
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/recents/tv/views/RecentsTvView.java
@@ -0,0 +1,252 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.systemui.recents.tv.views;
+
+import android.content.Context;
+import android.graphics.Rect;
+
+import android.util.AttributeSet;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.WindowInsets;
+import android.widget.FrameLayout;
+import com.android.internal.logging.MetricsLogger;
+import com.android.systemui.R;
+import com.android.systemui.recents.Recents;
+import com.android.systemui.recents.RecentsActivity;
+import com.android.systemui.recents.RecentsActivityLaunchState;
+import com.android.systemui.recents.RecentsConfiguration;
+import com.android.systemui.recents.events.EventBus;
+import com.android.systemui.recents.events.activity.CancelEnterRecentsWindowAnimationEvent;
+import com.android.systemui.recents.events.activity.DismissRecentsToHomeAnimationStarted;
+import com.android.systemui.recents.events.activity.TaskStackUpdatedEvent;
+import com.android.systemui.recents.events.component.RecentsVisibilityChangedEvent;
+import com.android.systemui.recents.misc.SystemServicesProxy;
+import com.android.systemui.recents.model.Task;
+import com.android.systemui.recents.model.TaskStack;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
+
+/**
+ * Top level layout of recents for TV. This will show the TaskStacks using a HorizontalGridView.
+ */
+public class RecentsTvView extends FrameLayout {
+
+    private static final String TAG = "RecentsTvView";
+    private static final boolean DEBUG = false;
+
+    private TaskStack mStack;
+    private TaskStackHorizontalGridView mTaskStackHorizontalView;
+    private View mEmptyView;
+    private boolean mAwaitingFirstLayout = true;
+    private Rect mSystemInsets = new Rect();
+
+
+    public RecentsTvView(Context context) {
+        this(context, null);
+    }
+
+    public RecentsTvView(Context context, AttributeSet attrs) {
+        this(context, attrs, 0);
+    }
+
+    public RecentsTvView(Context context, AttributeSet attrs, int defStyleAttr) {
+        this(context, attrs, defStyleAttr, 0);
+    }
+
+    public RecentsTvView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
+        super(context, attrs, defStyleAttr, defStyleRes);
+
+        setWillNotDraw(false);
+
+        LayoutInflater inflater = LayoutInflater.from(context);
+        mEmptyView = inflater.inflate(R.layout.recents_empty, this, false);
+        addView(mEmptyView);
+    }
+
+    public void setTaskStack(TaskStack stack) {
+        RecentsConfiguration config = Recents.getConfiguration();
+        RecentsActivityLaunchState launchState = config.getLaunchState();
+        mStack = stack;
+
+        if (mTaskStackHorizontalView != null) {
+            mTaskStackHorizontalView.reset();
+            mTaskStackHorizontalView.setStack(stack);
+        } else {
+            mTaskStackHorizontalView = (TaskStackHorizontalGridView) findViewById(R.id.task_list);
+            mTaskStackHorizontalView.setStack(stack);
+        }
+
+
+        if (stack.getStackTaskCount() > 0) {
+            hideEmptyView();
+        } else {
+            showEmptyView();
+        }
+
+        requestLayout();
+    }
+
+    public Task getNextTaskOrTopTask(Task taskToSearch) {
+        Task returnTask = null;
+        boolean found = false;
+        if (mTaskStackHorizontalView != null) {
+            TaskStack stack = mTaskStackHorizontalView.getStack();
+            ArrayList<Task> taskList = stack.getStackTasks();
+            // Iterate the stack views and try and find the focused task
+            for (int j = taskList.size() - 1; j >= 0; --j) {
+                Task task = taskList.get(j);
+                // Return the next task in the line.
+                if (found)
+                    return task;
+                // Remember the first possible task as the top task.
+                if (returnTask == null)
+                    returnTask = task;
+                if (task == taskToSearch)
+                    found = true;
+            }
+        }
+        return returnTask;
+    }
+
+    public boolean launchFocusedTask() {
+        if (mTaskStackHorizontalView != null) {
+            Task task = mTaskStackHorizontalView.getFocusedTask();
+            if (task != null) {
+                SystemServicesProxy ssp = Recents.getSystemServices();
+                ssp.startActivityFromRecents(getContext(), task.key.id, task.title, null);
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /** Launches the task that recents was launched from if possible */
+    public boolean launchPreviousTask() {
+        if (mTaskStackHorizontalView != null) {
+            TaskStack stack = mTaskStackHorizontalView.getStack();
+            Task task = stack.getLaunchTarget();
+            if (task != null) {
+                SystemServicesProxy ssp = Recents.getSystemServices();
+                ssp.startActivityFromRecents(getContext(), task.key.id, task.title, null);
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /** Launches a given task. */
+    public boolean launchTask(Task task, Rect taskBounds, int destinationStack) {
+        if (mTaskStackHorizontalView != null) {
+            // Iterate the stack views and try and find the given task.
+            List<TaskCardView> taskViews = mTaskStackHorizontalView.getTaskViews();
+            int taskViewCount = taskViews.size();
+            for (int j = 0; j < taskViewCount; j++) {
+                TaskCardView tv = taskViews.get(j);
+                if (tv.getTask() == task) {
+                    SystemServicesProxy ssp = Recents.getSystemServices();
+                    ssp.startActivityFromRecents(getContext(), task.key.id, task.title, null);
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Hides the task stack and shows the empty view.
+     */
+    public void showEmptyView() {
+        mEmptyView.setVisibility(View.VISIBLE);
+        mEmptyView.bringToFront();
+    }
+
+    /**
+     * Shows the task stack and hides the empty view.
+     */
+    public void hideEmptyView() {
+        mEmptyView.setVisibility(View.INVISIBLE);
+    }
+
+    /**
+     * Returns the last known system insets.
+     */
+    public Rect getSystemInsets() {
+        return mSystemInsets;
+    }
+
+    @Override
+    protected void onAttachedToWindow() {
+        EventBus.getDefault().register(this, RecentsActivity.EVENT_BUS_PRIORITY + 1);
+        super.onAttachedToWindow();
+    }
+
+    @Override
+    protected void onDetachedFromWindow() {
+        super.onDetachedFromWindow();
+        EventBus.getDefault().unregister(this);
+    }
+
+    /**
+     * This is called with the full size of the window since we are handling our own insets.
+     */
+    @Override
+    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+        if (mTaskStackHorizontalView != null && mTaskStackHorizontalView.getVisibility() != GONE) {
+            mTaskStackHorizontalView.layout(left, top, left + getMeasuredWidth(), top + getMeasuredHeight());
+        }
+
+        // Layout the empty view
+        mEmptyView.layout(left, top, right, bottom);
+    }
+
+    @Override
+    public WindowInsets onApplyWindowInsets(WindowInsets insets) {
+        mSystemInsets.set(insets.getSystemWindowInsets());
+        requestLayout();
+        return insets;
+    }
+
+    /**** EventBus Events ****/
+
+    public final void onBusEvent(DismissRecentsToHomeAnimationStarted event) {
+        // If we are going home, cancel the previous task's window transition
+        EventBus.getDefault().send(new CancelEnterRecentsWindowAnimationEvent(null));
+    }
+
+    public final void onBusEvent(TaskStackUpdatedEvent event) {
+        mStack.setTasks(event.stack.computeAllTasksList(), true /* notifyStackChanges */);
+        mStack.createAffiliatedGroupings(getContext());
+    }
+
+
+    public final void onBusEvent(RecentsVisibilityChangedEvent event) {
+        if (!event.visible) {
+            // Reset the view state
+            mAwaitingFirstLayout = true;
+        }
+    }
+
+
+    public void setTaskStackViewAdapter(TaskStackHorizontalViewAdapter taskStackViewAdapter) {
+        if(mTaskStackHorizontalView != null) {
+            mTaskStackHorizontalView.setAdapter(taskStackViewAdapter);
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/tv/views/TaskCardView.java b/packages/SystemUI/src/com/android/systemui/recents/tv/views/TaskCardView.java
new file mode 100644
index 0000000..b36a228
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/recents/tv/views/TaskCardView.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.systemui.recents.tv.views;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.widget.ImageView;
+import android.widget.RelativeLayout;
+import android.widget.TextView;
+import com.android.systemui.R;
+import com.android.systemui.recents.tv.animations.ViewFocusAnimator;
+import com.android.systemui.recents.model.Task;
+
+public class TaskCardView extends RelativeLayout {
+
+    private ImageView mThumbnailView;
+    private TextView mTitleTextView;
+    private TextView mContentTextView;
+    private ImageView mBadgeView;
+    private Task mTask;
+
+    private ViewFocusAnimator mViewFocusAnimator;
+
+    public TaskCardView(Context context) {
+        this(context, null);
+    }
+
+    public TaskCardView(Context context, AttributeSet attrs) {
+        this(context, attrs, 0);
+    }
+
+    public TaskCardView(Context context, AttributeSet attrs, int defStyleAttr) {
+        super(context, attrs, defStyleAttr);
+        mViewFocusAnimator = new ViewFocusAnimator(this);
+    }
+
+    @Override
+    protected void onFinishInflate() {
+        mThumbnailView = (ImageView) findViewById(R.id.card_view_thumbnail);
+        mTitleTextView = (TextView) findViewById(R.id.card_title_text);
+        mContentTextView = (TextView) findViewById(R.id.card_content_text);
+        mBadgeView = (ImageView) findViewById(R.id.card_extra_badge);
+    }
+
+    public void init(Task task) {
+        mTask = task;
+        mThumbnailView.setImageBitmap(task.thumbnail);
+        mTitleTextView.setText(task.title);
+        mContentTextView.setText(task.contentDescription);
+        mBadgeView.setImageDrawable(task.icon);
+    }
+
+    public Task getTask() {
+        return mTask;
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/tv/views/TaskStackHorizontalGridView.java b/packages/SystemUI/src/com/android/systemui/recents/tv/views/TaskStackHorizontalGridView.java
new file mode 100644
index 0000000..b505d65
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/recents/tv/views/TaskStackHorizontalGridView.java
@@ -0,0 +1,157 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.systemui.recents.tv.views;
+
+
+import android.support.v17.leanback.widget.HorizontalGridView;
+import android.util.AttributeSet;
+import android.content.Context;
+import android.view.View;
+import com.android.systemui.R;
+import com.android.systemui.recents.RecentsActivity;
+import com.android.systemui.recents.events.EventBus;
+import com.android.systemui.recents.events.ui.AllTaskViewsDismissedEvent;
+import com.android.systemui.recents.model.Task;
+import com.android.systemui.recents.model.TaskStack;
+import com.android.systemui.recents.model.TaskStack.TaskStackCallbacks;
+import com.android.systemui.recents.views.TaskViewAnimation;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Horizontal Grid View Implementation to show the Task Stack for TV.
+ */
+public class TaskStackHorizontalGridView extends HorizontalGridView implements TaskStackCallbacks{
+
+    private TaskStack mStack;
+    private ArrayList<TaskCardView> mTaskViews = new ArrayList<>();
+    private Task mFocusedTask;
+
+
+    public TaskStackHorizontalGridView(Context context, AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    @Override
+    protected void onAttachedToWindow() {
+        EventBus.getDefault().register(this, RecentsActivity.EVENT_BUS_PRIORITY + 1);
+        setItemMargin((int) getResources().getDimension(R.dimen.recents_tv_gird_card_spacing));
+        setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_YES);
+        super.onAttachedToWindow();
+    }
+
+    @Override
+    protected void onDetachedFromWindow() {
+        super.onDetachedFromWindow();
+        EventBus.getDefault().unregister(this);
+    }
+    /**
+     * Resets this view for reuse.
+     */
+    public void reset() {
+        // Reset the focused task
+        resetFocusedTask(getFocusedTask());
+        requestLayout();
+    }
+
+    /**
+     * @param task - Task to reset
+     */
+    private void resetFocusedTask(Task task) {
+        if (task != null) {
+            TaskCardView tv = getChildViewForTask(task);
+            if (tv != null) {
+                tv.requestFocus();
+            }
+        }
+        mFocusedTask = null;
+    }
+
+    /**
+     * Sets the task stack.
+     * @param stack
+     */
+    public void setStack(TaskStack stack) {
+        //Set new stack
+        mStack = stack;
+        if (mStack != null) {
+            mStack.setCallbacks(this);
+        }
+        //Layout with new stack
+        requestLayout();
+    }
+
+    /**
+     * @return Returns the task stack.
+     */
+    public TaskStack getStack() {
+        return mStack;
+    }
+
+    /**
+     * @return - The focused task.
+     */
+    public Task getFocusedTask() {
+        return mFocusedTask;
+    }
+
+    /**
+     * @param task
+     * @return Child view for given task
+     */
+    public TaskCardView getChildViewForTask(Task task) {
+        List<TaskCardView> taskViews = getTaskViews();
+        int taskViewCount = taskViews.size();
+        for (int i = 0; i < taskViewCount; i++) {
+            TaskCardView tv = taskViews.get(i);
+            if (tv.getTask() == task) {
+                return tv;
+            }
+        }
+        return null;
+    }
+
+    public List<TaskCardView> getTaskViews() {
+        return mTaskViews;
+    }
+
+    @Override
+    public void onStackTaskAdded(TaskStack stack, Task newTask){
+        getAdapter().notifyItemInserted(stack.getStackTasks().indexOf(newTask));
+    }
+
+    @Override
+    public void onStackTaskRemoved(TaskStack stack, Task removedTask, boolean wasFrontMostTask,
+            Task newFrontMostTask, TaskViewAnimation animation) {
+        getAdapter().notifyItemRemoved(stack.getStackTasks().indexOf(removedTask));
+        if (mFocusedTask == removedTask) {
+            resetFocusedTask(removedTask);
+        }
+        // If there are no remaining tasks, then just close recents
+        if (mStack.getStackTaskCount() == 0) {
+            boolean shouldFinishActivity = (mStack.getStackTaskCount() == 0);
+            if (shouldFinishActivity) {
+                EventBus.getDefault().send(new AllTaskViewsDismissedEvent());
+            }
+        }
+    }
+
+    @Override
+    public void onHistoryTaskRemoved(TaskStack stack, Task removedTask, TaskViewAnimation animation) {
+        //No history task on tv
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/tv/views/TaskStackHorizontalViewAdapter.java b/packages/SystemUI/src/com/android/systemui/recents/tv/views/TaskStackHorizontalViewAdapter.java
new file mode 100644
index 0000000..0ee7b49
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/recents/tv/views/TaskStackHorizontalViewAdapter.java
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.systemui.recents.tv.views;
+
+import android.app.Activity;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import com.android.systemui.R;
+import com.android.systemui.recents.model.Task;
+import android.support.v7.widget.RecyclerView;
+import android.app.ActivityManagerNative;
+import android.app.IActivityManager;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class TaskStackHorizontalViewAdapter extends
+        RecyclerView.Adapter<TaskStackHorizontalViewAdapter.ViewHolder> {
+
+    private static final String TAG = "TaskStackHorizontalViewAdapter";
+    private List<Task> mTaskList;
+
+    static class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener{
+        private TaskCardView mTaskCardView;
+        private Task mTask;
+        public ViewHolder(View v) {
+            super(v);
+            if(v instanceof TaskCardView) {
+                mTaskCardView = (TaskCardView) v;
+            }
+        }
+
+        public void init(Task task) {
+            mTaskCardView.init(task);
+            mTask = task;
+            mTaskCardView.setOnClickListener(this);
+        }
+
+        @Override
+        public void onClick(View v) {
+            try {
+                ActivityManagerNative.getDefault().startActivityFromRecents(mTask.key.id, null);
+                ((Activity)(v.getContext())).finish();
+            } catch (Exception e) {
+                Log.e(TAG, v.getContext()
+                        .getString(R.string.recents_launch_error_message, mTask.title), e);
+            }
+
+        }
+    }
+
+    public TaskStackHorizontalViewAdapter(List tasks) {
+        mTaskList = new ArrayList<>(tasks);
+    }
+
+    public void setNewStackTasks(List tasks) {
+        mTaskList.clear();
+        mTaskList.addAll(tasks);
+        notifyDataSetChanged();
+    }
+    @Override
+    public TaskStackHorizontalViewAdapter.ViewHolder onCreateViewHolder(ViewGroup parent,
+            int viewType) {
+        View view = LayoutInflater.from(parent.getContext())
+                .inflate(R.layout.recents_task_card_view, parent, false);
+        ViewHolder viewHolder = new ViewHolder(view);
+        return viewHolder;
+    }
+
+    @Override
+    public void onBindViewHolder(ViewHolder holder, int position) {
+        holder.init(mTaskList.get(position));
+    }
+
+    @Override
+    public int getItemCount() {
+        return mTaskList.size();
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/AnimateableViewBounds.java b/packages/SystemUI/src/com/android/systemui/recents/views/AnimateableViewBounds.java
index 5a72897..5842095 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/AnimateableViewBounds.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/AnimateableViewBounds.java
@@ -48,10 +48,16 @@
     @Override
     public void getOutline(View view, Outline outline) {
         outline.setAlpha(mMinAlpha + mAlpha / (1f - mMinAlpha));
-        outline.setRoundRect(mClipRect.left, mClipRect.top,
-                mSourceView.getWidth() - mClipRect.right,
-                mSourceView.getHeight() - mClipRect.bottom,
-                mCornerRadius);
+        if (mCornerRadius > 0) {
+            outline.setRoundRect(mClipRect.left, mClipRect.top,
+                    mSourceView.getWidth() - mClipRect.right,
+                    mSourceView.getHeight() - mClipRect.bottom,
+                    mCornerRadius);
+        } else {
+            outline.setRect(mClipRect.left, mClipRect.top,
+                    mSourceView.getWidth() - mClipRect.right,
+                    mSourceView.getHeight() - mClipRect.bottom);
+        }
     }
 
     /** Sets the view outline alpha. */
@@ -63,6 +69,17 @@
         }
     }
 
+    /** Sets the top clip. */
+    public void setClipTop(int top) {
+        mClipRect.top = top;
+        updateClipBounds();
+    }
+
+    /** Returns the top clip. */
+    public int getClipTop() {
+        return mClipRect.top;
+    }
+
     /** Sets the bottom clip. */
     public void setClipBottom(int bottom) {
         mClipRect.bottom = bottom;
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
index a00b497..b59ff30 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
@@ -18,9 +18,13 @@
 
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
+import android.animation.ObjectAnimator;
 import android.content.Context;
 import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Outline;
 import android.graphics.Rect;
+import android.graphics.drawable.ColorDrawable;
 import android.graphics.drawable.Drawable;
 import android.os.Handler;
 import android.util.ArraySet;
@@ -28,6 +32,7 @@
 import android.view.LayoutInflater;
 import android.view.MotionEvent;
 import android.view.View;
+import android.view.ViewOutlineProvider;
 import android.view.ViewPropertyAnimator;
 import android.view.WindowInsets;
 import android.view.animation.AnimationUtils;
@@ -45,20 +50,25 @@
 import com.android.systemui.recents.RecentsConfiguration;
 import com.android.systemui.recents.RecentsDebugFlags;
 import com.android.systemui.recents.events.EventBus;
-import com.android.systemui.recents.events.activity.CancelEnterRecentsWindowAnimationEvent;
+import com.android.systemui.recents.events.activity.ClearHistoryEvent;
 import com.android.systemui.recents.events.activity.DismissRecentsToHomeAnimationStarted;
+import com.android.systemui.recents.events.activity.EnterRecentsWindowAnimationCompletedEvent;
 import com.android.systemui.recents.events.activity.HideHistoryButtonEvent;
 import com.android.systemui.recents.events.activity.HideHistoryEvent;
 import com.android.systemui.recents.events.activity.LaunchTaskEvent;
 import com.android.systemui.recents.events.activity.ShowHistoryButtonEvent;
 import com.android.systemui.recents.events.activity.ShowHistoryEvent;
 import com.android.systemui.recents.events.activity.TaskStackUpdatedEvent;
+import com.android.systemui.recents.events.activity.ToggleHistoryEvent;
 import com.android.systemui.recents.events.component.RecentsVisibilityChangedEvent;
 import com.android.systemui.recents.events.ui.DraggingInRecentsEndedEvent;
 import com.android.systemui.recents.events.ui.DraggingInRecentsEvent;
+import com.android.systemui.recents.events.ui.ResetBackgroundScrimEvent;
+import com.android.systemui.recents.events.ui.UpdateBackgroundScrimEvent;
 import com.android.systemui.recents.events.ui.dragndrop.DragDropTargetChangedEvent;
 import com.android.systemui.recents.events.ui.dragndrop.DragEndEvent;
 import com.android.systemui.recents.events.ui.dragndrop.DragStartEvent;
+import com.android.systemui.recents.history.RecentsHistoryView;
 import com.android.systemui.recents.misc.ReferenceCountedTrigger;
 import com.android.systemui.recents.misc.SystemServicesProxy;
 import com.android.systemui.recents.misc.Utilities;
@@ -80,6 +90,8 @@
 public class RecentsView extends FrameLayout {
 
     private static final int DOCK_AREA_OVERLAY_TRANSITION_DURATION = 135;
+    private static final int DEFAULT_UPDATE_SCRIM_DURATION = 200;
+    private static final float DEFAULT_SCRIM_ALPHA = 0.33f;
 
     private final Handler mHandler;
 
@@ -87,12 +99,18 @@
     private TaskStackView mTaskStackView;
     private RecentsAppWidgetHostView mSearchBar;
     private TextView mHistoryButton;
+    private TextView mHistoryClearAllButton;
     private View mEmptyView;
+    private RecentsHistoryView mHistoryView;
+
     private boolean mAwaitingFirstLayout = true;
     private boolean mLastTaskLaunchedWasFreeform;
     private Rect mSystemInsets = new Rect();
     private int mDividerSize;
 
+    private ColorDrawable mBackgroundScrim = new ColorDrawable(Color.BLACK);
+    private Animator mBackgroundScrimAnimator;
+
     private RecentsTransitionHelper mTransitionHelper;
     private RecentsViewTouchHandler mTouchHandler;
 
@@ -127,17 +145,28 @@
         mTouchHandler = new RecentsViewTouchHandler(this);
         mFlingAnimationUtils = new FlingAnimationUtils(context, 0.3f);
 
+        final float cornerRadius = context.getResources().getDimensionPixelSize(
+                R.dimen.recents_task_view_rounded_corners_radius);
         LayoutInflater inflater = LayoutInflater.from(context);
         mHistoryButton = (TextView) inflater.inflate(R.layout.recents_history_button, this, false);
         mHistoryButton.setOnClickListener(new View.OnClickListener() {
             @Override
             public void onClick(View v) {
-                EventBus.getDefault().send(new ShowHistoryEvent());
+                EventBus.getDefault().send(new ToggleHistoryEvent());
             }
         });
         addView(mHistoryButton);
+        mHistoryButton.setClipToOutline(true);
+        mHistoryButton.setOutlineProvider(new ViewOutlineProvider() {
+            @Override
+            public void getOutline(View view, Outline outline) {
+                outline.setRoundRect(0, 0, view.getWidth(), view.getHeight(), cornerRadius);
+            }
+        });
         mEmptyView = inflater.inflate(R.layout.recents_empty, this, false);
         addView(mEmptyView);
+
+        setBackground(mBackgroundScrim);
     }
 
     /** Set/get the bsp root node */
@@ -162,6 +191,15 @@
             addView(mTaskStackView);
         }
 
+        // If we are already occluded by the app, then just set the default background scrim now.
+        // Otherwise, defer until the enter animation completes to animate the scrim with the
+        // tasks for the home animation.
+        if (launchState.launchedFromAppWithThumbnail || mStack.getTaskCount() == 0) {
+            mBackgroundScrim.setAlpha((int) (DEFAULT_SCRIM_ALPHA * 255));
+        } else {
+            mBackgroundScrim.setAlpha(0);
+        }
+
         // Update the top level view's visibilities
         if (stack.getTaskCount() > 0) {
             hideEmptyView();
@@ -181,6 +219,13 @@
     }
 
     /**
+     * Returns whether the history is visible or not.
+     */
+    public boolean isHistoryVisible() {
+        return mHistoryView != null && mHistoryView.isVisible();
+    }
+
+    /**
      * Returns the currently set task stack.
      */
     public TaskStack getTaskStack() {
@@ -304,13 +349,6 @@
         mHistoryButton.bringToFront();
     }
 
-    /**
-     * Returns the last known system insets.
-     */
-    public Rect getSystemInsets() {
-        return mSystemInsets;
-    }
-
     @Override
     protected void onAttachedToWindow() {
         EventBus.getDefault().register(this, RecentsActivity.EVENT_BUS_PRIORITY + 1);
@@ -352,17 +390,28 @@
             mTaskStackView.measure(widthMeasureSpec, heightMeasureSpec);
         }
 
-        // Measure the empty view
-        measureChild(mEmptyView, MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY),
-                MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY));
+        // Measure the empty view to the full size of the screen
+        if (mEmptyView.getVisibility() != GONE) {
+            measureChild(mEmptyView, MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY),
+                    MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY));
+        }
 
-        // Measure the history button with the full space above the stack, but width-constrained
-        // to the stack
+        // Measure the history view
+        if (mHistoryView != null && mHistoryView.getVisibility() != GONE) {
+            measureChild(mHistoryView, MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY),
+                    MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY));
+        }
+
+        // Measure the history button within the constraints of the space above the stack
         Rect historyButtonRect = mTaskStackView.mLayoutAlgorithm.mHistoryButtonRect;
         measureChild(mHistoryButton,
-                MeasureSpec.makeMeasureSpec(historyButtonRect.width(), MeasureSpec.EXACTLY),
-                MeasureSpec.makeMeasureSpec(historyButtonRect.height(),
-                        MeasureSpec.EXACTLY));
+                MeasureSpec.makeMeasureSpec(historyButtonRect.width(), MeasureSpec.AT_MOST),
+                MeasureSpec.makeMeasureSpec(historyButtonRect.height(), MeasureSpec.AT_MOST));
+        if (mHistoryClearAllButton != null && mHistoryClearAllButton.getVisibility() != GONE) {
+            measureChild(mHistoryClearAllButton,
+                    MeasureSpec.makeMeasureSpec(historyButtonRect.width(), MeasureSpec.AT_MOST),
+                    MeasureSpec.makeMeasureSpec(historyButtonRect.height(), MeasureSpec.AT_MOST));
+        }
 
         setMeasuredDimension(width, height);
     }
@@ -389,13 +438,41 @@
         }
 
         // Layout the empty view
-        mEmptyView.layout(left, top, right, bottom);
+        if (mEmptyView.getVisibility() != GONE) {
+            mEmptyView.layout(left, top, right, bottom);
+        }
 
-        // Layout the history button left-aligned with the stack, but offset from the top of the
-        // view
+        // Layout the history view
+        if (mHistoryView != null && mHistoryView.getVisibility() != GONE) {
+            mHistoryView.layout(left, top, right, bottom);
+        }
+
+        // Layout the history button such that its drawable is start-aligned with the stack,
+        // vertically centered in the available space above the stack
         Rect historyButtonRect = mTaskStackView.mLayoutAlgorithm.mHistoryButtonRect;
-        mHistoryButton.layout(historyButtonRect.left, historyButtonRect.top,
-                historyButtonRect.right, historyButtonRect.bottom);
+        int historyLeft = isLayoutRtl()
+                ? historyButtonRect.right + mHistoryButton.getPaddingStart()
+                        - mHistoryButton.getMeasuredWidth()
+                : historyButtonRect.left - mHistoryButton.getPaddingStart();
+        int historyTop = historyButtonRect.top +
+                (historyButtonRect.height() - mHistoryButton.getMeasuredHeight()) / 2;
+        mHistoryButton.layout(historyLeft, historyTop,
+                historyLeft + mHistoryButton.getMeasuredWidth(),
+                historyTop + mHistoryButton.getMeasuredHeight());
+
+        // Layout the history clear all button such that it is end-aligned with the stack,
+        // vertically centered in the available space above the stack
+        if (mHistoryClearAllButton != null && mHistoryClearAllButton.getVisibility() != GONE) {
+            int clearAllLeft = isLayoutRtl()
+                    ? historyButtonRect.left - mHistoryClearAllButton.getPaddingStart()
+                    : historyButtonRect.right + mHistoryClearAllButton.getPaddingStart()
+                            - mHistoryClearAllButton.getMeasuredWidth();
+            int clearAllTop = historyButtonRect.top +
+                    (historyButtonRect.height() - mHistoryClearAllButton.getMeasuredHeight()) / 2;
+            mHistoryClearAllButton.layout(clearAllLeft, clearAllTop,
+                    clearAllLeft + mHistoryClearAllButton.getMeasuredWidth(),
+                    clearAllTop + mHistoryClearAllButton.getMeasuredHeight());
+        }
 
         if (mAwaitingFirstLayout) {
             mAwaitingFirstLayout = false;
@@ -464,10 +541,8 @@
         // Hide the history button
         int taskViewExitToHomeDuration = getResources().getInteger(
                 R.integer.recents_task_exit_to_home_duration);
-        hideHistoryButton(taskViewExitToHomeDuration);
-
-        // If we are going home, cancel the previous task's window transition
-        EventBus.getDefault().send(new CancelEnterRecentsWindowAnimationEvent(null));
+        hideHistoryButton(taskViewExitToHomeDuration, false /* translate */);
+        animateBackgroundScrim(0f, taskViewExitToHomeDuration);
     }
 
     public final void onBusEvent(DragStartEvent event) {
@@ -572,57 +647,150 @@
         animator.start();
     }
 
-    public final void onBusEvent(ShowHistoryEvent event) {
-        // Hide the history button when the history view is shown
-        hideHistoryButton(getResources().getInteger(R.integer.recents_history_transition_duration),
-                event.getAnimationTrigger());
-        event.addPostAnimationCallback(new Runnable() {
-            @Override
-            public void run() {
-                setAlpha(0f);
-            }
-        });
-    }
-
-    public final void onBusEvent(HideHistoryEvent event) {
-        // Show the history button when the history view is hidden
-        setAlpha(1f);
-        showHistoryButton(getResources().getInteger(R.integer.recents_history_transition_duration),
-                event.getAnimationTrigger());
-    }
-
-    public final void onBusEvent(ShowHistoryButtonEvent event) {
-        showHistoryButton(150);
-    }
-
-    public final void onBusEvent(HideHistoryButtonEvent event) {
-        hideHistoryButton(100);
-    }
-
     public final void onBusEvent(TaskStackUpdatedEvent event) {
         mStack.setTasks(event.stack.computeAllTasksList(), true /* notifyStackChanges */);
         mStack.createAffiliatedGroupings(getContext());
     }
 
+    public final void onBusEvent(EnterRecentsWindowAnimationCompletedEvent event) {
+        RecentsActivityLaunchState launchState = Recents.getConfiguration().getLaunchState();
+        if (!launchState.launchedFromAppWithThumbnail && mStack.getTaskCount() > 0) {
+            int taskViewEnterFromHomeDuration = getResources().getInteger(
+                    R.integer.recents_task_enter_from_home_duration);
+            animateBackgroundScrim(DEFAULT_SCRIM_ALPHA, taskViewEnterFromHomeDuration);
+        }
+    }
+
+    public final void onBusEvent(UpdateBackgroundScrimEvent event) {
+        animateBackgroundScrim(event.alpha, DEFAULT_UPDATE_SCRIM_DURATION);
+    }
+
+    public final void onBusEvent(ResetBackgroundScrimEvent event) {
+        animateBackgroundScrim(DEFAULT_SCRIM_ALPHA, DEFAULT_UPDATE_SCRIM_DURATION);
+    }
+
+    public final void onBusEvent(RecentsVisibilityChangedEvent event) {
+        if (!event.visible) {
+            // Reset the view state
+            mAwaitingFirstLayout = true;
+            mLastTaskLaunchedWasFreeform = false;
+            hideHistoryButton(0, false /* translate */);
+        }
+    }
+
+    public final void onBusEvent(ToggleHistoryEvent event) {
+        if (mHistoryView != null && mHistoryView.isVisible()) {
+            EventBus.getDefault().send(new HideHistoryEvent(true /* animate */));
+        } else {
+            EventBus.getDefault().send(new ShowHistoryEvent());
+        }
+    }
+
+    public final void onBusEvent(ShowHistoryEvent event) {
+        if (mHistoryView == null) {
+            LayoutInflater inflater = LayoutInflater.from(getContext());
+            mHistoryView = (RecentsHistoryView) inflater.inflate(R.layout.recents_history, this,
+                    false);
+            addView(mHistoryView);
+
+            final float cornerRadius = getResources().getDimensionPixelSize(
+                    R.dimen.recents_task_view_rounded_corners_radius);
+            mHistoryClearAllButton = (TextView) inflater.inflate(
+                    R.layout.recents_history_clear_all_button, this, false);
+            mHistoryClearAllButton.setOnClickListener(new View.OnClickListener() {
+                @Override
+                public void onClick(View v) {
+                    EventBus.getDefault().send(new ClearHistoryEvent());
+                }
+            });
+            mHistoryClearAllButton.setClipToOutline(true);
+            mHistoryClearAllButton.setOutlineProvider(new ViewOutlineProvider() {
+                @Override
+                public void getOutline(View view, Outline outline) {
+                    outline.setRoundRect(0, 0, view.getWidth(), view.getHeight(), cornerRadius);
+                }
+            });
+            addView(mHistoryClearAllButton);
+
+            // Since this history view is inflated by a view stub after the insets have already
+            // been applied, we have to set them ourselves initial from the insets that were last
+            // provided.
+            mHistoryView.setSystemInsets(mSystemInsets);
+            mHistoryView.setHeaderHeight(mHistoryButton.getMeasuredHeight());
+            mHistoryButton.bringToFront();
+            mHistoryClearAllButton.bringToFront();
+        }
+
+        // Animate the empty view in parallel with the history view (the task view animations are
+        // handled in TaskStackView)
+        Rect stackRect = mTaskStackView.mLayoutAlgorithm.mStackRect;
+        if (mEmptyView.getVisibility() == View.VISIBLE) {
+            int historyTransitionDuration = getResources().getInteger(
+                    R.integer.recents_history_transition_duration);
+            mEmptyView.animate()
+                    .alpha(0f)
+                    .translationY(stackRect.height() / 2)
+                    .setDuration(historyTransitionDuration)
+                    .setInterpolator(mFastOutSlowInInterpolator)
+                    .withEndAction(new Runnable() {
+                        @Override
+                        public void run() {
+                            mEmptyView.setVisibility(View.INVISIBLE);
+                        }
+                    })
+                    .start();
+        }
+
+        mHistoryView.show(mStack, stackRect.height(), mHistoryClearAllButton);
+    }
+
+    public final void onBusEvent(HideHistoryEvent event) {
+        // Animate the empty view in parallel with the history view (the task view animations are
+        // handled in TaskStackView)
+        Rect stackRect = mTaskStackView.mLayoutAlgorithm.mStackRect;
+        if (mStack.getTaskCount() == 0) {
+            int historyTransitionDuration = getResources().getInteger(
+                    R.integer.recents_history_transition_duration);
+            mEmptyView.setVisibility(View.VISIBLE);
+            mEmptyView.animate()
+                    .alpha(1f)
+                    .translationY(0)
+                    .setDuration(historyTransitionDuration)
+                    .setInterpolator(mFastOutSlowInInterpolator)
+                    .start();
+        }
+
+        mHistoryView.hide(event.animate, stackRect.height(), mHistoryClearAllButton);
+    }
+
+    public final void onBusEvent(ShowHistoryButtonEvent event) {
+        showHistoryButton(150, event.translate);
+    }
+
+    public final void onBusEvent(HideHistoryButtonEvent event) {
+        hideHistoryButton(100, true /* translate */);
+    }
+
     /**
      * Shows the history button.
      */
-    private void showHistoryButton(final int duration) {
-        ReferenceCountedTrigger postAnimationTrigger = new ReferenceCountedTrigger();
-        showHistoryButton(duration, postAnimationTrigger);
-        postAnimationTrigger.flushLastDecrementRunnables();
-    }
-
-    private void showHistoryButton(final int duration,
-            final ReferenceCountedTrigger postHideHistoryAnimationTrigger) {
-        mHistoryButton.setText(getContext().getString(R.string.recents_history_label_format,
-                mStack.getHistoricalTasks().size()));
+    private void showHistoryButton(final int duration, final boolean translate) {
+        final ReferenceCountedTrigger postAnimationTrigger = new ReferenceCountedTrigger();
         if (mHistoryButton.getVisibility() == View.INVISIBLE) {
             mHistoryButton.setVisibility(View.VISIBLE);
             mHistoryButton.setAlpha(0f);
-            postHideHistoryAnimationTrigger.addLastDecrementRunnable(new Runnable() {
+            if (translate) {
+                mHistoryButton.setTranslationY(-mHistoryButton.getMeasuredHeight() * 0.25f);
+            } else {
+                mHistoryButton.setTranslationY(0f);
+            }
+            postAnimationTrigger.addLastDecrementRunnable(new Runnable() {
                 @Override
                 public void run() {
+                    if (translate) {
+                        mHistoryButton.animate()
+                            .translationY(0f);
+                    }
                     mHistoryButton.animate()
                             .alpha(1f)
                             .setDuration(duration)
@@ -632,34 +800,42 @@
                 }
             });
         }
+        postAnimationTrigger.flushLastDecrementRunnables();
     }
 
     /**
      * Hides the history button.
      */
-    private void hideHistoryButton(int duration) {
-        ReferenceCountedTrigger postAnimationTrigger = new ReferenceCountedTrigger();
-        hideHistoryButton(duration, postAnimationTrigger);
+    private void hideHistoryButton(int duration, boolean translate) {
+        final ReferenceCountedTrigger postAnimationTrigger = new ReferenceCountedTrigger();
+        hideHistoryButton(duration, translate, postAnimationTrigger);
         postAnimationTrigger.flushLastDecrementRunnables();
     }
 
-    private void hideHistoryButton(int duration,
-            final ReferenceCountedTrigger postHideStackAnimationTrigger) {
+    /**
+     * Hides the history button.
+     */
+    private void hideHistoryButton(int duration, boolean translate,
+            final ReferenceCountedTrigger postAnimationTrigger) {
         if (mHistoryButton.getVisibility() == View.VISIBLE) {
+            if (translate) {
+                mHistoryButton.animate()
+                    .translationY(-mHistoryButton.getMeasuredHeight() * 0.25f);
+            }
             mHistoryButton.animate()
                     .alpha(0f)
                     .setDuration(duration)
-                    .setInterpolator(mFastOutLinearInInterpolator)
+                    .setInterpolator(mFastOutSlowInInterpolator)
                     .withEndAction(new Runnable() {
                         @Override
                         public void run() {
                             mHistoryButton.setVisibility(View.INVISIBLE);
-                            postHideStackAnimationTrigger.decrement();
+                            postAnimationTrigger.decrement();
                         }
                     })
                     .withLayer()
                     .start();
-            postHideStackAnimationTrigger.increment();
+            postAnimationTrigger.increment();
         }
     }
 
@@ -696,11 +872,18 @@
         }
     }
 
-    public final void onBusEvent(RecentsVisibilityChangedEvent event) {
-        if (!event.visible) {
-            // Reset the view state
-            mAwaitingFirstLayout = true;
-            mLastTaskLaunchedWasFreeform = false;
-        }
+    /**
+     * Animates the background scrim to the given {@param alpha}.
+     */
+    private void animateBackgroundScrim(float alpha, int duration) {
+        Utilities.cancelAnimationWithoutCallbacks(mBackgroundScrimAnimator);
+        int alphaInt = (int) (alpha * 255);
+        mBackgroundScrimAnimator = ObjectAnimator.ofInt(mBackgroundScrim, Utilities.DRAWABLE_ALPHA,
+                mBackgroundScrim.getAlpha(), alphaInt);
+        mBackgroundScrimAnimator.setDuration(duration);
+        mBackgroundScrimAnimator.setInterpolator(alphaInt > mBackgroundScrim.getAlpha()
+                ? PhoneStatusBar.ALPHA_OUT
+                : PhoneStatusBar.ALPHA_IN);
+        mBackgroundScrimAnimator.start();
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/SystemBarScrimViews.java b/packages/SystemUI/src/com/android/systemui/recents/views/SystemBarScrimViews.java
index 9618f212d..2254dfc 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/SystemBarScrimViews.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/SystemBarScrimViews.java
@@ -30,12 +30,9 @@
 
     Context mContext;
 
-    View mStatusBarScrimView;
     View mNavBarScrimView;
 
     boolean mHasNavBarScrim;
-    boolean mShouldAnimateStatusBarScrim;
-    boolean mHasStatusBarScrim;
     boolean mShouldAnimateNavBarScrim;
 
     int mNavBarScrimEnterDuration;
@@ -45,7 +42,6 @@
 
     public SystemBarScrimViews(Activity activity) {
         mContext = activity;
-        mStatusBarScrimView = activity.findViewById(R.id.status_bar_scrim);
         mNavBarScrimView = activity.findViewById(R.id.nav_bar_scrim);
         mNavBarScrimEnterDuration = activity.getResources().getInteger(
                 R.integer.recents_nav_bar_scrim_enter_duration);
@@ -59,17 +55,12 @@
      * Prepares the scrim views for animating when entering Recents. This will be called before
      * the first draw.
      */
-    public void prepareEnterRecentsAnimation(boolean hasStatusBarScrim, boolean animateStatusBarScrim,
-            boolean hasNavBarScrim, boolean animateNavBarScrim) {
-        mHasNavBarScrim = hasStatusBarScrim;
-        mShouldAnimateStatusBarScrim = animateStatusBarScrim;
-        mHasStatusBarScrim = hasNavBarScrim;
+    public void prepareEnterRecentsAnimation(boolean hasNavBarScrim, boolean animateNavBarScrim) {
+        mHasNavBarScrim = hasNavBarScrim;
         mShouldAnimateNavBarScrim = animateNavBarScrim;
 
         mNavBarScrimView.setVisibility(mHasNavBarScrim && !mShouldAnimateNavBarScrim ?
                 View.VISIBLE : View.INVISIBLE);
-        mStatusBarScrimView.setVisibility(mHasStatusBarScrim && !mShouldAnimateStatusBarScrim ?
-                View.VISIBLE : View.INVISIBLE);
     }
 
     /**** EventBus events ****/
@@ -78,20 +69,6 @@
      * Starts animating the scrim views when entering Recents.
      */
     public final void onBusEvent(EnterRecentsWindowAnimationCompletedEvent event) {
-        if (mHasStatusBarScrim && mShouldAnimateStatusBarScrim) {
-            mStatusBarScrimView.setTranslationY(-mStatusBarScrimView.getMeasuredHeight());
-            mStatusBarScrimView.animate()
-                    .translationY(0)
-                    .setDuration(mNavBarScrimEnterDuration)
-                    .setInterpolator(mQuintOutInterpolator)
-                    .withStartAction(new Runnable() {
-                        @Override
-                        public void run() {
-                            mStatusBarScrimView.setVisibility(View.VISIBLE);
-                        }
-                    })
-                    .start();
-        }
         if (mHasNavBarScrim && mShouldAnimateNavBarScrim) {
             mNavBarScrimView.setTranslationY(mNavBarScrimView.getMeasuredHeight());
             mNavBarScrimView.animate()
@@ -115,14 +92,6 @@
     public final void onBusEvent(DismissRecentsToHomeAnimationStarted event) {
         int taskViewExitToAppDuration = mContext.getResources().getInteger(
                 R.integer.recents_task_exit_to_app_duration);
-        if (mHasStatusBarScrim && mShouldAnimateStatusBarScrim) {
-            mStatusBarScrimView.animate()
-                    .translationY(-mStatusBarScrimView.getMeasuredHeight())
-                    .setStartDelay(0)
-                    .setDuration(taskViewExitToAppDuration)
-                    .setInterpolator(mFastOutSlowInInterpolator)
-                    .start();
-        }
         if (mHasNavBarScrim && mShouldAnimateNavBarScrim) {
             mNavBarScrimView.animate()
                     .translationY(mNavBarScrimView.getMeasuredHeight())
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackAnimationHelper.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackAnimationHelper.java
index ccbb329..d550d83 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackAnimationHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackAnimationHelper.java
@@ -138,7 +138,7 @@
             } else if (launchState.launchedFromHome) {
                 // Move the task view off screen (below) so we can animate it in
                 RectF bounds = new RectF(mTmpTransform.rect);
-                bounds.offset(0, offscreenY);
+                bounds.offsetTo(bounds.left, offscreenY);
                 tv.setLeftTopRightBottom((int) bounds.left, (int) bounds.top, (int) bounds.right,
                         (int) bounds.bottom);
             }
@@ -259,7 +259,7 @@
 
             stackLayout.getStackTransform(task, stackScroller.getStackScroll(), mTmpTransform,
                     null);
-            mTmpTransform.rect.offset(0, offscreenY);
+            mTmpTransform.rect.offsetTo(mTmpTransform.rect.left, offscreenY);
             mStackView.updateTaskViewToTransform(tv, mTmpTransform, taskAnimation);
         }
     }
@@ -346,61 +346,58 @@
     }
 
     /**
-     * Starts the animation to hide the {@link TaskView}s when the history is shown.  The history
-     * view's animation will be deferred until all the {@link TaskView}s are finished animating.
+     * Starts the animation to hide the {@link TaskView}s when the history is shown.
      */
     public void startShowHistoryAnimation(ReferenceCountedTrigger postAnimationTrigger) {
         Resources res = mStackView.getResources();
         TaskStackLayoutAlgorithm stackLayout = mStackView.getStackAlgorithm();
         TaskStackViewScroller stackScroller = mStackView.getScroller();
 
+        int offscreenY = stackLayout.mStackRect.bottom;
         int historyTransitionDuration = res.getInteger(
                 R.integer.recents_history_transition_duration);
+        int startDelayIncr = 16;
 
         List<TaskView> taskViews = mStackView.getTaskViews();
         int taskViewCount = taskViews.size();
         for (int i = taskViewCount - 1; i >= 0; i--) {
             TaskView tv = taskViews.get(i);
             Task task = tv.getTask();
-            TaskViewAnimation taskAnimation = new TaskViewAnimation(
-                    historyTransitionDuration, PhoneStatusBar.ALPHA_OUT,
+            TaskViewAnimation taskAnimation = new TaskViewAnimation(startDelayIncr * i,
+                    historyTransitionDuration, mFastOutSlowInInterpolator,
                     postAnimationTrigger.decrementOnAnimationEnd());
             postAnimationTrigger.increment();
 
             stackLayout.getStackTransform(task, stackScroller.getStackScroll(), mTmpTransform,
                     null);
             mTmpTransform.alpha = 0f;
+            mTmpTransform.rect.offsetTo(mTmpTransform.rect.left, offscreenY);
             mStackView.updateTaskViewToTransform(tv, mTmpTransform, taskAnimation);
         }
     }
 
     /**
-     * Starts the animation to show the {@link TaskView}s when the history is hidden.  The
-     * {@link TaskView} animations will be deferred until the history view has been animated away.
+     * Starts the animation to show the {@link TaskView}s when the history is hidden.
      */
-    public void startHideHistoryAnimation(final ReferenceCountedTrigger postAnimationTrigger) {
-        final Resources res = mStackView.getResources();
-        final TaskStackLayoutAlgorithm stackLayout = mStackView.getStackAlgorithm();
-        final TaskStackViewScroller stackScroller = mStackView.getScroller();
+    public void startHideHistoryAnimation() {
+        Resources res = mStackView.getResources();
+        TaskStackLayoutAlgorithm stackLayout = mStackView.getStackAlgorithm();
+        TaskStackViewScroller stackScroller = mStackView.getScroller();
 
-        final int historyTransitionDuration = res.getInteger(
+        int historyTransitionDuration = res.getInteger(
                 R.integer.recents_history_transition_duration);
+        int startDelayIncr = 16;
 
         List<TaskView> taskViews = mStackView.getTaskViews();
         int taskViewCount = taskViews.size();
         for (int i = taskViewCount - 1; i >= 0; i--) {
-            final TaskView tv = taskViews.get(i);
-            postAnimationTrigger.addLastDecrementRunnable(new Runnable() {
-                @Override
-                public void run() {
-                    TaskViewAnimation taskAnimation = new TaskViewAnimation(
-                            historyTransitionDuration, PhoneStatusBar.ALPHA_IN);
-                    stackLayout.getStackTransform(tv.getTask(), stackScroller.getStackScroll(),
-                            mTmpTransform, null);
-                    mTmpTransform.alpha = 1f;
-                    mStackView.updateTaskViewToTransform(tv, mTmpTransform, taskAnimation);
-                }
-            });
+            TaskView tv = taskViews.get(i);
+            TaskViewAnimation taskAnimation = new TaskViewAnimation(startDelayIncr * i,
+                    historyTransitionDuration, mFastOutSlowInInterpolator);
+            stackLayout.getStackTransform(tv.getTask(), stackScroller.getStackScroll(),
+                    mTmpTransform, null);
+            mTmpTransform.alpha = 1f;
+            mStackView.updateTaskViewToTransform(tv, mTmpTransform, taskAnimation);
         }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackLayoutAlgorithm.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackLayoutAlgorithm.java
index 93849dc..bb37c04 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackLayoutAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackLayoutAlgorithm.java
@@ -354,6 +354,7 @@
         mStackBottomOffset = mSystemInsets.bottom + heightPadding;
         state.computeRects(mFreeformRect, mStackRect, taskStackBounds, widthPadding, heightPadding,
                 mStackBottomOffset);
+        // The history button will take the full un-padded header space above the stack
         mHistoryButtonRect.set(mStackRect.left, mStackRect.top - heightPadding,
                 mStackRect.right, mStackRect.top + mFocusedPeekHeight);
 
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
index 7423b78..79f6381 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
@@ -74,6 +74,7 @@
 import com.android.systemui.recents.events.ui.focus.FocusNextTaskViewEvent;
 import com.android.systemui.recents.events.ui.focus.FocusPreviousTaskViewEvent;
 import com.android.systemui.recents.misc.DozeTrigger;
+import com.android.systemui.recents.misc.ReferenceCountedTrigger;
 import com.android.systemui.recents.misc.SystemServicesProxy;
 import com.android.systemui.recents.misc.Utilities;
 import com.android.systemui.recents.model.Task;
@@ -1117,12 +1118,19 @@
         // We can get spurious measure passes with the old bounds when docking, and since we are
         // using the current stack bounds during drag and drop, don't overwrite them until we
         // actually get new bounds
+        boolean requiresLayout = false;
         if (!taskStackBounds.equals(mStableStackBounds)) {
             mStableStackBounds.set(taskStackBounds);
             mStackBounds.set(taskStackBounds);
+            requiresLayout = true;
         }
-        mLayoutAlgorithm.setSystemInsets(systemInsets);
-        requestLayout();
+        if (!systemInsets.equals(mLayoutAlgorithm.mSystemInsets)) {
+            mLayoutAlgorithm.setSystemInsets(systemInsets);
+            requiresLayout = true;
+        }
+        if (requiresLayout) {
+            requestLayout();
+        }
     }
 
     /**
@@ -1235,7 +1243,7 @@
         // Update the history button visibility
         if (shouldShowHistoryButton() &&
                 mStackScroller.getStackScroll() < SHOW_HISTORY_BUTTON_SCROLL_THRESHOLD) {
-            EventBus.getDefault().send(new ShowHistoryButtonEvent());
+            EventBus.getDefault().send(new ShowHistoryButtonEvent(false /* translate */));
         } else {
             EventBus.getDefault().send(new HideHistoryButtonEvent());
         }
@@ -1409,7 +1417,7 @@
         tv.onTaskBound(task);
 
         // Load the task data
-        Recents.getTaskLoader().loadTaskData(task);
+        Recents.getTaskLoader().loadTaskData(task, true /* fetchAndInvalidateThumbnails */);
 
         // If the doze trigger has already fired, then update the state for this task view
         tv.setNoUserInteractionState();
@@ -1468,13 +1476,15 @@
             relayoutTaskViewsOnNextFrame(animation);
         }
 
-        if (shouldShowHistoryButton() &&
-                prevScroll > SHOW_HISTORY_BUTTON_SCROLL_THRESHOLD &&
-                curScroll <= SHOW_HISTORY_BUTTON_SCROLL_THRESHOLD) {
-            EventBus.getDefault().send(new ShowHistoryButtonEvent());
-        } else if (prevScroll < HIDE_HISTORY_BUTTON_SCROLL_THRESHOLD &&
-                curScroll >= HIDE_HISTORY_BUTTON_SCROLL_THRESHOLD) {
-            EventBus.getDefault().send(new HideHistoryButtonEvent());
+        if (mEnterAnimationComplete) {
+            if (shouldShowHistoryButton() &&
+                    prevScroll > SHOW_HISTORY_BUTTON_SCROLL_THRESHOLD &&
+                    curScroll <= SHOW_HISTORY_BUTTON_SCROLL_THRESHOLD) {
+                EventBus.getDefault().send(new ShowHistoryButtonEvent(true /* translate */));
+            } else if (prevScroll < HIDE_HISTORY_BUTTON_SCROLL_THRESHOLD &&
+                    curScroll >= HIDE_HISTORY_BUTTON_SCROLL_THRESHOLD) {
+                EventBus.getDefault().send(new HideHistoryButtonEvent());
+            }
         }
     }
 
@@ -1753,11 +1763,19 @@
     }
 
     public final void onBusEvent(ShowHistoryEvent event) {
-        mAnimationHelper.startShowHistoryAnimation(event.getAnimationTrigger());
+        ReferenceCountedTrigger postAnimTrigger = new ReferenceCountedTrigger();
+        postAnimTrigger.addLastDecrementRunnable(new Runnable() {
+            @Override
+            public void run() {
+                setVisibility(View.INVISIBLE);
+            }
+        });
+        mAnimationHelper.startShowHistoryAnimation(postAnimTrigger);
     }
 
     public final void onBusEvent(HideHistoryEvent event) {
-        mAnimationHelper.startHideHistoryAnimation(event.getAnimationTrigger());
+        setVisibility(View.VISIBLE);
+        mAnimationHelper.startHideHistoryAnimation();
     }
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewAnimation.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewAnimation.java
index 363ad66..74b16d0 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewAnimation.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewAnimation.java
@@ -45,6 +45,10 @@
         this(0 /* startDelay */, duration, interpolator, listener);
     }
 
+    public TaskViewAnimation(int startDelay, int duration, Interpolator interpolator) {
+        this(startDelay, duration, interpolator, null);
+    }
+
     public TaskViewAnimation(int startDelay, int duration, Interpolator interpolator,
             Animator.AnimatorListener listener) {
         this.startDelay = startDelay;
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java
index d5aea9d..36760f7 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java
@@ -47,7 +47,6 @@
 import com.android.systemui.R;
 import com.android.systemui.recents.Constants;
 import com.android.systemui.recents.Recents;
-import com.android.systemui.recents.RecentsDebugFlags;
 import com.android.systemui.recents.events.EventBus;
 import com.android.systemui.recents.events.activity.LaunchTaskEvent;
 import com.android.systemui.recents.events.ui.ShowApplicationInfoEvent;
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewThumbnail.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewThumbnail.java
index de96d9d..c3a0ac9 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewThumbnail.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewThumbnail.java
@@ -27,8 +27,6 @@
 import android.graphics.Rect;
 import android.graphics.Shader;
 import android.util.AttributeSet;
-import android.util.FloatProperty;
-import android.util.Property;
 import android.view.View;
 import android.view.animation.AnimationUtils;
 import android.view.animation.Interpolator;
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/WindowManagerProxy.java b/packages/SystemUI/src/com/android/systemui/stackdivider/WindowManagerProxy.java
index 67bb58a..24ab506 100644
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/WindowManagerProxy.java
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/WindowManagerProxy.java
@@ -97,7 +97,8 @@
         @Override
         public void run() {
             try {
-                ActivityManagerNative.getDefault().resizeStack(DOCKED_STACK_ID, null, true);
+                ActivityManagerNative.getDefault().resizeStack(DOCKED_STACK_ID, null, true, false,
+                        false);
             } catch (RemoteException e) {
                 Log.w(TAG, "Failed to resize stack: " + e);
             }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
index 5359dd2..4edb976 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
@@ -1175,7 +1175,7 @@
     }
 
     protected void toggleKeyboardShortcuts() {
-        getKeyboardShortcuts().toggleKeyboardShortcuts(mContext);
+        getKeyboardShortcuts().toggleKeyboardShortcuts();
     }
 
     protected void cancelPreloadingRecents() {
@@ -1225,10 +1225,7 @@
             final boolean allowedByUser = 0 != Settings.Secure.getIntForUser(
                     mContext.getContentResolver(),
                     Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0, userHandle);
-            final int dpmFlags = mDevicePolicyManager.getKeyguardDisabledFeatures(null /* admin */,
-                    userHandle);
-            final boolean allowedByDpm = (dpmFlags
-                    & DevicePolicyManager.KEYGUARD_DISABLE_UNREDACTED_NOTIFICATIONS) == 0;
+            final boolean allowedByDpm = adminAllowsUnredactedNotifications(userHandle);
             final boolean allowed = allowedByUser && allowedByDpm;
             mUsersAllowingPrivateNotifications.append(userHandle, allowed);
             return allowed;
@@ -1237,6 +1234,15 @@
         return mUsersAllowingPrivateNotifications.get(userHandle);
     }
 
+    private boolean adminAllowsUnredactedNotifications(int userHandle) {
+        if (userHandle == UserHandle.USER_ALL) {
+            return true;
+        }
+        final int dpmFlags = mDevicePolicyManager.getKeyguardDisabledFeatures(null /* admin */,
+                    userHandle);
+        return (dpmFlags & DevicePolicyManager.KEYGUARD_DISABLE_UNREDACTED_NOTIFICATIONS) == 0;
+    }
+
     /**
      * Returns true if we're on a secure lockscreen and the user wants to hide "sensitive"
      * notification data. If so, private notifications should show their (possibly
@@ -1512,7 +1518,7 @@
 
     protected KeyboardShortcuts getKeyboardShortcuts() {
         if (mKeyboardShortcuts == null) {
-            mKeyboardShortcuts = new KeyboardShortcuts();
+            mKeyboardShortcuts = new KeyboardShortcuts(mContext);
         }
 
         return mKeyboardShortcuts;
@@ -2047,6 +2053,21 @@
         entry.row.resetHeight();
     }
 
+    protected void updatePublicContentView(Entry entry,
+            StatusBarNotification sbn) {
+        final RemoteViews publicContentView = entry.cachedPublicContentView;
+        if (publicContentView != null && entry.getPublicContentView() != null) {
+            final boolean disabledByPolicy =
+                    !adminAllowsUnredactedNotifications(entry.notification.getUserId());
+            publicContentView.setTextViewText(android.R.id.title,
+                    mContext.getString(disabledByPolicy
+                            ? com.android.internal.R.string.notification_hidden_by_policy_text
+                            : com.android.internal.R.string.notification_hidden_text));
+            publicContentView.reapply(sbn.getPackageContext(mContext),
+                    entry.getPublicContentView(), mOnClickHandler);
+        }
+    }
+
     protected void notifyHeadsUpScreenOff() {
         maybeEscalateHeadsUp();
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcuts.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcuts.java
index b36fb7e..25e9a7a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcuts.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcuts.java
@@ -19,25 +19,38 @@
 import android.app.AlertDialog;
 import android.app.Dialog;
 import android.content.Context;
+import android.content.DialogInterface;
+import android.content.DialogInterface.OnClickListener;
+import android.graphics.Color;
+import android.graphics.Typeface;
 import android.graphics.drawable.ColorDrawable;
+import android.graphics.drawable.Drawable;
 import android.os.Handler;
+import android.os.Looper;
+import android.util.DisplayMetrics;
 import android.util.Log;
+import android.util.TypedValue;
 import android.view.KeyEvent;
 import android.view.KeyboardShortcutGroup;
 import android.view.KeyboardShortcutInfo;
 import android.view.LayoutInflater;
 import android.view.View;
+import android.view.ViewGroup;
+import android.view.ViewGroup.LayoutParams;
 import android.view.Window;
-import android.view.WindowManager;
 import android.view.WindowManager.KeyboardShortcutsReceiver;
+import android.widget.LinearLayout;
+import android.widget.ScrollView;
+import android.widget.TextView;
 
 import com.android.systemui.R;
 import com.android.systemui.recents.Recents;
 
+import java.util.ArrayList;
 import java.util.List;
 
 import static android.content.Context.LAYOUT_INFLATER_SERVICE;
-import static android.graphics.Color.TRANSPARENT;
+import static android.graphics.Color.WHITE;
 import static android.view.Gravity.TOP;
 import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_DIALOG;
 
@@ -45,33 +58,44 @@
  * Contains functionality for handling keyboard shortcuts.
  */
 public class KeyboardShortcuts {
-    private static final String TAG = "KeyboardShortcuts";
+    private static final char SYSTEM_HOME_BASE_CHARACTER = '\u2386';
+    private static final char SYSTEM_BACK_BASE_CHARACTER = '\u007F';
+    private static final char SYSTEM_RECENTS_BASE_CHARACTER = '\u0009';
+
+    private final Handler mHandler = new Handler(Looper.getMainLooper());
+    private final Context mContext;
+    private final OnClickListener dialogCloseListener =  new DialogInterface.OnClickListener() {
+        public void onClick(DialogInterface dialog, int id) {
+            dismissKeyboardShortcutsDialog();
+        }
+    };
 
     private Dialog mKeyboardShortcutsDialog;
 
-    public KeyboardShortcuts() {}
+    public KeyboardShortcuts(Context context) {
+        this.mContext = context;
+    }
 
-    public void toggleKeyboardShortcuts(final Context context) {
+    public void toggleKeyboardShortcuts() {
         if (mKeyboardShortcutsDialog == null) {
-            Recents.getSystemServices().requestKeyboardShortcuts(context,
+            Recents.getSystemServices().requestKeyboardShortcuts(mContext,
                 new KeyboardShortcutsReceiver() {
                     @Override
                     public void onKeyboardShortcutsReceived(
                             final List<KeyboardShortcutGroup> result) {
                         KeyboardShortcutGroup systemGroup = new KeyboardShortcutGroup(
-                            context.getString(R.string.keyboard_shortcut_group_system));
+                            mContext.getString(R.string.keyboard_shortcut_group_system), true);
                         systemGroup.addItem(new KeyboardShortcutInfo(
-                            context.getString(R.string.keyboard_shortcut_group_system_home),
-                            '\u2386', KeyEvent.META_META_ON));
+                            mContext.getString(R.string.keyboard_shortcut_group_system_home),
+                            SYSTEM_HOME_BASE_CHARACTER, KeyEvent.META_META_ON));
                         systemGroup.addItem(new KeyboardShortcutInfo(
-                            context.getString(R.string.keyboard_shortcut_group_system_back),
-                            '\u007F', KeyEvent.META_META_ON));
+                            mContext.getString(R.string.keyboard_shortcut_group_system_back),
+                            SYSTEM_BACK_BASE_CHARACTER, KeyEvent.META_META_ON));
                         systemGroup.addItem(new KeyboardShortcutInfo(
-                            context.getString(R.string.keyboard_shortcut_group_system_recents),
-                            '\u0009', KeyEvent.META_ALT_ON));
+                            mContext.getString(R.string.keyboard_shortcut_group_system_recents),
+                            SYSTEM_RECENTS_BASE_CHARACTER, KeyEvent.META_ALT_ON));
                         result.add(systemGroup);
-                        Log.i(TAG, "Keyboard shortcuts received: " + String.valueOf(result));
-                        showKeyboardShortcutsDialog(context);
+                        showKeyboardShortcutsDialog(result);
                     }
                 });
         } else {
@@ -79,33 +103,6 @@
         }
     }
 
-    private void showKeyboardShortcutsDialog(Context context) {
-        // Create dialog.
-        AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(context);
-        LayoutInflater inflater = (LayoutInflater) context.getSystemService(
-                LAYOUT_INFLATER_SERVICE);
-        final View keyboardShortcutsView = inflater.inflate(
-                R.layout.keyboard_shortcuts_view, null);
-
-        populateKeyboardShortcuts(keyboardShortcutsView.findViewById(
-                R.id.keyboard_shortcuts_wrapper));
-        dialogBuilder.setView(keyboardShortcutsView);
-        mKeyboardShortcutsDialog = dialogBuilder.create();
-        mKeyboardShortcutsDialog.setCanceledOnTouchOutside(true);
-
-        // Setup window.
-        Window keyboardShortcutsWindow = mKeyboardShortcutsDialog.getWindow();
-        keyboardShortcutsWindow.setType(TYPE_SYSTEM_DIALOG);
-        keyboardShortcutsWindow.setBackgroundDrawable(
-                new ColorDrawable(TRANSPARENT));
-        keyboardShortcutsWindow.setGravity(TOP);
-        keyboardShortcutsView.post(new Runnable() {
-            public void run() {
-                mKeyboardShortcutsDialog.show();
-            }
-        });
-    }
-
     public void dismissKeyboardShortcutsDialog() {
         if (mKeyboardShortcutsDialog != null) {
             mKeyboardShortcutsDialog.dismiss();
@@ -113,11 +110,99 @@
         }
     }
 
-    /**
-     * @return {@code true} if the keyboard shortcuts have been successfully populated.
-     */
-    private boolean populateKeyboardShortcuts(View keyboardShortcutsLayout) {
-        // TODO: Populate shortcuts.
-        return true;
+    private void showKeyboardShortcutsDialog(
+            final List<KeyboardShortcutGroup> keyboardShortcutGroups) {
+        // Need to post on the main thread.
+        mHandler.post(new Runnable() {
+            @Override
+            public void run() {
+                // TODO: break all this code out into a handleShowKeyboard...
+                // Might add more things posted; should consider adding a custom handler so
+                // you can send the keyboardShortcutsGroups as part of the message.
+                AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(mContext);
+                LayoutInflater inflater = (LayoutInflater) mContext.getSystemService(
+                        LAYOUT_INFLATER_SERVICE);
+                final View keyboardShortcutsView = inflater.inflate(
+                        R.layout.keyboard_shortcuts_view, null);
+                DisplayMetrics dm = mContext.getResources().getDisplayMetrics();
+                ScrollView scrollView = (ScrollView) keyboardShortcutsView.findViewById(
+                        R.id.keyboard_shortcuts_scroll_view);
+                // TODO: find a better way to set the height.
+                scrollView.setLayoutParams(new LinearLayout.LayoutParams(
+                        LayoutParams.WRAP_CONTENT,
+                        (int) (dm.heightPixels * dm.density)));
+
+                populateKeyboardShortcuts((LinearLayout) keyboardShortcutsView.findViewById(
+                        R.id.keyboard_shortcuts_container), keyboardShortcutGroups);
+                dialogBuilder.setView(keyboardShortcutsView);
+                dialogBuilder.setPositiveButton(R.string.quick_settings_done, dialogCloseListener);
+                mKeyboardShortcutsDialog = dialogBuilder.create();
+                mKeyboardShortcutsDialog.setCanceledOnTouchOutside(true);
+
+                // Setup window.
+                Window keyboardShortcutsWindow = mKeyboardShortcutsDialog.getWindow();
+                keyboardShortcutsWindow.setType(TYPE_SYSTEM_DIALOG);
+                keyboardShortcutsWindow.setBackgroundDrawable(
+                        mContext.getDrawable(R.color.ksh_dialog_background_color));
+                keyboardShortcutsWindow.setGravity(TOP);
+                mKeyboardShortcutsDialog.show();
+            }
+        });
+    }
+
+    private void populateKeyboardShortcuts(LinearLayout keyboardShortcutsLayout,
+            List<KeyboardShortcutGroup> keyboardShortcutGroups) {
+        LayoutInflater inflater = LayoutInflater.from(mContext);
+        final int keyboardShortcutGroupsSize = keyboardShortcutGroups.size();
+        for (int i = 0; i < keyboardShortcutGroupsSize; i++) {
+            KeyboardShortcutGroup group = keyboardShortcutGroups.get(i);
+            TextView categoryTitle = (TextView) inflater.inflate(
+                    R.layout.keyboard_shortcuts_category_title, keyboardShortcutsLayout, false);
+            categoryTitle.setText(group.getLabel());
+            categoryTitle.setTextColor(group.isSystemGroup()
+                    ? mContext.getColor(R.color.ksh_system_group_color)
+                    : mContext.getColor(R.color.ksh_application_group_color));
+            keyboardShortcutsLayout.addView(categoryTitle);
+
+            LinearLayout shortcutWrapper = (LinearLayout) inflater.inflate(
+                    R.layout.keyboard_shortcuts_wrapper, null);
+            final int itemsSize = group.getItems().size();
+            for (int j = 0; j < itemsSize; j++) {
+                KeyboardShortcutInfo info = group.getItems().get(j);
+                View shortcutView = inflater.inflate(R.layout.keyboard_shortcut_app_item, null);
+                TextView textView = (TextView) shortcutView
+                        .findViewById(R.id.keyboard_shortcuts_keyword);
+                textView.setText(info.getLabel());
+
+                List<String> shortcutKeys = getHumanReadableShortcutKeys(info);
+                final int shortcutKeysSize = shortcutKeys.size();
+                for (int k = 0; k < shortcutKeysSize; k++) {
+                    String shortcutKey = shortcutKeys.get(k);
+                    TextView shortcutKeyView = (TextView) inflater.inflate(
+                            R.layout.keyboard_shortcuts_key_view, null);
+                    shortcutKeyView.setText(shortcutKey);
+                    LinearLayout shortcutItemsContainer = (LinearLayout) shortcutView
+                            .findViewById(R.id.keyboard_shortcuts_item_container);
+                    shortcutItemsContainer.addView(shortcutKeyView);
+                }
+                shortcutWrapper.addView(shortcutView);
+            }
+
+            // TODO: merge container and wrapper into one xml file - wrapper is always a child of
+            // container.
+            LinearLayout shortcutsContainer = (LinearLayout) inflater.inflate(
+                    R.layout.keyboard_shortcuts_container, null);
+            shortcutsContainer.addView(shortcutWrapper);
+            keyboardShortcutsLayout.addView(shortcutsContainer);
+        }
+    }
+
+    private List<String> getHumanReadableShortcutKeys(KeyboardShortcutInfo info) {
+        // TODO: fix the shortcuts. Find or build an util which can produce human readable
+        // names of the baseCharacter and the modifiers.
+        List<String> shortcutKeys = new ArrayList<>();
+        shortcutKeys.add(KeyEvent.metaStateToString(info.getModifiers()).toUpperCase());
+        shortcutKeys.add(Character.getName(info.getBaseCharacter()).toUpperCase());
+        return shortcutKeys;
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java
index 635e66d..3da8098 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java
@@ -460,16 +460,16 @@
         if (mDark == dark || mContractedChild == null) return;
         mDark = dark;
         dark = dark && !mShowingLegacyBackground;
-        if (mVisibleType == VISIBLE_TYPE_CONTRACTED) {
+        if (mVisibleType == VISIBLE_TYPE_CONTRACTED || !dark) {
             mContractedWrapper.setDark(dark, fade, delay);
         }
-        if (mVisibleType == VISIBLE_TYPE_EXPANDED) {
+        if (mVisibleType == VISIBLE_TYPE_EXPANDED || (mExpandedChild != null && !dark)) {
             mExpandedWrapper.setDark(dark, fade, delay);
         }
-        if (mVisibleType == VISIBLE_TYPE_HEADSUP) {
+        if (mVisibleType == VISIBLE_TYPE_HEADSUP || (mHeadsUpChild != null && !dark)) {
             mHeadsUpWrapper.setDark(dark, fade, delay);
         }
-        if (mSingleLineView != null && mVisibleType == VISIBLE_TYPE_SINGLELINE) {
+        if (mSingleLineView != null && (mVisibleType == VISIBLE_TYPE_SINGLELINE || !dark)) {
             mSingleLineView.setDark(dark, fade, delay);
         }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarNavigationBarController.java b/packages/SystemUI/src/com/android/systemui/statusbar/car/CarNavigationBarController.java
index bed64a3..eb30120 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarNavigationBarController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/car/CarNavigationBarController.java
@@ -30,6 +30,7 @@
 
 import java.net.URISyntaxException;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.List;
 
 /**
@@ -62,8 +63,8 @@
     private SimpleArrayMap<String, Integer> mFacetPackageMap
             = new SimpleArrayMap<String, Integer>();
 
-    private List<Intent> mIntents = new ArrayList<Intent>();
-    private List<Intent> mLongPressIntents = new ArrayList<Intent>();
+    private List<Intent> mIntents;
+    private List<Intent> mLongPressIntents;
 
     private List<CarNavigationButton> mNavButtons = new ArrayList<CarNavigationButton>();
 
@@ -112,16 +113,19 @@
             throw new RuntimeException("car_facet array lengths do not match");
         }
 
+        mIntents = createEmptyIntentList(icons.length());
+        mLongPressIntents = createEmptyIntentList(icons.length());
+
         for (int i = 0; i < icons.length(); i++) {
             Drawable icon = icons.getDrawable(i);
             try {
-                mIntents.add(i,
+                mIntents.set(i,
                         Intent.parseUri(intents.getString(i), Intent.URI_INTENT_SCHEME));
 
                 String longpressUri = longpressIntents.getString(i);
                 boolean hasLongpress = !longpressUri.isEmpty();
                 if (hasLongpress) {
-                    mLongPressIntents.add(i,
+                    mLongPressIntents.set(i,
                             Intent.parseUri(longpressUri, Intent.URI_INTENT_SCHEME));
                 }
 
@@ -299,4 +303,8 @@
         setCurrentFacet(index);
         startActivity(mLongPressIntents.get(index));
     }
+
+    private List<Intent> createEmptyIntentList(int size) {
+        return Arrays.asList(new Intent[size]);
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationCustomViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationCustomViewWrapper.java
index 97bf4b4..60c1911 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationCustomViewWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationCustomViewWrapper.java
@@ -35,6 +35,10 @@
 
     @Override
     public void setDark(boolean dark, boolean fade, long delay) {
+        if (dark == mDark) {
+            return;
+        }
+        super.setDark(dark, fade, delay);
         if (fade) {
             mInvertHelper.fade(dark, delay);
         } else {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationHeaderViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationHeaderViewWrapper.java
index f43a5d0..85f789c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationHeaderViewWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationHeaderViewWrapper.java
@@ -95,6 +95,7 @@
 
     @Override
     public void notifyContentUpdated(StatusBarNotification notification) {
+        super.notifyContentUpdated(notification);
         // Reinspect the notification.
         resolveHeaderViews();
         updateInvertHelper();
@@ -150,6 +151,10 @@
 
     @Override
     public void setDark(boolean dark, boolean fade, long delay) {
+        if (dark == mDark) {
+            return;
+        }
+        super.setDark(dark, fade, delay);
         if (fade) {
             mInvertHelper.fade(dark, delay);
         } else {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationTemplateViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationTemplateViewWrapper.java
index 3475d13..9910dee 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationTemplateViewWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationTemplateViewWrapper.java
@@ -172,6 +172,9 @@
 
     @Override
     public void setDark(boolean dark, boolean fade, long delay) {
+        if (dark == mDark) {
+            return;
+        }
         super.setDark(dark, fade, delay);
         setPictureGrayscale(dark, fade, delay);
         setProgressBarDark(dark, fade, delay);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationViewWrapper.java
index a1cf07e..f50b976 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationViewWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationViewWrapper.java
@@ -31,6 +31,7 @@
 public abstract class NotificationViewWrapper implements TransformableView {
 
     protected final View mView;
+    protected boolean mDark;
 
     public static NotificationViewWrapper wrap(Context ctx, View v) {
         if (v.getId() == com.android.internal.R.id.status_bar_latest_event_content) {
@@ -56,13 +57,17 @@
      * @param fade whether to animate the transition if the mode changes
      * @param delay if fading, the delay of the animation
      */
-    public abstract void setDark(boolean dark, boolean fade, long delay);
+    public void setDark(boolean dark, boolean fade, long delay) {
+        mDark = dark;
+    }
 
     /**
      * Notifies this wrapper that the content of the view might have changed.
      * @param notification
      */
-    public void notifyContentUpdated(StatusBarNotification notification) {};
+    public void notifyContentUpdated(StatusBarNotification notification) {
+        mDark = false;
+    };
 
     /**
      * Update the appearance of the expand button.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarInflaterView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarInflaterView.java
index ba08ee7..6cfd715 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarInflaterView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarInflaterView.java
@@ -18,7 +18,10 @@
 import android.content.Context;
 import android.content.res.Configuration;
 import android.content.res.Resources;
+import android.graphics.drawable.Icon;
+import android.net.Uri;
 import android.util.AttributeSet;
+import android.util.Log;
 import android.util.SparseArray;
 import android.view.LayoutInflater;
 import android.view.View;
@@ -28,8 +31,10 @@
 import android.widget.LinearLayout;
 import android.widget.Space;
 import com.android.systemui.R;
+import com.android.systemui.statusbar.policy.KeyButtonView;
 import com.android.systemui.tuner.TunerService;
 
+import java.io.File;
 import java.util.Objects;
 
 public class NavigationBarInflaterView extends FrameLayout implements TunerService.Tunable {
@@ -38,20 +43,30 @@
 
     public static final String NAV_BAR_VIEWS = "sysui_nav_bar";
 
-    protected static final String MENU_IME = "menu_ime";
-    protected static final String BACK = "back";
-    protected static final String HOME = "home";
-    protected static final String RECENT = "recent";
-    protected static final String NAVSPACE = "space";
+    public static final String MENU_IME = "menu_ime";
+    public static final String BACK = "back";
+    public static final String HOME = "home";
+    public static final String RECENT = "recent";
+    public static final String NAVSPACE = "space";
+    public static final String CLIPBOARD = "clipboard";
+    public static final String KEY = "key";
 
     public static final String GRAVITY_SEPARATOR = ";";
     public static final String BUTTON_SEPARATOR = ",";
 
+    public static final String SIZE_MOD_START = "[";
+    public static final String SIZE_MOD_END = "]";
+
+    public static final String KEY_CODE_START = "(";
+    public static final String KEY_IMAGE_DELIM = ":";
+    public static final String KEY_CODE_END = ")";
+
     protected final LayoutInflater mLayoutInflater;
     protected final LayoutInflater mLandscapeInflater;
 
     protected FrameLayout mRot0;
     protected FrameLayout mRot90;
+
     private SparseArray<ButtonDispatcher> mButtonDispatchers;
     private String mCurrentLayout;
 
@@ -206,9 +221,11 @@
     }
 
     @Nullable
-    protected View inflateButton(String button, ViewGroup parent, boolean landscape) {
-        View v;
+    protected View inflateButton(String buttonSpec, ViewGroup parent, boolean landscape) {
         LayoutInflater inflater = landscape ? mLandscapeInflater : mLayoutInflater;
+        float size = extractSize(buttonSpec);
+        String button = extractButton(buttonSpec);
+        View v = null;
         if (HOME.equals(button)) {
             v = inflater.inflate(R.layout.home, parent, false);
             if (landscape && isSw600Dp()) {
@@ -228,15 +245,63 @@
             v = inflater.inflate(R.layout.menu_ime, parent, false);
         } else if (NAVSPACE.equals(button)) {
             v = inflater.inflate(R.layout.nav_key_space, parent, false);
+        } else if (CLIPBOARD.equals(button)) {
+            v = inflater.inflate(R.layout.clipboard, parent, false);
+        } else if (button.startsWith(KEY)) {
+            String uri = extractImage(button);
+            int code = extractKeycode(button);
+            v = inflater.inflate(R.layout.custom_key, parent, false);
+            ((KeyButtonView) v).setCode(code);
+            if (uri != null) {
+                ((KeyButtonView) v).loadAsync(uri);
+            }
         } else {
             return null;
         }
 
+        if (size != 0) {
+            ViewGroup.LayoutParams params = v.getLayoutParams();
+            params.width = (int) (params.width * size);
+        }
         parent.addView(v);
         addToDispatchers(v);
         return v;
     }
 
+    public static String extractImage(String buttonSpec) {
+        if (!buttonSpec.contains(KEY_IMAGE_DELIM)) {
+            return null;
+        }
+        final int start = buttonSpec.indexOf(KEY_IMAGE_DELIM);
+        String subStr = buttonSpec.substring(start + 1, buttonSpec.indexOf(KEY_CODE_END));
+        return subStr;
+    }
+
+    public static int extractKeycode(String buttonSpec) {
+        if (!buttonSpec.contains(KEY_CODE_START)) {
+            return 1;
+        }
+        final int start = buttonSpec.indexOf(KEY_CODE_START);
+        String subStr = buttonSpec.substring(start + 1, buttonSpec.indexOf(KEY_IMAGE_DELIM));
+        return Integer.parseInt(subStr);
+    }
+
+    public static float extractSize(String buttonSpec) {
+        if (!buttonSpec.contains(SIZE_MOD_START)) {
+            return 1;
+        }
+        final int sizeStart = buttonSpec.indexOf(SIZE_MOD_START);
+        String sizeStr = buttonSpec.substring(sizeStart + 1, buttonSpec.indexOf(SIZE_MOD_END));
+        return Float.parseFloat(sizeStr);
+    }
+
+    public static String extractButton(String buttonSpec) {
+        if (!buttonSpec.contains(SIZE_MOD_START)) {
+            return buttonSpec;
+        }
+        return buttonSpec.substring(0, buttonSpec.indexOf(SIZE_MOD_START));
+    }
+
     private void addToDispatchers(View v) {
         if (mButtonDispatchers != null) {
             final int indexOfKey = mButtonDispatchers.indexOfKey(v.getId());
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 f2dea3c..a6ebab3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -1373,6 +1373,9 @@
             boolean sensitivePackage = packageHasVisibilityOverride(ent.notification.getKey());
             boolean sensitive = (sensitiveNote && hideSensitive) || sensitivePackage;
             boolean showingPublic = sensitive && isLockscreenPublicMode();
+            if (showingPublic) {
+                updatePublicContentView(ent, ent.notification);
+            }
             ent.row.setSensitive(sensitive);
             if (ent.autoRedacted && ent.legacy) {
                 // TODO: Also fade this? Or, maybe easier (and better), provide a dark redacted form
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QSTileHost.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QSTileHost.java
index 9c2159be..b8cd7fb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QSTileHost.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QSTileHost.java
@@ -51,6 +51,7 @@
 import com.android.systemui.statusbar.policy.BatteryController;
 import com.android.systemui.statusbar.policy.BluetoothController;
 import com.android.systemui.statusbar.policy.CastController;
+import com.android.systemui.statusbar.policy.DisplayController;
 import com.android.systemui.statusbar.policy.FlashlightController;
 import com.android.systemui.statusbar.policy.HotspotController;
 import com.android.systemui.statusbar.policy.KeyguardMonitor;
@@ -101,6 +102,7 @@
     private final TileServices mServices;
 
     private final List<Callback> mCallbacks = new ArrayList<>();
+    private final DisplayController mDisplayController;
     private View mHeader;
 
     public QSTileHost(Context context, PhoneStatusBar statusBar,
@@ -127,6 +129,7 @@
         mSecurity = security;
         mBattery = battery;
         mIconController = iconController;
+        mDisplayController = new DisplayController(mContext);
 
         final HandlerThread ht = new HandlerThread(QSTileHost.class.getSimpleName(),
                 Process.THREAD_PRIORITY_BACKGROUND);
@@ -277,6 +280,10 @@
         return mIconController;
     }
 
+    public DisplayController getDisplayController() {
+        return mDisplayController;
+    }
+
     @Override
     public void onTuningChanged(String key, String newValue) {
         if (!TILES_SETTING.equals(key)) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothController.java
index 8fa9c7e..e7e2ac2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothController.java
@@ -33,6 +33,7 @@
     Collection<CachedBluetoothDevice> getDevices();
     void connect(CachedBluetoothDevice device);
     void disconnect(CachedBluetoothDevice device);
+    boolean canConfigBluetooth();
 
     public interface Callback {
         void onBluetoothStateChange(boolean enabled);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothControllerImpl.java
index a04edf7..6439bea 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothControllerImpl.java
@@ -16,11 +16,14 @@
 
 package com.android.systemui.statusbar.policy;
 
+import android.app.ActivityManager;
 import android.bluetooth.BluetoothAdapter;
 import android.content.Context;
 import android.os.Handler;
 import android.os.Looper;
 import android.os.Message;
+import android.os.UserHandle;
+import android.os.UserManager;
 import android.util.Log;
 
 import com.android.settingslib.bluetooth.BluetoothCallback;
@@ -39,6 +42,8 @@
 
     private final ArrayList<Callback> mCallbacks = new ArrayList<Callback>();
     private final LocalBluetoothManager mLocalBluetoothManager;
+    private final UserManager mUserManager;
+    private final int mCurrentUser;
 
     private boolean mEnabled;
     private int mConnectionState = BluetoothAdapter.STATE_DISCONNECTED;
@@ -54,6 +59,14 @@
             onBluetoothStateChanged(
                     mLocalBluetoothManager.getBluetoothAdapter().getBluetoothState());
         }
+        mUserManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
+        mCurrentUser = ActivityManager.getCurrentUser();
+    }
+
+    @Override
+    public boolean canConfigBluetooth() {
+        return !mUserManager.hasUserRestriction(UserManager.DISALLOW_CONFIG_BLUETOOTH,
+                UserHandle.of(mCurrentUser));
     }
 
     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DisplayController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DisplayController.java
new file mode 100644
index 0000000..e5d244f
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DisplayController.java
@@ -0,0 +1,191 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the
+ * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+
+package com.android.systemui.statusbar.policy;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.res.Configuration;
+import android.provider.Settings;
+import com.android.systemui.R;
+import com.android.systemui.tuner.TunerService;
+import libcore.util.Objects;
+
+import java.util.ArrayList;
+
+public class DisplayController implements TunerService.Tunable {
+
+    public static final String COLOR_MATRIX_CUSTOM_ENABLED = "tuner_color_custom_enabled";
+    public static final String COLOR_MATRIX_CUSTOM_VALUES = "tuner_color_custom_values";
+
+    public static final String COLOR_STATE = "sysui_color_matrix_state";
+
+    public static final int COLOR_STATE_DISABLED = 0;
+    public static final int COLOR_STATE_ENABLED = 1;
+    public static final int COLOR_STATE_AUTO = 2;
+
+    public static final String AUTO_STRING = "auto_mode";
+    public static final String NONE_STRING = "none";
+
+    public static final int AUTO_INDEX = 2;
+    public static final int CUSTOM_INDEX = 3;
+
+    // Night mode ~= 3400 K
+    private static final float[] NIGHT_VALUES = new float[] {
+        1, 0,     0,     0,
+        0, .754f, 0,     0,
+        0, 0,     .516f, 0,
+        0, 0,     0,     1,
+    };
+    public static final float[] IDENTITY_MATRIX = new float[] {
+        1, 0, 0, 0,
+        0, 1, 0, 0,
+        0, 0, 1, 0,
+        0, 0, 0, 1,
+    };
+
+    private final ArrayList<Listener> mListeners = new ArrayList<>();
+
+    private final Context mContext;
+
+    private String mCurrentValue;
+    private boolean mListening;
+
+    public DisplayController(Context context) {
+        mContext = context;
+        TunerService.get(mContext).addTunable(this, COLOR_STATE,
+                Settings.Secure.ACCESSIBILITY_DISPLAY_COLOR_MATRIX);
+    }
+
+    public void addListener(Listener listener) {
+        mListeners.add(listener);
+        listener.onCurrentMatrixChanged();
+    }
+
+    public void removeListener(Listener listener) {
+        mListeners.remove(listener);
+    }
+
+    public boolean isEnabled() {
+        return TunerService.get(mContext).getValue(COLOR_STATE, COLOR_STATE_DISABLED)
+                != COLOR_STATE_DISABLED;
+    }
+
+    public boolean isAuto() {
+        return mListening;
+    }
+
+    public void setAuto(boolean auto) {
+        TunerService.get(mContext).setValue(COLOR_STATE, auto ? COLOR_STATE_AUTO
+                : COLOR_STATE_DISABLED);
+    }
+
+    public boolean isCustomSet() {
+        return isCustomEnabled() && Objects.equal(getCurrentMatrix(), getCustomValues());
+    }
+
+    public String getCurrentMatrix() {
+        return mCurrentValue;
+    }
+
+    public String getCustomValues() {
+        return TunerService.get(mContext).getValue(COLOR_MATRIX_CUSTOM_VALUES);
+    }
+
+    public boolean isCustomEnabled() {
+        return TunerService.get(mContext).getValue(COLOR_MATRIX_CUSTOM_ENABLED, 0) != 0;
+    }
+
+    @Override
+    public void onTuningChanged(String key, String newValue) {
+        if (Settings.Secure.ACCESSIBILITY_DISPLAY_COLOR_MATRIX.equals(key)) {
+            mCurrentValue = newValue;
+            for (int i = 0; i < mListeners.size(); i++) {
+                mListeners.get(i).onCurrentMatrixChanged();
+            }
+        } else if (COLOR_STATE.equals(key)) {
+            final boolean listening = newValue != null
+                    && Integer.parseInt(newValue) == COLOR_STATE_AUTO;
+            if (listening && !mListening) {
+                mListening = true;
+                mContext.registerReceiver(mReceiver,
+                        new IntentFilter(Intent.ACTION_CONFIGURATION_CHANGED));
+                updateNightMode();
+            } else if (!listening && mListening) {
+                mListening = false;
+                mContext.unregisterReceiver(mReceiver);
+            }
+            for (int i = 0; i < mListeners.size(); i++) {
+                mListeners.get(i).onCurrentMatrixChanged();
+            }
+        }
+    }
+
+    private void updateNightMode() {
+        final int uiMode = mContext.getResources().getConfiguration().uiMode;
+        final boolean isNightMode = (uiMode & Configuration.UI_MODE_NIGHT_MASK)
+                == Configuration.UI_MODE_NIGHT_YES;
+        String value = null;
+        if (isNightMode) {
+            value = toString(NIGHT_VALUES);
+        }
+        TunerService.get(mContext).setValue(Settings.Secure.ACCESSIBILITY_DISPLAY_COLOR_MATRIX,
+                value);
+    }
+
+    private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            if (Intent.ACTION_CONFIGURATION_CHANGED.equals(intent.getAction())) {
+                updateNightMode();
+            }
+        }
+    };
+
+    public interface Listener {
+        void onCurrentMatrixChanged();
+    }
+
+    public static String[] getColorTransforms(Context context) {
+        return new String[] {
+                NONE_STRING,
+                toString(NIGHT_VALUES),
+                AUTO_STRING, // Blank spot for auto values
+                null, // Blank spot for custom values
+        };
+    }
+
+    public static CharSequence[] getColorTitles(Context context) {
+        // TODO: Move to string array resource.
+        return new CharSequence[]{
+                context.getString(R.string.color_matrix_none),
+                context.getString(R.string.color_matrix_night),
+                context.getString(R.string.color_matrix_auto),
+                context.getString(R.string.color_matrix_custom),
+        };
+    }
+
+    public static String toString(float[] values) {
+        StringBuilder builder = new StringBuilder();
+        for (int i = 0; i < values.length; i++) {
+            if (builder.length() != 0) {
+                builder.append(',');
+            }
+            builder.append(values[i]);
+        }
+        return builder.toString();
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HotspotController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HotspotController.java
index 7ca91a5..b036936 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HotspotController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HotspotController.java
@@ -22,6 +22,7 @@
     boolean isHotspotEnabled();
     boolean isHotspotSupported();
     void setHotspotEnabled(boolean enabled);
+    boolean isTetheringAllowed();
 
     public interface Callback {
         void onHotspotChanged(boolean enabled);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HotspotControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HotspotControllerImpl.java
index 5719f76..61d26c7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HotspotControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HotspotControllerImpl.java
@@ -16,12 +16,15 @@
 
 package com.android.systemui.statusbar.policy;
 
+import android.app.ActivityManager;
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.net.ConnectivityManager;
 import android.net.wifi.WifiManager;
+import android.os.UserHandle;
+import android.os.UserManager;
 import android.util.Log;
 
 import com.android.settingslib.TetherUtil;
@@ -39,13 +42,17 @@
     private final Receiver mReceiver = new Receiver();
     private final ConnectivityManager mConnectivityManager;
     private final Context mContext;
+    private final UserManager mUserManager;
+    private final int mCurrentUser;
 
     private int mHotspotState;
 
     public HotspotControllerImpl(Context context) {
         mContext = context;
-        mConnectivityManager = (ConnectivityManager)context.getSystemService(
+        mConnectivityManager = (ConnectivityManager) context.getSystemService(
                 Context.CONNECTIVITY_SERVICE);
+        mUserManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
+        mCurrentUser = ActivityManager.getCurrentUser();
     }
 
     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
@@ -95,6 +102,12 @@
         return TetherUtil.isTetheringSupported(mContext);
     }
 
+    @Override
+    public boolean isTetheringAllowed() {
+        return !mUserManager.hasUserRestriction(UserManager.DISALLOW_CONFIG_TETHERING,
+                UserHandle.of(mCurrentUser));
+    }
+
     static final class OnStartTetheringCallback extends
             ConnectivityManager.OnStartTetheringCallback {
         @Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java
index ba284c9..6e7cf19 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java
@@ -20,8 +20,12 @@
 import android.content.Context;
 import android.content.res.Configuration;
 import android.content.res.TypedArray;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.Icon;
 import android.hardware.input.InputManager;
 import android.media.AudioManager;
+import android.net.Uri;
+import android.os.AsyncTask;
 import android.os.Bundle;
 import android.os.SystemClock;
 import android.util.AttributeSet;
@@ -99,6 +103,24 @@
         setBackground(new KeyButtonRipple(context, this));
     }
 
+    public void setCode(int code) {
+        mCode = code;
+    }
+
+    public void loadAsync(String uri) {
+        new AsyncTask<String, Void, Drawable>() {
+            @Override
+            protected Drawable doInBackground(String... params) {
+                return Icon.createWithContentUri(params[0]).loadDrawable(mContext);
+            }
+
+            @Override
+            protected void onPostExecute(Drawable drawable) {
+                setImageDrawable(drawable);
+            }
+        }.execute(uri);
+    }
+
     @Override
     protected void onConfigurationChanged(Configuration newConfig) {
         super.onConfigurationChanged(newConfig);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/LocationController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/LocationController.java
index 29a8981..401943e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/LocationController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/LocationController.java
@@ -21,6 +21,7 @@
     boolean setLocationEnabled(boolean enabled);
     void addSettingsChangedCallback(LocationSettingsChangeCallback cb);
     void removeSettingsChangedCallback(LocationSettingsChangeCallback cb);
+    boolean isUserLocationRestricted();
 
     /**
      * A callback for change in location settings (the user has enabled/disabled location).
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/LocationControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/LocationControllerImpl.java
index 7517f97..436a40d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/LocationControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/LocationControllerImpl.java
@@ -52,6 +52,7 @@
 
     private AppOpsManager mAppOpsManager;
     private StatusBarManager mStatusBarManager;
+    private final int mCurrentUser;
 
     private boolean mAreActiveLocationRequests;
 
@@ -73,6 +74,7 @@
         mAppOpsManager = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
         mStatusBarManager
                 = (StatusBarManager) context.getSystemService(Context.STATUS_BAR_SERVICE);
+        mCurrentUser = ActivityManager.getCurrentUser();
 
         // Examine the current location state and initialize the status view.
         updateActiveLocationRequests();
@@ -103,10 +105,6 @@
      * @return true if attempt to change setting was successful.
      */
     public boolean setLocationEnabled(boolean enabled) {
-        int currentUserId = ActivityManager.getCurrentUser();
-        if (isUserLocationRestricted(currentUserId)) {
-            return false;
-        }
         final ContentResolver cr = mContext.getContentResolver();
         // When enabling location, a user consent dialog will pop up, and the
         // setting won't be fully enabled until the user accepts the agreement.
@@ -115,7 +113,7 @@
         // QuickSettings always runs as the owner, so specifically set the settings
         // for the current foreground user.
         return Settings.Secure
-                .putIntForUser(cr, Settings.Secure.LOCATION_MODE, mode, currentUserId);
+                .putIntForUser(cr, Settings.Secure.LOCATION_MODE, mode, mCurrentUser);
     }
 
     /**
@@ -133,11 +131,10 @@
     /**
      * Returns true if the current user is restricted from using location.
      */
-    private boolean isUserLocationRestricted(int userId) {
+    public boolean isUserLocationRestricted() {
         final UserManager um = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
-        return um.hasUserRestriction(
-                UserManager.DISALLOW_SHARE_LOCATION,
-                new UserHandle(userId));
+        return um.hasUserRestriction(UserManager.DISALLOW_SHARE_LOCATION,
+                UserHandle.of(mCurrentUser));
     }
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
index e4ded67..f5869b4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
@@ -622,7 +622,7 @@
 
     private void checkIfAddUserDisallowed(UserRecord record) {
         EnforcedAdmin admin = RestrictedLockUtils.checkIfRestrictionEnforced(mContext,
-                UserManager.DISALLOW_ADD_USER, UserHandle.myUserId());
+                UserManager.DISALLOW_ADD_USER, ActivityManager.getCurrentUser());
         if (admin != null) {
             record.isDisabledByAdmin = true;
             record.enforcedAdmin = admin;
diff --git a/packages/SystemUI/src/com/android/systemui/tuner/ClipboardView.java b/packages/SystemUI/src/com/android/systemui/tuner/ClipboardView.java
new file mode 100644
index 0000000..63ed912
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/tuner/ClipboardView.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the
+ * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+
+package com.android.systemui.tuner;
+
+import com.android.systemui.R;
+
+import android.content.ClipData;
+import android.content.ClipboardManager;
+import android.content.ClipboardManager.OnPrimaryClipChangedListener;
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.DragEvent;
+import android.view.MotionEvent;
+import android.view.View;
+import android.widget.ImageView;
+
+public class ClipboardView extends ImageView implements OnPrimaryClipChangedListener {
+
+    private static final int TARGET_COLOR = 0x4dffffff;
+    private final ClipboardManager mClipboardManager;
+    private ClipData mCurrentClip;
+
+    public ClipboardView(Context context, AttributeSet attrs) {
+        super(context, attrs);
+        mClipboardManager = context.getSystemService(ClipboardManager.class);
+    }
+
+    @Override
+    protected void onAttachedToWindow() {
+        super.onAttachedToWindow();
+        startListening();
+    }
+
+    @Override
+    protected void onDetachedFromWindow() {
+        super.onDetachedFromWindow();
+        stopListening();
+    }
+
+    @Override
+    public boolean onTouchEvent(MotionEvent ev) {
+        if (ev.getActionMasked() == MotionEvent.ACTION_DOWN && mCurrentClip != null) {
+            startPocketDrag();
+        }
+        return super.onTouchEvent(ev);
+    }
+
+    @Override
+    public boolean onDragEvent(DragEvent event) {
+        switch (event.getAction()) {
+            case DragEvent.ACTION_DRAG_ENTERED:
+                setBackgroundDragTarget(true);
+                break;
+            case DragEvent.ACTION_DROP:
+                mClipboardManager.setPrimaryClip(event.getClipData());
+            case DragEvent.ACTION_DRAG_EXITED:
+            case DragEvent.ACTION_DRAG_ENDED:
+                setBackgroundDragTarget(false);
+                break;
+        }
+        return true;
+    }
+
+    private void setBackgroundDragTarget(boolean isTarget) {
+        setBackgroundColor(isTarget ? TARGET_COLOR : 0);
+    }
+
+    public void startPocketDrag() {
+        startDragAndDrop(mCurrentClip, new View.DragShadowBuilder(this), null,
+                View.DRAG_FLAG_GLOBAL);
+    }
+
+    public void startListening() {
+        mClipboardManager.addPrimaryClipChangedListener(this);
+        onPrimaryClipChanged();
+    }
+
+    public void stopListening() {
+        mClipboardManager.removePrimaryClipChangedListener(this);
+    }
+
+    @Override
+    public void onPrimaryClipChanged() {
+        mCurrentClip = mClipboardManager.getPrimaryClip();
+        setImageResource(mCurrentClip != null
+                ? R.drawable.clipboard_full : R.drawable.clipboard_empty);
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/tuner/ColorMatrixFragment.java b/packages/SystemUI/src/com/android/systemui/tuner/ColorMatrixFragment.java
index 8ed1b06..dfacd03 100644
--- a/packages/SystemUI/src/com/android/systemui/tuner/ColorMatrixFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/tuner/ColorMatrixFragment.java
@@ -19,6 +19,7 @@
 import android.app.AlertDialog;
 import android.app.Dialog;
 import android.app.DialogFragment;
+import android.content.ContentResolver;
 import android.content.Context;
 import android.content.DialogInterface;
 import android.os.Bundle;
@@ -28,15 +29,15 @@
 import android.support.v7.preference.DropDownPreference;
 import android.support.v7.preference.Preference;
 import android.support.v7.preference.PreferenceViewHolder;
-import android.text.Editable;
-import android.text.TextUtils;
-import android.text.TextWatcher;
+import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
-import android.widget.Button;
-import android.widget.EditText;
+import android.widget.SeekBar;
+import android.widget.Switch;
+
 import com.android.systemui.R;
 import com.android.systemui.statusbar.phone.QSTileHost;
+import com.android.systemui.statusbar.policy.DisplayController;
 
 import java.util.Objects;
 
@@ -44,21 +45,6 @@
 
     private static final String TAG = "ColorMatrixFragment";
 
-    public static final int CUSTOM_INDEX = 2;
-
-    // Night mode ~= 3400 K
-    private static final float[] NIGHT_VALUES = new float[] {
-        1, 0,     0,     0,
-        0, .754f, 0,     0,
-        0, 0,     .516f, 0,
-        0, 0,     0,     1,
-    };
-    public static final float[] IDENTITY_MATRIX = new float[]{
-            1, 0, 0, 0,
-            0, 1, 0, 0,
-            0, 0, 1, 0,
-            0, 0, 0, 1,
-    };
     private static final long RESET_DELAY = 10000;
 
     private boolean mCustomEnabled;
@@ -67,20 +53,29 @@
     private String mCustomValues;
     private SwitchPreference mEnableCustomPreference;
     private MatrixPreference mCustomPreference;
-    private SwitchPreference mShowQs;
-    private String mTiles;
+    private int mState;
+    private Switch mSwitch;
 
     @Override
     public void onCreate(@Nullable Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
 
         Context context = getContext();
-        TunerService.get(context).addTunable(this, ColorMatrixTile.COLOR_MATRIX_CUSTOM_ENABLED,
-                ColorMatrixTile.COLOR_MATRIX_CUSTOM_VALUES, QSTileHost.TILES_SETTING,
+        TunerService.get(context).addTunable(this, DisplayController.COLOR_MATRIX_CUSTOM_ENABLED,
+                DisplayController.COLOR_MATRIX_CUSTOM_VALUES, DisplayController.COLOR_STATE,
                 Settings.Secure.ACCESSIBILITY_DISPLAY_COLOR_MATRIX);
     }
 
     @Override
+    public View onCreateView(LayoutInflater inflater, ViewGroup container,
+            Bundle savedInstanceState) {
+        final View view = LayoutInflater.from(getContext()).inflate(
+                R.layout.color_matrix_settings, container, false);
+        ((ViewGroup) view).addView(super.onCreateView(inflater, container, savedInstanceState));
+        return view;
+    }
+
+    @Override
     public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
         final Context context = getPreferenceManager().getContext();
         setPreferenceScreen(getPreferenceManager().createPreferenceScreen(context));
@@ -88,40 +83,33 @@
         mSelectPreference = new DropDownPreference(context);
         mSelectPreference.setTitle(R.string.color_transform);
         mSelectPreference.setSummary("%s");
-        mSelectPreference.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
+        mSelectPreference.setOnPreferenceChangeListener(
+                new Preference.OnPreferenceChangeListener() {
             @Override
             public boolean onPreferenceChange(Preference preference, Object newValue) {
-                int index = Integer.parseInt((String) newValue);
+                if (Objects.equals(newValue, DisplayController.AUTO_STRING)) {
+                    Settings.Secure.putInt(context.getContentResolver(),
+                            DisplayController.COLOR_STATE,
+                            DisplayController.COLOR_STATE_AUTO);
+                    return true;
+                }
+                if (Objects.equals(newValue, DisplayController.NONE_STRING)) {
+                    Settings.Secure.putString(context.getContentResolver(),
+                            Settings.Secure.ACCESSIBILITY_DISPLAY_COLOR_MATRIX, null);
+                    return true;
+                }
+                Settings.Secure.putInt(context.getContentResolver(),
+                        DisplayController.COLOR_STATE,
+                        DisplayController.COLOR_STATE_ENABLED);
+                final String value = (String) newValue;
                 Settings.Secure.putString(context.getContentResolver(),
-                        Settings.Secure.ACCESSIBILITY_DISPLAY_COLOR_MATRIX, getValues()[index]);
+                        Settings.Secure.ACCESSIBILITY_DISPLAY_COLOR_MATRIX,
+                        value);
                 return true;
             }
         });
         getPreferenceScreen().addPreference(mSelectPreference);
 
-        mShowQs = new SwitchPreference(context);
-        mShowQs.setTitle(R.string.color_matrix_show_qs);
-        mShowQs.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
-            @Override
-            public boolean onPreferenceChange(Preference preference, Object newValue) {
-                boolean showTile = (Boolean) newValue;
-                String newTiles;
-                if (showTile) {
-                    newTiles = mTiles != null ? mTiles + "," + ColorMatrixTile.COLOR_MATRIX_SPEC
-                            : "default," + ColorMatrixTile.COLOR_MATRIX_SPEC;
-                } else {
-                    newTiles =
-                            mTiles.replace(mTiles.contains(ColorMatrixTile.COLOR_MATRIX_SPEC+ ",")
-                            ? ColorMatrixTile.COLOR_MATRIX_SPEC + ","
-                            : "," + ColorMatrixTile.COLOR_MATRIX_SPEC, "");
-                }
-                Settings.Secure.putString(context.getContentResolver(), QSTileHost.TILES_SETTING,
-                        newTiles);
-                return true;
-            }
-        });
-        getPreferenceScreen().addPreference(mShowQs);
-
         mEnableCustomPreference = new SwitchPreference(context);
         mEnableCustomPreference.setTitle(R.string.color_enable_custom);
         mEnableCustomPreference.setOnPreferenceChangeListener(
@@ -129,8 +117,12 @@
             @Override
             public boolean onPreferenceChange(Preference preference, Object newValue) {
                 boolean enabled = (Boolean) newValue;
+                if (!enabled && Objects.equals(mCurrentValue, mCustomValues)) {
+                    Settings.Secure.putString(context.getContentResolver(),
+                            Settings.Secure.ACCESSIBILITY_DISPLAY_COLOR_MATRIX, null);
+                }
                 Settings.Secure.putInt(context.getContentResolver(),
-                        ColorMatrixTile.COLOR_MATRIX_CUSTOM_ENABLED, enabled ? 1 : 0);
+                        DisplayController.COLOR_MATRIX_CUSTOM_ENABLED, enabled ? 1 : 0);
                 return true;
             }
         });
@@ -141,6 +133,38 @@
     }
 
     @Override
+    public void onViewCreated(View view, Bundle savedInstanceState) {
+        super.onViewCreated(view, savedInstanceState);
+        View switchBar = view.findViewById(R.id.switch_bar);
+        mSwitch = (Switch) switchBar.findViewById(android.R.id.switch_widget);
+        mSwitch.setChecked(mState != DisplayController.COLOR_STATE_DISABLED);
+        switchBar.setOnClickListener(new View.OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                int newState = mState != DisplayController.COLOR_STATE_DISABLED
+                        ? DisplayController.COLOR_STATE_DISABLED
+                        : DisplayController.COLOR_STATE_ENABLED;
+                ContentResolver contentResolver = getContext().getContentResolver();
+                if (newState == DisplayController.COLOR_STATE_DISABLED) {
+                    String tiles = Settings.Secure.getString(contentResolver,
+                            QSTileHost.TILES_SETTING);
+                    if (tiles != null) {
+                        if (tiles.contains(",colors")) {
+                            tiles = tiles.replace(",colors", "");
+                        } else if (tiles.contains("colors,")) {
+                            tiles = tiles.replace("colors,", "");
+                        }
+                        Settings.Secure.putString(contentResolver, QSTileHost.TILES_SETTING,
+                                tiles);
+                    }
+                }
+                Settings.Secure.putInt(contentResolver,
+                        DisplayController.COLOR_STATE, newState);
+            }
+        });
+    }
+
+    @Override
     public void onDestroy() {
         super.onDestroy();
         TunerService.get(getContext()).removeTunable(this);
@@ -148,20 +172,28 @@
 
     @Override
     public void onTuningChanged(String key, String newValue) {
-        if (ColorMatrixTile.COLOR_MATRIX_CUSTOM_ENABLED.equals(key)) {
+        if (DisplayController.COLOR_MATRIX_CUSTOM_ENABLED.equals(key)) {
             mCustomEnabled = newValue != null && Integer.parseInt(newValue) != 0;
             mEnableCustomPreference.setChecked(mCustomEnabled);
-            mCustomPreference.setEnabled(mCustomEnabled);
+            mCustomPreference.setEnabled(mCustomEnabled
+                    && mState != DisplayController.COLOR_STATE_DISABLED);
             updateSelectOptions();
-        } else if (ColorMatrixTile.COLOR_MATRIX_CUSTOM_VALUES.equals(key)) {
+        } else if (DisplayController.COLOR_MATRIX_CUSTOM_VALUES.equals(key)) {
             mCustomValues = newValue;
+            if (mCustomValues == null) {
+                mCustomValues = DisplayController.toString(DisplayController.IDENTITY_MATRIX);
+            }
             mCustomPreference.setValues(mCustomValues);
             updateSelectOptions();
-        } else if (QSTileHost.TILES_SETTING.equals(key)) {
-            mTiles = newValue;
-            boolean hasTile = newValue != null
-                    && newValue.contains(ColorMatrixTile.COLOR_MATRIX_SPEC);
-            mShowQs.setChecked(hasTile);
+        } else if (DisplayController.COLOR_STATE.equals(key)) {
+            mState = newValue != null ? Integer.parseInt(newValue) : 0;
+            if (mSwitch != null) {
+                mSwitch.setChecked(mState != DisplayController.COLOR_STATE_DISABLED);
+            }
+            mSelectPreference.setEnabled(mState != DisplayController.COLOR_STATE_DISABLED);
+            mEnableCustomPreference.setEnabled(mState != DisplayController.COLOR_STATE_DISABLED);
+            mCustomPreference.setEnabled(mCustomEnabled
+                    && mState != DisplayController.COLOR_STATE_DISABLED);
         } else {
             mCurrentValue = newValue;
             updateSelectOptions();
@@ -169,41 +201,38 @@
     }
 
     private void updateSelectOptions() {
-        final int N = CUSTOM_INDEX + (mCustomEnabled ? 1 : 0);
+        final int N = DisplayController.CUSTOM_INDEX + (mCustomEnabled ? 1 : 0);
         String[] values = new String[N];
-        CharSequence[] totalNames = getColorTitles(getContext());
         CharSequence[] names = new CharSequence[N];
+        CharSequence[] totalNames = DisplayController.getColorTitles(getContext());
+        String[] entries = DisplayController.getColorTransforms(getContext());
+        entries[DisplayController.CUSTOM_INDEX] = mCustomValues != null ? mCustomValues : "";
         for (int i = 0; i < N; i++) {
-            values[i] = String.valueOf(i);
+            values[i] = entries[i];
             names[i] = totalNames[i];
         }
         mSelectPreference.setEntries(names);
         mSelectPreference.setEntryValues(values);
-        String[] entries = getValues();
-        for (int i = 0; i < values.length; i++) {
-            if (Objects.equals(entries[i], mCurrentValue)) {
-                mSelectPreference.setValueIndex(i);
-                return;
-            }
+        int index = 0;
+        if (mState == DisplayController.COLOR_STATE_AUTO) {
+            index = DisplayController.AUTO_INDEX;
+        } else if (mCustomValues != null && Objects.equals(mCurrentValue, mCustomValues)) {
+            index = DisplayController.CUSTOM_INDEX;
+        } else if (Objects.equals(mCurrentValue, entries[1])) {
+            index = 1;
         }
-        mSelectPreference.setSummary(R.string.color_matrix_unknown);
+        mSelectPreference.setValueIndex(index);
+        mSelectPreference.setSummary("%s");
         return;
     }
 
-    private String[] getValues() {
-        String[] ret = getColorTransforms();
-        // Fill in custom based on tuner settings.
-        ret[CUSTOM_INDEX] = mCustomValues;
-        return ret;
-    }
-
     private void startRevertTimer() {
         getView().postDelayed(mResetColorMatrix, RESET_DELAY);
     }
 
     private void onApply() {
         Settings.Secure.putString(getContext().getContentResolver(),
-                ColorMatrixTile.COLOR_MATRIX_CUSTOM_VALUES, mCurrentValue);
+                DisplayController.COLOR_MATRIX_CUSTOM_VALUES, mCurrentValue);
         getView().removeCallbacks(mResetColorMatrix);
     }
 
@@ -212,35 +241,6 @@
         mResetColorMatrix.run();
     }
 
-    public static String[] getColorTransforms() {
-        return new String[] {
-                null,
-                toString(NIGHT_VALUES),
-                null, // Blank spot for custom values
-                null, // Unknown
-        };
-    }
-
-    public static CharSequence[] getColorTitles(Context context) {
-        return new CharSequence[] {
-                context.getString(R.string.color_matrix_none),
-                context.getString(R.string.color_matrix_night),
-                context.getString(R.string.color_matrix_custom),
-                context.getString(R.string.color_matrix_unknown),
-        };
-    }
-
-    private static String toString(float[] values) {
-        StringBuilder builder = new StringBuilder();
-        for (int i = 0; i < values.length; i++) {
-            if (builder.length() != 0) {
-                builder.append(',');
-            }
-            builder.append(values[i]);
-        }
-        return builder.toString();
-    }
-
     private final Runnable mResetColorMatrix = new Runnable() {
         @Override
         public void run() {
@@ -259,14 +259,10 @@
         }
 
         public void setValues(String customValues) {
-            if (customValues == null) {
-                mValues = IDENTITY_MATRIX;
-            } else {
-                String[] strValues = customValues.split(",");
-                mValues = new float[strValues.length];
-                for (int i = 0; i < mValues.length; i++) {
-                    mValues[i] = Float.parseFloat(strValues[i]);
-                }
+            String[] strValues = customValues.split(",");
+            mValues = new float[strValues.length];
+            for (int i = 0; i < mValues.length; i++) {
+                mValues[i] = Float.parseFloat(strValues[i]);
             }
             notifyChanged();
         }
@@ -274,48 +270,38 @@
         @Override
         public void onBindViewHolder(PreferenceViewHolder holder) {
             super.onBindViewHolder(holder);
-            ViewGroup vg = (ViewGroup) holder.itemView.findViewById(R.id.edit_group);
-            if (mValues == null) {
-                return;
-            }
-            int childIndex = 0;
-            for (int i = 0; i < mValues.length; i++) {
-                final int index = i;
-                while (!(vg.getChildAt(childIndex) instanceof EditText)) {
-                    childIndex++;
+            bindView(holder.findViewById(R.id.r_group), 0);
+            bindView(holder.findViewById(R.id.g_group), 5);
+            bindView(holder.findViewById(R.id.b_group), 10);
+            holder.findViewById(R.id.apply).setOnClickListener(this);
+        }
+
+        private void bindView(View view, final int index) {
+            SeekBar seekBar = (SeekBar) view.findViewById(com.android.internal.R.id.seekbar);
+            seekBar.setMax(1000);
+            seekBar.setProgress((int) (1000 * mValues[index]));
+            seekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
+                @Override
+                public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
+                    mValues[index] = progress / 1000f;
                 }
-                final EditText editText = (EditText) vg.getChildAt(childIndex++);
-                editText.setText(String.valueOf(mValues[i]));
-                editText.addTextChangedListener(new TextWatcher() {
-                    @Override
-                    public void afterTextChanged(Editable s) {
-                        if (TextUtils.isEmpty(s.toString())) {
-                            return;
-                        }
-                        try {
-                            mValues[index] = Float.parseFloat(s.toString());
-                        } catch (NumberFormatException e) {
-                            mValues[index] = 0;
-                        }
-                    }
 
-                    @Override
-                    public void onTextChanged(CharSequence s, int start, int before, int count) {
-                    }
+                @Override
+                public void onStartTrackingTouch(SeekBar seekBar) {
+                }
 
-                    @Override
-                    public void beforeTextChanged(CharSequence s, int start, int count, int after) {
-                    }
-                });
-            }
-            ((Button) holder.itemView.findViewById(R.id.apply)).setOnClickListener(this);
+                @Override
+                public void onStopTrackingTouch(SeekBar seekBar) {
+                }
+            });
         }
 
         @Override
         public void onClick(View v) {
+            startRevertTimer();
             Settings.Secure.putString(getContext().getContentResolver(),
                     Settings.Secure.ACCESSIBILITY_DISPLAY_COLOR_MATRIX,
-                    ColorMatrixFragment.toString(mValues));
+                    DisplayController.toString(mValues));
             RevertWarning.show(ColorMatrixFragment.this);
         }
 
diff --git a/packages/SystemUI/src/com/android/systemui/tuner/ColorMatrixTile.java b/packages/SystemUI/src/com/android/systemui/tuner/ColorMatrixTile.java
index 3206882..1933c15 100644
--- a/packages/SystemUI/src/com/android/systemui/tuner/ColorMatrixTile.java
+++ b/packages/SystemUI/src/com/android/systemui/tuner/ColorMatrixTile.java
@@ -20,15 +20,17 @@
 import com.android.internal.logging.MetricsProto.MetricsEvent;
 import com.android.systemui.R;
 import com.android.systemui.qs.QSTile;
-import libcore.util.Objects;
+import com.android.systemui.statusbar.policy.DisplayController;
 
-public class ColorMatrixTile extends QSTile<QSTile.State> implements TunerService.Tunable {
+import java.util.Objects;
 
-    public static final String COLOR_MATRIX_CUSTOM_ENABLED = "tuner_color_custom_enabled";
-    public static final String COLOR_MATRIX_CUSTOM_VALUES = "tuner_color_custom_values";
+
+public class ColorMatrixTile extends QSTile<QSTile.State> implements DisplayController.Listener {
 
     public static final String COLOR_MATRIX_SPEC = "colors";
 
+    private final DisplayController mDisplayController;
+
     private int mIndex;
     private String mCurrentValue;
 
@@ -38,18 +40,17 @@
 
     public ColorMatrixTile(Host host) {
         super(host);
+        mDisplayController = host.getDisplayController();
     }
 
     @Override
     public void setListening(boolean listening) {
         if (listening) {
-            mValues = ColorMatrixFragment.getColorTransforms();
-            mValueTitles = ColorMatrixFragment.getColorTitles(mContext);
-            TunerService.get(mContext).addTunable(this, COLOR_MATRIX_CUSTOM_ENABLED,
-                    COLOR_MATRIX_CUSTOM_VALUES,
-                    Settings.Secure.ACCESSIBILITY_DISPLAY_COLOR_MATRIX);
+            mValues = DisplayController.getColorTransforms(mContext);
+            mValueTitles = DisplayController.getColorTitles(mContext);
+            mDisplayController.addListener(this);
         } else {
-            TunerService.get(mContext).removeTunable(this);
+            mDisplayController.removeListener(this);
         }
     }
 
@@ -61,46 +62,44 @@
     @Override
     protected void handleClick() {
         mIndex++;
-        if (!mCustomEnabled && (mIndex == ColorMatrixFragment.CUSTOM_INDEX)) {
-            mIndex++;
-        }
-        if (mIndex == mValues.length - 1) {
-            mIndex = 0;
-        }
-        Settings.Secure.putStringForUser(mContext.getContentResolver(),
-                Settings.Secure.ACCESSIBILITY_DISPLAY_COLOR_MATRIX, mValues[mIndex],
-                ActivityManager.getCurrentUser());
-        refreshState();
-    }
-
-    @Override
-    public void onTuningChanged(String key, String newValue) {
-        if (COLOR_MATRIX_CUSTOM_ENABLED.equals(key)) {
-            mCustomEnabled = newValue != null && Integer.parseInt(newValue) != 0;
-        } else if (COLOR_MATRIX_CUSTOM_VALUES.equals(key)) {
-            mValues[ColorMatrixFragment.CUSTOM_INDEX] = newValue;
+        if (mIndex == DisplayController.AUTO_INDEX) {
+            mDisplayController.setAuto(true);
         } else {
-            mCurrentValue = newValue;
-        }
-        // Last value is unknown, default to that.
-        mIndex = mValues.length - 1;
-        for (int i = 0; i < mValues.length - 1; i++) {
-            if (Objects.equal(mCurrentValue, mValues[i])) {
-                mIndex = i;
-                break;
+            mDisplayController.setAuto(false);
+            if (!mDisplayController.isCustomEnabled()
+                    && (mIndex == DisplayController.CUSTOM_INDEX)) {
+                mIndex++;
             }
+            if (mIndex == mValues.length - 1) {
+                mIndex = 0;
+            }
+            Settings.Secure.putStringForUser(mContext.getContentResolver(),
+                    Settings.Secure.ACCESSIBILITY_DISPLAY_COLOR_MATRIX, mValues[mIndex],
+                    ActivityManager.getCurrentUser());
         }
         refreshState();
     }
 
     @Override
     protected void handleUpdateState(State state, Object arg) {
+        if (mDisplayController.isAuto()) {
+            mIndex = DisplayController.AUTO_INDEX;
+        } else if (mDisplayController.isCustomSet()) {
+            mIndex = DisplayController.CUSTOM_INDEX;
+        } else {
+            mIndex = Objects.equals(mDisplayController.getCurrentMatrix(), mValues[1]) ? 1 : 0;
+        }
         state.icon = ResourceIcon.get(R.drawable.ic_colorize);
         state.label = mValueTitles[mIndex];
         state.contentDescription = mValueTitles[mIndex];
     }
 
     @Override
+    public void onCurrentMatrixChanged() {
+        refreshState();
+    }
+
+    @Override
     public int getMetricsCategory() {
         return MetricsEvent.QS_COLOR_MATRIX;
     }
diff --git a/packages/SystemUI/src/com/android/systemui/tuner/KeycodeSelectionHelper.java b/packages/SystemUI/src/com/android/systemui/tuner/KeycodeSelectionHelper.java
new file mode 100644
index 0000000..e6073ae
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/tuner/KeycodeSelectionHelper.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the
+ * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+
+package com.android.systemui.tuner;
+
+import android.app.AlertDialog;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.net.Uri;
+import android.util.Pair;
+import android.view.KeyEvent;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.Modifier;
+import java.util.ArrayList;
+import com.android.systemui.R;
+
+public class KeycodeSelectionHelper {
+
+    private static final ArrayList<String> mKeycodeStrings = new ArrayList<>();
+    private static final ArrayList<Integer> mKeycodes = new ArrayList<>();
+
+    private static final String KEYCODE_STRING = "KEYCODE_";
+
+    static {
+        Class<KeyEvent> cls = KeyEvent.class;
+        for (Field field : cls.getDeclaredFields()) {
+            if (Modifier.isStatic(field.getModifiers())
+                    && field.getName().startsWith(KEYCODE_STRING)
+                    && field.getType().equals(int.class)) {
+                try {
+                    mKeycodeStrings.add(formatString(field.getName()));
+                    mKeycodes.add((Integer) field.get(null));
+                } catch (IllegalAccessException e) {
+                }
+            }
+        }
+    }
+
+    // Force the string into something somewhat readable.
+    private static String formatString(String name) {
+        StringBuilder str = new StringBuilder(name.replace(KEYCODE_STRING, "").replace("_", " ")
+                .toLowerCase());
+        for (int i = 0; i < str.length(); i++) {
+            if (i == 0 || str.charAt(i - 1) == ' ') {
+                str.setCharAt(i, Character.toUpperCase(str.charAt(i)));
+            }
+        }
+        return str.toString();
+    }
+
+    public static void showKeycodeSelect(Context context, final OnSelectionComplete listener) {
+        new AlertDialog.Builder(context)
+                .setTitle(R.string.select_keycode)
+                .setItems(mKeycodeStrings.toArray(new String[0]),
+                        new DialogInterface.OnClickListener() {
+                    @Override
+                    public void onClick(DialogInterface dialog, int which) {
+                        listener.onSelectionComplete(mKeycodes.get(which));
+                    }
+                }).show();
+    }
+
+    public static Intent getSelectImageIntent() {
+        return new Intent(Intent.ACTION_OPEN_DOCUMENT).addCategory(Intent.CATEGORY_OPENABLE)
+                .setType("image/*");
+    }
+
+    public interface OnSelectionComplete {
+        void onSelectionComplete(int code);
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/tuner/NavBarTuner.java b/packages/SystemUI/src/com/android/systemui/tuner/NavBarTuner.java
new file mode 100644
index 0000000..6fba9dc
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/tuner/NavBarTuner.java
@@ -0,0 +1,608 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the
+ * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+
+package com.android.systemui.tuner;
+
+import com.android.systemui.R;
+
+import android.annotation.Nullable;
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.app.Fragment;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.res.ColorStateList;
+import android.content.res.Configuration;
+import android.content.res.TypedArray;
+import android.graphics.Canvas;
+import android.graphics.drawable.Drawable;
+import android.net.Uri;
+import android.os.Bundle;
+import android.provider.Settings;
+import android.support.v7.widget.LinearLayoutManager;
+import android.support.v7.widget.RecyclerView;
+import android.support.v7.widget.helper.ItemTouchHelper;
+import android.util.DisplayMetrics;
+import android.util.TypedValue;
+import android.view.Display;
+import android.view.LayoutInflater;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
+import android.view.MotionEvent;
+import android.view.Surface;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ImageView;
+import android.widget.SeekBar;
+import android.widget.TextView;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import static com.android.systemui.statusbar.phone.NavigationBarInflaterView.CLIPBOARD;
+import static com.android.systemui.statusbar.phone.NavigationBarInflaterView.KEY;
+import static com.android.systemui.statusbar.phone.NavigationBarInflaterView.KEY_CODE_END;
+import static com.android.systemui.statusbar.phone.NavigationBarInflaterView.KEY_CODE_START;
+import static com.android.systemui.statusbar.phone.NavigationBarInflaterView.KEY_IMAGE_DELIM;
+import static com.android.systemui.statusbar.phone.NavigationBarInflaterView.SIZE_MOD_END;
+import static com.android.systemui.statusbar.phone.NavigationBarInflaterView.SIZE_MOD_START;
+import static com.android.systemui.statusbar.phone.NavigationBarInflaterView.extractButton;
+import static com.android.systemui.statusbar.phone.NavigationBarInflaterView.extractSize;
+import static com.android.systemui.statusbar.phone.NavigationBarInflaterView.BACK;
+import static com.android.systemui.statusbar.phone.NavigationBarInflaterView.BUTTON_SEPARATOR;
+import static com.android.systemui.statusbar.phone.NavigationBarInflaterView.GRAVITY_SEPARATOR;
+import static com.android.systemui.statusbar.phone.NavigationBarInflaterView.HOME;
+import static com.android.systemui.statusbar.phone.NavigationBarInflaterView.MENU_IME;
+import static com.android.systemui.statusbar.phone.NavigationBarInflaterView.NAVSPACE;
+import static com.android.systemui.statusbar.phone.NavigationBarInflaterView.NAV_BAR_VIEWS;
+import static com.android.systemui.statusbar.phone.NavigationBarInflaterView.RECENT;
+
+public class NavBarTuner extends Fragment implements TunerService.Tunable {
+
+    private static final int SAVE = Menu.FIRST + 1;
+    private static final int RESET = Menu.FIRST + 2;
+    private static final int READ_REQUEST = 42;
+
+    private static final float PREVIEW_SCALE = .95f;
+    private static final float PREVIEW_SCALE_LANDSCAPE = .75f;
+
+    private NavBarAdapter mNavBarAdapter;
+    private PreviewNavInflater mPreview;
+
+    @Override
+    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container,
+            Bundle savedInstanceState) {
+        final View view = inflater.inflate(R.layout.nav_bar_tuner, container, false);
+        inflatePreview((ViewGroup) view.findViewById(R.id.nav_preview_frame));
+        return view;
+    }
+
+    private void inflatePreview(ViewGroup view) {
+        Display display = getActivity().getWindowManager().getDefaultDisplay();
+        boolean isRotated = display.getRotation() == Surface.ROTATION_90
+                || display.getRotation() == Surface.ROTATION_270;
+
+        Configuration config = new Configuration(getContext().getResources().getConfiguration());
+        boolean isPhoneLandscape = isRotated && (config.smallestScreenWidthDp < 600);
+        final float scale = isPhoneLandscape ? PREVIEW_SCALE_LANDSCAPE : PREVIEW_SCALE;
+        config.densityDpi = (int) (config.densityDpi * scale);
+
+        mPreview = (PreviewNavInflater) LayoutInflater.from(getContext().createConfigurationContext(
+                config)).inflate(R.layout.nav_bar_tuner_inflater, view, false);
+        final ViewGroup.LayoutParams layoutParams = mPreview.getLayoutParams();
+        layoutParams.width = (int) ((isPhoneLandscape ? display.getHeight() : display.getWidth())
+                * scale);
+        // Not sure why, but the height dimen is not being scaled with the dp, set it manually
+        // for now.
+        layoutParams.height = (int) (layoutParams.height * scale);
+        if (isPhoneLandscape) {
+            int width = layoutParams.width;
+            layoutParams.width = layoutParams.height;
+            layoutParams.height = width;
+        }
+        view.addView(mPreview);
+
+        if (isRotated) {
+            mPreview.findViewById(R.id.rot0).setVisibility(View.GONE);
+            final View rot90 = mPreview.findViewById(R.id.rot90);
+            rot90.findViewById(R.id.ends_group_lightsout).setVisibility(View.GONE);
+            rot90.findViewById(R.id.center_group_lightsout).setVisibility(View.GONE);
+        } else {
+            mPreview.findViewById(R.id.rot90).setVisibility(View.GONE);
+            final View rot0 = mPreview.findViewById(R.id.rot0);
+            rot0.findViewById(R.id.ends_group_lightsout).setVisibility(View.GONE);
+            rot0.findViewById(R.id.center_group_lightsout).setVisibility(View.GONE);
+        }
+    }
+
+    private void notifyChanged() {
+        mPreview.onTuningChanged(NAV_BAR_VIEWS, mNavBarAdapter.getNavString());
+    }
+
+    @Override
+    public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
+        super.onViewCreated(view, savedInstanceState);
+        RecyclerView recyclerView = (RecyclerView) view.findViewById(android.R.id.list);
+        final Context context = getContext();
+        recyclerView.setLayoutManager(new LinearLayoutManager(context));
+        mNavBarAdapter = new NavBarAdapter(context);
+        recyclerView.setAdapter(mNavBarAdapter);
+        recyclerView.addItemDecoration(new Dividers(context));
+        final ItemTouchHelper itemTouchHelper = new ItemTouchHelper(mNavBarAdapter.mCallbacks);
+        mNavBarAdapter.setTouchHelper(itemTouchHelper);
+        itemTouchHelper.attachToRecyclerView(recyclerView);
+
+        TunerService.get(getContext()).addTunable(this, NAV_BAR_VIEWS);
+    }
+
+    @Override
+    public void onDestroyView() {
+        super.onDestroyView();
+        TunerService.get(getContext()).removeTunable(this);
+    }
+
+    @Override
+    public void onTuningChanged(String key, String navLayout) {
+        if (!NAV_BAR_VIEWS.equals(key)) return;
+        Context context = getContext();
+        if (navLayout == null) {
+            navLayout = context.getString(R.string.config_navBarLayout);
+        }
+        String[] views = navLayout.split(GRAVITY_SEPARATOR);
+        String[] groups = new String[] { NavBarAdapter.START, NavBarAdapter.CENTER,
+                NavBarAdapter.END};
+        CharSequence[] groupLabels = new String[] { getString(R.string.start),
+                getString(R.string.center), getString(R.string.end) };
+        mNavBarAdapter.clear();
+        for (int i = 0; i < 3; i++) {
+            mNavBarAdapter.addButton(groups[i], groupLabels[i]);
+            for (String button : views[i].split(BUTTON_SEPARATOR)) {
+                mNavBarAdapter.addButton(button, getLabel(button, context));
+            }
+        }
+        mNavBarAdapter.addButton(NavBarAdapter.ADD, getString(R.string.add_button));
+        setHasOptionsMenu(true);
+    }
+
+    @Override
+    public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
+        super.onCreateOptionsMenu(menu, inflater);
+        // TODO: Show save button conditionally, only when there are changes.
+        menu.add(Menu.NONE, SAVE, Menu.NONE, getString(R.string.save))
+                .setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
+        menu.add(Menu.NONE, RESET, Menu.NONE, getString(R.string.reset));
+    }
+
+    @Override
+    public boolean onOptionsItemSelected(MenuItem item) {
+        if (item.getItemId() == SAVE) {
+            if (!mNavBarAdapter.hasHomeButton()) {
+                new AlertDialog.Builder(getContext())
+                        .setTitle(R.string.no_home_title)
+                        .setMessage(R.string.no_home_message)
+                        .setPositiveButton(android.R.string.ok, null)
+                        .show();
+            } else {
+                Settings.Secure.putString(getContext().getContentResolver(),
+                        NAV_BAR_VIEWS, mNavBarAdapter.getNavString());
+            }
+            return true;
+        } else if (item.getItemId() == RESET) {
+            Settings.Secure.putString(getContext().getContentResolver(),
+                    NAV_BAR_VIEWS, null);
+            return true;
+        }
+        return super.onOptionsItemSelected(item);
+    }
+
+    private static CharSequence getLabel(String button, Context context) {
+        if (button.startsWith(HOME)) {
+            return context.getString(R.string.accessibility_home);
+        } else if (button.startsWith(BACK)) {
+            return context.getString(R.string.accessibility_back);
+        } else if (button.startsWith(RECENT)) {
+            return context.getString(R.string.accessibility_recent);
+        } else if (button.startsWith(NAVSPACE)) {
+            return context.getString(R.string.space);
+        } else if (button.startsWith(MENU_IME)) {
+            return context.getString(R.string.menu_ime);
+        } else if (button.startsWith(CLIPBOARD)) {
+            return context.getString(R.string.clipboard);
+        } else if (button.startsWith(KEY)) {
+            return context.getString(R.string.keycode);
+        }
+        return button;
+    }
+
+    private static class Holder extends RecyclerView.ViewHolder {
+        private TextView title;
+
+        public Holder(View itemView) {
+            super(itemView);
+            title = (TextView) itemView.findViewById(android.R.id.title);
+        }
+    }
+
+    private static class Dividers extends RecyclerView.ItemDecoration {
+        private final Drawable mDivider;
+
+        public Dividers(Context context) {
+            TypedValue value = new TypedValue();
+            context.getTheme().resolveAttribute(android.R.attr.listDivider, value, true);
+            mDivider = context.getDrawable(value.resourceId);
+        }
+
+        @Override
+        public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
+            super.onDraw(c, parent, state);
+            final int left = parent.getPaddingLeft();
+            final int right = parent.getWidth() - parent.getPaddingRight();
+
+            final int childCount = parent.getChildCount();
+            for (int i = 0; i < childCount; i++) {
+                final View child = parent.getChildAt(i);
+                final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child
+                        .getLayoutParams();
+                final int top = child.getBottom() + params.bottomMargin;
+                final int bottom = top + mDivider.getIntrinsicHeight();
+                mDivider.setBounds(left, top, right, bottom);
+                mDivider.draw(c);
+            }
+        }
+    }
+
+    private void selectImage() {
+        startActivityForResult(KeycodeSelectionHelper.getSelectImageIntent(), READ_REQUEST);
+    }
+
+    @Override
+    public void onActivityResult(int requestCode, int resultCode, Intent data) {
+        if (requestCode == READ_REQUEST && resultCode == Activity.RESULT_OK && data != null) {
+            final Uri uri = data.getData();
+            final int takeFlags = data.getFlags() & (Intent.FLAG_GRANT_READ_URI_PERMISSION);
+            getContext().getContentResolver().takePersistableUriPermission(uri, takeFlags);
+            mNavBarAdapter.onImageSelected(uri);
+        } else {
+            super.onActivityResult(requestCode, resultCode, data);
+        }
+    }
+
+    private class NavBarAdapter extends RecyclerView.Adapter<Holder>
+            implements View.OnClickListener {
+
+        private static final String START = "start";
+        private static final String CENTER = "center";
+        private static final String END = "end";
+        private static final String ADD = "add";
+
+        private static final int ADD_ID = 0;
+        private static final int BUTTON_ID = 1;
+        private static final int CATEGORY_ID = 2;
+
+        private List<String> mButtons = new ArrayList<>();
+        private List<CharSequence> mLabels = new ArrayList<>();
+        private int mCategoryLayout;
+        private int mButtonLayout;
+        private ItemTouchHelper mTouchHelper;
+
+        // Stored keycode while we wait for image selection on a KEY.
+        private int mKeycode;
+
+        public NavBarAdapter(Context context) {
+            TypedArray attrs = context.getTheme().obtainStyledAttributes(null,
+                    android.R.styleable.Preference, android.R.attr.preferenceStyle, 0);
+            mButtonLayout = attrs.getResourceId(android.R.styleable.Preference_layout, 0);
+            attrs = context.getTheme().obtainStyledAttributes(null,
+                    android.R.styleable.Preference, android.R.attr.preferenceCategoryStyle, 0);
+            mCategoryLayout = attrs.getResourceId(android.R.styleable.Preference_layout, 0);
+        }
+
+        public void setTouchHelper(ItemTouchHelper itemTouchHelper) {
+            mTouchHelper = itemTouchHelper;
+        }
+
+        public void clear() {
+            mButtons.clear();
+            mLabels.clear();
+            notifyDataSetChanged();
+        }
+
+        public void addButton(String button, CharSequence label) {
+            mButtons.add(button);
+            mLabels.add(label);
+            notifyItemInserted(mLabels.size() - 1);
+            notifyChanged();
+        }
+
+        public boolean hasHomeButton() {
+            final int N = mButtons.size();
+            for (int i = 0; i < N; i++) {
+                if (mButtons.get(i).startsWith(HOME)) {
+                    return true;
+                }
+            }
+            return false;
+        }
+
+        public String getNavString() {
+            StringBuilder builder = new StringBuilder();
+            for (int i = 1; i < mButtons.size() - 1; i++) {
+                String button = mButtons.get(i);
+                if (button.equals(CENTER) || button.equals(END)) {
+                    if (builder.length() == 0 || builder.toString().endsWith(GRAVITY_SEPARATOR)) {
+                        // No start or center buttons, fill with a space.
+                        builder.append(NAVSPACE);
+                    }
+                    builder.append(GRAVITY_SEPARATOR);
+                    continue;
+                } else if (builder.length() != 0 && !builder.toString().endsWith(
+                        GRAVITY_SEPARATOR)) {
+                    builder.append(BUTTON_SEPARATOR);
+                }
+                builder.append(button);
+            }
+            if (builder.toString().endsWith(GRAVITY_SEPARATOR)) {
+                // No end buttons, fill with space.
+                builder.append(NAVSPACE);
+            }
+            return builder.toString();
+        }
+
+        @Override
+        public int getItemViewType(int position) {
+            String button = mButtons.get(position);
+            if (button.equals(START) || button.equals(CENTER) || button.equals(END)) {
+                return CATEGORY_ID;
+            }
+            if (button.equals(ADD)) {
+                return ADD_ID;
+            }
+            return BUTTON_ID;
+        }
+
+        @Override
+        public Holder onCreateViewHolder(ViewGroup parent, int viewType) {
+            final Context context = parent.getContext();
+            final LayoutInflater inflater = LayoutInflater.from(context);
+            final View view = inflater.inflate(getLayoutId(viewType), parent, false);
+            if (viewType == BUTTON_ID) {
+                inflater.inflate(R.layout.nav_control_widget,
+                        (ViewGroup) view.findViewById(android.R.id.widget_frame));
+            }
+            return new Holder(view);
+        }
+
+        private int getLayoutId(int viewType) {
+            if (viewType == CATEGORY_ID) {
+                return mCategoryLayout;
+            }
+            return mButtonLayout;
+        }
+
+        @Override
+        public void onBindViewHolder(Holder holder, int position) {
+            holder.title.setText(mLabels.get(position));
+            if (holder.getItemViewType() == BUTTON_ID) {
+                bindButton(holder, position);
+            } else if (holder.getItemViewType() == ADD_ID) {
+                bindAdd(holder);
+            }
+        }
+
+        private void bindAdd(Holder holder) {
+            TypedValue value = new TypedValue();
+            final Context context = holder.itemView.getContext();
+            context.getTheme().resolveAttribute(android.R.attr.colorAccent, value, true);
+            final ImageView icon = (ImageView) holder.itemView.findViewById(android.R.id.icon);
+            icon.setImageResource(R.drawable.ic_add);
+            icon.setImageTintList(ColorStateList.valueOf(context.getColor(value.resourceId)));
+            holder.itemView.findViewById(android.R.id.summary).setVisibility(View.GONE);
+            holder.itemView.setClickable(true);
+            holder.itemView.setOnClickListener(new View.OnClickListener() {
+                @Override
+                public void onClick(View v) {
+                    showAddDialog(v.getContext());
+                }
+            });
+        }
+
+        private void bindButton(final Holder holder, int position) {
+            holder.itemView.findViewById(android.R.id.icon_frame).setVisibility(View.GONE);
+            holder.itemView.findViewById(android.R.id.summary).setVisibility(View.GONE);
+            bindClick(holder.itemView.findViewById(R.id.close), holder);
+            bindClick(holder.itemView.findViewById(R.id.width), holder);
+            holder.itemView.findViewById(R.id.drag).setOnTouchListener(new View.OnTouchListener() {
+                @Override
+                public boolean onTouch(View v, MotionEvent event) {
+                    mTouchHelper.startDrag(holder);
+                    return true;
+                }
+            });
+        }
+
+        private void showAddDialog(final Context context) {
+            final String[] options = new String[] {
+                    BACK, HOME, RECENT, MENU_IME, NAVSPACE, CLIPBOARD, KEY,
+            };
+            final CharSequence[] labels = new CharSequence[options.length];
+            for (int i = 0; i < options.length; i++) {
+                labels[i] = getLabel(options[i], context);
+            }
+            new AlertDialog.Builder(context)
+                    .setTitle(R.string.select_button)
+                    .setItems(labels, new DialogInterface.OnClickListener() {
+                        @Override
+                        public void onClick(DialogInterface dialog, int which) {
+                            if (KEY.equals(options[which])) {
+                                showKeyDialogs(context);
+                            } else {
+                                int index = mButtons.size() - 1;
+                                showAddedMessage(context, options[which]);
+                                mButtons.add(index, options[which]);
+                                mLabels.add(index, labels[which]);
+
+                                notifyItemInserted(index);
+                                notifyChanged();
+                            }
+                        }
+                    }).setNegativeButton(android.R.string.cancel, null)
+                    .show();
+        }
+
+        private void onImageSelected(Uri uri) {
+            int index = mButtons.size() - 1;
+            mButtons.add(index, KEY + KEY_CODE_START + mKeycode + KEY_IMAGE_DELIM + uri.toString()
+                    + KEY_CODE_END);
+            mLabels.add(index, getLabel(KEY, getContext()));
+
+            notifyItemInserted(index);
+            notifyChanged();
+        }
+
+        private void showKeyDialogs(final Context context) {
+            final KeycodeSelectionHelper.OnSelectionComplete listener =
+                    new KeycodeSelectionHelper.OnSelectionComplete() {
+                        @Override
+                        public void onSelectionComplete(int code) {
+                            mKeycode = code;
+                            selectImage();
+                        }
+                    };
+            new AlertDialog.Builder(context)
+                    .setTitle(R.string.keycode)
+                    .setMessage(R.string.keycode_description)
+                    .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
+                        @Override
+                        public void onClick(DialogInterface dialog, int which) {
+                            KeycodeSelectionHelper.showKeycodeSelect(context, listener);
+                        }
+                    }).show();
+        }
+
+        private void showAddedMessage(Context context, String button) {
+            if (CLIPBOARD.equals(button)) {
+                new AlertDialog.Builder(context)
+                        .setTitle(R.string.clipboard)
+                        .setMessage(R.string.clipboard_description)
+                        .setPositiveButton(android.R.string.ok, null)
+                        .show();
+            }
+        }
+
+        private void bindClick(View view, Holder holder) {
+            view.setOnClickListener(this);
+            view.setTag(holder);
+        }
+
+        @Override
+        public void onClick(View v) {
+            Holder holder = (Holder) v.getTag();
+            if (v.getId() == R.id.width) {
+                showWidthDialog(holder, v.getContext());
+            } else if (v.getId() == R.id.close) {
+                int position = holder.getAdapterPosition();
+                mButtons.remove(position);
+                mLabels.remove(position);
+                notifyItemRemoved(position);
+                notifyChanged();
+            }
+        }
+
+        private void showWidthDialog(final Holder holder, Context context) {
+            final String buttonSpec = mButtons.get(holder.getAdapterPosition());
+            float amount = extractSize(buttonSpec);
+            final AlertDialog dialog = new AlertDialog.Builder(context)
+                    .setTitle(R.string.adjust_button_width)
+                    .setView(R.layout.nav_width_view)
+                    .setNegativeButton(android.R.string.cancel, null).create();
+            dialog.setButton(DialogInterface.BUTTON_POSITIVE,
+                    context.getString(android.R.string.ok),
+                    new DialogInterface.OnClickListener() {
+                        @Override
+                        public void onClick(DialogInterface d, int which) {
+                            final String button = extractButton(buttonSpec);
+                            SeekBar seekBar = (SeekBar) dialog.findViewById(R.id.seekbar);
+                            if (seekBar.getProgress() == 75) {
+                                mButtons.set(holder.getAdapterPosition(), button);
+                            } else {
+                                float amount = (seekBar.getProgress() + 25) / 100f;
+                                mButtons.set(holder.getAdapterPosition(), button
+                                        + SIZE_MOD_START + amount + SIZE_MOD_END);
+                            }
+                            notifyChanged();
+                        }
+                    });
+            dialog.show();
+            SeekBar seekBar = (SeekBar) dialog.findViewById(R.id.seekbar);
+            // Range is .25 - 1.75.
+            seekBar.setMax(150);
+            seekBar.setProgress((int) ((amount - .25f) * 100));
+        }
+
+        @Override
+        public int getItemCount() {
+            return mButtons.size();
+        }
+
+        private final ItemTouchHelper.Callback mCallbacks = new ItemTouchHelper.Callback() {
+            @Override
+            public boolean isLongPressDragEnabled() {
+                return false;
+            }
+
+            @Override
+            public boolean isItemViewSwipeEnabled() {
+                return false;
+            }
+
+            @Override
+            public int getMovementFlags(RecyclerView recyclerView,
+                    RecyclerView.ViewHolder viewHolder) {
+                if (viewHolder.getItemViewType() != BUTTON_ID) {
+                    return makeMovementFlags(0, 0);
+                }
+                int dragFlags = ItemTouchHelper.UP | ItemTouchHelper.DOWN;
+                return makeMovementFlags(dragFlags, 0);
+            }
+
+            @Override
+            public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder,
+                    RecyclerView.ViewHolder target) {
+                int from = viewHolder.getAdapterPosition();
+                int to = target.getAdapterPosition();
+                if (to == 0) {
+                    // Can't go above the top.
+                    return false;
+                }
+                move(from, to, mButtons);
+                move(from, to, mLabels);
+                notifyChanged();
+                notifyItemMoved(from, to);
+                return true;
+            }
+
+            private <T> void move(int from, int to, List<T> list) {
+                list.add(from > to ? to : to + 1, list.get(from));
+                list.remove(from > to ? from + 1 : from);
+            }
+
+            @Override
+            public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) {
+                // Don't care.
+            }
+        };
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/tuner/PreviewNavInflater.java b/packages/SystemUI/src/com/android/systemui/tuner/PreviewNavInflater.java
new file mode 100644
index 0000000..b30ca5c
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/tuner/PreviewNavInflater.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the
+ * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+
+package com.android.systemui.tuner;
+
+import com.android.systemui.statusbar.phone.NavigationBarInflaterView;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.MotionEvent;
+
+public class PreviewNavInflater extends NavigationBarInflaterView {
+
+    public PreviewNavInflater(Context context, AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    @Override
+    protected void onAttachedToWindow() {
+        super.onAttachedToWindow();
+        // Immediately remove tuner listening, since this is a preview, all values will be injected
+        // manually.
+        TunerService.get(getContext()).removeTunable(this);
+    }
+
+    @Override
+    public boolean onInterceptTouchEvent(MotionEvent ev) {
+        // Only a preview, not interactable.
+        return true;
+    }
+
+    @Override
+    public void onTuningChanged(String key, String newValue) {
+        if (NAV_BAR_VIEWS.equals(key)) {
+            // Since this is a preview we might get a bunch of random stuff, validate before sending
+            // for inflation.
+            if (isValidLayout(newValue)) {
+                super.onTuningChanged(key, newValue);
+            }
+        } else {
+            super.onTuningChanged(key, newValue);
+        }
+    }
+
+    private boolean isValidLayout(String newValue) {
+        if (newValue == null) {
+            return true;
+        }
+        int separatorCount = 0;
+        int lastGravitySeparator = 0;
+        for (int i = 0; i < newValue.length(); i++) {
+            if (newValue.charAt(i) == GRAVITY_SEPARATOR.charAt(0)) {
+                if (i == 0 || (i - lastGravitySeparator) == 1) {
+                    return false;
+                }
+                lastGravitySeparator = i;
+                separatorCount++;
+            }
+        }
+        return separatorCount == 2 && (newValue.length() - lastGravitySeparator) != 1;
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/tuner/TunerActivity.java b/packages/SystemUI/src/com/android/systemui/tuner/TunerActivity.java
index 4173ecc..4225b48 100644
--- a/packages/SystemUI/src/com/android/systemui/tuner/TunerActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/tuner/TunerActivity.java
@@ -30,11 +30,15 @@
         PreferenceFragment.OnPreferenceStartFragmentCallback,
         PreferenceFragment.OnPreferenceStartScreenCallback {
 
+    private static final String TAG_TUNER = "tuner";
+
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
 
-        getFragmentManager().beginTransaction().replace(R.id.content_frame, new TunerFragment())
-                .commit();
+        if (getFragmentManager().findFragmentByTag(TAG_TUNER) == null) {
+            getFragmentManager().beginTransaction().replace(R.id.content_frame, new TunerFragment(),
+                    TAG_TUNER).commit();
+        }
     }
 
     @Override
@@ -43,6 +47,7 @@
             Class<?> cls = Class.forName(pref.getFragment());
             Fragment fragment = (Fragment) cls.newInstance();
             FragmentTransaction transaction = getFragmentManager().beginTransaction();
+            setTitle(pref.getTitle());
             transaction.replace(R.id.content_frame, fragment);
             transaction.addToBackStack("PreferenceFragment");
             transaction.commit();
diff --git a/packages/SystemUI/src/com/android/systemui/tuner/TunerFragment.java b/packages/SystemUI/src/com/android/systemui/tuner/TunerFragment.java
index 427b5e8..e4d7be7 100644
--- a/packages/SystemUI/src/com/android/systemui/tuner/TunerFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/tuner/TunerFragment.java
@@ -61,11 +61,16 @@
     public void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
 
-        getActivity().getActionBar().setDisplayHomeAsUpEnabled(true);
         setHasOptionsMenu(true);
     }
 
     @Override
+    public void onActivityCreated(Bundle savedInstanceState) {
+        super.onActivityCreated(savedInstanceState);
+        getActivity().getActionBar().setDisplayHomeAsUpEnabled(true);
+    }
+
+    @Override
     public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
         addPreferencesFromResource(R.xml.tuner_prefs);
 
@@ -81,6 +86,7 @@
     @Override
     public void onResume() {
         super.onResume();
+        getActivity().setTitle(R.string.system_ui_tuner);
         updateBatteryPct();
         getContext().getContentResolver().registerContentObserver(
                 System.getUriFor(SHOW_PERCENT_SETTING), false, mSettingObserver);
diff --git a/packages/SystemUI/src/com/android/systemui/tuner/TunerService.java b/packages/SystemUI/src/com/android/systemui/tuner/TunerService.java
index 47a4667..abaa628 100644
--- a/packages/SystemUI/src/com/android/systemui/tuner/TunerService.java
+++ b/packages/SystemUI/src/com/android/systemui/tuner/TunerService.java
@@ -76,6 +76,22 @@
         mUserTracker.startTracking();
     }
 
+    public String getValue(String setting) {
+        return Settings.Secure.getStringForUser(mContentResolver, setting, mCurrentUser);
+    }
+
+    public void setValue(String setting, String value) {
+         Settings.Secure.putStringForUser(mContentResolver, setting, value, mCurrentUser);
+    }
+
+    public int getValue(String setting, int def) {
+        return Settings.Secure.getIntForUser(mContentResolver, setting, def, mCurrentUser);
+    }
+
+    public void setValue(String setting, int value) {
+         Settings.Secure.putIntForUser(mContentResolver, setting, value, mCurrentUser);
+    }
+
     public void addTunable(Tunable tunable, String... keys) {
         for (String key : keys) {
             addTunable(tunable, key);
diff --git a/packages/SystemUI/tests/Android.mk b/packages/SystemUI/tests/Android.mk
index b7a41e2..964688b 100644
--- a/packages/SystemUI/tests/Android.mk
+++ b/packages/SystemUI/tests/Android.mk
@@ -17,12 +17,15 @@
 
 LOCAL_MODULE_TAGS := tests
 
+LOCAL_JACK_FLAGS := --multi-dex native
+
 LOCAL_PROTOC_OPTIMIZE_TYPE := nano
 LOCAL_PROTOC_FLAGS := -I$(LOCAL_PATH)/..
 LOCAL_PROTO_JAVA_OUTPUT_PARAMS := optional_field_style=accessors
 
 LOCAL_AAPT_FLAGS := --auto-add-overlay \
-    --extra-packages com.android.systemui:com.android.keyguard:android.support.v14.preference:android.support.v7.preference:android.support.v7.appcompat:android.support.v7.recyclerview
+    --extra-packages com.android.systemui:com.android.keyguard:android.support.v14.preference:android.support.v7.preference:android.support.v7.appcompat:android.support.v7.recyclerview \
+    --extra-packages android.support.v17.leanback
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src) \
     $(call all-Iaidl-files-under, src) \
@@ -35,6 +38,7 @@
     frameworks/support/v14/preference/res \
     frameworks/support/v7/appcompat/res \
     frameworks/support/v7/recyclerview/res \
+    frameworks/support/v17/leanback/res \
     frameworks/base/packages/SystemUI/res \
     frameworks/base/packages/Keyguard/res
 
@@ -48,7 +52,8 @@
     android-support-v7-recyclerview \
     android-support-v7-preference \
     android-support-v7-appcompat \
-    android-support-v14-preference
+    android-support-v14-preference \
+    android-support-v17-leanback
 
 # sign this with platform cert, so this test is allowed to inject key events into
 # UI it doesn't own. This is necessary to allow screenshots to be taken
diff --git a/services/core/java/com/android/server/AppOpsService.java b/services/core/java/com/android/server/AppOpsService.java
index e6e69b1..a4455e9 100644
--- a/services/core/java/com/android/server/AppOpsService.java
+++ b/services/core/java/com/android/server/AppOpsService.java
@@ -1181,6 +1181,9 @@
                             } else if ("audioserver".equals(packageName)) {
                                 pkgUid = Process.AUDIOSERVER_UID;
                                 isPrivileged = false;
+                            } else if ("cameraserver".equals(packageName)) {
+                                pkgUid = Process.CAMERASERVER_UID;
+                                isPrivileged = false;
                             }
                         }
                     } catch (RemoteException e) {
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index c3c3e3f..2d1f96b 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -3800,6 +3800,12 @@
             throw new IllegalArgumentException("Bad timeout specified");
         }
 
+        if (NetworkCapabilities.MATCH_ALL_REQUESTS_NETWORK_SPECIFIER
+                .equals(networkCapabilities.getNetworkSpecifier())) {
+            throw new IllegalArgumentException("Invalid network specifier - must not be '"
+                    + NetworkCapabilities.MATCH_ALL_REQUESTS_NETWORK_SPECIFIER + "'");
+        }
+
         NetworkRequest networkRequest = new NetworkRequest(networkCapabilities, legacyType,
                 nextNetworkRequestId());
         NetworkRequestInfo nri = new NetworkRequestInfo(messenger, networkRequest, binder,
diff --git a/services/core/java/com/android/server/DiskStatsService.java b/services/core/java/com/android/server/DiskStatsService.java
index bc12fc5..9313148 100644
--- a/services/core/java/com/android/server/DiskStatsService.java
+++ b/services/core/java/com/android/server/DiskStatsService.java
@@ -21,6 +21,7 @@
 import android.os.Environment;
 import android.os.StatFs;
 import android.os.SystemClock;
+import android.os.storage.StorageManager;
 
 import java.io.File;
 import java.io.FileDescriptor;
@@ -79,6 +80,10 @@
         reportFreeSpace(Environment.getDownloadCacheDirectory(), "Cache", pw);
         reportFreeSpace(new File("/system"), "System", pw);
 
+        if (StorageManager.isNativeFileBasedEncryptionEnabled()) {
+            pw.println("File-based Encryption: true");
+        }
+
         // TODO: Read /proc/yaffs and report interesting values;
         // add configurable (through args) performance test parameters.
     }
diff --git a/services/core/java/com/android/server/LocationManagerService.java b/services/core/java/com/android/server/LocationManagerService.java
index 9405d8e0..c55c5b6 100644
--- a/services/core/java/com/android/server/LocationManagerService.java
+++ b/services/core/java/com/android/server/LocationManagerService.java
@@ -24,10 +24,10 @@
 import com.android.server.location.ActivityRecognitionProxy;
 import com.android.server.location.FlpHardwareProvider;
 import com.android.server.location.FusedProxy;
+import com.android.server.location.GnssLocationProvider;
 import com.android.server.location.GeocoderProxy;
 import com.android.server.location.GeofenceManager;
 import com.android.server.location.GeofenceProxy;
-import com.android.server.location.GpsLocationProvider;
 import com.android.server.location.GpsMeasurementsProvider;
 import com.android.server.location.GpsNavigationMessageProvider;
 import com.android.server.location.LocationBlacklist;
@@ -61,11 +61,11 @@
 import android.location.Criteria;
 import android.location.GeocoderParams;
 import android.location.Geofence;
+import android.location.IGnssStatusListener;
+import android.location.IGnssStatusProvider;
 import android.location.IGpsGeofenceHardware;
 import android.location.IGpsMeasurementsListener;
 import android.location.IGpsNavigationMessageListener;
-import android.location.IGpsStatusListener;
-import android.location.IGpsStatusProvider;
 import android.location.ILocationListener;
 import android.location.ILocationManager;
 import android.location.INetInitiatedListener;
@@ -157,7 +157,7 @@
     private PowerManager mPowerManager;
     private UserManager mUserManager;
     private GeocoderProxy mGeocodeProvider;
-    private IGpsStatusProvider mGpsStatusProvider;
+    private IGnssStatusProvider mGnssStatusProvider;
     private INetInitiatedListener mNetInitiatedListener;
     private LocationWorkerHandler mLocationHandler;
     private PassiveProvider mPassiveProvider;  // track passive provider for special cases
@@ -214,6 +214,8 @@
     private int mCurrentUserId = UserHandle.USER_SYSTEM;
     private int[] mCurrentUserProfiles = new int[] { UserHandle.USER_SYSTEM };
 
+    private GnssLocationProvider.GpsSystemInfoProvider mGpsSystemInfoProvider;
+
     public LocationManagerService(Context context) {
         super();
         mContext = context;
@@ -456,17 +458,18 @@
         mEnabledProviders.add(passiveProvider.getName());
         mPassiveProvider = passiveProvider;
 
-        if (GpsLocationProvider.isSupported()) {
+        if (GnssLocationProvider.isSupported()) {
             // Create a gps location provider
-            GpsLocationProvider gpsProvider = new GpsLocationProvider(mContext, this,
+            GnssLocationProvider gnssProvider = new GnssLocationProvider(mContext, this,
                     mLocationHandler.getLooper());
-            mGpsStatusProvider = gpsProvider.getGpsStatusProvider();
-            mNetInitiatedListener = gpsProvider.getNetInitiatedListener();
-            addProviderLocked(gpsProvider);
-            mRealProviders.put(LocationManager.GPS_PROVIDER, gpsProvider);
-            mGpsMeasurementsProvider = gpsProvider.getGpsMeasurementsProvider();
-            mGpsNavigationMessageProvider = gpsProvider.getGpsNavigationMessageProvider();
-            mGpsGeofenceProxy = gpsProvider.getGpsGeofenceProxy();
+            mGpsSystemInfoProvider = gnssProvider.getGpsSystemInfoProvider();
+            mGnssStatusProvider = gnssProvider.getGnssStatusProvider();
+            mNetInitiatedListener = gnssProvider.getNetInitiatedListener();
+            addProviderLocked(gnssProvider);
+            mRealProviders.put(LocationManager.GPS_PROVIDER, gnssProvider);
+            mGpsMeasurementsProvider = gnssProvider.getGpsMeasurementsProvider();
+            mGpsNavigationMessageProvider = gnssProvider.getGpsNavigationMessageProvider();
+            mGpsGeofenceProxy = gnssProvider.getGpsGeofenceProxy();
         }
 
         /*
@@ -986,6 +989,18 @@
         }
     }
 
+    /**
+     * Returns the system information of the GPS hardware.
+     */
+    @Override
+    public int getGpsYearOfHardware() {
+        if (mGpsNavigationMessageProvider != null) {
+            return mGpsSystemInfoProvider.getGpsYearOfHardware();
+        } else {
+            return 0;
+        }
+    }
+
     private void addProviderLocked(LocationProviderInterface provider) {
         mProviders.add(provider);
         mProvidersByName.put(provider.getName(), provider);
@@ -1867,7 +1882,7 @@
 
 
     @Override
-    public boolean addGpsStatusListener(IGpsStatusListener listener, String packageName) {
+    public boolean registerGnssStatusCallback(IGnssStatusListener callback, String packageName) {
         int allowedResolutionLevel = getCallerAllowedResolutionLevel();
         checkResolutionLevelIsSufficientForProviderUse(allowedResolutionLevel,
                 LocationManager.GPS_PROVIDER);
@@ -1883,26 +1898,26 @@
             Binder.restoreCallingIdentity(ident);
         }
 
-        if (mGpsStatusProvider == null) {
+        if (mGnssStatusProvider == null) {
             return false;
         }
 
         try {
-            mGpsStatusProvider.addGpsStatusListener(listener);
+            mGnssStatusProvider.registerGnssStatusCallback(callback);
         } catch (RemoteException e) {
-            Slog.e(TAG, "mGpsStatusProvider.addGpsStatusListener failed", e);
+            Slog.e(TAG, "mGpsStatusProvider.registerGnssStatusCallback failed", e);
             return false;
         }
         return true;
     }
 
     @Override
-    public void removeGpsStatusListener(IGpsStatusListener listener) {
+    public void unregisterGnssStatusCallback(IGnssStatusListener callback) {
         synchronized (mLock) {
             try {
-                mGpsStatusProvider.removeGpsStatusListener(listener);
+                mGnssStatusProvider.unregisterGnssStatusCallback(callback);
             } catch (Exception e) {
-                Slog.e(TAG, "mGpsStatusProvider.removeGpsStatusListener failed", e);
+                Slog.e(TAG, "mGpsStatusProvider.unregisterGnssStatusCallback failed", e);
             }
         }
     }
diff --git a/services/core/java/com/android/server/LockSettingsService.java b/services/core/java/com/android/server/LockSettingsService.java
index f6f05fe..d0cd536 100644
--- a/services/core/java/com/android/server/LockSettingsService.java
+++ b/services/core/java/com/android/server/LockSettingsService.java
@@ -17,11 +17,12 @@
 package com.android.server;
 
 import android.app.ActivityManagerNative;
-import android.app.AppGlobals;
+import android.app.Notification;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
 import android.app.admin.DevicePolicyManager;
 import android.app.backup.BackupManager;
 import android.app.trust.IStrongAuthTracker;
-import android.app.trust.ITrustManager;
 import android.app.trust.TrustManager;
 import android.content.BroadcastReceiver;
 import android.content.ContentResolver;
@@ -30,6 +31,8 @@
 import android.content.IntentFilter;
 import android.content.pm.PackageManager;
 import android.content.pm.UserInfo;
+import android.content.res.Resources;
+
 import static android.Manifest.permission.ACCESS_KEYGUARD_SECURE_STORAGE;
 import static android.content.Context.USER_SERVICE;
 import static android.Manifest.permission.READ_CONTACTS;
@@ -40,7 +43,6 @@
 import android.os.IBinder;
 import android.os.RemoteException;
 import android.os.storage.IMountService;
-import android.os.storage.StorageManager;
 import android.os.ServiceManager;
 import android.os.SystemProperties;
 import android.os.UserHandle;
@@ -70,19 +72,27 @@
  * @hide
  */
 public class LockSettingsService extends ILockSettings.Stub {
-
-    private static final String PERMISSION = ACCESS_KEYGUARD_SECURE_STORAGE;
-
     private static final String TAG = "LockSettingsService";
+    private static final String PERMISSION = ACCESS_KEYGUARD_SECURE_STORAGE;
+    private static final Intent ACTION_NULL; // hack to ensure notification shows the bouncer
+    private static final int FBE_ENCRYPTED_NOTIFICATION = 0;
+    private static final boolean DEBUG = false;
 
     private final Context mContext;
-
     private final LockSettingsStorage mStorage;
     private final LockSettingsStrongAuth mStrongAuth;
 
     private LockPatternUtils mLockPatternUtils;
     private boolean mFirstCallToVold;
     private IGateKeeperService mGateKeeperService;
+    private NotificationManager mNotificationManager;
+    private UserManager mUserManager;
+
+    static {
+        // Just launch the home screen, which happens anyway
+        ACTION_NULL = new Intent(Intent.ACTION_MAIN);
+        ACTION_NULL.addCategory(Intent.CATEGORY_HOME);
+    }
 
     private interface CredentialUtil {
         void setCredential(String credential, String savedCredential, int userId)
@@ -91,6 +101,42 @@
         String adjustForKeystore(String credential);
     }
 
+    // This class manages life cycle events for encrypted users on File Based Encryption (FBE)
+    // devices. The most basic of these is to show/hide notifications about missing features until
+    // the user unlocks the account and credential-encrypted storage is available.
+    public static final class Lifecycle extends SystemService {
+        private LockSettingsService mLockSettingsService;
+
+        public Lifecycle(Context context) {
+            super(context);
+        }
+
+        @Override
+        public void onStart() {
+            mLockSettingsService = new LockSettingsService(getContext());
+            publishBinderService("lock_settings", mLockSettingsService);
+        }
+
+        @Override
+        public void onBootPhase(int phase) {
+            if (phase == SystemService.PHASE_ACTIVITY_MANAGER_READY) {
+                mLockSettingsService.maybeShowEncryptionNotification(UserHandle.ALL);
+            } else if (phase == SystemService.PHASE_BOOT_COMPLETED) {
+                // TODO
+            }
+        }
+
+        @Override
+        public void onUnlockUser(int userHandle) {
+            mLockSettingsService.onUnlockUser(userHandle);
+        }
+
+        @Override
+        public void onCleanupUser(int userHandle) {
+            mLockSettingsService.onCleanupUser(userHandle);
+        }
+    }
+
     public LockSettingsService(Context context) {
         mContext = context;
         mStrongAuth = new LockSettingsStrongAuth(context);
@@ -117,6 +163,71 @@
                 }
             }
         });
+        mNotificationManager = (NotificationManager)
+                mContext.getSystemService(Context.NOTIFICATION_SERVICE);
+        mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
+    }
+
+    /**
+     * If the account is credential-encrypted, show notification requesting the user to unlock
+     * the device.
+     */
+    private void maybeShowEncryptionNotification(UserHandle userHandle) {
+        if (UserHandle.ALL.equals(userHandle)) {
+            final List<UserInfo> users = mUserManager.getUsers();
+            for (int i = 0; i < users.size(); i++) {
+                UserHandle user = users.get(i).getUserHandle();
+                if (!mUserManager.isUserUnlocked(user)) {
+                    showEncryptionNotification(user);
+                }
+            }
+        } else if (!mUserManager.isUserUnlocked(userHandle)){
+            showEncryptionNotification(userHandle);
+        }
+    }
+
+    private void showEncryptionNotification(UserHandle user) {
+        if (DEBUG) Slog.v(TAG, "showing encryption notification, user: " + user.getIdentifier());
+        Resources r = mContext.getResources();
+        CharSequence title = r.getText(
+                com.android.internal.R.string.user_encrypted_title);
+        CharSequence message = r.getText(
+                com.android.internal.R.string.user_encrypted_message);
+        CharSequence detail = r.getText(
+                com.android.internal.R.string.user_encrypted_detail);
+
+        PendingIntent intent = PendingIntent.getBroadcast(mContext, 0, ACTION_NULL,
+                PendingIntent.FLAG_UPDATE_CURRENT);
+
+        Notification notification = new Notification.Builder(mContext)
+                .setSmallIcon(com.android.internal.R.drawable.ic_secure)
+                .setWhen(0)
+                .setOngoing(true)
+                .setTicker(title)
+                .setDefaults(0)  // please be quiet
+                .setPriority(Notification.PRIORITY_MAX)
+                .setColor(mContext.getColor(
+                        com.android.internal.R.color.system_notification_accent_color))
+                .setContentTitle(title)
+                .setContentText(message)
+                .setContentInfo(detail)
+                .setVisibility(Notification.VISIBILITY_PUBLIC)
+                .setContentIntent(intent)
+                .build();
+        mNotificationManager.notifyAsUser(null, FBE_ENCRYPTED_NOTIFICATION, notification, user);
+    }
+
+    public void hideEncryptionNotification(UserHandle userHandle) {
+        if (DEBUG) Slog.v(TAG, "hide encryption notification, user: "+ userHandle.getIdentifier());
+        mNotificationManager.cancelAsUser(null, FBE_ENCRYPTED_NOTIFICATION, userHandle);
+    }
+
+    public void onCleanupUser(int userId) {
+        hideEncryptionNotification(new UserHandle(userId));
+    }
+
+    public void onUnlockUser(int userHandle) {
+        hideEncryptionNotification(new UserHandle(userHandle));
     }
 
     private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
@@ -144,6 +255,7 @@
         }
     };
 
+    @Override // binder interface
     public void systemReady() {
         migrateOldData();
         try {
diff --git a/services/core/java/com/android/server/SensorNotificationService.java b/services/core/java/com/android/server/SensorNotificationService.java
new file mode 100644
index 0000000..0610464
--- /dev/null
+++ b/services/core/java/com/android/server/SensorNotificationService.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.hardware.Sensor;
+import android.hardware.SensorEvent;
+import android.hardware.SensorEventListener;
+import android.hardware.SensorManager;
+import android.os.SystemClock;
+import android.os.SystemProperties;
+import android.os.UserHandle;
+import android.provider.Settings;
+import android.util.Slog;
+
+public class SensorNotificationService extends SystemService implements SensorEventListener {
+    //TODO: set DBG to false or remove Slog before release
+    private static final boolean DBG = true;
+    private static final String TAG = "SensorNotificationService";
+    private Context mContext;
+
+    private SensorManager mSensorManager;
+    private Sensor mMetaSensor;
+
+    public SensorNotificationService(Context context) {
+        super(context);
+        mContext = context;
+    }
+
+    public void onStart() {
+        LocalServices.addService(SensorNotificationService.class, this);
+    }
+
+    public void onBootPhase(int phase) {
+        if (phase == PHASE_THIRD_PARTY_APPS_CAN_START) {
+            // start
+            mSensorManager = (SensorManager) mContext.getSystemService(Context.SENSOR_SERVICE);
+            mMetaSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_DYNAMIC_SENSOR_META);
+            if (mMetaSensor == null) {
+                if (DBG) Slog.d(TAG, "Cannot obtain dynamic meta sensor, not supported.");
+            } else {
+                mSensorManager.registerListener(this, mMetaSensor,
+                        SensorManager.SENSOR_DELAY_FASTEST);
+            }
+        }
+    }
+
+    private void broadcastDynamicSensorChanged() {
+        Intent i = new Intent(Intent.ACTION_DYNAMIC_SENSOR_CHANGED);
+        i.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY); // avoid waking up manifest receivers
+        mContext.sendBroadcastAsUser(i, UserHandle.ALL);
+        if (DBG) Slog.d(TAG, "DYNS sent dynamic sensor broadcast");
+    }
+
+    @Override
+    public void onSensorChanged(SensorEvent event) {
+        if (event.sensor == mMetaSensor) {
+            broadcastDynamicSensorChanged();
+        }
+    }
+
+    @Override
+    public void onAccuracyChanged(Sensor sensor, int accuracy) {
+
+    }
+}
+
diff --git a/services/core/java/com/android/server/SystemConfig.java b/services/core/java/com/android/server/SystemConfig.java
index 4dc46ac..5aba22d 100644
--- a/services/core/java/com/android/server/SystemConfig.java
+++ b/services/core/java/com/android/server/SystemConfig.java
@@ -16,20 +16,24 @@
 
 package com.android.server;
 
+import static com.android.internal.util.ArrayUtils.appendInt;
+
 import android.app.ActivityManager;
 import android.content.pm.FeatureInfo;
-import android.os.*;
+import android.content.pm.PackageManager;
+import android.os.Environment;
 import android.os.Process;
+import android.os.storage.StorageManager;
 import android.util.ArrayMap;
 import android.util.ArraySet;
 import android.util.Slog;
 import android.util.SparseArray;
 import android.util.Xml;
 
-import libcore.io.IoUtils;
-
 import com.android.internal.util.XmlUtils;
 
+import libcore.io.IoUtils;
+
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
 
@@ -38,8 +42,6 @@
 import java.io.FileReader;
 import java.io.IOException;
 
-import static com.android.internal.util.ArrayUtils.appendInt;
-
 /**
  * Loads global system configuration info.
  */
@@ -351,10 +353,7 @@
                         Slog.w(TAG, "<feature> without name in " + permFile + " at "
                                 + parser.getPositionDescription());
                     } else if (allowed) {
-                        //Log.i(TAG, "Got feature " + fname);
-                        FeatureInfo fi = new FeatureInfo();
-                        fi.name = fname;
-                        mAvailableFeatures.put(fname, fi);
+                        addFeature(fname);
                     }
                     XmlUtils.skipCurrentTag(parser);
                     continue;
@@ -443,10 +442,29 @@
             IoUtils.closeQuietly(permReader);
         }
 
-        for (String fname : mUnavailableFeatures) {
-            if (mAvailableFeatures.remove(fname) != null) {
-                Slog.d(TAG, "Removed unavailable feature " + fname);
-            }
+        // Some devices can be field-converted to FBE, so offer to splice in
+        // those features if not already defined by the static config
+        if (StorageManager.isNativeFileBasedEncryptionEnabled()) {
+            addFeature(PackageManager.FEATURE_FILE_BASED_ENCRYPTION);
+            addFeature(PackageManager.FEATURE_SECURELY_REMOVES_USERS);
+        }
+
+        for (String featureName : mUnavailableFeatures) {
+            removeFeature(featureName);
+        }
+    }
+
+    private void addFeature(String featureName) {
+        if (!mAvailableFeatures.containsKey(featureName)) {
+            final FeatureInfo fi = new FeatureInfo();
+            fi.name = featureName;
+            mAvailableFeatures.put(featureName, fi);
+        }
+    }
+
+    private void removeFeature(String featureName) {
+        if (mAvailableFeatures.remove(featureName) != null) {
+            Slog.d(TAG, "Removed unavailable feature " + featureName);
         }
     }
 
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 2c55ee2..b5982c3 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -9411,14 +9411,24 @@
     }
 
     @Override
-    public void resizeStack(int stackId, Rect bounds, boolean allowResizeInDockedMode) {
+    public void resizeStack(int stackId, Rect bounds, boolean allowResizeInDockedMode,
+            boolean preserveWindows, boolean animate) {
         enforceCallingPermission(MANAGE_ACTIVITY_STACKS, "resizeStack()");
         long ident = Binder.clearCallingIdentity();
         try {
             synchronized (this) {
-                mStackSupervisor.resizeStackLocked(
-                        stackId, bounds, null /* tempTaskBounds */, null /* tempTaskInsetBounds */,
-                        !PRESERVE_WINDOWS, allowResizeInDockedMode);
+                if (animate) {
+                    if (stackId == PINNED_STACK_ID) {
+                        mWindowManager.animateResizePinnedStack(bounds);
+                    } else {
+                        throw new IllegalArgumentException("Stack: " + stackId
+                                + " doesn't support animated resize.");
+                    }
+                } else {
+                    mStackSupervisor.resizeStackLocked(stackId, bounds, null /* tempTaskBounds */,
+                            null /* tempTaskInsetBounds */, preserveWindows,
+                            allowResizeInDockedMode);
+                }
             }
         } finally {
             Binder.restoreCallingIdentity(ident);
@@ -12960,6 +12970,9 @@
 
         final StringBuilder sb = new StringBuilder(1024);
         appendDropBoxProcessHeaders(process, processName, sb);
+        sb.append("Foreground: ")
+                .append(process.isInterestingToUserLocked() ? "Yes" : "No")
+                .append("\n");
         if (activity != null) {
             sb.append("Activity: ").append(activity.shortComponentName).append("\n");
         }
diff --git a/services/core/java/com/android/server/am/ActivityRecord.java b/services/core/java/com/android/server/am/ActivityRecord.java
index 71008a9..d1fcd3b 100755
--- a/services/core/java/com/android/server/am/ActivityRecord.java
+++ b/services/core/java/com/android/server/am/ActivityRecord.java
@@ -19,8 +19,9 @@
 import static android.app.ActivityManager.StackId;
 import static android.app.ActivityManager.StackId.FREEFORM_WORKSPACE_STACK_ID;
 import static android.content.pm.ActivityInfo.FLAG_ALWAYS_FOCUSABLE;
-import static android.content.pm.ActivityInfo.FLAG_RESIZEABLE;
-import static android.content.pm.ActivityInfo.FLAG_SUPPORTS_PICTURE_IN_PICTURE;
+import static android.content.pm.ActivityInfo.RESIZE_MODE_CROP_WINDOWS;
+import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE;
+import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE_AND_PIPABLE;
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_CONFIGURATION;
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_SAVED_STATE;
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_SWITCH;
@@ -355,6 +356,9 @@
         if (connections != null) {
             pw.print(prefix); pw.print("connections="); pw.println(connections);
         }
+        if (info != null) {
+            pw.println(prefix + "resizeMode=" + ActivityInfo.resizeModeToString(info.resizeMode));
+        }
     }
 
     public boolean crossesHorizontalSizeThreshold(int firstDp, int secondDp) {
@@ -754,11 +758,16 @@
     }
 
     boolean isResizeable() {
-        return (info.flags & FLAG_RESIZEABLE) != 0;
+        return !isHomeActivity() && (info.resizeMode == RESIZE_MODE_RESIZEABLE
+                || info.resizeMode == RESIZE_MODE_RESIZEABLE_AND_PIPABLE);
     }
 
     boolean supportsPictureInPicture() {
-        return (info.flags & FLAG_SUPPORTS_PICTURE_IN_PICTURE) != 0;
+        return !isHomeActivity() && info.resizeMode == RESIZE_MODE_RESIZEABLE_AND_PIPABLE;
+    }
+
+    boolean cropAppWindows() {
+        return !isHomeActivity() && info.resizeMode == RESIZE_MODE_CROP_WINDOWS;
     }
 
     boolean isAlwaysFocusable() {
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index ef8d230..3e99558 100644
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -1410,14 +1410,11 @@
         }
 
         if (mStackId == DOCKED_STACK_ID) {
-            // Docked stack is always visible, except in the case where the home activity
-            // is the top running activity in the focused home stack.
-            if (focusedStackId != HOME_STACK_ID) {
-                return STACK_VISIBLE;
-            }
-            ActivityRecord topHomeActivity = focusedStack.topRunningActivityLocked();
-            return topHomeActivity == null || !topHomeActivity.isHomeActivity() ?
-                    STACK_VISIBLE : STACK_INVISIBLE;
+            // Docked stack is always visible, except in the case where the top running activity in
+            // the focus stack doesn't support any form of resizing.
+            final ActivityRecord r = focusedStack.topRunningActivityLocked();
+            return r == null || r.isResizeable() || r.cropAppWindows()
+                    ? STACK_VISIBLE : STACK_INVISIBLE;
         }
 
         // Find the first stack below focused stack that actually got something visible.
@@ -4819,7 +4816,7 @@
                 r.task.taskId, mStackId, r.info.screenOrientation, r.fullscreen,
                 (r.info.flags & FLAG_SHOW_FOR_ALL_USERS) != 0, r.userId, r.info.configChanges,
                 task.voiceSession != null, r.mLaunchTaskBehind, bounds, task.mOverrideConfig,
-                !r.isHomeActivity(), r.isAlwaysFocusable());
+                r.cropAppWindows() | r.isResizeable(), r.isAlwaysFocusable());
         mWindowManager.setTaskResizeable(task.taskId, task.mResizeable);
         r.taskConfigOverride = task.mOverrideConfig;
     }
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index 8db2f8f..11dd8a3 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -98,6 +98,7 @@
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
+import java.util.Objects;
 import java.util.Set;
 
 import static android.Manifest.permission.START_ANY_ACTIVITY;
@@ -2019,7 +2020,7 @@
         // If this is a forced resize, let it go through even if the bounds is not changing,
         // as we might need a relayout due to surface size change (to/from fullscreen).
         final boolean forced = (resizeMode & RESIZE_MODE_FORCED) != 0;
-        if (task.mBounds != null && task.mBounds.equals(bounds) && !forced) {
+        if (Objects.equals(task.mBounds, bounds) && !forced) {
             // Nothing to do here...
             return true;
         }
@@ -2257,10 +2258,7 @@
             // If we didn't actual do a relaunch (indicated by kept==true meaning we kept the old
             // window), we need to clear the replace window settings. Otherwise, we schedule a
             // timeout to remove the old window if the replacing window is not coming in time.
-            // In case of the pinned stack we don't resize the task during the move, but we will
-            // resize the stack soon after so we want to retain the replacing window.
-            mWindowManager.scheduleClearReplacingWindowIfNeeded(topActivity.appToken,
-                    !kept || stackId == PINNED_STACK_ID);
+            mWindowManager.scheduleClearReplacingWindowIfNeeded(topActivity.appToken, !kept);
         }
 
         // The task might have already been running and its visibility needs to be synchronized with
@@ -2294,7 +2292,8 @@
             return false;
         }
 
-        moveActivityToStackLocked(r, PINNED_STACK_ID, "moveTopActivityToPinnedStack", bounds);
+        moveActivityToStackLocked(r, PINNED_STACK_ID, "moveTopActivityToPinnedStack", null);
+        mWindowManager.animateResizePinnedStack(bounds);
         return true;
     }
 
diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java
index 0f7dff27..97ef10b 100644
--- a/services/core/java/com/android/server/am/BatteryStatsService.java
+++ b/services/core/java/com/android/server/am/BatteryStatsService.java
@@ -40,6 +40,7 @@
 import android.os.UserHandle;
 import android.os.WorkSource;
 import android.telephony.DataConnectionRealTimeInfo;
+import android.telephony.ModemActivityInfo;
 import android.telephony.SignalStrength;
 import android.telephony.TelephonyManager;
 import android.util.IntArray;
@@ -51,6 +52,7 @@
 import com.android.internal.os.BatteryStatsHelper;
 import com.android.internal.os.BatteryStatsImpl;
 import com.android.internal.os.PowerProfile;
+import com.android.internal.telephony.ITelephony;
 import com.android.server.FgThread;
 import com.android.server.LocalServices;
 
@@ -1329,6 +1331,24 @@
         return null;
     }
 
+    @GuardedBy("mExternalStatsLock")
+    private ModemActivityInfo pullModemActivityInfoLocked() {
+        ITelephony tm = ITelephony.Stub.asInterface(ServiceManager.getService(
+                Context.TELEPHONY_SERVICE));
+        try {
+            if (tm != null) {
+                ModemActivityInfo info = tm.getModemActivityInfo();
+                if (info == null || info.isValid()) {
+                    return info;
+                }
+                Slog.wtf(TAG, "Modem activity info is invalid: " + info);
+            }
+        } catch (RemoteException e) {
+            // Nothing to do.
+        }
+        return null;
+    }
+
     /**
      * Fetches data from external sources (WiFi controller, bluetooth chipset) and updates
      * batterystats with that information.
@@ -1358,6 +1378,11 @@
                 wifiEnergyInfo = pullWifiEnergyInfoLocked();
             }
 
+            ModemActivityInfo modemActivityInfo = null;
+            if ((updateFlags & UPDATE_RADIO) != 0) {
+                modemActivityInfo = pullModemActivityInfoLocked();
+            }
+
             BluetoothActivityEnergyInfo bluetoothEnergyInfo = null;
             if ((updateFlags & UPDATE_BT) != 0) {
                 // We only pull bluetooth stats when we have to, as we are not distributing its
@@ -1379,7 +1404,7 @@
                 }
 
                 if ((updateFlags & UPDATE_RADIO) != 0) {
-                    mStats.updateMobileRadioStateLocked(elapsedRealtime);
+                    mStats.updateMobileRadioStateLocked(elapsedRealtime, modemActivityInfo);
                 }
 
                 if ((updateFlags & UPDATE_WIFI) != 0) {
diff --git a/services/core/java/com/android/server/am/TaskRecord.java b/services/core/java/com/android/server/am/TaskRecord.java
index fd787df..c9d4595 100644
--- a/services/core/java/com/android/server/am/TaskRecord.java
+++ b/services/core/java/com/android/server/am/TaskRecord.java
@@ -24,7 +24,8 @@
 import static android.app.ActivityManager.StackId.PINNED_STACK_ID;
 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.FLAG_RESIZEABLE;
+import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE;
+import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE_AND_PIPABLE;
 import static android.content.pm.ActivityInfo.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;
@@ -447,7 +448,9 @@
         } else {
             autoRemoveRecents = false;
         }
-        mResizeable = (info.flags & FLAG_RESIZEABLE) != 0 || mService.mForceResizableActivities;
+        mResizeable = info.resizeMode == RESIZE_MODE_RESIZEABLE
+                || info.resizeMode == RESIZE_MODE_RESIZEABLE_AND_PIPABLE
+                || mService.mForceResizableActivities;
         mLockTaskMode = info.lockTaskLaunchMode;
         mPrivileged = (info.applicationInfo.privateFlags & PRIVATE_FLAG_PRIVILEGED) != 0;
         setLockTaskAuth();
diff --git a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
index 0029279..c5d38cb 100644
--- a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
+++ b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
@@ -248,10 +248,10 @@
     }
 
     public String toString() {
-        return "NetworkAgentInfo{ ni{" + networkInfo + "}  network{" +
-                network + "}  lp{" +
-                linkProperties + "}  nc{" +
-                networkCapabilities + "}  Score{" + getCurrentScore() + "}  " +
+        return "NetworkAgentInfo{ ni{" + networkInfo + "}  " +
+                "network{" + network + "}  nethandle{" + network.getNetworkHandle() + "}  " +
+                "lp{" + linkProperties + "}  " +
+                "nc{" + networkCapabilities + "}  Score{" + getCurrentScore() + "}  " +
                 "everValidated{" + everValidated + "}  lastValidated{" + lastValidated + "}  " +
                 "created{" + created + "} lingering{" + lingering + "} " +
                 "explicitlySelected{" + networkMisc.explicitlySelected + "} " +
diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java
index 573afd6..033a243 100644
--- a/services/core/java/com/android/server/input/InputManagerService.java
+++ b/services/core/java/com/android/server/input/InputManagerService.java
@@ -16,7 +16,9 @@
 
 package com.android.server.input;
 
+import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.util.LocaleList;
 import android.view.Display;
 import com.android.internal.inputmethod.InputMethodSubtypeHandle;
 import com.android.internal.os.SomeArgs;
@@ -780,8 +782,10 @@
                         || layout.getProductId() != d.getProductId()) {
                     return;
                 }
-                for (Locale l : layout.getLocales()) {
-                    if (isCompatibleLocale(systemLocale, l)) {
+                final LocaleList locales = layout.getLocales();
+                final int numLocales = locales.size();
+                for (int localeIndex = 0; localeIndex < numLocales; ++localeIndex) {
+                    if (isCompatibleLocale(systemLocale, locales.get(localeIndex))) {
                         layouts.add(layout);
                         break;
                     }
@@ -799,9 +803,12 @@
         final int N = layouts.size();
         for (int i = 0; i < N; i++) {
             KeyboardLayout layout = layouts.get(i);
-            for (Locale l : layout.getLocales()) {
-                if (l.getCountry().equals(systemLocale.getCountry())
-                        && l.getVariant().equals(systemLocale.getVariant())) {
+            final LocaleList locales = layout.getLocales();
+            final int numLocales = locales.size();
+            for (int localeIndex = 0; localeIndex < numLocales; ++localeIndex) {
+                final Locale locale = locales.get(localeIndex);
+                if (locale.getCountry().equals(systemLocale.getCountry())
+                        && locale.getVariant().equals(systemLocale.getVariant())) {
                     return layout.getDescriptor();
                 }
             }
@@ -809,8 +816,11 @@
         // Then try an exact match of language and country
         for (int i = 0; i < N; i++) {
             KeyboardLayout layout = layouts.get(i);
-            for (Locale l : layout.getLocales()) {
-                if (l.getCountry().equals(systemLocale.getCountry())) {
+            final LocaleList locales = layout.getLocales();
+            final int numLocales = locales.size();
+            for (int localeIndex = 0; localeIndex < numLocales; ++localeIndex) {
+                final Locale locale = locales.get(localeIndex);
+                if (locale.getCountry().equals(systemLocale.getCountry())) {
                     return layout.getDescriptor();
                 }
             }
@@ -1170,7 +1180,7 @@
                                     0);
                             String languageTags = a.getString(
                                     com.android.internal.R.styleable.KeyboardLayout_locale);
-                            Locale[] locales = getLocalesFromLanguageTags(languageTags);
+                            LocaleList locales = getLocalesFromLanguageTags(languageTags);
                             int vid = a.getInt(
                                     com.android.internal.R.styleable.KeyboardLayout_vendorId, -1);
                             int pid = a.getInt(
@@ -1210,16 +1220,12 @@
         }
     }
 
-    private static Locale[] getLocalesFromLanguageTags(String languageTags) {
+    @NonNull
+    private static LocaleList getLocalesFromLanguageTags(String languageTags) {
         if (TextUtils.isEmpty(languageTags)) {
-            return new Locale[0];
+            return LocaleList.getEmptyLocaleList();
         }
-        String[] tags = languageTags.split("\\|");
-        Locale[] locales = new Locale[tags.length];
-        for (int i = 0; i < tags.length; i++) {
-            locales[i] = Locale.forLanguageTag(tags[i]);
-        }
-        return locales;
+        return LocaleList.forLanguageTags(languageTags.replace('|', ','));
     }
 
     /**
@@ -1596,7 +1602,7 @@
         final int accessibilityConfig = Settings.Secure.getIntForUser(
                 mContext.getContentResolver(), Settings.Secure.ACCESSIBILITY_LARGE_POINTER_ICON,
                 0, UserHandle.USER_CURRENT);
-        PointerIcon.sUseLargeIcons = (accessibilityConfig == 1);
+        PointerIcon.setUseLargeIcons(accessibilityConfig == 1);
         nativeReloadPointerIcons(mPtr);
     }
 
diff --git a/services/core/java/com/android/server/location/GpsLocationProvider.java b/services/core/java/com/android/server/location/GnssLocationProvider.java
similarity index 96%
rename from services/core/java/com/android/server/location/GpsLocationProvider.java
rename to services/core/java/com/android/server/location/GnssLocationProvider.java
index 88ab2c6..9798e56 100644
--- a/services/core/java/com/android/server/location/GpsLocationProvider.java
+++ b/services/core/java/com/android/server/location/GnssLocationProvider.java
@@ -35,11 +35,12 @@
 import android.hardware.location.GeofenceHardwareImpl;
 import android.location.Criteria;
 import android.location.FusedBatchOptions;
+import android.location.GnssStatus;
+import android.location.IGnssStatusListener;
+import android.location.IGnssStatusProvider;
 import android.location.GpsMeasurementsEvent;
 import android.location.GpsNavigationMessageEvent;
 import android.location.IGpsGeofenceHardware;
-import android.location.IGpsStatusListener;
-import android.location.IGpsStatusProvider;
 import android.location.ILocationManager;
 import android.location.INetInitiatedListener;
 import android.location.Location;
@@ -100,9 +101,9 @@
  *
  * {@hide}
  */
-public class GpsLocationProvider implements LocationProviderInterface {
+public class GnssLocationProvider implements LocationProviderInterface {
 
-    private static final String TAG = "GpsLocationProvider";
+    private static final String TAG = "GnssLocationProvider";
 
     private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
     private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
@@ -366,7 +367,7 @@
     private final ILocationManager mILocationManager;
     private Location mLocation = new Location(LocationManager.GPS_PROVIDER);
     private Bundle mLocationExtras = new Bundle();
-    private final GpsStatusListenerHelper mListenerHelper;
+    private final GnssStatusListenerHelper mListenerHelper;
     private final GpsMeasurementsProvider mGpsMeasurementsProvider;
     private final GpsNavigationMessageProvider mGpsNavigationMessageProvider;
 
@@ -382,7 +383,7 @@
     private final GpsNetInitiatedHandler mNIHandler;
 
     // Wakelocks
-    private final static String WAKELOCK_KEY = "GpsLocationProvider";
+    private final static String WAKELOCK_KEY = "GnssLocationProvider";
     private final PowerManager.WakeLock mWakeLock;
 
     // Alarms
@@ -405,20 +406,22 @@
 
     private GeofenceHardwareImpl mGeofenceHardwareImpl;
 
-    private final IGpsStatusProvider mGpsStatusProvider = new IGpsStatusProvider.Stub() {
+    private int mYearOfHardware = 0;
+
+    private final IGnssStatusProvider mGnssStatusProvider = new IGnssStatusProvider.Stub() {
         @Override
-        public void addGpsStatusListener(IGpsStatusListener listener) {
-            mListenerHelper.addListener(listener);
+        public void registerGnssStatusCallback(IGnssStatusListener callback) {
+            mListenerHelper.addListener(callback);
         }
 
         @Override
-        public void removeGpsStatusListener(IGpsStatusListener listener) {
-            mListenerHelper.removeListener(listener);
+        public void unregisterGnssStatusCallback(IGnssStatusListener callback) {
+            mListenerHelper.removeListener(callback);
         }
     };
 
-    public IGpsStatusProvider getGpsStatusProvider() {
-        return mGpsStatusProvider;
+    public IGnssStatusProvider getGnssStatusProvider() {
+        return mGnssStatusProvider;
     }
 
     public IGpsGeofenceHardware getGpsGeofenceProxy() {
@@ -655,7 +658,7 @@
         return true;
     }
 
-    public GpsLocationProvider(Context context, ILocationManager ilocationManager,
+    public GnssLocationProvider(Context context, ILocationManager ilocationManager,
             Looper looper) {
         mContext = context;
         mNtpTime = NtpTrustedTime.getInstance(context);
@@ -698,7 +701,7 @@
                                                 mNetInitiatedListener,
                                                 mSuplEsEnabled);
 
-        mListenerHelper = new GpsStatusListenerHelper(mHandler) {
+        mListenerHelper = new GnssStatusListenerHelper(mHandler) {
             @Override
             protected boolean isAvailableInPlatform() {
                 return isSupported();
@@ -1554,34 +1557,40 @@
      * called from native code to update SV info
      */
     private void reportSvStatus() {
-        int svCount = native_read_sv_status(mSvs, mSnrs, mSvElevations, mSvAzimuths, mSvMasks);
+        int svCount = native_read_sv_status(mPrnWithFlags, mSnrs, mSvElevations, mSvAzimuths,
+                mConstellationTypes);
         mListenerHelper.onSvStatusChanged(
                 svCount,
-                mSvs,
+                mPrnWithFlags,
                 mSnrs,
                 mSvElevations,
                 mSvAzimuths,
-                mSvMasks[EPHEMERIS_MASK],
-                mSvMasks[ALMANAC_MASK],
-                mSvMasks[USED_FOR_FIX_MASK]);
+                mConstellationTypes);
 
         if (VERBOSE) {
-            Log.v(TAG, "SV count: " + svCount +
-                    " ephemerisMask: " + Integer.toHexString(mSvMasks[EPHEMERIS_MASK]) +
-                    " almanacMask: " + Integer.toHexString(mSvMasks[ALMANAC_MASK]));
-            for (int i = 0; i < svCount; i++) {
-                Log.v(TAG, "sv: " + mSvs[i] +
+            Log.v(TAG, "SV count: " + svCount);
+        }
+        // Calculate number of sets used in fix.
+        int usedInFixCount = 0;
+        for (int i = 0; i < svCount; i++) {
+            if ((mPrnWithFlags[i] & GnssStatus.GNSS_SV_FLAGS_USED_IN_FIX) != 0) {
+                ++usedInFixCount;
+            }
+            if (VERBOSE) {
+                Log.v(TAG, "prn: " + (mPrnWithFlags[i] >> GnssStatus.PRN_SHIFT_WIDTH) +
                         " snr: " + mSnrs[i]/10 +
                         " elev: " + mSvElevations[i] +
                         " azimuth: " + mSvAzimuths[i] +
-                        ((mSvMasks[EPHEMERIS_MASK] & (1 << (mSvs[i] - 1))) == 0 ? "  " : " E") +
-                        ((mSvMasks[ALMANAC_MASK] & (1 << (mSvs[i] - 1))) == 0 ? "  " : " A") +
-                        ((mSvMasks[USED_FOR_FIX_MASK] & (1 << (mSvs[i] - 1))) == 0 ? "" : "U"));
+                        ((mPrnWithFlags[i] & GnssStatus.GNSS_SV_FLAGS_HAS_EPHEMERIS_DATA) == 0
+                                ? "  " : " E") +
+                        ((mPrnWithFlags[i] & GnssStatus.GNSS_SV_FLAGS_HAS_ALMANAC_DATA) == 0
+                                ? "  " : " A") +
+                        ((mPrnWithFlags[i] & GnssStatus.GNSS_SV_FLAGS_USED_IN_FIX) == 0
+                                ? "" : "U"));
             }
         }
-
         // return number of sets used in fix instead of total
-        updateStatus(mStatus, Integer.bitCount(mSvMasks[USED_FOR_FIX_MASK]));
+        updateStatus(mStatus, usedInFixCount);
 
         if (mNavigating && mStatus == LocationProvider.AVAILABLE && mLastFixTime > 0 &&
             System.currentTimeMillis() - mLastFixTime > RECENT_FIX_TIMEOUT) {
@@ -1675,6 +1684,33 @@
     }
 
     /**
+     * Called from native code to inform us the hardware information.
+     */
+    private void setGpsYearOfHardware(int yearOfHardware) {
+        if (DEBUG) Log.d(TAG, "setGpsYearOfHardware called with " + yearOfHardware);
+        mYearOfHardware = yearOfHardware;
+    }
+
+    public interface GpsSystemInfoProvider {
+        /**
+         * Returns the year of GPS hardware.
+         */
+        int getGpsYearOfHardware();
+    }
+
+    /**
+     * @hide
+     */
+    public GpsSystemInfoProvider getGpsSystemInfoProvider() {
+        return new GpsSystemInfoProvider() {
+            @Override
+            public int getGpsYearOfHardware() {
+                return mYearOfHardware;
+            }
+        };
+    }
+
+    /**
      * called from native code to request XTRA data
      */
     private void xtraDownloadRequest() {
@@ -2067,7 +2103,7 @@
         }
 
         /**
-         * This method is bound to {@link #GpsLocationProvider(Context, ILocationManager, Looper)}.
+         * This method is bound to {@link #GnssLocationProvider(Context, ILocationManager, Looper)}.
          * It is in charge of loading properties and registering for events that will be posted to
          * this handler.
          */
@@ -2362,17 +2398,14 @@
     }
 
     // for GPS SV statistics
-    private static final int MAX_SVS = 32;
-    private static final int EPHEMERIS_MASK = 0;
-    private static final int ALMANAC_MASK = 1;
-    private static final int USED_FOR_FIX_MASK = 2;
+    private static final int MAX_SVS = 512;
 
     // preallocated arrays, to avoid memory allocation in reportStatus()
-    private int mSvs[] = new int[MAX_SVS];
+    private int mPrnWithFlags[] = new int[MAX_SVS];
     private float mSnrs[] = new float[MAX_SVS];
     private float mSvElevations[] = new float[MAX_SVS];
     private float mSvAzimuths[] = new float[MAX_SVS];
-    private int mSvMasks[] = new int[3];
+    private int mConstellationTypes[] = new int[MAX_SVS];
     private int mSvCount;
     // preallocated to avoid memory allocation in reportNmea()
     private byte[] mNmeaBuffer = new byte[120];
@@ -2392,8 +2425,8 @@
     private native void native_delete_aiding_data(int flags);
     // returns number of SVs
     // mask[0] is ephemeris mask and mask[1] is almanac mask
-    private native int native_read_sv_status(int[] svs, float[] snrs,
-            float[] elevations, float[] azimuths, int[] masks);
+    private native int native_read_sv_status(int[] prnWithFlags, float[] snrs, float[] elevations,
+            float[] azimuths, int[] constellationTypes);
     private native int native_read_nmea(byte[] buffer, int bufferSize);
     private native void native_inject_location(double latitude, double longitude, float accuracy);
 
diff --git a/services/core/java/com/android/server/location/GpsStatusListenerHelper.java b/services/core/java/com/android/server/location/GnssStatusListenerHelper.java
similarity index 63%
rename from services/core/java/com/android/server/location/GpsStatusListenerHelper.java
rename to services/core/java/com/android/server/location/GnssStatusListenerHelper.java
index 53ff6c2..9840c61 100644
--- a/services/core/java/com/android/server/location/GpsStatusListenerHelper.java
+++ b/services/core/java/com/android/server/location/GnssStatusListenerHelper.java
@@ -16,17 +16,17 @@
 
 package com.android.server.location;
 
-import android.location.IGpsStatusListener;
+import android.location.IGnssStatusListener;
 import android.os.Handler;
 import android.os.RemoteException;
 
 /**
- * Implementation of a handler for {@link IGpsStatusListener}.
+ * Implementation of a handler for {@link IGnssStatusListener}.
  */
-abstract class GpsStatusListenerHelper extends RemoteListenerHelper<IGpsStatusListener> {
-    protected GpsStatusListenerHelper(Handler handler) {
-        super(handler, "GpsStatusListenerHelper");
-        setSupported(GpsLocationProvider.isSupported());
+abstract class GnssStatusListenerHelper extends RemoteListenerHelper<IGnssStatusListener> {
+    protected GnssStatusListenerHelper(Handler handler) {
+        super(handler, "GnssStatusListenerHelper");
+        setSupported(GnssLocationProvider.isSupported());
     }
 
     @Override
@@ -38,7 +38,7 @@
     protected void unregisterFromService() {}
 
     @Override
-    protected ListenerOperation<IGpsStatusListener> getHandlerOperation(int result) {
+    protected ListenerOperation<IGnssStatusListener> getHandlerOperation(int result) {
         return null;
     }
 
@@ -47,15 +47,15 @@
         if (isNavigating) {
             operation = new Operation() {
                 @Override
-                public void execute(IGpsStatusListener listener) throws RemoteException {
-                    listener.onGpsStarted();
+                public void execute(IGnssStatusListener listener) throws RemoteException {
+                    listener.onGnssStarted();
                 }
             };
         } else {
             operation = new Operation() {
                 @Override
-                public void execute(IGpsStatusListener listener) throws RemoteException {
-                    listener.onGpsStopped();
+                public void execute(IGnssStatusListener listener) throws RemoteException {
+                    listener.onGnssStopped();
                 }
             };
         }
@@ -65,7 +65,7 @@
     public void onFirstFix(final int timeToFirstFix) {
         Operation operation = new Operation() {
             @Override
-            public void execute(IGpsStatusListener listener) throws RemoteException {
+            public void execute(IGnssStatusListener listener) throws RemoteException {
                 listener.onFirstFix(timeToFirstFix);
             }
         };
@@ -74,25 +74,21 @@
 
     public void onSvStatusChanged(
             final int svCount,
-            final int[] prns,
+            final int[] prnWithFlags,
             final float[] snrs,
             final float[] elevations,
             final float[] azimuths,
-            final int ephemerisMask,
-            final int almanacMask,
-            final int usedInFixMask) {
+            final int[] constellationTypes) {
         Operation operation = new Operation() {
             @Override
-            public void execute(IGpsStatusListener listener) throws RemoteException {
+            public void execute(IGnssStatusListener listener) throws RemoteException {
                 listener.onSvStatusChanged(
                         svCount,
-                        prns,
+                        prnWithFlags,
                         snrs,
                         elevations,
                         azimuths,
-                        ephemerisMask,
-                        almanacMask,
-                        usedInFixMask);
+                        constellationTypes);
             }
         };
         foreach(operation);
@@ -101,12 +97,12 @@
     public void onNmeaReceived(final long timestamp, final String nmea) {
         Operation operation = new Operation() {
             @Override
-            public void execute(IGpsStatusListener listener) throws RemoteException {
+            public void execute(IGnssStatusListener listener) throws RemoteException {
                 listener.onNmeaReceived(timestamp, nmea);
             }
         };
         foreach(operation);
     }
 
-    private interface Operation extends ListenerOperation<IGpsStatusListener> {}
+    private interface Operation extends ListenerOperation<IGnssStatusListener> {}
 }
diff --git a/services/core/java/com/android/server/net/NetworkStatsAccess.java b/services/core/java/com/android/server/net/NetworkStatsAccess.java
index 53ba718..479b065 100644
--- a/services/core/java/com/android/server/net/NetworkStatsAccess.java
+++ b/services/core/java/com/android/server/net/NetworkStatsAccess.java
@@ -66,15 +66,26 @@
          *
          * <p>Granted to:
          * <ul>
-         * <li>Apps with the PACKAGE_USAGE_STATS permission granted. Note that this is an AppOps bit
-         * so it is not necessarily sufficient to declare this in the manifest.
-         * <li>Apps with the (signature/privileged) READ_NETWORK_USAGE_HISTORY permission.
          * <li>Profile owners.
          * </ul>
          */
         int USER = 1;
 
         /**
+         * Access level for apps which can access usage summary of device. Device summary includes
+         * usage by apps running in any profiles/users, however this access level does not
+         * allow querying usage of individual apps running in other profiles/users.
+         *
+         * <p>Granted to:
+         * <ul>
+         * <li>Apps with the PACKAGE_USAGE_STATS permission granted. Note that this is an AppOps bit
+         * so it is not necessarily sufficient to declare this in the manifest.
+         * <li>Apps with the (signature/privileged) READ_NETWORK_USAGE_HISTORY permission.
+         * </ul>
+         */
+        int DEVICESUMMARY = 2;
+
+        /**
          * Access level for apps which can access usage for any app on the device, including apps
          * running on other users/profiles.
          *
@@ -85,7 +96,7 @@
          * <li>The system UID.
          * </ul>
          */
-        int DEVICE = 2;
+        int DEVICE = 3;
     }
 
     /** Returns the {@link NetworkStatsAccess.Level} for the given caller. */
@@ -107,11 +118,15 @@
             return NetworkStatsAccess.Level.DEVICE;
         }
 
+        boolean hasAppOpsPermission = hasAppOpsPermission(context, callingUid, callingPackage);
+        if (hasAppOpsPermission || context.checkCallingOrSelfPermission(
+                READ_NETWORK_USAGE_HISTORY) == PackageManager.PERMISSION_GRANTED) {
+            return NetworkStatsAccess.Level.DEVICESUMMARY;
+        }
+
         boolean isProfileOwner = dpmi != null && dpmi.isActiveAdminWithPolicy(callingUid,
                 DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
-        if (hasAppOpsPermission(context, callingUid, callingPackage) || isProfileOwner
-                || context.checkCallingOrSelfPermission(READ_NETWORK_USAGE_HISTORY) ==
-                PackageManager.PERMISSION_GRANTED) {
+        if (isProfileOwner) {
             // Apps with the AppOps permission, profile owners, and apps with the privileged
             // permission can access data usage for all apps in this user/profile.
             return NetworkStatsAccess.Level.USER;
@@ -131,6 +146,7 @@
             case NetworkStatsAccess.Level.DEVICE:
                 // Device-level access - can access usage for any uid.
                 return true;
+            case NetworkStatsAccess.Level.DEVICESUMMARY:
             case NetworkStatsAccess.Level.USER:
                 // User-level access - can access usage for any app running in the same user, along
                 // with some special uids (system, removed, or tethering).
diff --git a/services/core/java/com/android/server/net/NetworkStatsService.java b/services/core/java/com/android/server/net/NetworkStatsService.java
index b1d6f89..3aeceef 100644
--- a/services/core/java/com/android/server/net/NetworkStatsService.java
+++ b/services/core/java/com/android/server/net/NetworkStatsService.java
@@ -484,15 +484,19 @@
             public NetworkStats getDeviceSummaryForNetwork(NetworkTemplate template, long start,
                     long end) {
                 @NetworkStatsAccess.Level int accessLevel = checkAccessLevel(mCallingPackage);
-                if (accessLevel < NetworkStatsAccess.Level.DEVICE) {
+                if (accessLevel < NetworkStatsAccess.Level.DEVICESUMMARY) {
                     throw new SecurityException("Calling package " + mCallingPackage
-                            + " cannot access device-level network stats");
+                            + " cannot access device summary network stats");
                 }
                 NetworkStats result = new NetworkStats(end - start, 1);
                 final long ident = Binder.clearCallingIdentity();
                 try {
+                    // Using access level higher than the one we checked for above.
+                    // Reason is that we are combining usage data in a way that is not PII
+                    // anymore.
                     result.combineAllValues(
-                            internalGetSummaryForNetwork(template, start, end, accessLevel));
+                            internalGetSummaryForNetwork(template, start, end,
+                                    NetworkStatsAccess.Level.DEVICE));
                 } finally {
                     Binder.restoreCallingIdentity(ident);
                 }
diff --git a/services/core/java/com/android/server/pm/PackageDexOptimizer.java b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
index bce7223..4d66e10 100644
--- a/services/core/java/com/android/server/pm/PackageDexOptimizer.java
+++ b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
@@ -70,8 +70,7 @@
     }
 
     static boolean canOptimizePackage(PackageParser.Package pkg) {
-        return pkg.canHaveOatDir() &&
-                ((pkg.applicationInfo.flags & ApplicationInfo.FLAG_HAS_CODE) != 0);
+        return (pkg.applicationInfo.flags & ApplicationInfo.FLAG_HAS_CODE) != 0;
     }
 
     /**
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index d11da79..c3f20eb 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -76,6 +76,7 @@
 import static android.os.Trace.TRACE_TAG_PACKAGE_MANAGER;
 import static android.system.OsConstants.O_CREAT;
 import static android.system.OsConstants.O_RDWR;
+
 import static com.android.internal.app.IntentForwarderActivity.FORWARD_INTENT_TO_MANAGED_PROFILE;
 import static com.android.internal.app.IntentForwarderActivity.FORWARD_INTENT_TO_PARENT;
 import static com.android.internal.content.NativeLibraryHelper.LIB64_DIR_NAME;
diff --git a/services/core/java/com/android/server/tv/TvInputManagerService.java b/services/core/java/com/android/server/tv/TvInputManagerService.java
index 62e7fb4..318f966 100644
--- a/services/core/java/com/android/server/tv/TvInputManagerService.java
+++ b/services/core/java/com/android/server/tv/TvInputManagerService.java
@@ -71,6 +71,7 @@
 import android.os.Process;
 import android.os.RemoteException;
 import android.os.UserHandle;
+import android.text.TextUtils;
 import android.util.Slog;
 import android.util.SparseArray;
 import android.view.InputChannel;
@@ -779,15 +780,14 @@
         }
     }
 
-    private void notifyTvInputInfoChanged(UserState userState, String inputId,
-            TvInputInfo inputInfo) {
+    private void setTvInputInfoLocked(UserState userState, TvInputInfo inputInfo) {
         if (DEBUG) {
-            Slog.d(TAG, "notifyTvInputInfoChanged(inputId=" + inputId + ", inputInfo=" + inputInfo
-                    + ")");
+            Slog.d(TAG, "setTvInputInfoLocked(inputInfo=" + inputInfo + ")");
         }
+        // TODO: Also update the internal input list.
         for (ITvInputManagerCallback callback : userState.callbackSet) {
             try {
-                callback.onTvInputInfoChanged(inputId, inputInfo);
+                callback.onTvInputInfoChanged(inputInfo);
             } catch (RemoteException e) {
                 Slog.e(TAG, "failed to report changed input info to callback", e);
             }
@@ -846,6 +846,36 @@
             }
         }
 
+        public void setTvInputInfo(TvInputInfo inputInfo, int userId) {
+            String inputInfoPackageName = inputInfo.getServiceInfo().packageName;
+            String callingPackageName = getCallingPackageName();
+            if (!TextUtils.equals(inputInfoPackageName, callingPackageName)) {
+                throw new IllegalArgumentException("calling package " + callingPackageName
+                        + " is not allowed to change TvInputInfo for " + inputInfoPackageName);
+            }
+
+            final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(),
+                    Binder.getCallingUid(), userId, "setTvInputInfoChanged");
+            final long identity = Binder.clearCallingIdentity();
+            try {
+                synchronized (mLock) {
+                    UserState userState = getOrCreateUserStateLocked(resolvedUserId);
+                    setTvInputInfoLocked(userState, inputInfo);
+                }
+            } finally {
+                Binder.restoreCallingIdentity(identity);
+            }
+        }
+
+        private String getCallingPackageName() {
+            final String[] packages = mContext.getPackageManager().getPackagesForUid(
+                    Binder.getCallingUid());
+            if (packages != null && packages.length > 0) {
+                return packages[0];
+            }
+            return "unknown";
+        }
+
         @Override
         public int getTvInputState(String inputId, int userId) {
             final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(),
@@ -2233,18 +2263,6 @@
                 }
             }
         }
-
-        @Override
-        public void setTvInputInfo(String inputId, TvInputInfo inputInfo) {
-            ensureValidInput(inputInfo);
-            synchronized (mLock) {
-                if (DEBUG) {
-                    Slog.d(TAG, "setTvInputInfo(" + inputInfo + ")");
-                }
-                UserState userState = getOrCreateUserStateLocked(mUserId);
-                notifyTvInputInfoChanged(userState, inputId, inputInfo);
-            }
-        }
     }
 
     private final class SessionCallback extends ITvInputSessionCallback.Stub {
diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
index 39983dd..c7d7096 100644
--- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
+++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
@@ -42,6 +42,9 @@
 import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.pm.UserInfo;
 import android.content.res.Resources;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.graphics.BitmapRegionDecoder;
 import android.graphics.Point;
 import android.graphics.Rect;
 import android.os.Binder;
@@ -71,6 +74,7 @@
 import android.view.IWindowManager;
 import android.view.WindowManager;
 
+import java.io.BufferedOutputStream;
 import java.io.FileDescriptor;
 import java.io.IOException;
 import java.io.InputStream;
@@ -96,7 +100,7 @@
 
 public class WallpaperManagerService extends IWallpaperManager.Stub {
     static final String TAG = "WallpaperManagerService";
-    static final boolean DEBUG = false;
+    static final boolean DEBUG = true;
 
     final Object mLock = new Object[0];
 
@@ -106,7 +110,8 @@
      */
     static final long MIN_WALLPAPER_CRASH_TIME = 10000;
     static final int MAX_WALLPAPER_COMPONENT_LOG_LENGTH = 128;
-    static final String WALLPAPER = "wallpaper";
+    static final String WALLPAPER = "wallpaper_orig";
+    static final String WALLPAPER_CROP = "wallpaper";
     static final String WALLPAPER_INFO = "wallpaper_info.xml";
 
     /**
@@ -120,6 +125,7 @@
         final WallpaperData mWallpaper;
         final File mWallpaperDir;
         final File mWallpaperFile;
+        final File mWallpaperCropFile;
         final File mWallpaperInfoFile;
 
         public WallpaperObserver(WallpaperData wallpaper) {
@@ -128,6 +134,7 @@
             mWallpaperDir = getWallpaperDir(wallpaper.userId);
             mWallpaper = wallpaper;
             mWallpaperFile = new File(mWallpaperDir, WALLPAPER);
+            mWallpaperCropFile = new File(mWallpaperDir, WALLPAPER_CROP);
             mWallpaperInfoFile = new File(mWallpaperDir, WALLPAPER_INFO);
         }
 
@@ -136,8 +143,10 @@
             if (path == null) {
                 return;
             }
+            final boolean written = (event == CLOSE_WRITE || event == MOVED_TO);
+            final File changedFile = new File(mWallpaperDir, path);
+
             synchronized (mLock) {
-                File changedFile = new File(mWallpaperDir, path);
                 if (mWallpaperFile.equals(changedFile)
                         || mWallpaperInfoFile.equals(changedFile)) {
                     // changing the wallpaper means we'll need to back up the new one
@@ -148,22 +157,111 @@
                 }
                 if (mWallpaperFile.equals(changedFile)) {
                     notifyCallbacksLocked(mWallpaper);
-                    final boolean written = (event == CLOSE_WRITE || event == MOVED_TO);
                     if (mWallpaper.wallpaperComponent == null
                             || event != CLOSE_WRITE // includes the MOVED_TO case
                             || mWallpaper.imageWallpaperPending) {
                         if (written) {
+                            // The image source has finished writing the source image,
+                            // so we now produce the crop rect (in the background), and
+                            // only publish the new displayable (sub)image as a result
+                            // of that work.
+                            generateCrop(mWallpaper);
                             mWallpaper.imageWallpaperPending = false;
+                            if (mWallpaper.setComplete != null) {
+                                try {
+                                    mWallpaper.setComplete.onWallpaperChanged();
+                                } catch (RemoteException e) {
+                                    // if this fails we don't really care; the setting app may just
+                                    // have crashed and that sort of thing is a fact of life.
+                                }
+                            }
+                            bindWallpaperComponentLocked(mImageWallpaper, true,
+                                    false, mWallpaper, null);
+                            saveSettingsLocked(mWallpaper);
                         }
-                        bindWallpaperComponentLocked(mImageWallpaper, true,
-                                false, mWallpaper, null);
-                        saveSettingsLocked(mWallpaper);
                     }
                 }
             }
         }
     }
 
+    /**
+     * Once a new wallpaper has been written via setWallpaper(...), it needs to be cropped
+     * for display.
+     */
+    private void generateCrop(WallpaperData wallpaper) {
+        boolean success = false;
+        boolean needCrop = false;
+
+        // Analyse the source; needed in multiple cases
+        BitmapFactory.Options options = new BitmapFactory.Options();
+        options.inJustDecodeBounds = true;
+        BitmapFactory.decodeFile(wallpaper.wallpaperFile.getAbsolutePath(), options);
+
+        // Legacy case uses an empty crop rect here, so we just preserve the
+        // source image verbatim
+        if (!wallpaper.cropHint.isEmpty()) {
+            // ...clamp the crop rect to the measured bounds...
+            wallpaper.cropHint.right = Math.min(wallpaper.cropHint.right, options.outWidth);
+            wallpaper.cropHint.bottom = Math.min(wallpaper.cropHint.bottom, options.outHeight);
+            // ...and don't bother cropping if what we're left with is identity
+            needCrop = (options.outHeight >= wallpaper.cropHint.height()
+                    && options.outWidth >= wallpaper.cropHint.width());
+        }
+
+        if (!needCrop) {
+            // Simple case:  the nominal crop is at least as big as the source image,
+            // so we take the whole thing and just copy the image file directly.
+            if (DEBUG) {
+                Slog.v(TAG, "Null crop of new wallpaper; copying");
+            }
+            success = FileUtils.copyFile(wallpaper.wallpaperFile, wallpaper.cropFile);
+            if (!success) {
+                wallpaper.cropFile.delete();
+                // TODO: fall back to default wallpaper in this case
+            }
+        } else {
+            // Fancy case: the crop is a subrect of the source
+            FileOutputStream f = null;
+            BufferedOutputStream bos = null;
+            try {
+                BitmapRegionDecoder decoder = BitmapRegionDecoder.newInstance(
+                        wallpaper.wallpaperFile.getAbsolutePath(), false);
+                Bitmap cropped = decoder.decodeRegion(wallpaper.cropHint, null);
+                decoder.recycle();
+
+                if (cropped == null) {
+                    Slog.e(TAG, "Could not decode new wallpaper");
+                } else {
+                    f = new FileOutputStream(wallpaper.cropFile);
+                    bos = new BufferedOutputStream(f, 32*1024);
+                    cropped.compress(Bitmap.CompressFormat.PNG, 90, bos);
+                    bos.flush();  // don't rely on the implicit flush-at-close when noting success
+                    success = true;
+                }
+            } catch (IOException e) {
+                if (DEBUG) {
+                    Slog.e(TAG, "I/O error decoding crop: " + e.getMessage());
+                }
+            } finally {
+                IoUtils.closeQuietly(bos);
+                IoUtils.closeQuietly(f);
+            }
+        }
+
+        if (!success) {
+            Slog.e(TAG, "Unable to apply new wallpaper");
+            wallpaper.cropFile.delete();
+        }
+
+        if (wallpaper.cropFile.exists()) {
+            boolean didRestorecon = SELinux.restorecon(wallpaper.cropFile.getAbsoluteFile());
+            if (DEBUG) {
+                Slog.v(TAG, "restorecon() of crop file returned " + didRestorecon);
+            }
+        }
+    }
+
     final Context mContext;
     final IWindowManager mIWindowManager;
     final IPackageManager mIPackageManager;
@@ -191,7 +289,8 @@
 
         int userId;
 
-        File wallpaperFile;
+        final File wallpaperFile;
+        final File cropFile;
 
         /**
          * Client is currently writing a new image wallpaper.
@@ -199,6 +298,11 @@
         boolean imageWallpaperPending;
 
         /**
+         * Callback once the set + crop is finished
+         */
+        IWallpaperManagerCallback setComplete;
+
+        /**
          * Resource name if using a picture from the wallpaper gallery
          */
         String name = "";
@@ -232,11 +336,26 @@
         int width = -1;
         int height = -1;
 
+        /**
+         * The crop hint supplied for displaying a subset of the source image
+         */
+        final Rect cropHint = new Rect(0, 0, 0, 0);
+
         final Rect padding = new Rect(0, 0, 0, 0);
 
         WallpaperData(int userId) {
             this.userId = userId;
             wallpaperFile = new File(getWallpaperDir(userId), WALLPAPER);
+            cropFile = new File(getWallpaperDir(userId), WALLPAPER_CROP);
+        }
+
+        // Only called in single-threaded boot sequence mode
+        boolean ensureCropExists() {
+            // if the crop file is not present, copy over the source image to use verbatim
+            if (!cropFile.exists()) {
+                return FileUtils.copyFile(wallpaperFile, cropFile);
+            }
+            return true;
         }
     }
 
@@ -524,6 +643,9 @@
     public void systemRunning() {
         if (DEBUG) Slog.v(TAG, "systemReady");
         WallpaperData wallpaper = mWallpaperMap.get(UserHandle.USER_SYSTEM);
+        if (!wallpaper.ensureCropExists()) {
+            clearWallpaperLocked(false, UserHandle.USER_SYSTEM, null);
+        }
         switchWallpaper(wallpaper, null);
         wallpaper.wallpaperObserver = new WallpaperObserver(wallpaper);
         wallpaper.wallpaperObserver.startWatching();
@@ -602,6 +724,8 @@
             onStoppingUser(userId);
             File wallpaperFile = new File(getWallpaperDir(userId), WALLPAPER);
             wallpaperFile.delete();
+            File cropFile = new File(getWallpaperDir(userId), WALLPAPER_CROP);
+            cropFile.delete();
             File wallpaperInfoFile = new File(getWallpaperDir(userId), WALLPAPER_INFO);
             wallpaperInfoFile.delete();
         }
@@ -653,9 +777,9 @@
         if (wallpaper == null) {
             return;
         }
-        File f = new File(getWallpaperDir(userId), WALLPAPER);
-        if (f.exists()) {
-            f.delete();
+        if (wallpaper.wallpaperFile.exists()) {
+            wallpaper.wallpaperFile.delete();
+            wallpaper.cropFile.delete();
         }
         final long ident = Binder.clearCallingIdentity();
         try {
@@ -844,11 +968,10 @@
                     outParams.putInt("height", wallpaper.height);
                 }
                 wallpaper.callbacks.register(cb);
-                File f = new File(getWallpaperDir(wallpaperUserId), WALLPAPER);
-                if (!f.exists()) {
+                if (!wallpaper.cropFile.exists()) {
                     return null;
                 }
-                return ParcelFileDescriptor.open(f, MODE_READ_ONLY);
+                return ParcelFileDescriptor.open(wallpaper.cropFile, MODE_READ_ONLY);
             } catch (FileNotFoundException e) {
                 /* Shouldn't happen as we check to see if the file exists */
                 Slog.w(TAG, "Error getting wallpaper", e);
@@ -869,8 +992,8 @@
     }
 
     @Override
-    public ParcelFileDescriptor setWallpaper(String name, String callingPackage, Bundle extras,
-            int which) {
+    public ParcelFileDescriptor setWallpaper(String name, String callingPackage,
+            Rect cropHint, Bundle extras, int which, IWallpaperManagerCallback completion) {
         checkPermission(android.Manifest.permission.SET_WALLPAPER);
 
         if (which == 0) {
@@ -881,6 +1004,17 @@
             return null;
         }
 
+        // "null" means the no-op crop, preserving the full input image
+        if (cropHint == null) {
+            cropHint = new Rect(0, 0, 0, 0);
+        } else {
+            if (cropHint.isEmpty()
+                    || cropHint.left < 0
+                    || cropHint.top < 0) {
+                return null;
+            }
+        }
+
         synchronized (mLock) {
             if (DEBUG) Slog.v(TAG, "setWallpaper");
             int userId = UserHandle.getCallingUserId();
@@ -890,6 +1024,8 @@
                 ParcelFileDescriptor pfd = updateWallpaperBitmapLocked(name, wallpaper, extras);
                 if (pfd != null) {
                     wallpaper.imageWallpaperPending = true;
+                    wallpaper.setComplete = completion;
+                    wallpaper.cropHint.set(cropHint);
                 }
                 return pfd;
             } finally {
@@ -1196,6 +1332,12 @@
             out.attribute(null, "id", Integer.toString(wallpaper.wallpaperId));
             out.attribute(null, "width", Integer.toString(wallpaper.width));
             out.attribute(null, "height", Integer.toString(wallpaper.height));
+
+            out.attribute(null, "cropLeft", Integer.toString(wallpaper.cropHint.left));
+            out.attribute(null, "cropTop", Integer.toString(wallpaper.cropHint.top));
+            out.attribute(null, "cropRight", Integer.toString(wallpaper.cropHint.right));
+            out.attribute(null, "cropBottom", Integer.toString(wallpaper.cropHint.bottom));
+
             if (wallpaper.padding.left != 0) {
                 out.attribute(null, "paddingLeft", Integer.toString(wallpaper.padding.left));
             }
@@ -1208,6 +1350,7 @@
             if (wallpaper.padding.bottom != 0) {
                 out.attribute(null, "paddingBottom", Integer.toString(wallpaper.padding.bottom));
             }
+
             out.attribute(null, "name", wallpaper.name);
             if (wallpaper.wallpaperComponent != null
                     && !wallpaper.wallpaperComponent.equals(mImageWallpaper)) {
@@ -1304,6 +1447,10 @@
                         wallpaper.width = Integer.parseInt(parser.getAttributeValue(null, "width"));
                         wallpaper.height = Integer.parseInt(parser
                                 .getAttributeValue(null, "height"));
+                        wallpaper.cropHint.left = getAttributeInt(parser, "cropLeft", 0);
+                        wallpaper.cropHint.top = getAttributeInt(parser, "cropTop", 0);
+                        wallpaper.cropHint.right = getAttributeInt(parser, "cropRight", 0);
+                        wallpaper.cropHint.bottom = getAttributeInt(parser, "cropBottom", 0);
                         wallpaper.padding.left = getAttributeInt(parser, "paddingLeft", 0);
                         wallpaper.padding.top = getAttributeInt(parser, "paddingTop", 0);
                         wallpaper.padding.right = getAttributeInt(parser, "paddingRight", 0);
@@ -1322,6 +1469,7 @@
                         if (DEBUG) {
                             Slog.v(TAG, "mWidth:" + wallpaper.width);
                             Slog.v(TAG, "mHeight:" + wallpaper.height);
+                            Slog.v(TAG, "cropRect:" + wallpaper.cropHint);
                             Slog.v(TAG, "mName:" + wallpaper.name);
                             Slog.v(TAG, "mNextWallpaperComponent:"
                                     + wallpaper.nextWallpaperComponent);
@@ -1348,6 +1496,7 @@
         if (!success) {
             wallpaper.width = -1;
             wallpaper.height = -1;
+            wallpaper.cropHint.set(0, 0, 0, 0);
             wallpaper.padding.set(0, 0, 0, 0);
             wallpaper.name = "";
         } else {
@@ -1368,6 +1517,11 @@
         if (wallpaper.height < baseSize) {
             wallpaper.height = baseSize;
         }
+        // and crop, if not previously specified
+        if (wallpaper.cropHint.width() <= 0
+                || wallpaper.cropHint.height() <= 0) {
+            wallpaper.cropHint.set(0, 0, wallpaper.width, wallpaper.height);
+        }
     }
 
     private int getMaximumSizeDimension() {
@@ -1431,6 +1585,7 @@
         }
     }
 
+    // Restore the named resource bitmap to both source + crop files
     boolean restoreNamedResourceLocked(WallpaperData wallpaper) {
         if (wallpaper.name.length() > 4 && "res:".equals(wallpaper.name.substring(0, 4))) {
             String resName = wallpaper.name.substring(4);
@@ -1456,6 +1611,7 @@
                 int resId = -1;
                 InputStream res = null;
                 FileOutputStream fos = null;
+                FileOutputStream cos = null;
                 try {
                     Context c = mContext.createPackageContext(pkg, Context.CONTEXT_RESTRICTED);
                     Resources r = c.getResources();
@@ -1469,13 +1625,16 @@
                     res = r.openRawResource(resId);
                     if (wallpaper.wallpaperFile.exists()) {
                         wallpaper.wallpaperFile.delete();
+                        wallpaper.cropFile.delete();
                     }
                     fos = new FileOutputStream(wallpaper.wallpaperFile);
+                    cos = new FileOutputStream(wallpaper.cropFile);
 
                     byte[] buffer = new byte[32768];
                     int amt;
                     while ((amt=res.read(buffer)) > 0) {
                         fos.write(buffer, 0, amt);
+                        cos.write(buffer, 0, amt);
                     }
                     // mWallpaperObserver will notice the close and send the change broadcast
 
@@ -1491,8 +1650,12 @@
                     IoUtils.closeQuietly(res);
                     if (fos != null) {
                         FileUtils.sync(fos);
-                        IoUtils.closeQuietly(fos);
                     }
+                    if (cos != null) {
+                        FileUtils.sync(cos);
+                    }
+                    IoUtils.closeQuietly(fos);
+                    IoUtils.closeQuietly(cos);
                 }
             }
         }
@@ -1520,6 +1683,7 @@
                     pw.print(wallpaper.width);
                     pw.print(" mHeight=");
                     pw.println(wallpaper.height);
+                pw.print("  mCropHint="); pw.println(wallpaper.cropHint);
                 pw.print("  mPadding="); pw.println(wallpaper.padding);
                 pw.print("  mName=");  pw.println(wallpaper.name);
                 pw.print("  mWallpaperComponent="); pw.println(wallpaper.wallpaperComponent);
diff --git a/services/core/java/com/android/server/wm/AppTransition.java b/services/core/java/com/android/server/wm/AppTransition.java
index 552af03..43a17c9 100644
--- a/services/core/java/com/android/server/wm/AppTransition.java
+++ b/services/core/java/com/android/server/wm/AppTransition.java
@@ -134,7 +134,7 @@
     /** Fraction of animation at which the recents thumbnail becomes completely transparent */
     private static final float RECENTS_THUMBNAIL_FADEOUT_FRACTION = 0.5f;
 
-    private static final int DEFAULT_APP_TRANSITION_DURATION = 336;
+    static final int DEFAULT_APP_TRANSITION_DURATION = 336;
     private static final int THUMBNAIL_APP_TRANSITION_DURATION = 336;
     private static final int THUMBNAIL_APP_TRANSITION_ALPHA_DURATION = 336;
     private static final long APP_TRANSITION_TIMEOUT_MS = 5000;
diff --git a/services/core/java/com/android/server/wm/BoundsAnimationController.java b/services/core/java/com/android/server/wm/BoundsAnimationController.java
new file mode 100644
index 0000000..5f97478
--- /dev/null
+++ b/services/core/java/com/android/server/wm/BoundsAnimationController.java
@@ -0,0 +1,142 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm;
+
+import static com.android.server.wm.AppTransition.DEFAULT_APP_TRANSITION_DURATION;
+import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ANIM;
+import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
+import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
+
+import android.animation.Animator;
+import android.animation.ValueAnimator;
+import android.graphics.Rect;
+import android.util.ArrayMap;
+import android.util.Slog;
+import android.view.animation.LinearInterpolator;
+
+/**
+ * Enables animating bounds of objects.
+ *
+ * In multi-window world bounds of both stack and tasks can change. When we need these bounds to
+ * change smoothly and not require the app to relaunch (e.g. because it handles resizes and
+ * relaunching it would cause poorer experience), these class provides a way to directly animate
+ * the bounds of the resized object.
+ *
+ * The object that is resized needs to implement {@link AnimateBoundsUser} interface.
+ */
+public class BoundsAnimationController {
+    private static final String TAG = TAG_WITH_CLASS_NAME ? "BoundsAnimationController" : TAG_WM;
+
+    // Only acccessed on UI thread.
+    private ArrayMap<AnimateBoundsUser, BoundsAnimator> mRunningAnimations = new ArrayMap<>();
+
+    private final class BoundsAnimator extends ValueAnimator
+            implements ValueAnimator.AnimatorUpdateListener, ValueAnimator.AnimatorListener {
+        private final AnimateBoundsUser mTarget;
+        private final Rect mFrom;
+        private final Rect mTo;
+        private final Rect mTmpRect;
+
+        BoundsAnimator(AnimateBoundsUser target, Rect from, Rect to) {
+            super();
+            mTarget = target;
+            mFrom = from;
+            mTo = to;
+            mTmpRect = new Rect();
+            addUpdateListener(this);
+            addListener(this);
+        }
+
+        @Override
+        public void onAnimationUpdate(ValueAnimator animation) {
+            final float value = (Float) animation.getAnimatedValue();
+            final float remains = 1 - value;
+            mTmpRect.left = (int) (mFrom.left * remains + mTo.left * value);
+            mTmpRect.top = (int) (mFrom.top * remains + mTo.top * value);
+            mTmpRect.right = (int) (mFrom.right * remains + mTo.right * value);
+            mTmpRect.bottom = (int) (mFrom.bottom * remains + mTo.bottom * value);
+            if (DEBUG_ANIM) Slog.d(TAG, "animateUpdate: mTarget=" + mTarget + ", mBounds="
+                    + mTmpRect + ", from=" + mFrom + ", mTo=" + mTo + ", value=" + value
+                    + ", remains=" + remains);
+            if (!mTarget.setSize(mTmpRect)) {
+                // Whoops, the target doesn't feel like animating anymore. Let's immediately finish
+                // any further animation.
+                animation.cancel();
+            }
+        }
+
+
+        @Override
+        public void onAnimationStart(Animator animation) {
+
+        }
+
+        @Override
+        public void onAnimationEnd(Animator animation) {
+            finishAnimation();
+        }
+
+        @Override
+        public void onAnimationCancel(Animator animation) {
+            finishAnimation();
+        }
+
+        private void finishAnimation() {
+            mTarget.finishBoundsAnimation();
+            removeListener(this);
+            removeUpdateListener(this);
+            mRunningAnimations.remove(mTarget);
+        }
+
+        @Override
+        public void onAnimationRepeat(Animator animation) {
+
+        }
+    }
+
+    public interface AnimateBoundsUser {
+        /**
+         * Asks the target to directly (without any intermediate steps, like scheduling animation)
+         * resize its bounds.
+         *
+         * @return Whether the target still wants to be animated and successfully finished the
+         * operation. If it returns false, the animation will immediately be cancelled. The target
+         * should return false when something abnormal happened, e.g. it was completely removed
+         * from the hierarchy and is not valid anymore.
+         */
+        boolean setSize(Rect bounds);
+
+        /**
+         * Callback for the target to inform it that the animation is finished, so it can do some
+         * necessary cleanup.
+         */
+        void finishBoundsAnimation();
+    }
+
+    void animateBounds(AnimateBoundsUser target, Rect from, Rect to) {
+        final BoundsAnimator existing = mRunningAnimations.get(target);
+        if (existing != null) {
+            existing.cancel();
+        }
+        BoundsAnimator animator = new BoundsAnimator(target, from, to);
+        mRunningAnimations.put(target, animator);
+        animator.setFloatValues(0f, 1f);
+        animator.setDuration(DEFAULT_APP_TRANSITION_DURATION);
+        animator.setInterpolator(new LinearInterpolator());
+        animator.start();
+    }
+}
diff --git a/services/core/java/com/android/server/wm/TaskStack.java b/services/core/java/com/android/server/wm/TaskStack.java
index f02e49e..e6fa837 100644
--- a/services/core/java/com/android/server/wm/TaskStack.java
+++ b/services/core/java/com/android/server/wm/TaskStack.java
@@ -20,6 +20,7 @@
 import android.content.res.Configuration;
 import android.graphics.Rect;
 import android.os.Debug;
+import android.os.RemoteException;
 import android.util.EventLog;
 import android.util.Slog;
 import android.util.SparseArray;
@@ -47,7 +48,8 @@
 import static com.android.server.wm.WindowManagerService.H.RESIZE_STACK;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
 
-public class TaskStack implements DimLayer.DimLayerUser {
+public class TaskStack implements DimLayer.DimLayerUser,
+        BoundsAnimationController.AnimateBoundsUser {
 
     // If the stack should be resized to fullscreen.
     private static final boolean FULLSCREEN = true;
@@ -72,6 +74,12 @@
     /** Content limits relative to the DisplayContent this sits in. */
     private Rect mBounds = new Rect();
 
+    /** Screen content area excluding IM windows, etc. */
+    private final Rect mContentBounds = new Rect();
+
+    /** Stack bounds adjusted to screen content area (taking into account IM windows, etc.) */
+    private final Rect mAdjustedBounds = new Rect();
+
     /** Whether mBounds is fullscreen */
     private boolean mFullscreen = true;
 
@@ -162,6 +170,85 @@
         return mTmpRect.equals(bounds);
     }
 
+    void alignTasksToAdjustedBounds(final Rect adjustedBounds) {
+        if (mFullscreen) {
+            return;
+        }
+        // Update bounds of containing tasks.
+        for (int taskNdx = mTasks.size() - 1; taskNdx >= 0; --taskNdx) {
+            final Task task = mTasks.get(taskNdx);
+            if (task.isTwoFingerScrollMode()) {
+                // If we're scrolling we don't care about your bounds or configs,
+                // they should be null as if we were in fullscreen.
+                task.resizeLocked(null, null, false /* forced */);
+                task.getBounds(mTmpRect2);
+                task.scrollLocked(mTmpRect2);
+            } else if (task.isResizeable()) {
+                task.getBounds(mTmpRect2);
+                mTmpRect2.offsetTo(adjustedBounds.left, adjustedBounds.top);
+                task.resizeLocked(mTmpRect2, task.mOverrideConfig, false /* forced */);
+            }
+        }
+    }
+
+    void adjustForIME(final WindowState imeWin) {
+        final int dockedSide = getDockSide();
+        final boolean dockedTopOrBottom = dockedSide == DOCKED_TOP || dockedSide == DOCKED_BOTTOM;
+        final Rect adjustedBounds = mAdjustedBounds;
+        if (imeWin == null || !dockedTopOrBottom) {
+            // If mContentBounds is already empty, it means we're not applying
+            // any adjustments, so nothing to do; otherwise clear any adjustments.
+            if (!mContentBounds.isEmpty()) {
+                mContentBounds.setEmpty();
+                adjustedBounds.set(mBounds);
+                alignTasksToAdjustedBounds(adjustedBounds);
+            }
+            return;
+        }
+
+        final Rect displayContentRect = mTmpRect;
+        final Rect contentBounds = mTmpRect2;
+
+        // Calculate the content bounds excluding the area occupied by IME
+        mDisplayContent.getContentRect(displayContentRect);
+        contentBounds.set(displayContentRect);
+        int imeTop = Math.max(imeWin.getDisplayFrameLw().top, contentBounds.top);
+        imeTop += imeWin.getGivenContentInsetsLw().top;
+        if (contentBounds.bottom > imeTop) {
+            contentBounds.bottom = imeTop;
+        }
+
+        // If content bounds not changing, nothing to do.
+        if (mContentBounds.equals(contentBounds)) {
+            return;
+        }
+
+        // Content bounds changed, need to apply adjustments depending on dock sides.
+        mContentBounds.set(contentBounds);
+        adjustedBounds.set(mBounds);
+        final int yOffset = displayContentRect.bottom - contentBounds.bottom;
+
+        if (dockedSide == DOCKED_TOP) {
+            // If this stack is docked on top, we make it smaller so the bottom stack is not
+            // occluded by IME. We shift its bottom up by the height of the IME (capped by
+            // the display content rect). Note that we don't change the task bounds.
+            adjustedBounds.bottom = Math.max(
+                    adjustedBounds.bottom - yOffset, displayContentRect.top);
+        } else {
+            // If this stack is docked on bottom, we shift it up so that it's not occluded by
+            // IME. We try to move it up by the height of the IME window (although the best
+            // we could do is to make the top stack fully collapsed).
+            final int dividerWidth = mDisplayContent.mDividerControllerLocked.getContentWidth();
+            adjustedBounds.top = Math.max(
+                    adjustedBounds.top - yOffset, displayContentRect.top + dividerWidth);
+            adjustedBounds.bottom = adjustedBounds.top + mBounds.height();
+
+            // We also move the member tasks together, taking care not to resize them.
+            // Resizing might cause relaunch, and IME window may not come back after that.
+            alignTasksToAdjustedBounds(adjustedBounds);
+        }
+    }
+
     private boolean setBounds(Rect bounds) {
         boolean oldFullscreen = mFullscreen;
         int rotation = Surface.ROTATION_0;
@@ -191,6 +278,16 @@
 
         mBounds.set(bounds);
         mRotation = rotation;
+
+        // Clear the adjusted content bounds as they're no longer valid.
+        // If IME is still visible, these will be re-applied.
+        // Note that we don't clear mContentBounds here, so that we know the last IME
+        // adjust we applied.
+        // If user starts dragging the dock divider while IME is visible, the new bounds
+        // we received are based on the actual screen location of the divider. It already
+        // accounted for the IME window, so we don't want to adjust again.
+        mAdjustedBounds.set(mBounds);
+
         return true;
     }
 
@@ -215,9 +312,14 @@
 
     public void getBounds(Rect out) {
         if (useCurrentBounds()) {
-            // No need to adjust the output bounds if fullscreen or the docked stack is visible
+            // If we're currently adjusting for IME, we use the adjusted bounds; otherwise,
+            // no need to adjust the output bounds if fullscreen or the docked stack is visible
             // since it is already what we want to represent to the rest of the system.
-            out.set(mBounds);
+            if (!mContentBounds.isEmpty()) {
+                out.set(mAdjustedBounds);
+            } else {
+                out.set(mBounds);
+            }
             return;
         }
 
@@ -804,4 +906,32 @@
         }
         return false;
     }
+
+    @Override  // AnimatesBounds
+    public boolean setSize(Rect bounds) {
+        synchronized (mService.mWindowMap) {
+            if (mDisplayContent == null) {
+                return false;
+            }
+        }
+        try {
+            mService.mActivityManager.resizeStack(mStackId, bounds, false, true, false);
+        } catch (RemoteException e) {
+        }
+        return true;
+    }
+
+    @Override  // AnimatesBounds
+    public void finishBoundsAnimation() {
+        synchronized (mService.mWindowMap) {
+            if (mTasks.isEmpty()) {
+                return;
+            }
+            final Task task = mTasks.get(mTasks.size() - 1);
+            if (task != null) {
+                task.setDragResizing(false);
+                mService.requestTraversal();
+            }
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/wm/WindowManagerDebugConfig.java b/services/core/java/com/android/server/wm/WindowManagerDebugConfig.java
index 0979cd3..66aa863 100644
--- a/services/core/java/com/android/server/wm/WindowManagerDebugConfig.java
+++ b/services/core/java/com/android/server/wm/WindowManagerDebugConfig.java
@@ -35,10 +35,10 @@
 
     static final boolean DEBUG_RESIZE = false;
     static final boolean DEBUG = false;
-    static final boolean DEBUG_ADD_REMOVE = false;
+    static final boolean DEBUG_ADD_REMOVE = true;
     static final boolean DEBUG_FOCUS = false;
     static final boolean DEBUG_FOCUS_LIGHT = DEBUG_FOCUS || false;
-    static final boolean DEBUG_ANIM = false;
+    static final boolean DEBUG_ANIM = true;
     static final boolean DEBUG_KEYGUARD = false;
     static final boolean DEBUG_LAYOUT = false;
     static final boolean DEBUG_LAYERS = false;
@@ -50,7 +50,7 @@
     static final boolean DEBUG_ORIENTATION = false;
     static final boolean DEBUG_APP_ORIENTATION = false;
     static final boolean DEBUG_CONFIGURATION = false;
-    static final boolean DEBUG_APP_TRANSITIONS = false;
+    static final boolean DEBUG_APP_TRANSITIONS = true;
     static final boolean DEBUG_STARTING_WINDOW = false;
     static final boolean DEBUG_WALLPAPER = false;
     static final boolean DEBUG_WALLPAPER_LIGHT = false || DEBUG_WALLPAPER;
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 93a1015..e0e44dc 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -157,9 +157,11 @@
 import static android.app.ActivityManager.DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT;
 import static android.app.ActivityManager.StackId.DOCKED_STACK_ID;
 import static android.app.ActivityManager.StackId.FREEFORM_WORKSPACE_STACK_ID;
+import static android.app.ActivityManager.StackId.PINNED_STACK_ID;
 import static android.app.StatusBarManager.DISABLE_MASK;
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_BEHIND;
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
+import static android.view.WindowManager.DOCKED_BOTTOM;
 import static android.view.WindowManager.DOCKED_INVALID;
 import static android.view.WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW;
 import static android.view.WindowManager.LayoutParams.FIRST_SUB_WINDOW;
@@ -643,6 +645,9 @@
 
     final WindowAnimator mAnimator;
 
+    private final BoundsAnimationController mBoundsAnimationController =
+            new BoundsAnimationController();
+
     SparseArray<Task> mTaskIdToTask = new SparseArray<>();
 
     /** All of the TaskStacks in the window manager, unordered. For an ordered list call
@@ -2848,11 +2853,12 @@
         if ((result & WindowManagerGlobal.RELAYOUT_RES_FIRST_TIME) != 0) {
             win.prepareWindowToDisplayDuringRelayout(outConfig);
         }
-        if ((attrChanges& LayoutParams.FORMAT_CHANGED) != 0) {
-            // If the format can be changed in place yaay!
-            // If not, fall back to a surface re-build
+        if ((attrChanges & LayoutParams.FORMAT_CHANGED) != 0) {
+            // If the format can't be changed in place, preserve the old surface until the app draws
+            // on the new one. This prevents blinking when we change elevation of freeform and
+            // pinned windows.
             if (!winAnimator.tryChangeFormatInPlaceLocked()) {
-                winAnimator.destroySurfaceLocked();
+                winAnimator.preserveSurfaceLocked();
                 result |= RELAYOUT_RES_SURFACE_CHANGED
                         | WindowManagerGlobal.RELAYOUT_RES_FIRST_TIME;
             }
@@ -8070,8 +8076,29 @@
                 }
                 case UPDATE_DOCKED_STACK_DIVIDER: {
                     synchronized (mWindowMap) {
-                        getDefaultDisplayContentLocked().getDockedDividerController()
-                                .reevaluateVisibility(false);
+                        final DisplayContent displayContent = getDefaultDisplayContentLocked();
+
+                        displayContent.getDockedDividerController().reevaluateVisibility(false);
+
+                        final WindowState imeWin = mInputMethodWindow;
+                        final TaskStack focusedStack =
+                                mCurrentFocus != null ? mCurrentFocus.getStack() : null;
+                        if (imeWin != null && focusedStack != null && imeWin.isVisibleNow()
+                                && focusedStack.getDockSide() == DOCKED_BOTTOM){
+                            final ArrayList<TaskStack> stacks = displayContent.getStacks();
+                            for (int i = stacks.size() - 1; i >= 0; --i) {
+                                final TaskStack stack = stacks.get(i);
+                                if (stack.isVisibleLocked()) {
+                                    stack.adjustForIME(imeWin);
+                                }
+                            }
+                        } else {
+                            final ArrayList<TaskStack> stacks = displayContent.getStacks();
+                            for (int i = stacks.size() - 1; i >= 0; --i) {
+                                final TaskStack stack = stacks.get(i);
+                                stack.adjustForIME(null);
+                            }
+                        }
                     }
                 }
                 break;
@@ -8085,7 +8112,8 @@
                 break;
                 case RESIZE_STACK: {
                     try {
-                        mActivityManager.resizeStack(msg.arg1, (Rect) msg.obj, msg.arg2 == 1);
+                        mActivityManager.resizeStack(msg.arg1, (Rect) msg.obj, msg.arg2 == 1, false,
+                                false);
                     } catch (RemoteException e) {
                         // This will not happen since we are in the same process.
                     }
@@ -10273,6 +10301,31 @@
         }
     }
 
+    public void animateResizePinnedStack(final Rect bounds) {
+        synchronized (mWindowMap) {
+            final TaskStack stack = mStackIdToStack.get(PINNED_STACK_ID);
+            if (stack == null) {
+                Slog.w(TAG, "animateResizePinnedStack: stackId " + PINNED_STACK_ID + " not found.");
+                return;
+            }
+            final ArrayList<Task> tasks = stack.getTasks();
+            if (tasks.isEmpty()) {
+                Slog.w(TAG, "animateResizePinnedStack: pinned stack doesn't have any tasks.");
+                return;
+            }
+            final Task task = tasks.get(tasks.size() - 1);
+            task.setDragResizing(true);
+            final Rect originalBounds = new Rect();
+            stack.getBounds(originalBounds);
+            UiThread.getHandler().post(new Runnable() {
+                @Override
+                public void run() {
+                    mBoundsAnimationController.animateBounds(stack, originalBounds, bounds);
+                }
+            });
+        }
+    }
+
     public void setTaskResizeable(int taskId, boolean resizeable) {
         synchronized (mWindowMap) {
             Task task = mTaskIdToTask.get(taskId);
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index c541b3f..4dd2b4d 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -57,6 +57,7 @@
 import java.util.ArrayList;
 
 import static android.app.ActivityManager.StackId;
+import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
 import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
 import static android.view.ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_CONTENT;
 import static android.view.ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_FRAME;
@@ -750,15 +751,16 @@
                     Math.min(mStableFrame.bottom, frame.bottom));
         }
 
-        mOverscanInsets.set(Math.max(mOverscanFrame.left - frame.left, 0),
-                Math.max(mOverscanFrame.top - frame.top, 0),
-                Math.max(frame.right - mOverscanFrame.right, 0),
-                Math.max(frame.bottom - mOverscanFrame.bottom, 0));
-
-
+        if (!inFreeformWorkspace()) {
+            // Freeform windows can be positioned outside of the display frame, but that is not a
+            // reason to provide them with overscan insets.
+            mOverscanInsets.set(Math.max(mOverscanFrame.left - frame.left, 0),
+                    Math.max(mOverscanFrame.top - frame.top, 0),
+                    Math.max(frame.right - mOverscanFrame.right, 0),
+                    Math.max(frame.bottom - mOverscanFrame.bottom, 0));
+        }
 
         if (mAttrs.type == TYPE_DOCK_DIVIDER) {
-
             // For the docked divider, we calculate the stable insets like a full-screen window
             // so it can use it to calculate the snap positions.
             mStableInsets.set(Math.max(mStableFrame.left - mDisplayFrame.left, 0),
@@ -2051,10 +2053,21 @@
         // until the window to small size, otherwise the multithread renderer will shift last
         // one or more frame to wrong offset. So here we send fullscreen backdrop if either
         // isDragResizing() or isDragResizeChanged() is true.
+        boolean resizing = isDragResizing() || isDragResizeChanged();
+        if (StackId.useWindowFrameForBackdrop(getStackId()) || !resizing) {
+            return frame;
+        }
         DisplayInfo displayInfo = getDisplayInfo();
         mTmpRect.set(0, 0, displayInfo.logicalWidth, displayInfo.logicalHeight);
-        boolean resizing = isDragResizing() || isDragResizeChanged();
-        return (inFreeformWorkspace() || !resizing) ? frame : mTmpRect;
+        return mTmpRect;
+    }
+
+    private int getStackId() {
+        final TaskStack stack = getStack();
+        if (stack == null) {
+            return INVALID_STACK_ID;
+        }
+        return stack.mStackId;
     }
 
     private void dispatchResized(Rect frame, Rect overscanInsets, Rect contentInsets,
@@ -2226,7 +2239,8 @@
         }
         pw.print(prefix); pw.print("mHasSurface="); pw.print(mHasSurface);
                 pw.print(" mShownPosition="); mShownPosition.printShortString(pw);
-                pw.print(" isReadyForDisplay()="); pw.println(isReadyForDisplay());
+                pw.print(" isReadyForDisplay()="); pw.print(isReadyForDisplay());
+                pw.print(" hasSavedSurface()="); pw.println(hasSavedSurface());
         if (dumpAll) {
             pw.print(prefix); pw.print("mFrame="); mFrame.printShortString(pw);
                     pw.print(" last="); mLastFrame.printShortString(pw);
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index cffcc5d..428ab7a 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -567,6 +567,10 @@
 
     WindowSurfaceController createSurfaceLocked() {
         final WindowState w = mWin;
+        if (w.hasSavedSurface()) {
+            Slog.i(TAG, "***** createSurface: " + this + ": called when we had a saved surface");
+        }
+
         if (mSurfaceController == null) {
             if (DEBUG_ANIM || DEBUG_ORIENTATION) Slog.i(TAG,
                     "createSurface " + this + ": mDrawState=DRAW_PENDING");
diff --git a/services/core/jni/Android.mk b/services/core/jni/Android.mk
index 98d8d08..d1b8648 100644
--- a/services/core/jni/Android.mk
+++ b/services/core/jni/Android.mk
@@ -22,7 +22,7 @@
     $(LOCAL_REL_DIR)/com_android_server_input_InputManagerService.cpp \
     $(LOCAL_REL_DIR)/com_android_server_input_InputWindowHandle.cpp \
     $(LOCAL_REL_DIR)/com_android_server_lights_LightsService.cpp \
-    $(LOCAL_REL_DIR)/com_android_server_location_GpsLocationProvider.cpp \
+    $(LOCAL_REL_DIR)/com_android_server_location_GnssLocationProvider.cpp \
     $(LOCAL_REL_DIR)/com_android_server_location_FlpHardwareProvider.cpp \
     $(LOCAL_REL_DIR)/com_android_server_power_PowerManagerService.cpp \
     $(LOCAL_REL_DIR)/com_android_server_SerialService.cpp \
diff --git a/services/core/jni/com_android_server_location_GpsLocationProvider.cpp b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
similarity index 79%
rename from services/core/jni/com_android_server_location_GpsLocationProvider.cpp
rename to services/core/jni/com_android_server_location_GnssLocationProvider.cpp
index b8d4196..0c85a15 100644
--- a/services/core/jni/com_android_server_location_GpsLocationProvider.cpp
+++ b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
@@ -14,14 +14,14 @@
  * limitations under the License.
  */
 
-#define LOG_TAG "GpsLocationProvider"
+#define LOG_TAG "GnssLocationProvider"
 
 #define LOG_NDEBUG 0
 
 #include "JNIHelp.h"
 #include "jni.h"
 #include "hardware/hardware.h"
-#include "hardware/gps.h"
+#include "hardware/gps_internal.h"
 #include "hardware_legacy/power.h"
 #include "utils/Log.h"
 #include "utils/misc.h"
@@ -42,6 +42,7 @@
 static jmethodID method_reportAGpsStatus;
 static jmethodID method_reportNmea;
 static jmethodID method_setEngineCapabilities;
+static jmethodID method_setGpsYearOfHardware;
 static jmethodID method_xtraDownloadRequest;
 static jmethodID method_reportNiNotification;
 static jmethodID method_requestRefLocation;
@@ -67,8 +68,14 @@
 static const GpsNavigationMessageInterface* sGpsNavigationMessageInterface = NULL;
 static const GnssConfigurationInterface* sGnssConfigurationInterface = NULL;
 
+#define MAX_SATELLITE_COUNT 512
+#define MAX_GPS_SATELLITE_COUNT 512
+
+#define PRN_SHIFT_WIDTH 3
+
 // temporary storage for GPS callbacks
-static GpsSvStatus  sGpsSvStatus;
+static GnssSvInfo sGnssSvList[MAX_SATELLITE_COUNT];
+static size_t sGnssSvListSize;
 static const char* sNmeaString;
 static int sNmeaStringLength;
 
@@ -105,7 +112,57 @@
 static void sv_status_callback(GpsSvStatus* sv_status)
 {
     JNIEnv* env = AndroidRuntime::getJNIEnv();
-    memcpy(&sGpsSvStatus, sv_status, sizeof(sGpsSvStatus));
+    size_t status_size = sv_status->size;
+    // Some drive doesn't set the size field correctly. Assume GpsSvStatus_v1 if
+    // it doesn't provide a valid size.
+    if (status_size == 0) {
+        status_size = sizeof(GpsSvStatus_v1);
+    }
+    if (status_size == sizeof(GpsSvStatus)) {
+        sGnssSvListSize = sv_status->gnss_sv_list_size;
+        // Cramp the list size
+        if (sGnssSvListSize > MAX_SATELLITE_COUNT) {
+            sGnssSvListSize = MAX_SATELLITE_COUNT;
+        }
+        // Copy GNSS SV info into sGnssSvList, if any.
+        if (sGnssSvListSize > 0 && sv_status->gnss_sv_list) {
+            memcpy(sGnssSvList, sv_status->gnss_sv_list, sizeof(GnssSvInfo) * sGnssSvListSize);
+        }
+    } else if (status_size == sizeof(GpsSvStatus_v1)) {
+        sGnssSvListSize = sv_status->num_svs;
+        // Cramp the list size
+        if (sGnssSvListSize > MAX_GPS_SATELLITE_COUNT) {
+            sGnssSvListSize = MAX_GPS_SATELLITE_COUNT;
+        }
+        uint32_t ephemeris_mask = sv_status->ephemeris_mask;
+        uint32_t almanac_mask = sv_status->almanac_mask;
+        uint32_t used_in_fix_mask = sv_status->used_in_fix_mask;
+        for (size_t i = 0; i < sGnssSvListSize; i++) {
+            GnssSvInfo& info = sGnssSvList[i];
+            info.constellation = GNSS_CONSTELLATION_GPS;
+            info.prn = sv_status->sv_list[i].prn;
+            info.snr = sv_status->sv_list[i].snr;
+            info.elevation = sv_status->sv_list[i].elevation;
+            info.azimuth = sv_status->sv_list[i].azimuth;
+            info.flags = GNSS_SV_FLAGS_NONE;
+            if (info.prn > 0 && info.prn <= 32) {
+              int32_t this_prn_mask = (1 << (info.prn - 1));
+              if ((ephemeris_mask & this_prn_mask) != 0) {
+                info.flags |= GNSS_SV_FLAGS_HAS_EPHEMERIS_DATA;
+              }
+              if ((almanac_mask & this_prn_mask) != 0) {
+                info.flags |= GNSS_SV_FLAGS_HAS_ALMANAC_DATA;
+              }
+              if ((used_in_fix_mask & this_prn_mask) != 0) {
+                info.flags |= GNSS_SV_FLAGS_USED_IN_FIX;
+              }
+            }
+        }
+    } else {
+        sGnssSvListSize = 0;
+        ALOGE("Invalid size of GpsSvStatus found: %zd.", status_size);
+        return;
+    }
     env->CallVoidMethod(mCallbacksObj, method_reportSvStatus);
     checkAndClearExceptionFromCallback(env, __FUNCTION__);
 }
@@ -121,6 +178,14 @@
     checkAndClearExceptionFromCallback(env, __FUNCTION__);
 }
 
+static void set_system_info_callback(const GpsSystemInfo* info) {
+    ALOGD("set_system_info_callback: year_of_hw=%d\n", info->year_of_hw);
+    JNIEnv* env = AndroidRuntime::getJNIEnv();
+    env->CallVoidMethod(mCallbacksObj, method_setGpsYearOfHardware,
+                        info->year_of_hw);
+    checkAndClearExceptionFromCallback(env, __FUNCTION__);
+}
+
 static void set_capabilities_callback(uint32_t capabilities)
 {
     ALOGD("set_capabilities_callback: %du\n", capabilities);
@@ -162,6 +227,7 @@
     release_wakelock_callback,
     create_thread_callback,
     request_utc_time_callback,
+    set_system_info_callback,
 };
 
 static void xtra_download_request_callback()
@@ -213,7 +279,7 @@
     bool isSupported = false;
 
     size_t status_size = agps_status->size;
-    if (status_size == sizeof(AGpsStatus_v3)) {
+    if (status_size == sizeof(AGpsStatus)) {
       ALOGV("AGpsStatus is V3: %zd", status_size);
       switch (agps_status->addr.ss_family)
       {
@@ -439,7 +505,7 @@
     create_thread_callback,
 };
 
-static void android_location_GpsLocationProvider_class_init_native(JNIEnv* env, jclass clazz) {
+static void android_location_GnssLocationProvider_class_init_native(JNIEnv* env, jclass clazz) {
     int err;
     hw_module_t* module;
 
@@ -449,6 +515,7 @@
     method_reportAGpsStatus = env->GetMethodID(clazz, "reportAGpsStatus", "(II[B)V");
     method_reportNmea = env->GetMethodID(clazz, "reportNmea", "(J)V");
     method_setEngineCapabilities = env->GetMethodID(clazz, "setEngineCapabilities", "(I)V");
+    method_setGpsYearOfHardware = env->GetMethodID(clazz, "setGpsYearOfHardware", "(I)V");
     method_xtraDownloadRequest = env->GetMethodID(clazz, "xtraDownloadRequest", "()V");
     method_reportNiNotification = env->GetMethodID(clazz, "reportNiNotification",
             "(IIIIILjava/lang/String;Ljava/lang/String;IILjava/lang/String;)V");
@@ -509,13 +576,13 @@
     }
 }
 
-static jboolean android_location_GpsLocationProvider_is_supported(
+static jboolean android_location_GnssLocationProvider_is_supported(
         JNIEnv* /* env */, jclass /* clazz */)
 {
     return (sGpsInterface != NULL) ?  JNI_TRUE : JNI_FALSE;
 }
 
-static jboolean android_location_GpsLocationProvider_is_agps_ril_supported(
+static jboolean android_location_GnssLocationProvider_is_agps_ril_supported(
         JNIEnv* /* env */, jclass /* clazz */)
 {
     return (sAGpsRilInterface != NULL) ? JNI_TRUE : JNI_FALSE;
@@ -527,7 +594,7 @@
     return (sGnssConfigurationInterface != NULL) ? JNI_TRUE : JNI_FALSE;
 }
 
-static jboolean android_location_GpsLocationProvider_init(JNIEnv* env, jobject obj)
+static jboolean android_location_GnssLocationProvider_init(JNIEnv* env, jobject obj)
 {
     // this must be set before calling into the HAL library
     if (!mCallbacksObj)
@@ -553,13 +620,13 @@
     return JNI_TRUE;
 }
 
-static void android_location_GpsLocationProvider_cleanup(JNIEnv* /* env */, jobject /* obj */)
+static void android_location_GnssLocationProvider_cleanup(JNIEnv* /* env */, jobject /* obj */)
 {
     if (sGpsInterface)
         sGpsInterface->cleanup();
 }
 
-static jboolean android_location_GpsLocationProvider_set_position_mode(JNIEnv* /* env */,
+static jboolean android_location_GnssLocationProvider_set_position_mode(JNIEnv* /* env */,
         jobject /* obj */, jint mode, jint recurrence, jint min_interval, jint preferred_accuracy,
         jint preferred_time)
 {
@@ -575,7 +642,7 @@
         return JNI_FALSE;
 }
 
-static jboolean android_location_GpsLocationProvider_start(JNIEnv* /* env */, jobject /* obj */)
+static jboolean android_location_GnssLocationProvider_start(JNIEnv* /* env */, jobject /* obj */)
 {
     if (sGpsInterface) {
         if (sGpsInterface->start() == 0) {
@@ -588,7 +655,7 @@
         return JNI_FALSE;
 }
 
-static jboolean android_location_GpsLocationProvider_stop(JNIEnv* /* env */, jobject /* obj */)
+static jboolean android_location_GnssLocationProvider_stop(JNIEnv* /* env */, jobject /* obj */)
 {
     if (sGpsInterface) {
         if (sGpsInterface->stop() == 0) {
@@ -601,7 +668,7 @@
         return JNI_FALSE;
 }
 
-static void android_location_GpsLocationProvider_delete_aiding_data(JNIEnv* /* env */,
+static void android_location_GnssLocationProvider_delete_aiding_data(JNIEnv* /* env */,
                                                                     jobject /* obj */,
                                                                     jint flags)
 {
@@ -609,38 +676,36 @@
         sGpsInterface->delete_aiding_data(flags);
 }
 
-static jint android_location_GpsLocationProvider_read_sv_status(JNIEnv* env, jobject /* obj */,
-        jintArray prnArray, jfloatArray snrArray, jfloatArray elevArray, jfloatArray azumArray,
-        jintArray maskArray)
+static jint android_location_GnssLocationProvider_read_sv_status(JNIEnv* env, jobject /* obj */,
+        jintArray prnWithFlagArray, jfloatArray snrArray, jfloatArray elevArray,
+        jfloatArray azumArray, jintArray constellationTypeArray)
 {
     // this should only be called from within a call to reportSvStatus
-
-    jint* prns = env->GetIntArrayElements(prnArray, 0);
+    jint* prnWithFlags = env->GetIntArrayElements(prnWithFlagArray, 0);
     jfloat* snrs = env->GetFloatArrayElements(snrArray, 0);
     jfloat* elev = env->GetFloatArrayElements(elevArray, 0);
     jfloat* azim = env->GetFloatArrayElements(azumArray, 0);
-    jint* mask = env->GetIntArrayElements(maskArray, 0);
+    jint* constellationTypes = env->GetIntArrayElements(constellationTypeArray, 0);
 
-    int num_svs = sGpsSvStatus.num_svs;
-    for (int i = 0; i < num_svs; i++) {
-        prns[i] = sGpsSvStatus.sv_list[i].prn;
-        snrs[i] = sGpsSvStatus.sv_list[i].snr;
-        elev[i] = sGpsSvStatus.sv_list[i].elevation;
-        azim[i] = sGpsSvStatus.sv_list[i].azimuth;
+    // GNSS SV info.
+    for (size_t i = 0; i < sGnssSvListSize; ++i) {
+        const GnssSvInfo& info = sGnssSvList[i];
+        constellationTypes[i] = info.constellation;
+        prnWithFlags[i] = (info.prn << PRN_SHIFT_WIDTH) | info.flags;
+        snrs[i] = info.snr;
+        elev[i] = info.elevation;
+        azim[i] = info.azimuth;
     }
-    mask[0] = sGpsSvStatus.ephemeris_mask;
-    mask[1] = sGpsSvStatus.almanac_mask;
-    mask[2] = sGpsSvStatus.used_in_fix_mask;
 
-    env->ReleaseIntArrayElements(prnArray, prns, 0);
+    env->ReleaseIntArrayElements(prnWithFlagArray, prnWithFlags, 0);
     env->ReleaseFloatArrayElements(snrArray, snrs, 0);
     env->ReleaseFloatArrayElements(elevArray, elev, 0);
     env->ReleaseFloatArrayElements(azumArray, azim, 0);
-    env->ReleaseIntArrayElements(maskArray, mask, 0);
-    return (jint) num_svs;
+    env->ReleaseIntArrayElements(constellationTypeArray, constellationTypes, 0);
+    return (jint) sGnssSvListSize;
 }
 
-static void android_location_GpsLocationProvider_agps_set_reference_location_cellid(
+static void android_location_GnssLocationProvider_agps_set_reference_location_cellid(
         JNIEnv* /* env */, jobject /* obj */, jint type, jint mcc, jint mnc, jint lac, jint cid)
 {
     AGpsRefLocation location;
@@ -667,7 +732,7 @@
     sAGpsRilInterface->set_ref_location(&location, sizeof(location));
 }
 
-static void android_location_GpsLocationProvider_agps_send_ni_message(JNIEnv* env,
+static void android_location_GnssLocationProvider_agps_send_ni_message(JNIEnv* env,
         jobject /* obj */, jbyteArray ni_msg, jint size)
 {
     size_t sz;
@@ -684,7 +749,7 @@
     env->ReleaseByteArrayElements(ni_msg,b,0);
 }
 
-static void android_location_GpsLocationProvider_agps_set_id(JNIEnv *env, jobject /* obj */,
+static void android_location_GnssLocationProvider_agps_set_id(JNIEnv *env, jobject /* obj */,
                                                              jint type, jstring  setid_string)
 {
     if (!sAGpsRilInterface) {
@@ -697,7 +762,7 @@
     env->ReleaseStringUTFChars(setid_string, setid);
 }
 
-static jint android_location_GpsLocationProvider_read_nmea(JNIEnv* env, jobject /* obj */,
+static jint android_location_GnssLocationProvider_read_nmea(JNIEnv* env, jobject /* obj */,
                                             jbyteArray nmeaArray, jint buffer_size)
 {
     // this should only be called from within a call to reportNmea
@@ -710,27 +775,27 @@
     return (jint) length;
 }
 
-static void android_location_GpsLocationProvider_inject_time(JNIEnv* /* env */, jobject /* obj */,
+static void android_location_GnssLocationProvider_inject_time(JNIEnv* /* env */, jobject /* obj */,
         jlong time, jlong timeReference, jint uncertainty)
 {
     if (sGpsInterface)
         sGpsInterface->inject_time(time, timeReference, uncertainty);
 }
 
-static void android_location_GpsLocationProvider_inject_location(JNIEnv* /* env */,
+static void android_location_GnssLocationProvider_inject_location(JNIEnv* /* env */,
         jobject /* obj */, jdouble latitude, jdouble longitude, jfloat accuracy)
 {
     if (sGpsInterface)
         sGpsInterface->inject_location(latitude, longitude, accuracy);
 }
 
-static jboolean android_location_GpsLocationProvider_supports_xtra(
+static jboolean android_location_GnssLocationProvider_supports_xtra(
         JNIEnv* /* env */, jobject /* obj */)
 {
     return (sGpsXtraInterface != NULL) ? JNI_TRUE : JNI_FALSE;
 }
 
-static void android_location_GpsLocationProvider_inject_xtra_data(JNIEnv* env, jobject /* obj */,
+static void android_location_GnssLocationProvider_inject_xtra_data(JNIEnv* env, jobject /* obj */,
         jbyteArray data, jint length)
 {
     if (!sGpsXtraInterface) {
@@ -743,7 +808,7 @@
     env->ReleasePrimitiveArrayCritical(data, bytes, JNI_ABORT);
 }
 
-static void android_location_GpsLocationProvider_agps_data_conn_open(
+static void android_location_GnssLocationProvider_agps_data_conn_open(
         JNIEnv* env, jobject /* obj */, jstring apn, jint apnIpType)
 {
     if (!sAGpsInterface) {
@@ -758,7 +823,7 @@
     const char *apnStr = env->GetStringUTFChars(apn, NULL);
 
     size_t interface_size = sAGpsInterface->size;
-    if (interface_size == sizeof(AGpsInterface_v2)) {
+    if (interface_size == sizeof(AGpsInterface)) {
         sAGpsInterface->data_conn_open_with_apn_ip_type(apnStr, apnIpType);
     } else if (interface_size == sizeof(AGpsInterface_v1)) {
         sAGpsInterface->data_conn_open(apnStr);
@@ -769,7 +834,7 @@
     env->ReleaseStringUTFChars(apn, apnStr);
 }
 
-static void android_location_GpsLocationProvider_agps_data_conn_closed(JNIEnv* /* env */,
+static void android_location_GnssLocationProvider_agps_data_conn_closed(JNIEnv* /* env */,
                                                                        jobject /* obj */)
 {
     if (!sAGpsInterface) {
@@ -779,7 +844,7 @@
     sAGpsInterface->data_conn_closed();
 }
 
-static void android_location_GpsLocationProvider_agps_data_conn_failed(JNIEnv* /* env */,
+static void android_location_GnssLocationProvider_agps_data_conn_failed(JNIEnv* /* env */,
                                                                        jobject /* obj */)
 {
     if (!sAGpsInterface) {
@@ -789,7 +854,7 @@
     sAGpsInterface->data_conn_failed();
 }
 
-static void android_location_GpsLocationProvider_set_agps_server(JNIEnv* env, jobject /* obj */,
+static void android_location_GnssLocationProvider_set_agps_server(JNIEnv* env, jobject /* obj */,
         jint type, jstring hostname, jint port)
 {
     if (!sAGpsInterface) {
@@ -801,7 +866,7 @@
     env->ReleaseStringUTFChars(hostname, c_hostname);
 }
 
-static void android_location_GpsLocationProvider_send_ni_response(JNIEnv* /* env */,
+static void android_location_GnssLocationProvider_send_ni_response(JNIEnv* /* env */,
       jobject /* obj */, jint notifId, jint response)
 {
     if (!sGpsNiInterface) {
@@ -812,7 +877,7 @@
     sGpsNiInterface->respond(notifId, response);
 }
 
-static jstring android_location_GpsLocationProvider_get_internal_state(JNIEnv* env,
+static jstring android_location_GnssLocationProvider_get_internal_state(JNIEnv* env,
                                                                        jobject /* obj */) {
     jstring result = NULL;
     if (sGpsDebugInterface) {
@@ -826,7 +891,7 @@
     return result;
 }
 
-static void android_location_GpsLocationProvider_update_network_state(JNIEnv* env, jobject /* obj */,
+static void android_location_GnssLocationProvider_update_network_state(JNIEnv* env, jobject /* obj */,
         jboolean connected, jint type, jboolean roaming, jboolean available, jstring extraInfo, jstring apn)
 {
 
@@ -849,13 +914,13 @@
     }
 }
 
-static jboolean android_location_GpsLocationProvider_is_geofence_supported(
+static jboolean android_location_GnssLocationProvider_is_geofence_supported(
         JNIEnv* /* env */, jobject /* obj */)
 {
     return (sGpsGeofencingInterface != NULL) ? JNI_TRUE : JNI_FALSE;
 }
 
-static jboolean android_location_GpsLocationProvider_add_geofence(JNIEnv* /* env */,
+static jboolean android_location_GnssLocationProvider_add_geofence(JNIEnv* /* env */,
         jobject /* obj */, jint geofence_id, jdouble latitude, jdouble longitude, jdouble radius,
         jint last_transition, jint monitor_transition, jint notification_responsiveness,
         jint unknown_timer) {
@@ -870,7 +935,7 @@
     return JNI_FALSE;
 }
 
-static jboolean android_location_GpsLocationProvider_remove_geofence(JNIEnv* /* env */,
+static jboolean android_location_GnssLocationProvider_remove_geofence(JNIEnv* /* env */,
         jobject /* obj */, jint geofence_id) {
     if (sGpsGeofencingInterface != NULL) {
         sGpsGeofencingInterface->remove_geofence_area(geofence_id);
@@ -881,7 +946,7 @@
     return JNI_FALSE;
 }
 
-static jboolean android_location_GpsLocationProvider_pause_geofence(JNIEnv* /* env */,
+static jboolean android_location_GnssLocationProvider_pause_geofence(JNIEnv* /* env */,
         jobject /* obj */, jint geofence_id) {
     if (sGpsGeofencingInterface != NULL) {
         sGpsGeofencingInterface->pause_geofence(geofence_id);
@@ -892,7 +957,7 @@
     return JNI_FALSE;
 }
 
-static jboolean android_location_GpsLocationProvider_resume_geofence(JNIEnv* /* env */,
+static jboolean android_location_GnssLocationProvider_resume_geofence(JNIEnv* /* env */,
         jobject /* obj */, jint geofence_id, jint monitor_transition) {
     if (sGpsGeofencingInterface != NULL) {
         sGpsGeofencingInterface->resume_geofence(geofence_id, monitor_transition);
@@ -903,10 +968,12 @@
     return JNI_FALSE;
 }
 
-static jobject translate_gps_clock(JNIEnv* env, GpsClock* clock) {
+static jobject translate_gps_clock(JNIEnv* env, void* data, size_t size) {
     const char* doubleSignature = "(D)V";
     const char* longSignature = "(J)V";
 
+    GpsClock* clock = reinterpret_cast<GpsClock*>(data);
+
     jclass gpsClockClass = env->FindClass("android/location/GpsClock");
     jmethodID gpsClockCtor = env->GetMethodID(gpsClockClass, "<init>", "()V");
 
@@ -958,11 +1025,23 @@
         env->CallVoidMethod(gpsClockObject, setterMethod, clock->drift_uncertainty_nsps);
     }
 
+    if (flags & GPS_CLOCK_TYPE_LOCAL_HW_TIME) {
+        if (size == sizeof(GpsClock)) {
+            jmethodID setterMethod =
+                    env->GetMethodID(gpsClockClass,
+                                     "setTimeOfLastHwClockDiscontinuityInNs",
+                                     longSignature);
+            env->CallVoidMethod(gpsClockObject,
+                                setterMethod,
+                                clock->time_of_last_hw_clock_discontinuity_ns);
+        }
+    }
+
     env->DeleteLocalRef(gpsClockClass);
     return gpsClockObject;
 }
 
-static jobject translate_gps_measurement(JNIEnv* env, GpsMeasurement* measurement) {
+static jobject translate_gps_measurement(JNIEnv* env, void* data, size_t size) {
     const char* byteSignature = "(B)V";
     const char* shortSignature = "(S)V";
     const char* intSignature = "(I)V";
@@ -972,6 +1051,7 @@
 
     jclass gpsMeasurementClass = env->FindClass("android/location/GpsMeasurement");
     jmethodID gpsMeasurementCtor = env->GetMethodID(gpsMeasurementClass, "<init>", "()V");
+    GpsMeasurement* measurement = reinterpret_cast<GpsMeasurement*>(data);
 
     jobject gpsMeasurementObject = env->NewObject(gpsMeasurementClass, gpsMeasurementCtor);
     GpsMeasurementFlags flags = measurement->flags;
@@ -1205,12 +1285,38 @@
             usedInFixSetterMethod,
             (flags & GPS_MEASUREMENT_HAS_USED_IN_FIX) && measurement->used_in_fix);
 
+    if (size == sizeof(GpsMeasurement)) {
+      jmethodID setterMethod =
+          env->GetMethodID(gpsMeasurementClass,
+                           "setPseudorangeRateCarrierInMetersPerSec",
+                           doubleSignature);
+      env->CallVoidMethod(
+          gpsMeasurementObject,
+          setterMethod,
+          measurement->pseudorange_rate_carrier_mps);
+
+      setterMethod =
+          env->GetMethodID(gpsMeasurementClass,
+                           "setPseudorangeRateCarrierUncertaintyInMetersPerSec",
+                           doubleSignature);
+      env->CallVoidMethod(
+          gpsMeasurementObject,
+          setterMethod,
+          measurement->pseudorange_rate_carrier_uncertainty_mps);
+    }
+
     env->DeleteLocalRef(gpsMeasurementClass);
     return gpsMeasurementObject;
 }
 
-static jobjectArray translate_gps_measurements(JNIEnv* env, GpsData* data) {
-    size_t measurementCount = data->measurement_count;
+/**
+ * <T> can only be GpsData or GpsData_v1. Must rewrite this function if more
+ * types are introduced in the future releases.
+ */
+template<class T>
+static jobjectArray translate_gps_measurements(JNIEnv* env, void* data) {
+    T* gps_data = reinterpret_cast<T*>(data);
+    size_t measurementCount = gps_data->measurement_count;
     if (measurementCount == 0) {
         return NULL;
     }
@@ -1221,9 +1327,11 @@
             gpsMeasurementClass,
             NULL /* initialElement */);
 
-    GpsMeasurement* gpsMeasurements = data->measurements;
     for (uint16_t i = 0; i < measurementCount; ++i) {
-        jobject gpsMeasurement = translate_gps_measurement(env, &gpsMeasurements[i]);
+        jobject gpsMeasurement = translate_gps_measurement(
+            env,
+            &(gps_data->measurements[i]),
+            sizeof(gps_data->measurements[0]));
         env->SetObjectArrayElement(gpsMeasurementArray, i, gpsMeasurement);
         env->DeleteLocalRef(gpsMeasurement);
     }
@@ -1238,33 +1346,39 @@
         ALOGE("Invalid data provided to gps_measurement_callback");
         return;
     }
-
-    if (data->size == sizeof(GpsData)) {
-        jobject gpsClock = translate_gps_clock(env, &data->clock);
-        jobjectArray measurementArray = translate_gps_measurements(env, data);
-
-        jclass gpsMeasurementsEventClass = env->FindClass("android/location/GpsMeasurementsEvent");
-        jmethodID gpsMeasurementsEventCtor = env->GetMethodID(
-                gpsMeasurementsEventClass,
-                "<init>",
-                "(Landroid/location/GpsClock;[Landroid/location/GpsMeasurement;)V");
-
-        jobject gpsMeasurementsEvent = env->NewObject(
-                gpsMeasurementsEventClass,
-                gpsMeasurementsEventCtor,
-                gpsClock,
-                measurementArray);
-
-        env->CallVoidMethod(mCallbacksObj, method_reportMeasurementData, gpsMeasurementsEvent);
-        checkAndClearExceptionFromCallback(env, __FUNCTION__);
-
-        env->DeleteLocalRef(gpsClock);
-        env->DeleteLocalRef(measurementArray);
-        env->DeleteLocalRef(gpsMeasurementsEventClass);
-        env->DeleteLocalRef(gpsMeasurementsEvent);
-    } else {
+    if (data->size != sizeof(GpsData) && data->size != sizeof(GpsData_v1)) {
         ALOGE("Invalid GpsData size found in gps_measurement_callback, size=%zd", data->size);
+        return;
     }
+
+    jobject gpsClock;
+    jobjectArray measurementArray;
+    if (data->size == sizeof(GpsData)) {
+        gpsClock = translate_gps_clock(env, &data->clock, sizeof(GpsClock));
+        measurementArray = translate_gps_measurements<GpsData>(env, data);
+    } else {
+        gpsClock = translate_gps_clock(env, &data->clock, sizeof(GpsClock_v1));
+        measurementArray = translate_gps_measurements<GpsData_v1>(env, data);
+    }
+    jclass gpsMeasurementsEventClass = env->FindClass("android/location/GpsMeasurementsEvent");
+    jmethodID gpsMeasurementsEventCtor = env->GetMethodID(
+        gpsMeasurementsEventClass,
+        "<init>",
+        "(Landroid/location/GpsClock;[Landroid/location/GpsMeasurement;)V");
+
+    jobject gpsMeasurementsEvent = env->NewObject(
+        gpsMeasurementsEventClass,
+        gpsMeasurementsEventCtor,
+        gpsClock,
+        measurementArray);
+
+    env->CallVoidMethod(mCallbacksObj, method_reportMeasurementData, gpsMeasurementsEvent);
+    checkAndClearExceptionFromCallback(env, __FUNCTION__);
+
+    env->DeleteLocalRef(gpsClock);
+    env->DeleteLocalRef(measurementArray);
+    env->DeleteLocalRef(gpsMeasurementsEventClass);
+    env->DeleteLocalRef(gpsMeasurementsEvent);
 }
 
 GpsMeasurementCallbacks sGpsMeasurementCallbacks = {
@@ -1272,7 +1386,7 @@
     measurement_callback,
 };
 
-static jboolean android_location_GpsLocationProvider_is_measurement_supported(
+static jboolean android_location_GnssLocationProvider_is_measurement_supported(
         JNIEnv* env,
         jclass clazz) {
     if (sGpsMeasurementInterface != NULL) {
@@ -1281,7 +1395,7 @@
     return JNI_FALSE;
 }
 
-static jboolean android_location_GpsLocationProvider_start_measurement_collection(
+static jboolean android_location_GnssLocationProvider_start_measurement_collection(
         JNIEnv* env,
         jobject obj) {
     if (sGpsMeasurementInterface == NULL) {
@@ -1298,7 +1412,7 @@
     return JNI_TRUE;
 }
 
-static jboolean android_location_GpsLocationProvider_stop_measurement_collection(
+static jboolean android_location_GnssLocationProvider_stop_measurement_collection(
         JNIEnv* env,
         jobject obj) {
     if (sGpsMeasurementInterface == NULL) {
@@ -1382,7 +1496,7 @@
     navigation_message_callback,
 };
 
-static jboolean android_location_GpsLocationProvider_is_navigation_message_supported(
+static jboolean android_location_GnssLocationProvider_is_navigation_message_supported(
         JNIEnv* env,
         jclass clazz) {
     if(sGpsNavigationMessageInterface != NULL) {
@@ -1391,7 +1505,7 @@
     return JNI_FALSE;
 }
 
-static jboolean android_location_GpsLocationProvider_start_navigation_message_collection(
+static jboolean android_location_GnssLocationProvider_start_navigation_message_collection(
         JNIEnv* env,
         jobject obj) {
     if (sGpsNavigationMessageInterface == NULL) {
@@ -1408,7 +1522,7 @@
     return JNI_TRUE;
 }
 
-static jboolean android_location_GpsLocationProvider_stop_navigation_message_collection(
+static jboolean android_location_GnssLocationProvider_stop_navigation_message_collection(
         JNIEnv* env,
         jobject obj) {
     if (sGpsNavigationMessageInterface == NULL) {
@@ -1420,7 +1534,7 @@
     return JNI_TRUE;
 }
 
-static void android_location_GpsLocationProvider_configuration_update(JNIEnv* env, jobject obj,
+static void android_location_GnssLocationProvider_configuration_update(JNIEnv* env, jobject obj,
         jstring config_content)
 {
     if (!sGnssConfigurationInterface) {
@@ -1436,105 +1550,105 @@
 
 static const JNINativeMethod sMethods[] = {
      /* name, signature, funcPtr */
-    {"class_init_native", "()V", (void *)android_location_GpsLocationProvider_class_init_native},
-    {"native_is_supported", "()Z", (void*)android_location_GpsLocationProvider_is_supported},
+    {"class_init_native", "()V", (void *)android_location_GnssLocationProvider_class_init_native},
+    {"native_is_supported", "()Z", (void*)android_location_GnssLocationProvider_is_supported},
     {"native_is_agps_ril_supported", "()Z",
-            (void*)android_location_GpsLocationProvider_is_agps_ril_supported},
+            (void*)android_location_GnssLocationProvider_is_agps_ril_supported},
     {"native_is_gnss_configuration_supported", "()Z",
             (void*)android_location_gpsLocationProvider_is_gnss_configuration_supported},
-    {"native_init", "()Z", (void*)android_location_GpsLocationProvider_init},
-    {"native_cleanup", "()V", (void*)android_location_GpsLocationProvider_cleanup},
+    {"native_init", "()Z", (void*)android_location_GnssLocationProvider_init},
+    {"native_cleanup", "()V", (void*)android_location_GnssLocationProvider_cleanup},
     {"native_set_position_mode",
             "(IIIII)Z",
-            (void*)android_location_GpsLocationProvider_set_position_mode},
-    {"native_start", "()Z", (void*)android_location_GpsLocationProvider_start},
-    {"native_stop", "()Z", (void*)android_location_GpsLocationProvider_stop},
+            (void*)android_location_GnssLocationProvider_set_position_mode},
+    {"native_start", "()Z", (void*)android_location_GnssLocationProvider_start},
+    {"native_stop", "()Z", (void*)android_location_GnssLocationProvider_stop},
     {"native_delete_aiding_data",
             "(I)V",
-            (void*)android_location_GpsLocationProvider_delete_aiding_data},
+            (void*)android_location_GnssLocationProvider_delete_aiding_data},
     {"native_read_sv_status",
             "([I[F[F[F[I)I",
-            (void*)android_location_GpsLocationProvider_read_sv_status},
-    {"native_read_nmea", "([BI)I", (void*)android_location_GpsLocationProvider_read_nmea},
-    {"native_inject_time", "(JJI)V", (void*)android_location_GpsLocationProvider_inject_time},
+            (void*)android_location_GnssLocationProvider_read_sv_status},
+    {"native_read_nmea", "([BI)I", (void*)android_location_GnssLocationProvider_read_nmea},
+    {"native_inject_time", "(JJI)V", (void*)android_location_GnssLocationProvider_inject_time},
     {"native_inject_location",
             "(DDF)V",
-            (void*)android_location_GpsLocationProvider_inject_location},
-    {"native_supports_xtra", "()Z", (void*)android_location_GpsLocationProvider_supports_xtra},
+            (void*)android_location_GnssLocationProvider_inject_location},
+    {"native_supports_xtra", "()Z", (void*)android_location_GnssLocationProvider_supports_xtra},
     {"native_inject_xtra_data",
             "([BI)V",
-            (void*)android_location_GpsLocationProvider_inject_xtra_data},
+            (void*)android_location_GnssLocationProvider_inject_xtra_data},
     {"native_agps_data_conn_open",
             "(Ljava/lang/String;I)V",
-            (void*)android_location_GpsLocationProvider_agps_data_conn_open},
+            (void*)android_location_GnssLocationProvider_agps_data_conn_open},
     {"native_agps_data_conn_closed",
             "()V",
-            (void*)android_location_GpsLocationProvider_agps_data_conn_closed},
+            (void*)android_location_GnssLocationProvider_agps_data_conn_closed},
     {"native_agps_data_conn_failed",
             "()V",
-            (void*)android_location_GpsLocationProvider_agps_data_conn_failed},
+            (void*)android_location_GnssLocationProvider_agps_data_conn_failed},
     {"native_agps_set_id",
             "(ILjava/lang/String;)V",
-            (void*)android_location_GpsLocationProvider_agps_set_id},
+            (void*)android_location_GnssLocationProvider_agps_set_id},
     {"native_agps_set_ref_location_cellid",
             "(IIIII)V",
-            (void*)android_location_GpsLocationProvider_agps_set_reference_location_cellid},
+            (void*)android_location_GnssLocationProvider_agps_set_reference_location_cellid},
     {"native_set_agps_server",
             "(ILjava/lang/String;I)V",
-            (void*)android_location_GpsLocationProvider_set_agps_server},
+            (void*)android_location_GnssLocationProvider_set_agps_server},
     {"native_send_ni_response",
             "(II)V",
-            (void*)android_location_GpsLocationProvider_send_ni_response},
+            (void*)android_location_GnssLocationProvider_send_ni_response},
     {"native_agps_ni_message",
             "([BI)V",
-            (void *)android_location_GpsLocationProvider_agps_send_ni_message},
+            (void *)android_location_GnssLocationProvider_agps_send_ni_message},
     {"native_get_internal_state",
             "()Ljava/lang/String;",
-            (void*)android_location_GpsLocationProvider_get_internal_state},
+            (void*)android_location_GnssLocationProvider_get_internal_state},
     {"native_update_network_state",
             "(ZIZZLjava/lang/String;Ljava/lang/String;)V",
-            (void*)android_location_GpsLocationProvider_update_network_state },
+            (void*)android_location_GnssLocationProvider_update_network_state },
     {"native_is_geofence_supported",
             "()Z",
-            (void*) android_location_GpsLocationProvider_is_geofence_supported},
+            (void*) android_location_GnssLocationProvider_is_geofence_supported},
     {"native_add_geofence",
             "(IDDDIIII)Z",
-            (void *)android_location_GpsLocationProvider_add_geofence},
+            (void *)android_location_GnssLocationProvider_add_geofence},
     {"native_remove_geofence",
             "(I)Z",
-            (void *)android_location_GpsLocationProvider_remove_geofence},
-    {"native_pause_geofence", "(I)Z", (void *)android_location_GpsLocationProvider_pause_geofence},
+            (void *)android_location_GnssLocationProvider_remove_geofence},
+    {"native_pause_geofence", "(I)Z", (void *)android_location_GnssLocationProvider_pause_geofence},
     {"native_resume_geofence",
             "(II)Z",
-            (void *)android_location_GpsLocationProvider_resume_geofence},
+            (void *)android_location_GnssLocationProvider_resume_geofence},
     {"native_is_measurement_supported",
             "()Z",
-            (void*) android_location_GpsLocationProvider_is_measurement_supported},
+            (void*) android_location_GnssLocationProvider_is_measurement_supported},
     {"native_start_measurement_collection",
             "()Z",
-            (void*) android_location_GpsLocationProvider_start_measurement_collection},
+            (void*) android_location_GnssLocationProvider_start_measurement_collection},
     {"native_stop_measurement_collection",
             "()Z",
-            (void*) android_location_GpsLocationProvider_stop_measurement_collection},
+            (void*) android_location_GnssLocationProvider_stop_measurement_collection},
     {"native_is_navigation_message_supported",
             "()Z",
-            (void*) android_location_GpsLocationProvider_is_navigation_message_supported},
+            (void*) android_location_GnssLocationProvider_is_navigation_message_supported},
     {"native_start_navigation_message_collection",
             "()Z",
-            (void*) android_location_GpsLocationProvider_start_navigation_message_collection},
+            (void*) android_location_GnssLocationProvider_start_navigation_message_collection},
     {"native_stop_navigation_message_collection",
             "()Z",
-            (void*) android_location_GpsLocationProvider_stop_navigation_message_collection},
+            (void*) android_location_GnssLocationProvider_stop_navigation_message_collection},
     {"native_configuration_update",
             "(Ljava/lang/String;)V",
-            (void*)android_location_GpsLocationProvider_configuration_update},
+            (void*)android_location_GnssLocationProvider_configuration_update},
 };
 
-int register_android_server_location_GpsLocationProvider(JNIEnv* env)
+int register_android_server_location_GnssLocationProvider(JNIEnv* env)
 {
     return jniRegisterNativeMethods(
             env,
-            "com/android/server/location/GpsLocationProvider",
+            "com/android/server/location/GnssLocationProvider",
             sMethods,
             NELEM(sMethods));
 }
diff --git a/services/core/jni/onload.cpp b/services/core/jni/onload.cpp
index 1f3fde6..a7010bc 100644
--- a/services/core/jni/onload.cpp
+++ b/services/core/jni/onload.cpp
@@ -36,7 +36,7 @@
 int register_android_server_UsbMidiDevice(JNIEnv* env);
 int register_android_server_UsbHostManager(JNIEnv* env);
 int register_android_server_VibratorService(JNIEnv* env);
-int register_android_server_location_GpsLocationProvider(JNIEnv* env);
+int register_android_server_location_GnssLocationProvider(JNIEnv* env);
 int register_android_server_location_FlpHardwareProvider(JNIEnv* env);
 int register_android_server_connectivity_Vpn(JNIEnv* env);
 int register_android_server_hdmi_HdmiCecController(JNIEnv* env);
@@ -71,7 +71,7 @@
     register_android_server_UsbHostManager(env);
     register_android_server_VibratorService(env);
     register_android_server_SystemServer(env);
-    register_android_server_location_GpsLocationProvider(env);
+    register_android_server_location_GnssLocationProvider(env);
     register_android_server_location_FlpHardwareProvider(env);
     register_android_server_connectivity_Vpn(env);
     register_android_server_AssetAtlasService(env);
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 4bda7d9..1b0660d 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -49,6 +49,7 @@
 import android.app.admin.IDevicePolicyManager;
 import android.app.admin.SystemUpdatePolicy;
 import android.app.backup.IBackupManager;
+import android.auditing.SecurityLog;
 import android.content.BroadcastReceiver;
 import android.content.ComponentName;
 import android.content.ContentResolver;
@@ -194,7 +195,7 @@
 
     private static final long EXPIRATION_GRACE_PERIOD_MS = 5 * MS_PER_DAY; // 5 days, in ms
 
-    protected static final String ACTION_EXPIRED_PASSWORD_NOTIFICATION
+    private static final String ACTION_EXPIRED_PASSWORD_NOTIFICATION
             = "com.android.server.ACTION_EXPIRED_PASSWORD_NOTIFICATION";
 
     private static final int MONITORING_CERT_NOTIFICATION_ID = R.string.ssl_ca_cert_warning;
@@ -1702,8 +1703,8 @@
      * Set an alarm for an upcoming event - expiration warning, expiration, or post-expiration
      * reminders.  Clears alarm if no expirations are configured.
      */
-    private void setExpirationAlarmCheckLocked(Context context, int userHandle) {
-        final long expiration = getPasswordExpirationLocked(null, userHandle, /* parent */ false);
+    private void setExpirationAlarmCheckLocked(Context context, int userHandle, boolean parent) {
+        final long expiration = getPasswordExpirationLocked(null, userHandle, parent);
         final long now = System.currentTimeMillis();
         final long timeToExpire = expiration - now;
         final long alarmTime;
@@ -1725,11 +1726,12 @@
 
         long token = mInjector.binderClearCallingIdentity();
         try {
+            int affectedUserHandle = parent ? getProfileParentId(userHandle) : userHandle;
             AlarmManager am = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
             PendingIntent pi = PendingIntent.getBroadcastAsUser(context, REQUEST_EXPIRE_PASSWORD,
                     new Intent(ACTION_EXPIRED_PASSWORD_NOTIFICATION),
                     PendingIntent.FLAG_ONE_SHOT | PendingIntent.FLAG_UPDATE_CURRENT,
-                    UserHandle.of(userHandle));
+                    UserHandle.of(affectedUserHandle));
             am.cancel(pi);
             if (alarmTime != 0) {
                 am.set(AlarmManager.RTC, alarmTime, pi);
@@ -1983,7 +1985,7 @@
         final ResolveInfo ri = infos.get(0);
 
         if (!permission.BIND_DEVICE_ADMIN.equals(ri.activityInfo.permission)) {
-            final String message = "DeviceAdminReceiver " + adminName + " must be protected with"
+            final String message = "DeviceAdminReceiver " + adminName + " must be protected with "
                     + permission.BIND_DEVICE_ADMIN;
             Slog.w(LOG_TAG, message);
             if (throwForMissiongPermission &&
@@ -2488,7 +2490,6 @@
         synchronized (this) {
             final long now = System.currentTimeMillis();
 
-            // Return the strictest policy across all participating admins.
             List<ActiveAdmin> admins = getActiveAdminsForLockscreenPoliciesLocked(
                     userHandle, /* parent */ false);
             final int N = admins.size();
@@ -2502,7 +2503,7 @@
                             DeviceAdminReceiver.ACTION_PASSWORD_EXPIRING);
                 }
             }
-            setExpirationAlarmCheckLocked(mContext, userHandle);
+            setExpirationAlarmCheckLocked(mContext, userHandle, /* parent */ false);
         }
     }
 
@@ -2975,8 +2976,7 @@
             saveSettingsLocked(userHandle);
 
             // in case this is the first one, set the alarm on the appropriate user.
-            int affectedUserHandle = parent ? getProfileParentId(userHandle) : userHandle;
-            setExpirationAlarmCheckLocked(mContext, affectedUserHandle);
+            setExpirationAlarmCheckLocked(mContext, userHandle, parent);
         }
     }
 
@@ -4323,7 +4323,7 @@
                 policy.mFailedPasswordAttempts = 0;
                 saveSettingsLocked(userHandle);
                 updatePasswordExpirationsLocked(userHandle);
-                setExpirationAlarmCheckLocked(mContext, userHandle);
+                setExpirationAlarmCheckLocked(mContext, userHandle, /* parent */ false);
 
                 // Send a broadcast to each profile using this password as its primary unlock.
                 sendAdminCommandForLockscreenPoliciesLocked(
@@ -4401,6 +4401,10 @@
         } finally {
             mInjector.binderRestoreCallingIdentity(ident);
         }
+
+        if (SecurityLog.isLoggingEnabled()) {
+            SecurityLog.writeEvent(SecurityLog.TAG_KEYGUARD_DISMISS_AUTH_ATTEMPT, /*result*/ 0);
+        }
     }
 
     @Override
@@ -4427,6 +4431,28 @@
                 }
             }
         }
+
+        if (SecurityLog.isLoggingEnabled()) {
+            SecurityLog.writeEvent(SecurityLog.TAG_KEYGUARD_DISMISS_AUTH_ATTEMPT, /*result*/ 1);
+        }
+    }
+
+    @Override
+    public void reportKeyguardDismissed() {
+        mContext.enforceCallingOrSelfPermission(
+                android.Manifest.permission.BIND_DEVICE_ADMIN, null);
+        if (SecurityLog.isLoggingEnabled()) {
+            SecurityLog.writeEvent(SecurityLog.TAG_KEYGUARD_DISMISSED);
+        }
+    }
+
+    @Override
+    public void reportKeyguardSecured() {
+        mContext.enforceCallingOrSelfPermission(
+                android.Manifest.permission.BIND_DEVICE_ADMIN, null);
+        if (SecurityLog.isLoggingEnabled()) {
+            SecurityLog.writeEvent(SecurityLog.TAG_KEYGUARD_SECURED);
+        }
     }
 
     @Override
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 361c251..254a37a 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -68,6 +68,7 @@
 import com.android.server.input.InputManagerService;
 import com.android.server.job.JobSchedulerService;
 import com.android.server.lights.LightsService;
+import com.android.internal.widget.ILockSettings;
 import com.android.server.media.MediaRouterService;
 import com.android.server.media.MediaSessionService;
 import com.android.server.media.MediaResourceMonitorService;
@@ -143,6 +144,8 @@
             "com.android.server.ethernet.EthernetService";
     private static final String JOB_SCHEDULER_SERVICE_CLASS =
             "com.android.server.job.JobSchedulerService";
+    private static final String LOCK_SETTINGS_SERVICE_CLASS =
+            "com.android.server.LockSettingsService$Lifecycle";
     private static final String MOUNT_SERVICE_CLASS =
             "com.android.server.MountService$Lifecycle";
     private static final String SEARCH_MANAGER_SERVICE_CLASS =
@@ -607,7 +610,7 @@
         LocationManagerService location = null;
         CountryDetectorService countryDetector = null;
         TextServicesManagerService tsms = null;
-        LockSettingsService lockSettings = null;
+        ILockSettings lockSettings = null;
         AssetAtlasService atlas = null;
         MediaRouterService mediaRouter = null;
 
@@ -680,8 +683,9 @@
             if (!disableNonCoreServices) {
                 traceBeginAndSlog("StartLockSettingsService");
                 try {
-                    lockSettings = new LockSettingsService(context);
-                    ServiceManager.addService("lock_settings", lockSettings);
+                    mSystemServiceManager.startService(LOCK_SETTINGS_SERVICE_CLASS);
+                    lockSettings = ILockSettings.Stub.asInterface(
+                            ServiceManager.getService("lock_settings"));
                 } catch (Throwable e) {
                     reportWtf("starting LockSettingsService service", e);
                 }
@@ -979,6 +983,7 @@
                     Slog.i(TAG, "Gesture Launcher Service");
                     mSystemServiceManager.startService(GestureLauncherService.class);
                 }
+                mSystemServiceManager.startService(SensorNotificationService.class);
             }
 
             traceBeginAndSlog("StartDiskStatsService");
diff --git a/services/tests/servicestests/src/com/android/server/ConnectivityServiceTest.java b/services/tests/servicestests/src/com/android/server/ConnectivityServiceTest.java
index 27d5207..5874429 100644
--- a/services/tests/servicestests/src/com/android/server/ConnectivityServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/ConnectivityServiceTest.java
@@ -52,10 +52,12 @@
 import android.os.ConditionVariable;
 import android.os.Handler;
 import android.os.HandlerThread;
+import android.os.IBinder;
 import android.os.INetworkManagementService;
 import android.os.Looper;
 import android.os.Message;
 import android.os.MessageQueue;
+import android.os.Messenger;
 import android.os.MessageQueue.IdleHandler;
 import android.test.AndroidTestCase;
 import android.test.suitebuilder.annotation.LargeTest;
@@ -1298,6 +1300,36 @@
         validatedCallback.expectCallback(CallbackState.LOST);
     }
 
+    @SmallTest
+    public void testInvalidNetworkSpecifier() {
+        boolean execptionCalled = true;
+
+        try {
+            NetworkRequest.Builder builder = new NetworkRequest.Builder();
+            builder.setNetworkSpecifier(MATCH_ALL_REQUESTS_NETWORK_SPECIFIER);
+            execptionCalled = false;
+        } catch (IllegalArgumentException e) {
+            // do nothing - should get here
+        }
+
+        assertTrue("NetworkReqeuest builder with MATCH_ALL_REQUESTS_NETWORK_SPECIFIER",
+                execptionCalled);
+
+        try {
+            NetworkCapabilities networkCapabilities = new NetworkCapabilities();
+            networkCapabilities.addTransportType(TRANSPORT_WIFI)
+                    .setNetworkSpecifier(NetworkCapabilities.MATCH_ALL_REQUESTS_NETWORK_SPECIFIER);
+            mService.requestNetwork(networkCapabilities, null, 0, null,
+                    ConnectivityManager.TYPE_WIFI);
+            execptionCalled = false;
+        } catch (IllegalArgumentException e) {
+            // do nothing - should get here
+        }
+
+        assertTrue("ConnectivityService requestNetwork with MATCH_ALL_REQUESTS_NETWORK_SPECIFIER",
+                execptionCalled);
+    }
+
     private static class TestKeepaliveCallback extends PacketKeepaliveCallback {
 
         public static enum CallbackType { ON_STARTED, ON_STOPPED, ON_ERROR };
diff --git a/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java b/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java
index b4bca3e..6c2fcd5 100644
--- a/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java
@@ -26,7 +26,10 @@
 import android.os.UserManager;
 import android.test.AndroidTestCase;
 
+import com.android.internal.util.ArrayUtils;
+
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.List;
 
 /** Test {@link UserManager} functionality. */
@@ -196,6 +199,17 @@
         assertEquals(user2.id, mUserManager.getUserHandle(serialNumber2));
     }
 
+    public void testGetSerialNumbersOfUsers() {
+        UserInfo user1 = createUser("User 1", 0);
+        UserInfo user2 = createUser("User 2", 0);
+        long[] serialNumbersOfUsers = mUserManager.getSerialNumbersOfUsers(false);
+        String errMsg = "Array " + Arrays.toString(serialNumbersOfUsers) + " should contain ";
+        assertTrue(errMsg + user1.serialNumber,
+                ArrayUtils.contains(serialNumbersOfUsers, user1.serialNumber));
+        assertTrue(errMsg + user2.serialNumber,
+                ArrayUtils.contains(serialNumbersOfUsers, user2.serialNumber));
+    }
+
     public void testMaxUsers() {
         int N = UserManager.getMaxSupportedUsers();
         int count = mUserManager.getUsers().size();
diff --git a/services/usage/java/com/android/server/usage/UsageStatsService.java b/services/usage/java/com/android/server/usage/UsageStatsService.java
index 81031da..bfdac7e 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsService.java
@@ -123,6 +123,7 @@
     static final int MSG_PAROLE_END_TIMEOUT = 7;
     static final int MSG_REPORT_CONTENT_PROVIDER_USAGE = 8;
     static final int MSG_PAROLE_STATE_CHANGED = 9;
+    static final int MSG_ONE_TIME_CHECK_IDLE_STATES = 10;
 
     private final Object mLock = new Object();
     Handler mHandler;
@@ -145,7 +146,7 @@
     private long mLastAppIdleParoledTime;
 
     long mScreenOnTime;
-    long mScreenOnSystemTimeSnapshot;
+    long mLastScreenOnEventRealtime;
 
     @GuardedBy("mLock")
     private AppIdleHistory mAppIdleHistory = new AppIdleHistory();
@@ -188,6 +189,8 @@
 
         synchronized (mLock) {
             cleanUpRemovedUsersLocked();
+            mLastScreenOnEventRealtime = SystemClock.elapsedRealtime();
+            mScreenOnTime = readScreenOnTimeLocked();
         }
 
         mRealTimeSnapshot = SystemClock.elapsedRealtime();
@@ -214,10 +217,6 @@
                     Context.DISPLAY_SERVICE);
             mPowerManager = getContext().getSystemService(PowerManager.class);
 
-            mScreenOnSystemTimeSnapshot = System.currentTimeMillis();
-            synchronized (mLock) {
-                mScreenOnTime = readScreenOnTimeLocked();
-            }
             mDisplayManager.registerDisplayListener(mDisplayListener, mHandler);
             synchronized (mLock) {
                 updateDisplayLocked();
@@ -281,6 +280,11 @@
     }
 
     @Override
+    public void onStatsReloaded() {
+        postOneTimeCheckIdleStates();
+    }
+
+    @Override
     public long getAppIdleRollingWindowDurationMillis() {
         return mAppIdleWallclockThresholdMillis * 2;
     }
@@ -359,6 +363,14 @@
         mHandler.sendMessage(mHandler.obtainMessage(MSG_CHECK_IDLE_STATES, userId, 0));
     }
 
+    /**
+     * We send a different message to check idle states once, otherwise we would end up
+     * scheduling a series of repeating checkIdleStates each time we fired off one.
+     */
+    void postOneTimeCheckIdleStates() {
+        mHandler.sendEmptyMessage(MSG_ONE_TIME_CHECK_IDLE_STATES);
+    }
+
     /** Check all running users' or specified user's apps to see if they enter an idle state. */
     void checkIdleStates(int checkUserId) {
         if (!mAppIdleEnabled) {
@@ -385,7 +397,7 @@
                             userId);
             synchronized (mLock) {
                 final long timeNow = checkAndGetTimeLocked();
-                final long screenOnTime = getScreenOnTimeLocked(timeNow);
+                final long screenOnTime = getScreenOnTimeLocked();
                 UserUsageStatsService service = getUserDataAndInitializeIfNeededLocked(userId,
                         timeNow);
                 final int packageCount = packages.size();
@@ -401,8 +413,6 @@
                 }
             }
         }
-        mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_CHECK_IDLE_STATES, checkUserId, 0),
-                mCheckIdleIntervalMillis);
     }
 
     /** Check if it's been a while since last parole and let idle apps do some work */
@@ -443,21 +453,21 @@
         if (screenOn == mScreenOn) return;
 
         mScreenOn = screenOn;
-        long now = System.currentTimeMillis();
+        long now = SystemClock.elapsedRealtime();
         if (mScreenOn) {
-            mScreenOnSystemTimeSnapshot = now;
+            mLastScreenOnEventRealtime = now;
         } else {
-            mScreenOnTime += now - mScreenOnSystemTimeSnapshot;
+            mScreenOnTime += now - mLastScreenOnEventRealtime;
             writeScreenOnTimeLocked(mScreenOnTime);
         }
     }
 
-    private long getScreenOnTimeLocked(long now) {
+    long getScreenOnTimeLocked() {
+        long screenOnTime = mScreenOnTime;
         if (mScreenOn) {
-            return now - mScreenOnSystemTimeSnapshot + mScreenOnTime;
-        } else {
-            return mScreenOnTime;
+            screenOnTime += SystemClock.elapsedRealtime() - mLastScreenOnEventRealtime;
         }
+        return screenOnTime;
     }
 
     private File getScreenOnTimeFile() {
@@ -527,7 +537,7 @@
         if (service == null) {
             service = new UserUsageStatsService(getContext(), userId,
                     new File(mUsageStatsDir, Integer.toString(userId)), this);
-            service.init(currentTimeMillis, getScreenOnTimeLocked(currentTimeMillis));
+            service.init(currentTimeMillis, getScreenOnTimeLocked());
             mUserState.put(userId, service);
         }
         return service;
@@ -540,25 +550,18 @@
         final long actualSystemTime = System.currentTimeMillis();
         final long actualRealtime = SystemClock.elapsedRealtime();
         final long expectedSystemTime = (actualRealtime - mRealTimeSnapshot) + mSystemTimeSnapshot;
-        boolean resetBeginIdleTime = false;
-        if (Math.abs(actualSystemTime - expectedSystemTime) > TIME_CHANGE_THRESHOLD_MILLIS) {
+        final long diffSystemTime = actualSystemTime - expectedSystemTime;
+        if (Math.abs(diffSystemTime) > TIME_CHANGE_THRESHOLD_MILLIS) {
             // The time has changed.
-
-            // Check if it's severe enough a change to reset screenOnTime
-            if (Math.abs(actualSystemTime - expectedSystemTime) > mAppIdleDurationMillis) {
-                mScreenOnSystemTimeSnapshot = actualSystemTime;
-                mScreenOnTime = 0;
-                resetBeginIdleTime = true;
-            }
+            Slog.i(TAG, "Time changed in UsageStats by " + (diffSystemTime / 1000) + " seconds");
             final int userCount = mUserState.size();
             for (int i = 0; i < userCount; i++) {
                 final UserUsageStatsService service = mUserState.valueAt(i);
-                service.onTimeChanged(expectedSystemTime, actualSystemTime, mScreenOnTime,
-                        resetBeginIdleTime);
+                service.onTimeChanged(expectedSystemTime, actualSystemTime, getScreenOnTimeLocked(),
+                        false);
             }
             mRealTimeSnapshot = actualRealtime;
             mSystemTimeSnapshot = actualSystemTime;
-            postCheckIdleStates(UserHandle.USER_ALL);
         }
         return actualSystemTime;
     }
@@ -587,7 +590,7 @@
     void reportEvent(UsageEvents.Event event, int userId) {
         synchronized (mLock) {
             final long timeNow = checkAndGetTimeLocked();
-            final long screenOnTime = getScreenOnTimeLocked(timeNow);
+            final long screenOnTime = getScreenOnTimeLocked();
             convertToSystemTimeLocked(event);
 
             final UserUsageStatsService service =
@@ -603,7 +606,6 @@
                     || event.mEventType == Event.SYSTEM_INTERACTION
                     || event.mEventType == Event.USER_INTERACTION)) {
                 if (previouslyIdle) {
-                    //Slog.d(TAG, "Informing listeners of out-of-idle " + event.mPackage);
                     mHandler.sendMessage(mHandler.obtainMessage(MSG_INFORM_LISTENERS, userId,
                             /* idle = */ 0, event.mPackage));
                     notifyBatteryStats(event.mPackage, userId, false);
@@ -643,7 +645,7 @@
     void forceIdleState(String packageName, int userId, boolean idle) {
         synchronized (mLock) {
             final long timeNow = checkAndGetTimeLocked();
-            final long screenOnTime = getScreenOnTimeLocked(timeNow);
+            final long screenOnTime = getScreenOnTimeLocked();
             final long deviceUsageTime = screenOnTime - (idle ? mAppIdleDurationMillis : 0) - 5000;
 
             final UserUsageStatsService service =
@@ -657,7 +659,6 @@
                     timeNow - (idle ? mAppIdleWallclockThresholdMillis : 0) - 5000);
             // Inform listeners if necessary
             if (previouslyIdle != idle) {
-                // Slog.d(TAG, "Informing listeners of out-of-idle " + packageName);
                 mHandler.sendMessage(mHandler.obtainMessage(MSG_INFORM_LISTENERS, userId,
                         /* idle = */ idle ? 1 : 0, packageName));
                 if (!idle) {
@@ -796,7 +797,7 @@
                 timeNow = checkAndGetTimeLocked();
             }
             userService = getUserDataAndInitializeIfNeededLocked(userId, timeNow);
-            screenOnTime = getScreenOnTimeLocked(timeNow);
+            screenOnTime = getScreenOnTimeLocked();
         }
         return isAppIdleFiltered(packageName, UserHandle.getAppId(uidForAppId), userId,
                 userService, timeNow, screenOnTime);
@@ -865,7 +866,7 @@
         synchronized (mLock) {
             timeNow = checkAndGetTimeLocked();
             userService = getUserDataAndInitializeIfNeededLocked(userId, timeNow);
-            screenOnTime = getScreenOnTimeLocked(timeNow);
+            screenOnTime = getScreenOnTimeLocked();
         }
 
         List<ApplicationInfo> apps;
@@ -987,7 +988,7 @@
      */
     void dump(String[] args, PrintWriter pw) {
         synchronized (mLock) {
-            final long screenOnTime = getScreenOnTimeLocked(checkAndGetTimeLocked());
+            final long screenOnTime = getScreenOnTimeLocked();
             IndentingPrintWriter idpw = new IndentingPrintWriter(pw, "  ");
             ArraySet<String> argSet = new ArraySet<>();
             argSet.addAll(Arrays.asList(args));
@@ -1008,7 +1009,11 @@
                 }
                 idpw.decreaseIndent();
             }
-            pw.println("Screen On Timebase:" + mScreenOnTime);
+            pw.print("Screen On Timebase: ");
+            pw.print(screenOnTime);
+            pw.print(" (");
+            TimeUtils.formatDuration(screenOnTime, pw);
+            pw.println(")");
 
             pw.println();
             pw.println("Settings:");
@@ -1042,8 +1047,8 @@
             pw.println();
             pw.print("mScreenOnTime="); TimeUtils.formatDuration(mScreenOnTime, pw);
             pw.println();
-            pw.print("mScreenOnSystemTimeSnapshot=");
-            TimeUtils.formatDuration(mScreenOnSystemTimeSnapshot, pw);
+            pw.print("mLastScreenOnEventRealtime=");
+            TimeUtils.formatDuration(mLastScreenOnEventRealtime, pw);
             pw.println();
         }
     }
@@ -1078,6 +1083,14 @@
 
                 case MSG_CHECK_IDLE_STATES:
                     checkIdleStates(msg.arg1);
+                    mHandler.sendMessageDelayed(mHandler.obtainMessage(
+                            MSG_CHECK_IDLE_STATES, msg.arg1, 0),
+                            mCheckIdleIntervalMillis);
+                    break;
+
+                case MSG_ONE_TIME_CHECK_IDLE_STATES:
+                    mHandler.removeMessages(MSG_ONE_TIME_CHECK_IDLE_STATES);
+                    checkIdleStates(UserHandle.USER_ALL);
                     break;
 
                 case MSG_CHECK_PAROLE_TIMEOUT:
@@ -1138,7 +1151,7 @@
         @Override
         public void onChange(boolean selfChange) {
             updateSettings();
-            postCheckIdleStates(UserHandle.USER_ALL);
+            postOneTimeCheckIdleStates();
         }
 
         void updateSettings() {
diff --git a/services/usage/java/com/android/server/usage/UserUsageStatsService.java b/services/usage/java/com/android/server/usage/UserUsageStatsService.java
index aa2cf77..f2045d3 100644
--- a/services/usage/java/com/android/server/usage/UserUsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UserUsageStatsService.java
@@ -73,6 +73,7 @@
 
     interface StatsUpdatedListener {
         void onStatsUpdated();
+        void onStatsReloaded();
         long getAppIdleRollingWindowDurationMillis();
     }
 
@@ -523,6 +524,9 @@
 
         mStatsChanged = false;
         updateRolloverDeadline();
+
+        // Tell the listener that the stats reloaded, which may have changed idle states.
+        mListener.onStatsReloaded();
     }
 
     private void updateRolloverDeadline() {
diff --git a/telecomm/java/android/telecom/CallScreeningService.java b/telecomm/java/android/telecom/CallScreeningService.java
index 1b6e162..f62b170 100644
--- a/telecomm/java/android/telecom/CallScreeningService.java
+++ b/telecomm/java/android/telecom/CallScreeningService.java
@@ -89,7 +89,7 @@
     /*
      * Information about how to respond to an incoming call.
      */
-    public class CallResponse {
+    public static class CallResponse {
         private final boolean mShouldDisallowCall;
         private final boolean mShouldRejectCall;
         private final boolean mShouldSkipCallLog;
@@ -140,7 +140,7 @@
             return mShouldSkipNotification;
         }
 
-        public class Builder {
+        public static class Builder {
             private boolean mShouldDisallowCall;
             private boolean mShouldRejectCall;
             private boolean mShouldSkipCallLog;
diff --git a/telephony/java/android/telephony/ModemActivityInfo.java b/telephony/java/android/telephony/ModemActivityInfo.java
index ea96e7c..84883d8 100644
--- a/telephony/java/android/telephony/ModemActivityInfo.java
+++ b/telephony/java/android/telephony/ModemActivityInfo.java
@@ -48,7 +48,9 @@
         mTimestamp = timestamp;
         mSleepTimeMs = sleepTimeMs;
         mIdleTimeMs = idleTimeMs;
-        System.arraycopy(txTimeMs, 0, mTxTimeMs, 0, Math.min(txTimeMs.length, TX_POWER_LEVELS));
+        if (txTimeMs != null) {
+            System.arraycopy(txTimeMs, 0, mTxTimeMs, 0, Math.min(txTimeMs.length, TX_POWER_LEVELS));
+        }
         mRxTimeMs = rxTimeMs;
         mEnergyUsed = energyUsed;
     }
@@ -58,6 +60,7 @@
         return "ModemActivityInfo{"
             + " mTimestamp=" + mTimestamp
             + " mSleepTimeMs=" + mSleepTimeMs
+            + " mIdleTimeMs=" + mIdleTimeMs
             + " mTxTimeMs[]=" + Arrays.toString(mTxTimeMs)
             + " mRxTimeMs=" + mRxTimeMs
             + " mEnergyUsed=" + mEnergyUsed
@@ -153,7 +156,7 @@
         for (int i = 0; i < TX_POWER_LEVELS; i++) {
             totalTxTimeMs += txTime[i];
         }
-        return ((getIdleTimeMillis() != 0) || (totalTxTimeMs != 0)
-                || (getSleepTimeMillis() != 0) || (getIdleTimeMillis() != 0));
+        return ((getIdleTimeMillis() >= 0) && (totalTxTimeMs >= 0)
+                && (getSleepTimeMillis() >= 0) && (getIdleTimeMillis() >= 0));
     }
 }
diff --git a/telephony/java/android/telephony/PhoneNumberUtils.java b/telephony/java/android/telephony/PhoneNumberUtils.java
index b089387..962a600 100644
--- a/telephony/java/android/telephony/PhoneNumberUtils.java
+++ b/telephony/java/android/telephony/PhoneNumberUtils.java
@@ -1857,9 +1857,6 @@
         // to the list.
         number = extractNetworkPortionAlt(number);
 
-        Rlog.d(LOG_TAG, "subId:" + subId + ", defaultCountryIso:" +
-                ((defaultCountryIso == null) ? "NULL" : defaultCountryIso));
-
         String emergencyNumbers = "";
         int slotId = SubscriptionManager.getSlotId(subId);
 
@@ -1869,7 +1866,8 @@
 
         emergencyNumbers = SystemProperties.get(ecclist, "");
 
-        Rlog.d(LOG_TAG, "slotId:" + slotId + ", emergencyNumbers: " +  emergencyNumbers);
+        Rlog.d(LOG_TAG, "slotId:" + slotId + " subId:" + subId + " country:"
+                + defaultCountryIso + " emergencyNumbers: " +  emergencyNumbers);
 
         if (TextUtils.isEmpty(emergencyNumbers)) {
             // then read-only ecclist property since old RIL only uses this
diff --git a/telephony/java/android/telephony/ServiceState.java b/telephony/java/android/telephony/ServiceState.java
index c680999..ad007c6 100644
--- a/telephony/java/android/telephony/ServiceState.java
+++ b/telephony/java/android/telephony/ServiceState.java
@@ -37,6 +37,7 @@
 
     static final String LOG_TAG = "PHONE";
     static final boolean DBG = true;
+    static final boolean VDBG = false;  // STOPSHIP if true
 
     /**
      * Normal operation condition, the phone is registered
@@ -829,7 +830,7 @@
     /** @hide */
     public void setDataRegState(int state) {
         mDataRegState = state;
-        if (DBG) Rlog.d(LOG_TAG, "[ServiceState] setDataRegState=" + mDataRegState);
+        if (VDBG) Rlog.d(LOG_TAG, "[ServiceState] setDataRegState=" + mDataRegState);
     }
 
     public void setRoaming(boolean roaming) {
@@ -1017,7 +1018,8 @@
     /** @hide */
     public void setRilDataRadioTechnology(int rt) {
         this.mRilDataRadioTechnology = rt;
-        if (DBG) Rlog.d(LOG_TAG, "[ServiceState] setDataRadioTechnology=" + mRilDataRadioTechnology);
+        if (VDBG) Rlog.d(LOG_TAG, "[ServiceState] setRilDataRadioTechnology=" +
+                mRilDataRadioTechnology);
     }
 
     /** @hide */
diff --git a/telephony/java/com/android/internal/telephony/ITelephonyDebug.aidl b/telephony/java/com/android/internal/telephony/ITelephonyDebug.aidl
index 5dffa28..069fcbf 100644
--- a/telephony/java/com/android/internal/telephony/ITelephonyDebug.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephonyDebug.aidl
@@ -16,8 +16,9 @@
 
 package com.android.internal.telephony;
 
-import android.os.Bundle;
+import com.android.internal.telephony.ITelephonyDebugSubscriber;
 
+import android.os.Bundle;
 
 /**
  * Interface used to interact with the Telephony debug service.
@@ -36,4 +37,7 @@
      * @param data optional
      */
     void writeEvent(long timestamp, int phoneId, int tag, int param1, int param2, in Bundle data);
+
+    void subscribe(in ITelephonyDebugSubscriber subscriber);
+    void unsubscribe(in ITelephonyDebugSubscriber subscriber);
 }
diff --git a/telephony/java/com/android/internal/telephony/ITelephonyDebugSubscriber.aidl b/telephony/java/com/android/internal/telephony/ITelephonyDebugSubscriber.aidl
new file mode 100644
index 0000000..64eb0f1
--- /dev/null
+++ b/telephony/java/com/android/internal/telephony/ITelephonyDebugSubscriber.aidl
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony;
+
+import com.android.internal.telephony.TelephonyEvent;
+
+import android.os.Bundle;
+
+/**
+ * Interface used to subscribe for events from Telephony debug service.
+ *
+ * {@hide}
+ */
+oneway interface ITelephonyDebugSubscriber {
+
+    /**
+     * Called when Telephony debug service has events.
+     */
+    void onEvents(in TelephonyEvent[] events);
+}
diff --git a/telephony/java/com/android/internal/telephony/TelephonyEvent.aidl b/telephony/java/com/android/internal/telephony/TelephonyEvent.aidl
new file mode 100644
index 0000000..1e74b31
--- /dev/null
+++ b/telephony/java/com/android/internal/telephony/TelephonyEvent.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony;
+
+parcelable TelephonyEvent;
diff --git a/telephony/java/com/android/internal/telephony/TelephonyEvent.java b/telephony/java/com/android/internal/telephony/TelephonyEvent.java
new file mode 100644
index 0000000..26d466d
--- /dev/null
+++ b/telephony/java/com/android/internal/telephony/TelephonyEvent.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony;
+
+import android.os.Bundle;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ *  A parcelable used in ITelephonyDebugSubscriber.aidl
+ */
+public class TelephonyEvent implements Parcelable {
+
+    final public long timestamp;
+    final public int phoneId;
+    final public int tag;
+    final public int param1;
+    final public int param2;
+    final public Bundle data;
+
+    public TelephonyEvent(long timestamp, int phoneId, int tag,
+            int param1, int param2, Bundle data) {
+        this.timestamp = timestamp;
+        this.phoneId = phoneId;
+        this.tag = tag;
+        this.param1 = param1;
+        this.param2 = param2;
+        this.data = data;
+    }
+
+    /** Implement the Parcelable interface */
+    public static final Parcelable.Creator<TelephonyEvent> CREATOR
+            = new Parcelable.Creator<TelephonyEvent> (){
+        public TelephonyEvent createFromParcel(Parcel source) {
+            final long timestamp = source.readLong();
+            final int phoneId = source.readInt();
+            final int tag = source.readInt();
+            final int param1 = source.readInt();
+            final int param2 = source.readInt();
+            final Bundle data = source.readBundle();
+            return new TelephonyEvent(timestamp, phoneId, tag, param1, param2, data);
+        }
+
+        public TelephonyEvent[] newArray(int size) {
+            return new TelephonyEvent[size];
+        }
+    };
+
+    /** Implement the Parcelable interface */
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    /** Implement the Parcelable interface */
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeLong(timestamp);
+        dest.writeInt(phoneId);
+        dest.writeInt(tag);
+        dest.writeInt(param1);
+        dest.writeInt(param2);
+        dest.writeBundle(data);
+    }
+
+    public String toString() {
+        return String.format("%d,%d,%d,%d,%d,%s",
+                timestamp, phoneId, tag, param1, param2, data);
+    }
+}
diff --git a/test-runner/src/android/test/ActivityInstrumentationTestCase.java b/test-runner/src/android/test/ActivityInstrumentationTestCase.java
index a59ee35..aca1c16 100644
--- a/test-runner/src/android/test/ActivityInstrumentationTestCase.java
+++ b/test-runner/src/android/test/ActivityInstrumentationTestCase.java
@@ -23,15 +23,15 @@
  * be created using the system infrastructure (by calling InstrumentationTestCase.launchActivity())
  * and you will then be able to manipulate your Activity directly.  Most of the work is handled
  * automatically here by {@link #setUp} and {@link #tearDown}.
- * 
+ *
  * <p>If you prefer an isolated unit test, see {@link android.test.ActivityUnitTestCase}.
- * 
- * @deprecated new tests should be written using 
+ *
+ * @deprecated new tests should be written using
  * {@link android.test.ActivityInstrumentationTestCase2}, which provides more options for
  * configuring the Activity under test
  */
 @Deprecated
-public abstract class ActivityInstrumentationTestCase<T extends Activity> 
+public abstract class ActivityInstrumentationTestCase<T extends Activity>
         extends ActivityTestCase {
     String mPackage;
     Class<T> mActivityClass;
@@ -39,7 +39,7 @@
 
     /**
      * Creates an {@link ActivityInstrumentationTestCase} in non-touch mode.
-     * 
+     *
      * @param pkg ignored - no longer in use.
      * @param activityClass The activity to test. This must be a class in the instrumentation
      * targetPackage specified in the AndroidManifest.xml
@@ -56,7 +56,7 @@
      * targetPackage specified in the AndroidManifest.xml
      * @param initialTouchMode true = in touch mode
      */
-    public ActivityInstrumentationTestCase(String pkg, Class<T> activityClass, 
+    public ActivityInstrumentationTestCase(String pkg, Class<T> activityClass,
             boolean initialTouchMode) {
         mActivityClass = activityClass;
         mInitialTouchMode = initialTouchMode;
@@ -80,8 +80,8 @@
     protected void tearDown() throws Exception {
         getActivity().finish();
         setActivity(null);
-        
-        // Scrub out members - protects against memory leaks in the case where someone 
+
+        // Scrub out members - protects against memory leaks in the case where someone
         // creates a non-static inner class (thus referencing the test case) and gives it to
         // someone else to hold onto
         scrubClass(ActivityInstrumentationTestCase.class);
diff --git a/test-runner/src/android/test/ActivityInstrumentationTestCase2.java b/test-runner/src/android/test/ActivityInstrumentationTestCase2.java
index c4bcf31..0e61ce7 100644
--- a/test-runner/src/android/test/ActivityInstrumentationTestCase2.java
+++ b/test-runner/src/android/test/ActivityInstrumentationTestCase2.java
@@ -25,26 +25,26 @@
  * This class provides functional testing of a single activity.  The activity under test will
  * be created using the system infrastructure (by calling InstrumentationTestCase.launchActivity())
  * and you will then be able to manipulate your Activity directly.
- * 
+ *
  * <p>Other options supported by this test case include:
  * <ul>
  * <li>You can run any test method on the UI thread (see {@link android.test.UiThreadTest}).</li>
- * <li>You can inject custom Intents into your Activity (see 
+ * <li>You can inject custom Intents into your Activity (see
  * {@link #setActivityIntent(Intent)}).</li>
  * </ul>
- * 
+ *
  * <p>This class replaces {@link android.test.ActivityInstrumentationTestCase}, which is deprecated.
  * New tests should be written using this base class.
- * 
+ *
  * <p>If you prefer an isolated unit test, see {@link android.test.ActivityUnitTestCase}.
  *
- * <div class="special reference">
- * <h3>Developer Guides</h3>
- * <p>For more information about application testing, read the
- * <a href="{@docRoot}guide/topics/testing/index.html">Testing</a> developer guide.</p>
- * </div>
+ * @deprecated Use
+ * <a href="{@docRoot}reference/android/support/test/rule/ActivityTestRule.html">
+ * ActivityTestRule</a> instead. New tests should be written using the
+ * <a href="{@docRoot}tools/testing-support-library/index.html">Android Testing Support Library</a>.
  */
-public abstract class ActivityInstrumentationTestCase2<T extends Activity> 
+@Deprecated
+public abstract class ActivityInstrumentationTestCase2<T extends Activity>
         extends ActivityTestCase {
     Class<T> mActivityClass;
     boolean mInitialTouchMode = false;
@@ -78,18 +78,18 @@
      * Get the Activity under test, starting it if necessary.
      *
      * For each test method invocation, the Activity will not actually be created until the first
-     * time this method is called. 
-     * 
-     * <p>If you wish to provide custom setup values to your Activity, you may call 
-     * {@link #setActivityIntent(Intent)} and/or {@link #setActivityInitialTouchMode(boolean)} 
-     * before your first call to getActivity().  Calling them after your Activity has 
+     * time this method is called.
+     *
+     * <p>If you wish to provide custom setup values to your Activity, you may call
+     * {@link #setActivityIntent(Intent)} and/or {@link #setActivityInitialTouchMode(boolean)}
+     * before your first call to getActivity().  Calling them after your Activity has
      * started will have no effect.
      *
      * <p><b>NOTE:</b> Activities under test may not be started from within the UI thread.
      * If your test method is annotated with {@link android.test.UiThreadTest}, then your Activity
      * will be started automatically just before your test method is run.  You still call this
      * method in order to get the Activity under test.
-     * 
+     *
      * @return the Activity under test
      */
     @Override
@@ -113,10 +113,10 @@
     /**
      * Call this method before the first call to {@link #getActivity} to inject a customized Intent
      * into the Activity under test.
-     * 
+     *
      * <p>If you do not call this, the default intent will be provided.  If you call this after
      * your Activity has been started, it will have no effect.
-     * 
+     *
      * <p><b>NOTE:</b> Activities under test may not be started from within the UI thread.
      * If your test method is annotated with {@link android.test.UiThreadTest}, then you must call
      * {@link #setActivityIntent(Intent)} from {@link #setUp()}.
@@ -131,28 +131,28 @@
     public void setActivityIntent(Intent i) {
         mActivityIntent = i;
     }
-    
+
     /**
      * Call this method before the first call to {@link #getActivity} to set the initial touch
      * mode for the Activity under test.
-     * 
+     *
      * <p>If you do not call this, the touch mode will be false.  If you call this after
      * your Activity has been started, it will have no effect.
-     * 
+     *
      * <p><b>NOTE:</b> Activities under test may not be started from within the UI thread.
      * If your test method is annotated with {@link android.test.UiThreadTest}, then you must call
      * {@link #setActivityInitialTouchMode(boolean)} from {@link #setUp()}.
-     * 
+     *
      * @param initialTouchMode true if the Activity should be placed into "touch mode" when started
      */
     public void setActivityInitialTouchMode(boolean initialTouchMode) {
         mInitialTouchMode = initialTouchMode;
     }
-    
+
     @Override
     protected void setUp() throws Exception {
         super.setUp();
-        
+
         mInitialTouchMode = false;
         mActivityIntent = null;
     }
@@ -165,8 +165,8 @@
             a.finish();
             setActivity(null);
         }
-        
-        // Scrub out members - protects against memory leaks in the case where someone 
+
+        // Scrub out members - protects against memory leaks in the case where someone
         // creates a non-static inner class (thus referencing the test case) and gives it to
         // someone else to hold onto
         scrubClass(ActivityInstrumentationTestCase2.class);
diff --git a/test-runner/src/android/test/ActivityTestCase.java b/test-runner/src/android/test/ActivityTestCase.java
index c7b1d70..51dd3ef 100644
--- a/test-runner/src/android/test/ActivityTestCase.java
+++ b/test-runner/src/android/test/ActivityTestCase.java
@@ -25,7 +25,11 @@
  * This is common code used to support Activity test cases.  For more useful classes, please see
  * {@link android.test.ActivityUnitTestCase} and
  * {@link android.test.ActivityInstrumentationTestCase}.
+ *
+ * @deprecated New tests should be written using the
+ * <a href="{@docRoot}tools/testing-support-library/index.html">Android Testing Support Library</a>.
  */
+@Deprecated
 public abstract class ActivityTestCase extends InstrumentationTestCase {
 
     /**
diff --git a/test-runner/src/android/test/ActivityUnitTestCase.java b/test-runner/src/android/test/ActivityUnitTestCase.java
index 40cca90..a191445 100644
--- a/test-runner/src/android/test/ActivityUnitTestCase.java
+++ b/test-runner/src/android/test/ActivityUnitTestCase.java
@@ -32,14 +32,14 @@
 
 /**
  * This class provides isolated testing of a single activity.  The activity under test will
- * be created with minimal connection to the system infrastructure, and you can inject mocked or 
+ * be created with minimal connection to the system infrastructure, and you can inject mocked or
  * wrappered versions of many of Activity's dependencies.  Most of the work is handled
  * automatically here by {@link #setUp} and {@link #tearDown}.
- * 
+ *
  * <p>If you prefer a functional test, see {@link android.test.ActivityInstrumentationTestCase}.
- * 
+ *
  * <p>It must be noted that, as a true unit test, your Activity will not be running in the
- * normal system and will not participate in the normal interactions with other Activities.  
+ * normal system and will not participate in the normal interactions with other Activities.
  * The following methods should not be called in this configuration - most of them will throw
  * exceptions:
  * <ul>
@@ -54,17 +54,17 @@
  * <li>{@link android.app.Activity#isTaskRoot()}</li>
  * <li>{@link android.app.Activity#moveTaskToBack(boolean)}</li>
  * </ul>
- * 
- * <p>The following methods may be called but will not do anything.  For test purposes, you can use 
- * the methods {@link #getStartedActivityIntent()} and {@link #getStartedActivityRequest()} to 
+ *
+ * <p>The following methods may be called but will not do anything.  For test purposes, you can use
+ * the methods {@link #getStartedActivityIntent()} and {@link #getStartedActivityRequest()} to
  * inspect the parameters that they were called with.
  * <ul>
  * <li>{@link android.app.Activity#startActivity(Intent)}</li>
  * <li>{@link android.app.Activity#startActivityForResult(Intent, int)}</li>
  * </ul>
  *
- * <p>The following methods may be called but will not do anything.  For test purposes, you can use 
- * the methods {@link #isFinishCalled()} and {@link #getFinishedActivityRequest()} to inspect the 
+ * <p>The following methods may be called but will not do anything.  For test purposes, you can use
+ * the methods {@link #isFinishCalled()} and {@link #getFinishedActivityRequest()} to inspect the
  * parameters that they were called with.
  * <ul>
  * <li>{@link android.app.Activity#finish()}</li>
@@ -72,8 +72,12 @@
  * <li>{@link android.app.Activity#finishActivity(int requestCode)}</li>
  * </ul>
  *
+ * @deprecated Write
+ * <a href="{@docRoot}training/testing/unit-testing/local-unit-tests.html">Local Unit Tests</a>
+ * instead.
  */
-public abstract class ActivityUnitTestCase<T extends Activity> 
+@Deprecated
+public abstract class ActivityUnitTestCase<T extends Activity>
         extends ActivityTestCase {
 
     private static final String TAG = "ActivityUnitTestCase";
@@ -102,31 +106,31 @@
         // default value for target context, as a default
       mActivityContext = getInstrumentation().getTargetContext();
     }
-    
+
     /**
      * Start the activity under test, in the same way as if it was started by
-     * {@link android.content.Context#startActivity Context.startActivity()}, providing the 
+     * {@link android.content.Context#startActivity Context.startActivity()}, providing the
      * arguments it supplied.  When you use this method to start the activity, it will automatically
      * be stopped by {@link #tearDown}.
-     * 
-     * <p>This method will call onCreate(), but if you wish to further exercise Activity life 
+     *
+     * <p>This method will call onCreate(), but if you wish to further exercise Activity life
      * cycle methods, you must call them yourself from your test case.
-     * 
+     *
      * <p><i>Do not call from your setUp() method.  You must call this method from each of your
      * test methods.</i>
-     *  
+     *
      * @param intent The Intent as if supplied to {@link android.content.Context#startActivity}.
      * @param savedInstanceState The instance state, if you are simulating this part of the life
      * cycle.  Typically null.
-     * @param lastNonConfigurationInstance This Object will be available to the 
-     * Activity if it calls {@link android.app.Activity#getLastNonConfigurationInstance()}.  
+     * @param lastNonConfigurationInstance This Object will be available to the
+     * Activity if it calls {@link android.app.Activity#getLastNonConfigurationInstance()}.
      * Typically null.
      * @return Returns the Activity that was created
      */
     protected T startActivity(Intent intent, Bundle savedInstanceState,
             Object lastNonConfigurationInstance) {
         assertFalse("Activity already created", mCreated);
-        
+
         if (!mAttached) {
             assertNotNull(mActivityClass);
             setActivity(null);
@@ -154,7 +158,7 @@
 
             assertNotNull(newActivity);
             setActivity(newActivity);
-            
+
             mAttached = true;
         }
 
@@ -165,22 +169,22 @@
         }
         return result;
     }
-    
+
     @Override
     protected void tearDown() throws Exception {
-        
+
         setActivity(null);
-        
-        // Scrub out members - protects against memory leaks in the case where someone 
+
+        // Scrub out members - protects against memory leaks in the case where someone
         // creates a non-static inner class (thus referencing the test case) and gives it to
         // someone else to hold onto
         scrubClass(ActivityInstrumentationTestCase.class);
 
         super.tearDown();
     }
-    
+
     /**
-     * Set the application for use during the test.  You must call this function before calling 
+     * Set the application for use during the test.  You must call this function before calling
      * {@link #startActivity}.  If your test does not call this method,
      * @param application The Application object that will be injected into the Activity under test.
      */
@@ -198,7 +202,7 @@
     }
 
     /**
-     * This method will return the value if your Activity under test calls 
+     * This method will return the value if your Activity under test calls
      * {@link android.app.Activity#setRequestedOrientation}.
      */
     public int getRequestedOrientation() {
@@ -207,10 +211,10 @@
         }
         return 0;
     }
-    
+
     /**
-     * This method will return the launch intent if your Activity under test calls 
-     * {@link android.app.Activity#startActivity(Intent)} or 
+     * This method will return the launch intent if your Activity under test calls
+     * {@link android.app.Activity#startActivity(Intent)} or
      * {@link android.app.Activity#startActivityForResult(Intent, int)}.
      * @return The Intent provided in the start call, or null if no start call was made.
      */
@@ -220,9 +224,9 @@
         }
         return null;
     }
-    
+
     /**
-     * This method will return the launch request code if your Activity under test calls 
+     * This method will return the launch request code if your Activity under test calls
      * {@link android.app.Activity#startActivityForResult(Intent, int)}.
      * @return The request code provided in the start call, or -1 if no start call was made.
      */
@@ -234,9 +238,9 @@
     }
 
     /**
-     * This method will notify you if the Activity under test called 
-     * {@link android.app.Activity#finish()}, 
-     * {@link android.app.Activity#finishFromChild(Activity)}, or 
+     * This method will notify you if the Activity under test called
+     * {@link android.app.Activity#finish()},
+     * {@link android.app.Activity#finishFromChild(Activity)}, or
      * {@link android.app.Activity#finishActivity(int)}.
      * @return Returns true if one of the listed finish methods was called.
      */
@@ -246,9 +250,9 @@
         }
         return false;
     }
-    
+
     /**
-     * This method will return the request code if the Activity under test called 
+     * This method will return the request code if the Activity under test called
      * {@link android.app.Activity#finishActivity(int)}.
      * @return The request code provided in the start call, or -1 if no finish call was made.
      */
@@ -258,7 +262,7 @@
         }
         return 0;
     }
-    
+
     /**
      * This mock Activity represents the "parent" activity.  By injecting this, we allow the user
      * to call a few more Activity methods, including:
@@ -269,7 +273,7 @@
      * <li>{@link android.app.Activity#finishActivity(int requestCode)}</li>
      * <li>{@link android.app.Activity#finishFromChild(Activity child)}</li>
      * </ul>
-     * 
+     *
      * TODO: Make this overrideable, and the unit test can look for calls to other methods
      */
     private static class MockParent extends Activity {
@@ -303,7 +307,7 @@
         public Window getWindow() {
             return null;
         }
-        
+
         /**
          * By defining this in the parent, we allow the tested activity to call
          * <ul>
@@ -316,7 +320,7 @@
             mStartedActivityIntent = intent;
             mStartedActivityRequest = requestCode;
         }
-        
+
         /**
          * By defining this in the parent, we allow the tested activity to call
          * <ul>
diff --git a/test-runner/src/android/test/AndroidTestRunner.java b/test-runner/src/android/test/AndroidTestRunner.java
index aa7c677..50eaafb 100644
--- a/test-runner/src/android/test/AndroidTestRunner.java
+++ b/test-runner/src/android/test/AndroidTestRunner.java
@@ -33,6 +33,13 @@
 import java.lang.reflect.InvocationTargetException;
 import java.util.List;
 
+/**
+ * @deprecated Use
+ * <a href="{@docRoot}reference/android/support/test/runner/AndroidJUnitRunner.html">
+ * AndroidJUnitRunner</a> instead. New tests should be written using the
+ * <a href="{@docRoot}tools/testing-support-library/index.html">Android Testing Support Library</a>.
+ */
+@Deprecated
 public class AndroidTestRunner extends BaseTestRunner {
 
     private TestResult mTestResult;
diff --git a/test-runner/src/android/test/ApplicationTestCase.java b/test-runner/src/android/test/ApplicationTestCase.java
index f093181..4d73f53 100644
--- a/test-runner/src/android/test/ApplicationTestCase.java
+++ b/test-runner/src/android/test/ApplicationTestCase.java
@@ -32,7 +32,7 @@
  * In order to support the lifecycle of a Application, this test case will make the
  * following calls at the following times.
  *
- * <ul><li>The test case will not call onCreate() until your test calls 
+ * <ul><li>The test case will not call onCreate() until your test calls
  * {@link #createApplication()}.  This gives you a chance
  * to set up or adjust any additional framework or test logic before
  * onCreate().</li>
@@ -40,22 +40,28 @@
  * automatically called, and it will stop & destroy your application by calling its
  * onDestroy() method.</li>
  * </ul>
- * 
+ *
  * <p><b>Dependency Injection.</b>
  * Every Application has one inherent dependency, the {@link android.content.Context Context} in
  * which it runs.
- * This framework allows you to inject a modified, mock, or isolated replacement for this 
+ * This framework allows you to inject a modified, mock, or isolated replacement for this
  * dependencies, and thus perform a true unit test.
- * 
+ *
  * <p>If simply run your tests as-is, your Application will be injected with a fully-functional
  * Context.
- * You can create and inject alternative types of Contexts by calling 
+ * You can create and inject alternative types of Contexts by calling
  * {@link AndroidTestCase#setContext(Context) setContext()}.  You must do this <i>before</i> calling
  * {@link #createApplication()}.  The test framework provides a
- * number of alternatives for Context, including {@link android.test.mock.MockContext MockContext}, 
- * {@link android.test.RenamingDelegatingContext RenamingDelegatingContext}, and 
+ * number of alternatives for Context, including {@link android.test.mock.MockContext MockContext},
+ * {@link android.test.RenamingDelegatingContext RenamingDelegatingContext}, and
  * {@link android.content.ContextWrapper ContextWrapper}.
+ *
+ * @deprecated Use
+ * <a href="{@docRoot}reference/android/support/test/InstrumentationRegistry.html">
+ * InstrumentationRegistry</a> instead. New tests should be written using the
+ * <a href="{@docRoot}tools/testing-support-library/index.html">Android Testing Support Library</a>.
  */
+@Deprecated
 public abstract class ApplicationTestCase<T extends Application> extends AndroidTestCase {
 
     Class<T> mApplicationClass;
@@ -78,17 +84,17 @@
     }
 
     /**
-     * This will do the work to instantiate the Application under test.  After this, your test 
+     * This will do the work to instantiate the Application under test.  After this, your test
      * code must also start and stop the Application.
      */
     @Override
     protected void setUp() throws Exception {
         super.setUp();
-        
+
         // get the real context, before the individual tests have a chance to muck with it
         mSystemContext = getContext();
     }
-    
+
     /**
      * Load and attach the application under test.
      */
@@ -101,26 +107,26 @@
         }
         mAttached = true;
     }
-    
+
     /**
-     * Start the Application under test, in the same way as if it was started by the system.  
+     * Start the Application under test, in the same way as if it was started by the system.
      * If you use this method to start the Application, it will automatically
      * be stopped by {@link #tearDown}.  If you wish to inject a specialized Context for your
-     * test, by calling {@link AndroidTestCase#setContext(Context) setContext()}, 
+     * test, by calling {@link AndroidTestCase#setContext(Context) setContext()},
      * you must do so  before calling this method.
      */
     final protected void createApplication() {
         assertFalse(mCreated);
-        
+
         if (!mAttached) {
             setupApplication();
         }
         assertNotNull(mApplication);
-        
+
         mApplication.onCreate();
         mCreated = true;
     }
-    
+
     /**
      * This will make the necessary calls to terminate the Application under test (it will
      * call onTerminate().  Ordinarily this will be called automatically (by {@link #tearDown}, but
@@ -131,13 +137,13 @@
             mApplication.onTerminate();
         }
     }
-    
+
     /**
-     * Shuts down the Application under test.  Also makes sure all resources are cleaned up and 
+     * Shuts down the Application under test.  Also makes sure all resources are cleaned up and
      * garbage collected before moving on to the next
      * test.  Subclasses that override this method should make sure they call super.tearDown()
      * at the end of the overriding method.
-     * 
+     *
      * @throws Exception
      */
     @Override
@@ -145,7 +151,7 @@
         terminateApplication();
         mApplication = null;
 
-        // Scrub out members - protects against memory leaks in the case where someone 
+        // Scrub out members - protects against memory leaks in the case where someone
         // creates a non-static inner class (thus referencing the test case) and gives it to
         // someone else to hold onto
         scrubClass(ApplicationTestCase.class);
@@ -156,7 +162,7 @@
     /**
      * Return a real (not mocked or instrumented) system Context that can be used when generating
      * Mock or other Context objects for your Application under test.
-     * 
+     *
      * @return Returns a reference to a normal Context.
      */
     public Context getSystemContext() {
@@ -165,7 +171,7 @@
 
     /**
      * This test simply confirms that the Application class can be instantiated properly.
-     * 
+     *
      * @throws Exception
      */
     final public void testApplicationTestCaseSetUpProperly() throws Exception {
diff --git a/test-runner/src/android/test/AssertionFailedError.java b/test-runner/src/android/test/AssertionFailedError.java
index b3ac6d1..fc3e98e 100644
--- a/test-runner/src/android/test/AssertionFailedError.java
+++ b/test-runner/src/android/test/AssertionFailedError.java
@@ -18,17 +18,18 @@
 
 /**
  * Thrown when an assertion failed.
- * 
+ *
  * @deprecated use junit.framework.AssertionFailedError
  */
+@Deprecated
 public class AssertionFailedError extends Error {
-    
+
     /**
      * It is more typical to call {@link #AssertionFailedError(String)}.
      */
     public AssertionFailedError() {
     }
-    
+
     public AssertionFailedError(String errorMessage) {
         super(errorMessage);
     }
diff --git a/test-runner/src/android/test/ClassPathPackageInfo.java b/test-runner/src/android/test/ClassPathPackageInfo.java
index 1f6e647..1ab7c7f 100644
--- a/test-runner/src/android/test/ClassPathPackageInfo.java
+++ b/test-runner/src/android/test/ClassPathPackageInfo.java
@@ -24,9 +24,10 @@
 /**
  * The Package object doesn't allow you to iterate over the contained
  * classes and subpackages of that package.  This is a version that does.
- * 
+ *
  * {@hide} Not needed for 1.0 SDK.
  */
+@Deprecated
 public class ClassPathPackageInfo {
 
     private final ClassPathPackageInfoSource source;
diff --git a/test-runner/src/android/test/ClassPathPackageInfoSource.java b/test-runner/src/android/test/ClassPathPackageInfoSource.java
index 0ffecdb..89bb494 100644
--- a/test-runner/src/android/test/ClassPathPackageInfoSource.java
+++ b/test-runner/src/android/test/ClassPathPackageInfoSource.java
@@ -33,9 +33,10 @@
 
 /**
  * Generate {@link ClassPathPackageInfo}s by scanning apk paths.
- * 
+ *
  * {@hide} Not needed for 1.0 SDK.
  */
+@Deprecated
 public class ClassPathPackageInfoSource {
 
     private static final String CLASS_EXTENSION = ".class";
@@ -82,7 +83,7 @@
                 // Don't try to load classes that are generated. They usually aren't in test apks.
                 continue;
             }
-            
+
             try {
                 // We get errors in the emulator if we don't use the caller's class loader.
                 topLevelClasses.add(Class.forName(className, false,
diff --git a/test-runner/src/android/test/DatabaseTestUtils.java b/test-runner/src/android/test/DatabaseTestUtils.java
index 23e0aba..42ef48b 100644
--- a/test-runner/src/android/test/DatabaseTestUtils.java
+++ b/test-runner/src/android/test/DatabaseTestUtils.java
@@ -27,6 +27,7 @@
  * A collection of utilities for writing unit tests for database code.
  * @hide pending API council approval
  */
+@Deprecated
 public class DatabaseTestUtils {
 
     /**
diff --git a/test-runner/src/android/test/InstrumentationCoreTestRunner.java b/test-runner/src/android/test/InstrumentationCoreTestRunner.java
index 655a65c..2b05e4a 100644
--- a/test-runner/src/android/test/InstrumentationCoreTestRunner.java
+++ b/test-runner/src/android/test/InstrumentationCoreTestRunner.java
@@ -41,6 +41,7 @@
  *
  * @hide
  */
+@Deprecated
 public class InstrumentationCoreTestRunner extends InstrumentationTestRunner {
 
     /**
diff --git a/test-runner/src/android/test/InstrumentationTestRunner.java b/test-runner/src/android/test/InstrumentationTestRunner.java
index db80ef951..9bd4c96 100644
--- a/test-runner/src/android/test/InstrumentationTestRunner.java
+++ b/test-runner/src/android/test/InstrumentationTestRunner.java
@@ -155,6 +155,10 @@
  * -e coverageFile /sdcard/myFile.ec
  * <br/>
  * in addition to the other arguments.
+ * @deprecated Use
+ * <a href="{@docRoot}reference/android/support/test/runner/AndroidJUnitRunner.html">
+ * AndroidJUnitRunner</a> instead. New tests should be written using the
+ * <a href="{@docRoot}tools/testing-support-library/index.html">Android Testing Support Library</a>.
  */
 
 /* (not JavaDoc)
@@ -170,6 +174,7 @@
  *
  * This model is used by many existing app tests, but can probably be deprecated.
  */
+@Deprecated
 public class InstrumentationTestRunner extends Instrumentation implements TestSuiteProvider {
 
     /** @hide */
diff --git a/test-runner/src/android/test/InstrumentationUtils.java b/test-runner/src/android/test/InstrumentationUtils.java
index 1a7002a..cc50813 100644
--- a/test-runner/src/android/test/InstrumentationUtils.java
+++ b/test-runner/src/android/test/InstrumentationUtils.java
@@ -17,17 +17,17 @@
 package android.test;
 
 /**
- * 
  * The InstrumentationUtils class has all the utility functions needed for
  * instrumentation tests.
  *
  * {@hide} - Not currently used.
  */
+@Deprecated
 public class InstrumentationUtils {
     /**
      * An utility function that returns the menu identifier for a particular
      * menu item.
-     * 
+     *
      * @param cls Class object of the class that handles the menu ite,.
      * @param identifier Menu identifier.
      * @return The integer corresponding to the menu item.
@@ -35,7 +35,7 @@
     public static int getMenuIdentifier(Class cls, String identifier) {
         int id = -1;
         try {
-            Integer field = (Integer)cls.getDeclaredField(identifier).get(cls);   
+            Integer field = (Integer)cls.getDeclaredField(identifier).get(cls);
             id = field.intValue();
         } catch (NoSuchFieldException e) {
             e.printStackTrace();
diff --git a/test-runner/src/android/test/IsolatedContext.java b/test-runner/src/android/test/IsolatedContext.java
index f971b5d..3abf38f 100644
--- a/test-runner/src/android/test/IsolatedContext.java
+++ b/test-runner/src/android/test/IsolatedContext.java
@@ -43,9 +43,13 @@
 
 
 /**
-     * A mock context which prevents its users from talking to the rest of the device while
+ * A mock context which prevents its users from talking to the rest of the device while
  * stubbing enough methods to satify code that tries to talk to other packages.
+ *
+ * @deprecated New tests should be written using the
+ * <a href="{@docRoot}tools/testing-support-library/index.html">Android Testing Support Library</a>.
  */
+@Deprecated
 public class IsolatedContext extends ContextWrapper {
 
     private ContentResolver mResolver;
diff --git a/test-runner/src/android/test/LaunchPerformanceBase.java b/test-runner/src/android/test/LaunchPerformanceBase.java
index d423e62..62c90d6 100644
--- a/test-runner/src/android/test/LaunchPerformanceBase.java
+++ b/test-runner/src/android/test/LaunchPerformanceBase.java
@@ -26,6 +26,7 @@
  *
  * @hide
  */
+@Deprecated
 public class LaunchPerformanceBase extends Instrumentation {
 
     public static final String LOG_TAG = "Launch Performance";
@@ -39,7 +40,7 @@
         mIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
         setAutomaticPerformanceSnapshots();
     }
-   
+
     /**
      * Launches intent, and waits for idle before returning.
      *
diff --git a/test-runner/src/android/test/MoreAsserts.java b/test-runner/src/android/test/MoreAsserts.java
index 3364895..d33911a 100644
--- a/test-runner/src/android/test/MoreAsserts.java
+++ b/test-runner/src/android/test/MoreAsserts.java
@@ -30,7 +30,10 @@
 
 /**
  * Contains additional assertion methods not found in JUnit.
+ * @deprecated Use
+ * <a href="https://github.com/hamcrest">Hamcrest matchers</a> instead.
  */
+@Deprecated
 public final class MoreAsserts {
 
     private MoreAsserts() { }
@@ -375,7 +378,7 @@
                 failWithMessage(message, "Extra object in actual: (" + actualObj.toString() + ")");
             }
         }
-        
+
         if (expectedMap.size() > 0) {
             failWithMessage(message, "Extra objects in expected.");
         }
diff --git a/test-runner/src/android/test/NoExecTestResult.java b/test-runner/src/android/test/NoExecTestResult.java
index 1ee62c1..a01b6aa 100644
--- a/test-runner/src/android/test/NoExecTestResult.java
+++ b/test-runner/src/android/test/NoExecTestResult.java
@@ -19,11 +19,12 @@
 import junit.framework.TestResult;
 
 /**
- * A benign test result that does no actually test execution, just runs 
+ * A benign test result that does no actually test execution, just runs
  * through the motions
- * 
+ *
  * {@hide} Not needed for SDK.
  */
+@Deprecated
 class NoExecTestResult extends TestResult {
 
     /**
diff --git a/test-runner/src/android/test/PackageInfoSources.java b/test-runner/src/android/test/PackageInfoSources.java
index ef37449..205f86b 100644
--- a/test-runner/src/android/test/PackageInfoSources.java
+++ b/test-runner/src/android/test/PackageInfoSources.java
@@ -19,6 +19,7 @@
 /**
  * {@hide} Not needed for SDK.
  */
+@Deprecated
 public class PackageInfoSources {
 
     private static ClassPathPackageInfoSource classPathSource;
diff --git a/test-runner/src/android/test/PerformanceCollectorTestCase.java b/test-runner/src/android/test/PerformanceCollectorTestCase.java
index 4309ff7..3a5dafc 100644
--- a/test-runner/src/android/test/PerformanceCollectorTestCase.java
+++ b/test-runner/src/android/test/PerformanceCollectorTestCase.java
@@ -30,6 +30,7 @@
  *
  * {@hide} Not needed for SDK.
  */
+@Deprecated
 public interface PerformanceCollectorTestCase {
     public PerformanceCollector mPerfCollector = new PerformanceCollector();
 
diff --git a/test-runner/src/android/test/RenamingDelegatingContext.java b/test-runner/src/android/test/RenamingDelegatingContext.java
index 3c4da9e..36786b0 100644
--- a/test-runner/src/android/test/RenamingDelegatingContext.java
+++ b/test-runner/src/android/test/RenamingDelegatingContext.java
@@ -36,7 +36,11 @@
  * This is a class which delegates to the given context, but performs database
  * and file operations with a renamed database/file name (prefixes default
  * names with a given prefix).
+ *
+ * @deprecated New tests should be written using the
+ * <a href="{@docRoot}tools/testing-support-library/index.html">Android Testing Support Library</a>.
  */
+@Deprecated
 public class RenamingDelegatingContext extends ContextWrapper {
 
     private Context mFileContext;
@@ -168,7 +172,7 @@
             return false;
         }
     }
-    
+
     @Override
     public File getDatabasePath(String name) {
         return mFileContext.getDatabasePath(renamedFileName(name));
@@ -216,7 +220,7 @@
     public String[] fileList() {
         return mFileNames.toArray(new String[]{});
     }
-    
+
     /**
      * In order to support calls to getCacheDir(), we create a temp cache dir (inside the real
      * one) and return it instead.  This code is basically getCacheDir(), except it uses the real
@@ -241,21 +245,4 @@
         }
         return mCacheDir;
     }
-
-
-//    /**
-//     * Given an array of files returns only those whose names indicate that they belong to this
-//     * context.
-//     * @param allFiles the original list of files
-//     * @return the pruned list of files
-//     */
-//    private String[] prunedFileList(String[] allFiles) {
-//        List<String> files = Lists.newArrayList();
-//        for (String file : allFiles) {
-//            if (file.startsWith(mFilePrefix)) {
-//                files.add(file);
-//            }
-//        }
-//        return files.toArray(new String[]{});
-//    }
 }
diff --git a/test-runner/src/android/test/ServiceTestCase.java b/test-runner/src/android/test/ServiceTestCase.java
index ba20c09..c8ff0f9 100644
--- a/test-runner/src/android/test/ServiceTestCase.java
+++ b/test-runner/src/android/test/ServiceTestCase.java
@@ -92,7 +92,13 @@
  *      {@link android.test.RenamingDelegatingContext RenamingDelegatingContext},
  *      {@link android.content.ContextWrapper ContextWrapper}, and
  *      {@link android.test.IsolatedContext}.
+ *
+ * @deprecated Use
+ * <a href="{@docRoot}reference/android/support/test/rule/ServiceTestRule.html">
+ * ServiceTestRule</a> instead. New tests should be written using the
+ * <a href="{@docRoot}tools/testing-support-library/index.html">Android Testing Support Library</a>.
  */
+@Deprecated
 public abstract class ServiceTestCase<T extends Service> extends AndroidTestCase {
 
     Class<T> mServiceClass;
diff --git a/test-runner/src/android/test/SimpleCache.java b/test-runner/src/android/test/SimpleCache.java
index 44424ec..46143e4 100644
--- a/test-runner/src/android/test/SimpleCache.java
+++ b/test-runner/src/android/test/SimpleCache.java
@@ -19,6 +19,7 @@
 import java.util.HashMap;
 import java.util.Map;
 
+@Deprecated
 abstract class SimpleCache<K, V> {
     private Map<K, V> map = new HashMap<K, V>();
 
diff --git a/test-runner/src/android/test/SingleLaunchActivityTestCase.java b/test-runner/src/android/test/SingleLaunchActivityTestCase.java
index 72c93ce..af1448e 100644
--- a/test-runner/src/android/test/SingleLaunchActivityTestCase.java
+++ b/test-runner/src/android/test/SingleLaunchActivityTestCase.java
@@ -22,13 +22,19 @@
  * If you would like to test a single activity with an
  * {@link android.test.InstrumentationTestCase}, this provides some of the boiler plate to
  * launch and finish the activity in {@link #setUp} and {@link #tearDown}.
- * 
- * This launches the activity only once for the entire class instead of doing it 
+ *
+ * This launches the activity only once for the entire class instead of doing it
  * in every setup / teardown call.
+ *
+ * @deprecated Use
+ * <a href="{@docRoot}reference/android/support/test/rule/ActivityTestRule.html">
+ * ActivityTestRule</a> instead. New tests should be written using the
+ * <a href="{@docRoot}tools/testing-support-library/index.html">Android Testing Support Library</a>.
  */
+@Deprecated
 public abstract class SingleLaunchActivityTestCase<T extends Activity>
         extends InstrumentationTestCase {
-    
+
     String mPackage;
     Class<T> mActivityClass;
     private static int sTestCaseCounter = 0;
@@ -44,10 +50,10 @@
      */
     public SingleLaunchActivityTestCase(String pkg, Class<T> activityClass) {
         mPackage = pkg;
-        mActivityClass = activityClass;        
-        sTestCaseCounter ++;                
+        mActivityClass = activityClass;
+        sTestCaseCounter ++;
     }
-    
+
     /**
      * The activity that will be set up for use in each test method.
      */
@@ -66,7 +72,7 @@
             getInstrumentation().setInTouchMode(false);
             sActivity = launchActivity(mPackage, mActivityClass, null);
             sActivityLaunchedFlag = true;
-        }                        
+        }
     }
 
     @Override
@@ -75,7 +81,7 @@
         sTestCaseCounter --;
         if (sTestCaseCounter == 0) {
             sActivity.finish();
-        }        
+        }
         super.tearDown();
     }
 
diff --git a/test-runner/src/android/test/SyncBaseInstrumentation.java b/test-runner/src/android/test/SyncBaseInstrumentation.java
index 7d418f0..de36b4f 100644
--- a/test-runner/src/android/test/SyncBaseInstrumentation.java
+++ b/test-runner/src/android/test/SyncBaseInstrumentation.java
@@ -27,7 +27,13 @@
  * If you would like to test sync a single provider with an
  * {@link InstrumentationTestCase}, this provides some of the boiler plate in {@link #setUp} and
  * {@link #tearDown}.
+ *
+ * @deprecated Use
+ * <a href="{@docRoot}reference/android/support/test/InstrumentationRegistry.html">
+ * InstrumentationRegistry</a> instead. New tests should be written using the
+ * <a href="{@docRoot}tools/testing-support-library/index.html">Android Testing Support Library</a>.
  */
+@Deprecated
 public class SyncBaseInstrumentation extends InstrumentationTestCase {
     private Context mTargetContext;
     ContentResolver mContentResolver;
diff --git a/test-runner/src/android/test/TestCase.java b/test-runner/src/android/test/TestCase.java
index 5432ce8..b234f44 100644
--- a/test-runner/src/android/test/TestCase.java
+++ b/test-runner/src/android/test/TestCase.java
@@ -23,14 +23,13 @@
  * More complex interface for test cases.
  *
  * <p>Just implementing Runnable is enough for many test cases.  If you
- * have additional setup or teardown, this interface might be for you, 
+ * have additional setup or teardown, this interface might be for you,
  * especially if you need to share it between different test cases, or your
  * teardown code must execute regardless of whether your test passed.
  *
  * <p>See the android.test package documentation (click the more... link)
  * for a full description
  */
-
 @Deprecated
 public interface TestCase extends Runnable
 {
diff --git a/test-runner/src/android/test/TestCaseUtil.java b/test-runner/src/android/test/TestCaseUtil.java
index 3ba9711..c46d403 100644
--- a/test-runner/src/android/test/TestCaseUtil.java
+++ b/test-runner/src/android/test/TestCaseUtil.java
@@ -35,6 +35,7 @@
  * @hide - This is part of a framework that is under development and should not be used for
  * active development.
  */
+@Deprecated
 public class TestCaseUtil {
 
     private TestCaseUtil() {
@@ -67,7 +68,7 @@
              */
             if (test instanceof TestCase &&
                     ((TestCase)test).getName() == null) {
-                workingTest = invokeSuiteMethodIfPossible(test.getClass(), 
+                workingTest = invokeSuiteMethodIfPossible(test.getClass(),
                         seen);
             }
             if (workingTest == null) {
@@ -155,7 +156,7 @@
     public static TestSuite createTestSuite(Class<? extends Test> testClass)
             throws InstantiationException, IllegalAccessException {
 
-        Test test = invokeSuiteMethodIfPossible(testClass, 
+        Test test = invokeSuiteMethodIfPossible(testClass,
                 new HashSet<Class<?>>());
         if (test == null) {
             return new TestSuite(testClass);
diff --git a/test-runner/src/android/test/TestPrinter.java b/test-runner/src/android/test/TestPrinter.java
index 37bd721..a23f06d 100644
--- a/test-runner/src/android/test/TestPrinter.java
+++ b/test-runner/src/android/test/TestPrinter.java
@@ -30,9 +30,10 @@
  * probably will not need to create or extend this class or call its methods manually.
  * See the full {@link android.test} package description for information about
  * getting test results.
- * 
+ *
  * {@hide} Not needed for 1.0 SDK.
  */
+@Deprecated
 public class TestPrinter implements TestRunner.Listener, TestListener {
 
     private String mTag;
@@ -89,7 +90,7 @@
         mFailedTests.add(test.toString());
         failed(test.toString(), t);
     }
-    
+
     public void addError(Test test, Throwable t) {
         failed(test, t);
     }
diff --git a/test-runner/src/android/test/TestRunner.java b/test-runner/src/android/test/TestRunner.java
index 012df35..beecc6f 100644
--- a/test-runner/src/android/test/TestRunner.java
+++ b/test-runner/src/android/test/TestRunner.java
@@ -42,6 +42,7 @@
  *
  * {@hide} Not needed for 1.0 SDK.
  */
+@Deprecated
 public class TestRunner implements PerformanceTestCase.Intermediates {
     public static final int REGRESSION = 0;
     public static final int PERFORMANCE = 1;
diff --git a/test-runner/src/android/test/TestSuiteProvider.java b/test-runner/src/android/test/TestSuiteProvider.java
index dc9ce6e..c74651c 100644
--- a/test-runner/src/android/test/TestSuiteProvider.java
+++ b/test-runner/src/android/test/TestSuiteProvider.java
@@ -21,6 +21,7 @@
 /**
  * Implementors will know how to get a test suite.
  */
+@Deprecated
 public interface TestSuiteProvider {
 
     TestSuite getTestSuite();
diff --git a/test-runner/src/android/test/TimedTest.java b/test-runner/src/android/test/TimedTest.java
index 95cc9bf..cb15ef9 100644
--- a/test-runner/src/android/test/TimedTest.java
+++ b/test-runner/src/android/test/TimedTest.java
@@ -30,6 +30,7 @@
  *
  * {@hide} Pending approval for public API.
  */
+@Deprecated
 @Retention(RetentionPolicy.RUNTIME)
 public @interface TimedTest {
     boolean includeDetailedStats() default false;
diff --git a/test-runner/src/android/test/TouchUtils.java b/test-runner/src/android/test/TouchUtils.java
index 1b854b0..28dc7b2 100644
--- a/test-runner/src/android/test/TouchUtils.java
+++ b/test-runner/src/android/test/TouchUtils.java
@@ -31,9 +31,15 @@
  * Reusable methods for generating touch events. These methods can be used with
  * InstrumentationTestCase or ActivityInstrumentationTestCase2 to simulate user interaction with
  * the application through a touch screen.
+ *
+ * @deprecated Use
+ * <a href="{@docRoot}training/testing/ui-testing/espresso-testing.html">Espresso UI testing
+ * framework</a> instead. New tests should be written using the
+ * <a href="{@docRoot}tools/testing-support-library/index.html">Android Testing Support Library</a>.
  */
+@Deprecated
 public class TouchUtils {
-    
+
     /**
      * Simulate touching in the center of the screen and dragging one quarter of the way down
      * @param test The test case that is being run
@@ -46,7 +52,7 @@
     public static void dragQuarterScreenDown(ActivityInstrumentationTestCase test) {
         dragQuarterScreenDown(test, test.getActivity());
     }
-    
+
     /**
      * Simulate touching in the center of the screen and dragging one quarter of the way down
      * @param test The test case that is being run
@@ -56,14 +62,14 @@
         Display display = activity.getWindowManager().getDefaultDisplay();
         final Point size = new Point();
         display.getSize(size);
-        
+
         final float x = size.x / 2.0f;
         final float fromY = size.y * 0.5f;
         final float toY = size.y * 0.75f;
-      
+
         drag(test, x, x, fromY, toY, 4);
     }
-    
+
     /**
      * Simulate touching in the center of the screen and dragging one quarter of the way up
      * @param test The test case that is being run
@@ -76,7 +82,7 @@
     public static void dragQuarterScreenUp(ActivityInstrumentationTestCase test) {
         dragQuarterScreenUp(test, test.getActivity());
     }
-    
+
     /**
      * Simulate touching in the center of the screen and dragging one quarter of the way up
      * @param test The test case that is being run
@@ -86,18 +92,18 @@
         Display display = activity.getWindowManager().getDefaultDisplay();
         final Point size = new Point();
         display.getSize(size);
-        
+
         final float x = size.x / 2.0f;
         final float fromY = size.y * 0.5f;
         final float toY = size.y * 0.25f;
-      
+
         drag(test, x, x, fromY, toY, 4);
     }
-    
+
     /**
      * Scroll a ViewGroup to the bottom by repeatedly calling
      * {@link #dragQuarterScreenUp(InstrumentationTestCase, Activity)}
-     * 
+     *
      * @param test The test case that is being run
      * @param v The ViewGroup that should be dragged
      *
@@ -109,11 +115,11 @@
     public static void scrollToBottom(ActivityInstrumentationTestCase test, ViewGroup v) {
         scrollToBottom(test, test.getActivity(), v);
     }
-    
+
     /**
      * Scroll a ViewGroup to the bottom by repeatedly calling
      * {@link #dragQuarterScreenUp(InstrumentationTestCase, Activity)}
-     * 
+     *
      * @param test The test case that is being run
      * @param activity The activity that is in the foreground of the test case
      * @param v The ViewGroup that should be dragged
@@ -132,7 +138,7 @@
     /**
      * Scroll a ViewGroup to the top by repeatedly calling
      * {@link #dragQuarterScreenDown(InstrumentationTestCase, Activity)}
-     * 
+     *
      * @param test The test case that is being run
      * @param v The ViewGroup that should be dragged
      *
@@ -144,11 +150,11 @@
     public static void scrollToTop(ActivityInstrumentationTestCase test, ViewGroup v) {
         scrollToTop(test, test.getActivity(), v);
     }
-    
+
     /**
      * Scroll a ViewGroup to the top by repeatedly calling
      * {@link #dragQuarterScreenDown(InstrumentationTestCase, Activity)}
-     * 
+     *
      * @param test The test case that is being run
      * @param activity The activity that is in the foreground of the test case
      * @param v The ViewGroup that should be dragged
@@ -162,10 +168,10 @@
             next = new ViewStateSnapshot(v);
         } while (!prev.equals(next));
     }
-    
+
     /**
      * Simulate touching the center of a view and dragging to the bottom of the screen.
-     * 
+     *
      * @param test The test case that is being run
      * @param v The view that should be dragged
      *
@@ -177,10 +183,10 @@
     public static void dragViewToBottom(ActivityInstrumentationTestCase test, View v) {
         dragViewToBottom(test, test.getActivity(), v, 4);
     }
-    
+
     /**
      * Simulate touching the center of a view and dragging to the bottom of the screen.
-     * 
+     *
      * @param test The test case that is being run
      * @param activity The activity that is in the foreground of the test case
      * @param v The view that should be dragged
@@ -188,10 +194,10 @@
     public static void dragViewToBottom(InstrumentationTestCase test, Activity activity, View v) {
         dragViewToBottom(test, activity, v, 4);
     }
-    
+
     /**
      * Simulate touching the center of a view and dragging to the bottom of the screen.
-     * 
+     *
      * @param test The test case that is being run
      * @param v The view that should be dragged
      * @param stepCount How many move steps to include in the drag
@@ -205,10 +211,10 @@
             int stepCount) {
         dragViewToBottom(test, test.getActivity(), v, stepCount);
     }
-    
+
     /**
      * Simulate touching the center of a view and dragging to the bottom of the screen.
-     * 
+     *
      * @param test The test case that is being run
      * @param activity The activity that is in the foreground of the test case
      * @param v The view that should be dragged
@@ -220,38 +226,38 @@
 
         int[] xy = new int[2];
         v.getLocationOnScreen(xy);
-        
+
         final int viewWidth = v.getWidth();
         final int viewHeight = v.getHeight();
-        
+
         final float x = xy[0] + (viewWidth / 2.0f);
         float fromY = xy[1] + (viewHeight / 2.0f);
         float toY = screenHeight - 1;
-        
+
         drag(test, x, x, fromY, toY, stepCount);
     }
 
     /**
      * Simulate touching the center of a view and releasing quickly (before the tap timeout).
-     * 
+     *
      * @param test The test case that is being run
      * @param v The view that should be clicked
      */
     public static void tapView(InstrumentationTestCase test, View v) {
         int[] xy = new int[2];
         v.getLocationOnScreen(xy);
-        
+
         final int viewWidth = v.getWidth();
         final int viewHeight = v.getHeight();
-        
+
         final float x = xy[0] + (viewWidth / 2.0f);
         float y = xy[1] + (viewHeight / 2.0f);
-        
+
         Instrumentation inst = test.getInstrumentation();
 
         long downTime = SystemClock.uptimeMillis();
         long eventTime = SystemClock.uptimeMillis();
-        
+
         MotionEvent event = MotionEvent.obtain(downTime, eventTime,
                 MotionEvent.ACTION_DOWN, x, y, 0);
         inst.sendPointerSync(event);
@@ -308,30 +314,30 @@
 
     /**
      * Simulate touching the center of a view and releasing.
-     * 
+     *
      * @param test The test case that is being run
      * @param v The view that should be clicked
      */
     public static void clickView(InstrumentationTestCase test, View v) {
         int[] xy = new int[2];
         v.getLocationOnScreen(xy);
-        
+
         final int viewWidth = v.getWidth();
         final int viewHeight = v.getHeight();
-        
+
         final float x = xy[0] + (viewWidth / 2.0f);
         float y = xy[1] + (viewHeight / 2.0f);
-        
+
         Instrumentation inst = test.getInstrumentation();
 
         long downTime = SystemClock.uptimeMillis();
         long eventTime = SystemClock.uptimeMillis();
-        
+
         MotionEvent event = MotionEvent.obtain(downTime, eventTime,
                 MotionEvent.ACTION_DOWN, x, y, 0);
         inst.sendPointerSync(event);
         inst.waitForIdleSync();
-        
+
 
         eventTime = SystemClock.uptimeMillis();
         final int touchSlop = ViewConfiguration.get(v.getContext()).getScaledTouchSlop();
@@ -344,7 +350,7 @@
         event = MotionEvent.obtain(downTime, eventTime, MotionEvent.ACTION_UP, x, y, 0);
         inst.sendPointerSync(event);
         inst.waitForIdleSync();
-        
+
         try {
             Thread.sleep(1000);
         } catch (InterruptedException e) {
@@ -354,7 +360,7 @@
 
     /**
      * Simulate touching the center of a view, holding until it is a long press, and then releasing.
-     * 
+     *
      * @param test The test case that is being run
      * @param v The view that should be clicked
      *
@@ -369,20 +375,20 @@
 
     /**
      * Simulate touching the center of a view, holding until it is a long press, and then releasing.
-     * 
+     *
      * @param test The test case that is being run
      * @param v The view that should be clicked
      */
     public static void longClickView(InstrumentationTestCase test, View v) {
         int[] xy = new int[2];
         v.getLocationOnScreen(xy);
-        
+
         final int viewWidth = v.getWidth();
         final int viewHeight = v.getHeight();
-        
+
         final float x = xy[0] + (viewWidth / 2.0f);
         float y = xy[1] + (viewHeight / 2.0f);
-        
+
         Instrumentation inst = test.getInstrumentation();
 
         long downTime = SystemClock.uptimeMillis();
@@ -399,7 +405,7 @@
                 x + touchSlop / 2, y + touchSlop / 2, 0);
         inst.sendPointerSync(event);
         inst.waitForIdleSync();
-        
+
         try {
             Thread.sleep((long)(ViewConfiguration.getLongPressTimeout() * 1.5f));
         } catch (InterruptedException e) {
@@ -414,7 +420,7 @@
 
     /**
      * Simulate touching the center of a view and dragging to the top of the screen.
-     * 
+     *
      * @param test The test case that is being run
      * @param v The view that should be dragged
      *
@@ -426,10 +432,10 @@
     public static void dragViewToTop(ActivityInstrumentationTestCase test, View v) {
         dragViewToTop((InstrumentationTestCase) test, v, 4);
     }
-    
+
     /**
      * Simulate touching the center of a view and dragging to the top of the screen.
-     * 
+     *
      * @param test The test case that is being run
      * @param v The view that should be dragged
      * @param stepCount How many move steps to include in the drag
@@ -442,20 +448,20 @@
     public static void dragViewToTop(ActivityInstrumentationTestCase test, View v, int stepCount) {
         dragViewToTop((InstrumentationTestCase) test, v, stepCount);
     }
-    
+
     /**
      * Simulate touching the center of a view and dragging to the top of the screen.
-     * 
+     *
      * @param test The test case that is being run
      * @param v The view that should be dragged
      */
     public static void dragViewToTop(InstrumentationTestCase test, View v) {
         dragViewToTop(test, v, 4);
     }
-    
+
     /**
      * Simulate touching the center of a view and dragging to the top of the screen.
-     * 
+     *
      * @param test The test case that is being run
      * @param v The view that should be dragged
      * @param stepCount How many move steps to include in the drag
@@ -463,21 +469,21 @@
     public static void dragViewToTop(InstrumentationTestCase test, View v, int stepCount) {
         int[] xy = new int[2];
         v.getLocationOnScreen(xy);
-        
+
         final int viewWidth = v.getWidth();
         final int viewHeight = v.getHeight();
-        
+
         final float x = xy[0] + (viewWidth / 2.0f);
         float fromY = xy[1] + (viewHeight / 2.0f);
         float toY = 0;
-        
+
         drag(test, x, x, fromY, toY, stepCount);
     }
-    
+
     /**
      * Get the location of a view. Use the gravity param to specify which part of the view to
      * return.
-     * 
+     *
      * @param v View to find
      * @param gravity A combination of (TOP, CENTER_VERTICAL, BOTTOM) and (LEFT, CENTER_HORIZONTAL,
      *        RIGHT)
@@ -488,7 +494,7 @@
 
         final int viewWidth = v.getWidth();
         final int viewHeight = v.getHeight();
-        
+
         switch (gravity & Gravity.VERTICAL_GRAVITY_MASK) {
         case Gravity.TOP:
             break;
@@ -501,7 +507,7 @@
         default:
             // Same as top -- do nothing
         }
-        
+
         switch (gravity & Gravity.HORIZONTAL_GRAVITY_MASK) {
         case Gravity.LEFT:
             break;
@@ -518,14 +524,14 @@
 
     /**
      * Simulate touching a view and dragging it by the specified amount.
-     * 
+     *
      * @param test The test case that is being run
      * @param v The view that should be dragged
      * @param gravity Which part of the view to use for the initial down event. A combination of
      *        (TOP, CENTER_VERTICAL, BOTTOM) and (LEFT, CENTER_HORIZONTAL, RIGHT)
      * @param deltaX Amount to drag horizontally in pixels
      * @param deltaY Amount to drag vertically in pixels
-     * 
+     *
      * @return distance in pixels covered by the drag
      *
      * @deprecated {@link android.test.ActivityInstrumentationTestCase} is deprecated in favor of
@@ -537,17 +543,17 @@
             int deltaX, int deltaY) {
         return dragViewBy((InstrumentationTestCase) test, v, gravity, deltaX, deltaY);
     }
-    
+
     /**
      * Simulate touching a view and dragging it by the specified amount.
-     * 
+     *
      * @param test The test case that is being run
      * @param v The view that should be dragged
      * @param gravity Which part of the view to use for the initial down event. A combination of
      *        (TOP, CENTER_VERTICAL, BOTTOM) and (LEFT, CENTER_HORIZONTAL, RIGHT)
      * @param deltaX Amount to drag horizontally in pixels
      * @param deltaY Amount to drag vertically in pixels
-     * 
+     *
      * @return distance in pixels covered by the drag
      *
      * @deprecated {@link android.test.ActivityInstrumentationTestCase} is deprecated in favor of
@@ -558,29 +564,29 @@
     public static int dragViewBy(InstrumentationTestCase test, View v, int gravity, int deltaX,
             int deltaY) {
         int[] xy = new int[2];
-        
+
         getStartLocation(v, gravity, xy);
 
         final int fromX = xy[0];
         final int fromY = xy[1];
-        
+
         int distance = (int) Math.hypot(deltaX, deltaY);
 
         drag(test, fromX, fromX + deltaX, fromY, fromY + deltaY, distance);
 
         return distance;
     }
-    
+
     /**
      * Simulate touching a view and dragging it to a specified location.
-     * 
+     *
      * @param test The test case that is being run
      * @param v The view that should be dragged
      * @param gravity Which part of the view to use for the initial down event. A combination of
      *        (TOP, CENTER_VERTICAL, BOTTOM) and (LEFT, CENTER_HORIZONTAL, RIGHT)
      * @param toX Final location of the view after dragging
      * @param toY Final location of the view after dragging
-     * 
+     *
      * @return distance in pixels covered by the drag
      *
      * @deprecated {@link android.test.ActivityInstrumentationTestCase} is deprecated in favor of
@@ -595,14 +601,14 @@
 
     /**
      * Simulate touching a view and dragging it to a specified location.
-     * 
+     *
      * @param test The test case that is being run
      * @param v The view that should be dragged
      * @param gravity Which part of the view to use for the initial down event. A combination of
      *        (TOP, CENTER_VERTICAL, BOTTOM) and (LEFT, CENTER_HORIZONTAL, RIGHT)
      * @param toX Final location of the view after dragging
      * @param toY Final location of the view after dragging
-     * 
+     *
      * @return distance in pixels covered by the drag
      */
     public static int dragViewTo(InstrumentationTestCase test, View v, int gravity, int toX,
@@ -610,28 +616,28 @@
         int[] xy = new int[2];
 
         getStartLocation(v, gravity, xy);
-        
+
         final int fromX = xy[0];
         final int fromY = xy[1];
-        
+
         int deltaX = fromX - toX;
         int deltaY = fromY - toY;
-        
+
         int distance = (int)Math.hypot(deltaX, deltaY);
         drag(test, fromX, toX, fromY, toY, distance);
-        
+
         return distance;
     }
 
     /**
      * Simulate touching a view and dragging it to a specified location. Only moves horizontally.
-     * 
+     *
      * @param test The test case that is being run
      * @param v The view that should be dragged
      * @param gravity Which part of the view to use for the initial down event. A combination of
      *        (TOP, CENTER_VERTICAL, BOTTOM) and (LEFT, CENTER_HORIZONTAL, RIGHT)
      * @param toX Final location of the view after dragging
-     * 
+     *
      * @return distance in pixels covered by the drag
      *
      * @deprecated {@link android.test.ActivityInstrumentationTestCase} is deprecated in favor of
@@ -646,39 +652,39 @@
 
     /**
      * Simulate touching a view and dragging it to a specified location. Only moves horizontally.
-     * 
+     *
      * @param test The test case that is being run
      * @param v The view that should be dragged
      * @param gravity Which part of the view to use for the initial down event. A combination of
      *        (TOP, CENTER_VERTICAL, BOTTOM) and (LEFT, CENTER_HORIZONTAL, RIGHT)
      * @param toX Final location of the view after dragging
-     * 
+     *
      * @return distance in pixels covered by the drag
      */
     public static int dragViewToX(InstrumentationTestCase test, View v, int gravity, int toX) {
         int[] xy = new int[2];
 
         getStartLocation(v, gravity, xy);
-        
+
         final int fromX = xy[0];
         final int fromY = xy[1];
-        
+
         int deltaX = fromX - toX;
 
         drag(test, fromX, toX, fromY, fromY, deltaX);
-        
+
         return deltaX;
     }
-    
+
     /**
      * Simulate touching a view and dragging it to a specified location. Only moves vertically.
-     * 
+     *
      * @param test The test case that is being run
      * @param v The view that should be dragged
      * @param gravity Which part of the view to use for the initial down event. A combination of
      *        (TOP, CENTER_VERTICAL, BOTTOM) and (LEFT, CENTER_HORIZONTAL, RIGHT)
      * @param toY Final location of the view after dragging
-     * 
+     *
      * @return distance in pixels covered by the drag
      *
      * @deprecated {@link android.test.ActivityInstrumentationTestCase} is deprecated in favor of
@@ -690,37 +696,37 @@
             int toY) {
         return dragViewToY((InstrumentationTestCase) test, v, gravity, toY);
     }
-    
+
     /**
      * Simulate touching a view and dragging it to a specified location. Only moves vertically.
-     * 
+     *
      * @param test The test case that is being run
      * @param v The view that should be dragged
      * @param gravity Which part of the view to use for the initial down event. A combination of
      *        (TOP, CENTER_VERTICAL, BOTTOM) and (LEFT, CENTER_HORIZONTAL, RIGHT)
      * @param toY Final location of the view after dragging
-     * 
+     *
      * @return distance in pixels covered by the drag
      */
     public static int dragViewToY(InstrumentationTestCase test, View v, int gravity, int toY) {
         int[] xy = new int[2];
 
         getStartLocation(v, gravity, xy);
-       
+
         final int fromX = xy[0];
         final int fromY = xy[1];
-        
+
         int deltaY = fromY - toY;
 
         drag(test, fromX, fromX, fromY, toY, deltaY);
-        
+
         return deltaY;
     }
-    
+
 
     /**
      * Simulate touching a specific location and dragging to a new location.
-     * 
+     *
      * @param test The test case that is being run
      * @param fromX X coordinate of the initial touch, in screen coordinates
      * @param toX Xcoordinate of the drag destination, in screen coordinates
@@ -737,10 +743,10 @@
             float fromY, float toY, int stepCount) {
         drag((InstrumentationTestCase) test, fromX, toX, fromY, toY, stepCount);
     }
-    
+
     /**
      * Simulate touching a specific location and dragging to a new location.
-     * 
+     *
      * @param test The test case that is being run
      * @param fromX X coordinate of the initial touch, in screen coordinates
      * @param toX Xcoordinate of the drag destination, in screen coordinates
@@ -757,7 +763,7 @@
 
         float y = fromY;
         float x = fromX;
-        
+
         float yStep = (toY - fromY) / stepCount;
         float xStep = (toX - fromX) / stepCount;
 
diff --git a/test-runner/src/android/test/ViewAsserts.java b/test-runner/src/android/test/ViewAsserts.java
index c575fc5..00ab443 100644
--- a/test-runner/src/android/test/ViewAsserts.java
+++ b/test-runner/src/android/test/ViewAsserts.java
@@ -23,7 +23,15 @@
 
 /**
  * Some useful assertions about views.
+ *
+ * @deprecated Use
+ * <a href="{@docRoot}reference/android/support/test/espresso/matcher/ViewMatchers.html">Espresso
+ * View Matchers</a> instead. New test should be written using the
+ * <a href="{@docRoot}tools/testing-support-library/index.html">Android Testing Support Library</a>.
+ * For more information about UI testing, take the
+ * <a href="{@docRoot}tools/testing-support-library/index.html">Espresso UI testing</a> training.
  */
+@Deprecated
 public class ViewAsserts {
 
     private ViewAsserts() {}
diff --git a/test-runner/src/android/test/mock/MockApplication.java b/test-runner/src/android/test/mock/MockApplication.java
index 572dfbf..3257ecf 100644
--- a/test-runner/src/android/test/mock/MockApplication.java
+++ b/test-runner/src/android/test/mock/MockApplication.java
@@ -20,12 +20,17 @@
 import android.content.res.Configuration;
 
 /**
- * A mock {@link android.app.Application} class.  All methods are non-functional and throw 
- * {@link java.lang.UnsupportedOperationException}.  Override it as necessary to provide the 
+ * A mock {@link android.app.Application} class.  All methods are non-functional and throw
+ * {@link java.lang.UnsupportedOperationException}.  Override it as necessary to provide the
  * operations that you need.
+ *
+ * @deprecated Use a mocking framework like <a href="https://github.com/mockito/mockito">Mockito</a>.
+ * New tests should be written using the
+ * <a href="{@docRoot}tools/testing-support-library/index.html">Android Testing Support Library</a>.
  */
+@Deprecated
 public class MockApplication extends Application {
-    
+
     public MockApplication() {
     }
 
@@ -38,7 +43,7 @@
     public void onTerminate() {
         throw new UnsupportedOperationException();
     }
-    
+
     @Override
     public void onConfigurationChanged(Configuration newConfig) {
         throw new UnsupportedOperationException();
diff --git a/test-runner/src/android/test/mock/MockContentProvider.java b/test-runner/src/android/test/mock/MockContentProvider.java
index 5ef71df..3743fb0 100644
--- a/test-runner/src/android/test/mock/MockContentProvider.java
+++ b/test-runner/src/android/test/mock/MockContentProvider.java
@@ -41,7 +41,12 @@
  * Mock implementation of ContentProvider.  All methods are non-functional and throw
  * {@link java.lang.UnsupportedOperationException}.  Tests can extend this class to
  * implement behavior needed for tests.
+ *
+ * @deprecated Use a mocking framework like <a href="https://github.com/mockito/mockito">Mockito</a>.
+ * New tests should be written using the
+ * <a href="{@docRoot}tools/testing-support-library/index.html">Android Testing Support Library</a>.
  */
+@Deprecated
 public class MockContentProvider extends ContentProvider {
     /*
      * Note: if you add methods to ContentProvider, you must add similar methods to
diff --git a/test-runner/src/android/test/mock/MockContentResolver.java b/test-runner/src/android/test/mock/MockContentResolver.java
index aec6c77..75c8335 100644
--- a/test-runner/src/android/test/mock/MockContentResolver.java
+++ b/test-runner/src/android/test/mock/MockContentResolver.java
@@ -49,8 +49,12 @@
  * <p>For more information about application testing, read the
  * <a href="{@docRoot}guide/topics/testing/index.html">Testing</a> developer guide.</p>
  * </div>
+ *
+ * @deprecated Use a mocking framework like <a href="https://github.com/mockito/mockito">Mockito</a>.
+ * New tests should be written using the
+ * <a href="{@docRoot}tools/testing-support-library/index.html">Android Testing Support Library</a>.
  */
-
+@Deprecated
 public class MockContentResolver extends ContentResolver {
     Map<String, ContentProvider> mProviders;
 
diff --git a/test-runner/src/android/test/mock/MockContext.java b/test-runner/src/android/test/mock/MockContext.java
index 64d2978..9b93bda 100644
--- a/test-runner/src/android/test/mock/MockContext.java
+++ b/test-runner/src/android/test/mock/MockContext.java
@@ -52,10 +52,15 @@
 import java.io.InputStream;
 
 /**
- * A mock {@link android.content.Context} class.  All methods are non-functional and throw 
+ * A mock {@link android.content.Context} class.  All methods are non-functional and throw
  * {@link java.lang.UnsupportedOperationException}.  You can use this to inject other dependencies,
  * mocks, or monitors into the classes you are testing.
+ *
+ * @deprecated Use a mocking framework like <a href="https://github.com/mockito/mockito">Mockito</a>.
+ * New tests should be written using the
+ * <a href="{@docRoot}tools/testing-support-library/index.html">Android Testing Support Library</a>.
  */
+@Deprecated
 public class MockContext extends Context {
 
     @Override
@@ -82,12 +87,12 @@
     public Looper getMainLooper() {
         throw new UnsupportedOperationException();
     }
-    
+
     @Override
     public Context getApplicationContext() {
         throw new UnsupportedOperationException();
     }
-    
+
     @Override
     public void setTheme(int resid) {
         throw new UnsupportedOperationException();
@@ -124,7 +129,7 @@
     public ApplicationInfo getApplicationInfo() {
         throw new UnsupportedOperationException();
     }
-    
+
     @Override
     public String getPackageResourcePath() {
         throw new UnsupportedOperationException();
@@ -194,7 +199,7 @@
     public File getObbDir() {
         throw new UnsupportedOperationException();
     }
-    
+
     @Override
     public File getCacheDir() {
         throw new UnsupportedOperationException();
@@ -216,7 +221,7 @@
     }
 
     @Override
-    public SQLiteDatabase openOrCreateDatabase(String file, int mode, 
+    public SQLiteDatabase openOrCreateDatabase(String file, int mode,
             SQLiteDatabase.CursorFactory factory) {
         throw new UnsupportedOperationException();
     }
@@ -310,7 +315,7 @@
             Bundle options) throws IntentSender.SendIntentException {
         startIntentSender(intent, fillInIntent, flagsMask, flagsValues, extraFlags);
     }
-    
+
     @Override
     public void sendBroadcast(Intent intent) {
         throw new UnsupportedOperationException();
diff --git a/test-runner/src/android/test/mock/MockCursor.java b/test-runner/src/android/test/mock/MockCursor.java
index 28fa0f8..576f24a 100644
--- a/test-runner/src/android/test/mock/MockCursor.java
+++ b/test-runner/src/android/test/mock/MockCursor.java
@@ -25,15 +25,18 @@
 import android.os.Bundle;
 
 /**
- * <P>
  * A mock {@link android.database.Cursor} class that isolates the test code from real
  * Cursor implementation.
- * </P>
- * <P>
+ *
+ * <p>
  * All methods including ones related to querying the state of the cursor are
  * are non-functional and throw {@link java.lang.UnsupportedOperationException}.
- * </P>
+ *
+ * @deprecated Use a mocking framework like <a href="https://github.com/mockito/mockito">Mockito</a>.
+ * New tests should be written using the
+ * <a href="{@docRoot}tools/testing-support-library/index.html">Android Testing Support Library</a>.
  */
+@Deprecated
 public class MockCursor implements Cursor {
     @Override
     public int getColumnCount() {
diff --git a/test-runner/src/android/test/mock/MockDialogInterface.java b/test-runner/src/android/test/mock/MockDialogInterface.java
index e4dd0ba..d0a5a09 100644
--- a/test-runner/src/android/test/mock/MockDialogInterface.java
+++ b/test-runner/src/android/test/mock/MockDialogInterface.java
@@ -1,14 +1,33 @@
-// Copyright 2008 The Android Open Source Project
+/*
+ * 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.test.mock;
 
 import android.content.DialogInterface;
 
 /**
- * A mock {@link android.content.DialogInterface} class.  All methods are non-functional and throw 
- * {@link java.lang.UnsupportedOperationException}. Override it to provide the operations that you 
+ * A mock {@link android.content.DialogInterface} class.  All methods are non-functional and throw
+ * {@link java.lang.UnsupportedOperationException}. Override it to provide the operations that you
  * need.
+ *
+ * @deprecated Use a mocking framework like <a href="https://github.com/mockito/mockito">Mockito</a>.
+ * New tests should be written using the
+ * <a href="{@docRoot}tools/testing-support-library/index.html">Android Testing Support Library</a>.
  */
+@Deprecated
 public class MockDialogInterface implements DialogInterface {
     public void cancel() {
         throw new UnsupportedOperationException("not implemented yet");
diff --git a/test-runner/src/android/test/mock/MockPackageManager.java b/test-runner/src/android/test/mock/MockPackageManager.java
index 5296d4d..ffb73f6 100644
--- a/test-runner/src/android/test/mock/MockPackageManager.java
+++ b/test-runner/src/android/test/mock/MockPackageManager.java
@@ -60,7 +60,12 @@
  * A mock {@link android.content.pm.PackageManager} class.  All methods are non-functional and throw
  * {@link java.lang.UnsupportedOperationException}. Override it to provide the operations that you
  * need.
+ *
+ * @deprecated Use a mocking framework like <a href="https://github.com/mockito/mockito">Mockito</a>.
+ * New tests should be written using the
+ * <a href="{@docRoot}tools/testing-support-library/index.html">Android Testing Support Library</a>.
  */
+@Deprecated
 public class MockPackageManager extends PackageManager {
 
     @Override
diff --git a/test-runner/src/android/test/mock/MockResources.java b/test-runner/src/android/test/mock/MockResources.java
index 18752ce..880343e 100644
--- a/test-runner/src/android/test/mock/MockResources.java
+++ b/test-runner/src/android/test/mock/MockResources.java
@@ -32,10 +32,15 @@
 import java.io.InputStream;
 
 /**
- * A mock {@link android.content.res.Resources} class.  All methods are non-functional and throw 
- * {@link java.lang.UnsupportedOperationException}. Override it to provide the operations that you 
+ * A mock {@link android.content.res.Resources} class. All methods are non-functional and throw
+ * {@link java.lang.UnsupportedOperationException}. Override it to provide the operations that you
  * need.
+ *
+ * @deprecated Use a mocking framework like <a href="https://github.com/mockito/mockito">Mockito</a>.
+ * New tests should be written using the
+ * <a href="{@docRoot}tools/testing-support-library/index.html">Android Testing Support Library</a>.
  */
+@Deprecated
 public class MockResources extends Resources {
 
     public MockResources() {
diff --git a/tests/VectorDrawableTest/res/color/fill_gradient_linear.xml b/tests/VectorDrawableTest/res/color/fill_gradient_linear.xml
new file mode 100644
index 0000000..e0e3f03
--- /dev/null
+++ b/tests/VectorDrawableTest/res/color/fill_gradient_linear.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+-->
+<gradient xmlns:android="http://schemas.android.com/apk/res/android"
+          android:angle="90"
+          android:startColor="?android:attr/colorPrimary"
+          android:endColor="?android:attr/colorControlActivated"
+          android:centerColor="#00ff0000"
+          android:startX="0"
+          android:startY="0"
+          android:endX="100"
+          android:endY="100"
+          android:type="linear">
+</gradient>
\ No newline at end of file
diff --git a/tests/VectorDrawableTest/res/color/fill_gradient_linear_item.xml b/tests/VectorDrawableTest/res/color/fill_gradient_linear_item.xml
new file mode 100644
index 0000000..cfb1236
--- /dev/null
+++ b/tests/VectorDrawableTest/res/color/fill_gradient_linear_item.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+-->
+<gradient xmlns:android="http://schemas.android.com/apk/res/android"
+          android:angle="90"
+          android:startColor="?android:attr/colorPrimary"
+          android:endColor="?android:attr/colorControlActivated"
+          android:centerColor="#f00"
+          android:startX="0"
+          android:startY="0"
+          android:endX="100"
+          android:endY="100"
+          android:type="linear">
+    <item android:offset="0.1" android:color="?android:attr/colorPrimary"/>
+    <item android:offset="0.4" android:color="#fff"/>
+    <item android:offset="0.9" android:color="?android:attr/colorControlActivated"/>
+</gradient>
\ No newline at end of file
diff --git a/tests/VectorDrawableTest/res/color/fill_gradient_linear_item_overlap.xml b/tests/VectorDrawableTest/res/color/fill_gradient_linear_item_overlap.xml
new file mode 100644
index 0000000..18274b9
--- /dev/null
+++ b/tests/VectorDrawableTest/res/color/fill_gradient_linear_item_overlap.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+-->
+<gradient xmlns:android="http://schemas.android.com/apk/res/android"
+          android:angle="90"
+          android:startColor="?android:attr/colorPrimary"
+          android:endColor="?android:attr/colorControlActivated"
+          android:centerColor="#f00"
+          android:startX="0"
+          android:startY="0"
+          android:endX="100"
+          android:endY="100"
+          android:type="linear">
+    <item android:offset="0.1" android:color="?android:attr/colorPrimary"/>
+    <item android:offset="0.4" android:color="#f00"/>
+    <item android:offset="0.4" android:color="#fff"/>
+    <item android:offset="0.9" android:color="?android:attr/colorControlActivated"/>
+</gradient>
\ No newline at end of file
diff --git a/tests/VectorDrawableTest/res/color/fill_gradient_radial.xml b/tests/VectorDrawableTest/res/color/fill_gradient_radial.xml
new file mode 100644
index 0000000..ef6fd70
--- /dev/null
+++ b/tests/VectorDrawableTest/res/color/fill_gradient_radial.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+-->
+<gradient xmlns:android="http://schemas.android.com/apk/res/android"
+          android:centerColor="#ff0000"
+          android:endColor="?android:attr/colorControlActivated"
+          android:centerX="300"
+          android:centerY="300"
+          android:gradientRadius="100"
+          android:startColor="?android:attr/colorPrimary"
+          android:type="radial">
+</gradient>
\ No newline at end of file
diff --git a/tests/VectorDrawableTest/res/color/fill_gradient_radial_item.xml b/tests/VectorDrawableTest/res/color/fill_gradient_radial_item.xml
new file mode 100644
index 0000000..51b0e17
--- /dev/null
+++ b/tests/VectorDrawableTest/res/color/fill_gradient_radial_item.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+    <!--
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+-->
+<gradient xmlns:android="http://schemas.android.com/apk/res/android"
+          android:centerColor="#ff0000"
+          android:endColor="#ff0000ff"
+          android:centerX="300"
+          android:centerY="300"
+          android:gradientRadius="100"
+          android:startColor="#ffffffff"
+          android:type="radial">
+    <item android:offset="0.1" android:color="?android:attr/colorPrimary"/>
+    <item android:offset="0.4" android:color="#fff"/>
+    <item android:offset="0.9" android:color="?android:attr/colorControlActivated"/>
+</gradient>
\ No newline at end of file
diff --git a/tests/VectorDrawableTest/res/color/fill_gradient_radial_item_short.xml b/tests/VectorDrawableTest/res/color/fill_gradient_radial_item_short.xml
new file mode 100644
index 0000000..8caa1b4
--- /dev/null
+++ b/tests/VectorDrawableTest/res/color/fill_gradient_radial_item_short.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+    <!--
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+-->
+<gradient xmlns:android="http://schemas.android.com/apk/res/android"
+          android:centerX="300"
+          android:centerY="300"
+          android:gradientRadius="100"
+          android:type="radial">
+    <item android:offset="0.1" android:color="?android:attr/colorPrimary"/>
+    <item android:offset="0.9" android:color="?android:attr/colorControlActivated"/>
+</gradient>
\ No newline at end of file
diff --git a/tests/VectorDrawableTest/res/color/fill_gradient_sweep.xml b/tests/VectorDrawableTest/res/color/fill_gradient_sweep.xml
new file mode 100644
index 0000000..e1fbd10
--- /dev/null
+++ b/tests/VectorDrawableTest/res/color/fill_gradient_sweep.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+-->
+<gradient xmlns:android="http://schemas.android.com/apk/res/android"
+          android:centerColor="#ff0000"
+          android:endColor="#ff0000ff"
+          android:centerX="500"
+          android:centerY="500"
+          android:gradientRadius="10"
+          android:startColor="#ffffffff"
+          android:type="sweep">
+</gradient>
diff --git a/tests/VectorDrawableTest/res/color/fill_gradient_sweep_item.xml b/tests/VectorDrawableTest/res/color/fill_gradient_sweep_item.xml
new file mode 100644
index 0000000..332b938
--- /dev/null
+++ b/tests/VectorDrawableTest/res/color/fill_gradient_sweep_item.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+-->
+<gradient xmlns:android="http://schemas.android.com/apk/res/android"
+          android:centerColor="#ff0000"
+          android:endColor="#ff0000ff"
+          android:centerX="500"
+          android:centerY="500"
+          android:gradientRadius="10"
+          android:startColor="#ffffffff"
+          android:type="sweep">
+    <item android:offset="0.1" android:color="?android:attr/colorPrimary"/>
+    <item android:offset="0.4" android:color="#fff"/>
+    <item android:offset="0.9" android:color="?android:attr/colorControlActivated"/>
+</gradient>
diff --git a/tests/VectorDrawableTest/res/color/fill_gradient_sweep_item_long.xml b/tests/VectorDrawableTest/res/color/fill_gradient_sweep_item_long.xml
new file mode 100644
index 0000000..3931288
--- /dev/null
+++ b/tests/VectorDrawableTest/res/color/fill_gradient_sweep_item_long.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+-->
+<gradient xmlns:android="http://schemas.android.com/apk/res/android"
+          android:centerX="500"
+          android:centerY="500"
+          android:gradientRadius="10"
+          android:type="sweep">
+    <item android:offset="-0.3" android:color="#f00"/>
+    <item android:offset="0.1" android:color="?android:attr/colorPrimary"/>
+    <item android:offset="0.4" android:color="#0f0"/>
+    <item android:offset="0.6" android:color="#00f"/>
+    <item android:offset="0.7" android:color="?android:attr/colorControlActivated"/>
+    <item android:offset="1.5" android:color="#00f"/>
+</gradient>
diff --git a/tests/VectorDrawableTest/res/color/stroke_gradient.xml b/tests/VectorDrawableTest/res/color/stroke_gradient.xml
new file mode 100644
index 0000000..cb324c9
--- /dev/null
+++ b/tests/VectorDrawableTest/res/color/stroke_gradient.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+-->
+<gradient xmlns:android="http://schemas.android.com/apk/res/android"
+          android:angle="90"
+          android:centerColor="#7f7f7f"
+          android:endColor="#ffffff"
+          android:startColor="#000000"
+          android:startX="0"
+          android:endX="100"
+          android:startY="0"
+          android:endY="0"
+          android:type="linear">
+</gradient>
\ No newline at end of file
diff --git a/tests/VectorDrawableTest/res/color/stroke_gradient_item.xml b/tests/VectorDrawableTest/res/color/stroke_gradient_item.xml
new file mode 100644
index 0000000..15d948c
--- /dev/null
+++ b/tests/VectorDrawableTest/res/color/stroke_gradient_item.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+-->
+<gradient xmlns:android="http://schemas.android.com/apk/res/android"
+          android:angle="90"
+          android:centerColor="#7f7f7f"
+          android:endColor="#ffffff"
+          android:startColor="#000000"
+          android:startX="0"
+          android:endX="100"
+          android:startY="0"
+          android:endY="0"
+          android:type="linear">
+    <item android:offset="0.1" android:color="#f00"/>
+    <item android:offset="0.2" android:color="#f0f"/>
+    <item android:offset="0.9" android:color="#f00f"/>
+</gradient>
\ No newline at end of file
diff --git a/tests/VectorDrawableTest/res/color/stroke_gradient_item_alpha.xml b/tests/VectorDrawableTest/res/color/stroke_gradient_item_alpha.xml
new file mode 100644
index 0000000..fda2b88
--- /dev/null
+++ b/tests/VectorDrawableTest/res/color/stroke_gradient_item_alpha.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+-->
+<gradient xmlns:android="http://schemas.android.com/apk/res/android"
+          android:startX="0"
+          android:endX="100"
+          android:startY="0"
+          android:endY="0"
+          android:type="linear">
+    <item android:offset="0.1" android:color="#f00"/>
+    <item android:offset="0.2" android:color="#2f0f"/>
+    <item android:offset="0.9" android:color="#f00f"/>
+</gradient>
\ No newline at end of file
diff --git a/tests/VectorDrawableTest/res/color/vector_icon_fill_state_list.xml b/tests/VectorDrawableTest/res/color/vector_icon_fill_state_list.xml
new file mode 100644
index 0000000..45d88b5
--- /dev/null
+++ b/tests/VectorDrawableTest/res/color/vector_icon_fill_state_list.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+-->
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:color="?android:attr/colorPrimary" android:state_pressed="true" />
+    <item android:color="?android:attr/colorControlActivated" />
+</selector>
\ No newline at end of file
diff --git a/tests/VectorDrawableTest/res/color/vector_icon_fill_state_list_simple.xml b/tests/VectorDrawableTest/res/color/vector_icon_fill_state_list_simple.xml
new file mode 100644
index 0000000..0e2467f
--- /dev/null
+++ b/tests/VectorDrawableTest/res/color/vector_icon_fill_state_list_simple.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+-->
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:color="#f00" android:state_pressed="true" />
+    <item android:color="#00f" />
+</selector>
\ No newline at end of file
diff --git a/tests/VectorDrawableTest/res/color/vector_icon_stroke_state_list.xml b/tests/VectorDrawableTest/res/color/vector_icon_stroke_state_list.xml
new file mode 100644
index 0000000..16251c8
--- /dev/null
+++ b/tests/VectorDrawableTest/res/color/vector_icon_stroke_state_list.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+-->
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:color="?android:attr/colorControlActivated" android:state_pressed="true" />
+    <item android:color="?android:attr/colorPrimary" />
+</selector>
\ No newline at end of file
diff --git a/tests/VectorDrawableTest/res/color/vector_icon_stroke_state_list_simple.xml b/tests/VectorDrawableTest/res/color/vector_icon_stroke_state_list_simple.xml
new file mode 100644
index 0000000..7e6c8ce
--- /dev/null
+++ b/tests/VectorDrawableTest/res/color/vector_icon_stroke_state_list_simple.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+-->
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:color="#00f" android:state_pressed="true" />
+    <item android:color="#f00" />
+</selector>
\ No newline at end of file
diff --git a/tests/VectorDrawableTest/res/drawable/vector_drawable01.xml b/tests/VectorDrawableTest/res/drawable/vector_drawable01.xml
index 2be99be..89afde2 100644
--- a/tests/VectorDrawableTest/res/drawable/vector_drawable01.xml
+++ b/tests/VectorDrawableTest/res/drawable/vector_drawable01.xml
@@ -27,8 +27,8 @@
         <path
             android:name="box1"
             android:pathData="m20,200l100,90l180-180l-35-35l-145,145l-60-60l-40,40z"
-            android:fillColor="?android:attr/colorControlNormal"
-            android:strokeColor="?android:attr/colorControlNormal"
+            android:fillColor="?android:attr/colorPrimary"
+            android:strokeColor="?android:attr/colorPrimary"
             android:strokeLineCap="round"
             android:strokeLineJoin="round" />
     </group>
diff --git a/tests/VectorDrawableTest/res/drawable/vector_icon_gradient_1.xml b/tests/VectorDrawableTest/res/drawable/vector_icon_gradient_1.xml
new file mode 100644
index 0000000..d67aca7
--- /dev/null
+++ b/tests/VectorDrawableTest/res/drawable/vector_icon_gradient_1.xml
@@ -0,0 +1,91 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:height="64dp"
+        android:width="64dp"
+        android:viewportHeight="400"
+        android:viewportWidth="400" >
+
+<group android:name="backgroundGroup"
+       android:scaleX="0.5"
+       android:scaleY="0.5">
+    <path
+            android:name="background1"
+            android:fillColor="@color/fill_gradient_linear"
+            android:pathData="M 0,0 l 200,0 l 0, 200 l -200, 0 z" />
+    <path
+            android:name="background2"
+            android:fillColor="@color/fill_gradient_radial"
+            android:pathData="M 200,200 l 200,0 l 0, 200 l -200, 0 z" />
+    <path
+            android:name="background3"
+            android:fillColor="@color/fill_gradient_sweep"
+            android:pathData="M 400,400 l 200,0 l 0, 200 l -200, 0 z" />
+</group>
+<group
+        android:name="translateToCenterGroup"
+        android:translateX="50.0"
+        android:translateY="90.0" >
+    <path
+            android:name="twoLines"
+            android:pathData="@string/twoLinePathData"
+            android:strokeColor="@color/stroke_gradient"
+            android:strokeWidth="20" />
+
+    <group
+            android:name="rotationGroup"
+            android:pivotX="0.0"
+            android:pivotY="0.0"
+            android:rotation="-45.0">
+        <path
+                android:name="twoLines1"
+                android:pathData="@string/twoLinePathData"
+                android:strokeColor="@color/stroke_gradient"
+                android:strokeWidth="20" />
+
+        <group
+                android:name="translateGroup"
+                android:translateX="130.0"
+                android:translateY="160.0">
+            <group android:name="scaleGroup" >
+                <path
+                        android:name="twoLines3"
+                        android:pathData="@string/twoLinePathData"
+                        android:strokeColor="@color/stroke_gradient"
+                        android:strokeWidth="20" />
+            </group>
+        </group>
+
+        <group
+                android:name="translateGroupHalf"
+                android:translateX="65.0"
+                android:translateY="80.0">
+            <group android:name="scaleGroup" >
+                <path
+                        android:name="twoLines2"
+                        android:pathData="@string/twoLinePathData"
+                        android:fillColor="@color/fill_gradient_linear"
+                        android:strokeColor="@color/stroke_gradient"
+                        android:strokeWidth="20" />
+            </group>
+        </group>
+    </group>
+</group>
+
+</vector>
\ No newline at end of file
diff --git a/tests/VectorDrawableTest/res/drawable/vector_icon_gradient_2.xml b/tests/VectorDrawableTest/res/drawable/vector_icon_gradient_2.xml
new file mode 100644
index 0000000..abf3c7a
--- /dev/null
+++ b/tests/VectorDrawableTest/res/drawable/vector_icon_gradient_2.xml
@@ -0,0 +1,90 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:height="64dp"
+        android:width="64dp"
+        android:viewportHeight="400"
+        android:viewportWidth="400" >
+
+<group android:name="backgroundGroup"
+       android:scaleX="0.5"
+       android:scaleY="0.5">
+    <path
+            android:name="background1"
+            android:fillColor="@color/fill_gradient_linear_item"
+            android:pathData="M 0,0 l 200,0 l 0, 200 l -200, 0 z" />
+    <path
+            android:name="background2"
+            android:fillColor="@color/fill_gradient_radial_item"
+            android:pathData="M 200,200 l 200,0 l 0, 200 l -200, 0 z" />
+    <path
+            android:name="background3"
+            android:fillColor="@color/fill_gradient_sweep_item"
+            android:pathData="M 400,400 l 200,0 l 0, 200 l -200, 0 z" />
+</group>
+<group
+        android:name="translateToCenterGroup"
+        android:translateX="50.0"
+        android:translateY="90.0" >
+    <path
+            android:name="twoLines"
+            android:pathData="@string/twoLinePathData"
+            android:strokeColor="@color/stroke_gradient_item"
+            android:strokeWidth="20" />
+
+    <group
+            android:name="rotationGroup"
+            android:pivotX="0.0"
+            android:pivotY="0.0"
+            android:rotation="-45.0">
+        <path
+                android:name="twoLines1"
+                android:pathData="@string/twoLinePathData"
+                android:strokeColor="@color/stroke_gradient_item"
+                android:strokeWidth="20" />
+
+        <group
+                android:name="translateGroup"
+                android:translateX="130.0"
+                android:translateY="160.0">
+            <group android:name="scaleGroup" >
+                <path
+                        android:name="twoLines3"
+                        android:pathData="@string/twoLinePathData"
+                        android:strokeColor="@color/stroke_gradient_item"
+                        android:strokeWidth="20" />
+            </group>
+        </group>
+
+        <group
+                android:name="translateGroupHalf"
+                android:translateX="65.0"
+                android:translateY="80.0">
+            <group android:name="scaleGroup" >
+                <path
+                        android:name="twoLines2"
+                        android:pathData="@string/twoLinePathData"
+                        android:strokeColor="@color/stroke_gradient_item"
+                        android:strokeWidth="20" />
+            </group>
+        </group>
+    </group>
+</group>
+
+</vector>
\ No newline at end of file
diff --git a/tests/VectorDrawableTest/res/drawable/vector_icon_gradient_3.xml b/tests/VectorDrawableTest/res/drawable/vector_icon_gradient_3.xml
new file mode 100644
index 0000000..5f9726f
--- /dev/null
+++ b/tests/VectorDrawableTest/res/drawable/vector_icon_gradient_3.xml
@@ -0,0 +1,90 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:height="64dp"
+        android:width="64dp"
+        android:viewportHeight="400"
+        android:viewportWidth="400" >
+
+<group android:name="backgroundGroup"
+       android:scaleX="0.5"
+       android:scaleY="0.5">
+    <path
+            android:name="background1"
+            android:fillColor="@color/fill_gradient_linear_item_overlap"
+            android:pathData="M 0,0 l 200,0 l 0, 200 l -200, 0 z" />
+    <path
+            android:name="background2"
+            android:fillColor="@color/fill_gradient_radial_item_short"
+            android:pathData="M 200,200 l 200,0 l 0, 200 l -200, 0 z" />
+    <path
+            android:name="background3"
+            android:fillColor="@color/fill_gradient_sweep_item_long"
+            android:pathData="M 400,400 l 200,0 l 0, 200 l -200, 0 z" />
+</group>
+<group
+        android:name="translateToCenterGroup"
+        android:translateX="50.0"
+        android:translateY="90.0" >
+    <path
+            android:name="twoLines"
+            android:pathData="@string/twoLinePathData"
+            android:strokeColor="@color/stroke_gradient_item_alpha"
+            android:strokeWidth="20" />
+
+    <group
+            android:name="rotationGroup"
+            android:pivotX="0.0"
+            android:pivotY="0.0"
+            android:rotation="-45.0">
+        <path
+                android:name="twoLines1"
+                android:pathData="@string/twoLinePathData"
+                android:strokeColor="@color/stroke_gradient_item_alpha"
+                android:strokeWidth="20" />
+
+        <group
+                android:name="translateGroup"
+                android:translateX="130.0"
+                android:translateY="160.0">
+            <group android:name="scaleGroup" >
+                <path
+                        android:name="twoLines3"
+                        android:pathData="@string/twoLinePathData"
+                        android:strokeColor="@color/stroke_gradient_item_alpha"
+                        android:strokeWidth="20" />
+            </group>
+        </group>
+
+        <group
+                android:name="translateGroupHalf"
+                android:translateX="65.0"
+                android:translateY="80.0">
+            <group android:name="scaleGroup" >
+                <path
+                        android:name="twoLines2"
+                        android:pathData="@string/twoLinePathData"
+                        android:strokeColor="@color/stroke_gradient_item_alpha"
+                        android:strokeWidth="20" />
+            </group>
+        </group>
+    </group>
+</group>
+
+</vector>
\ No newline at end of file
diff --git a/tests/VectorDrawableTest/res/drawable/vector_icon_state_list_simple.xml b/tests/VectorDrawableTest/res/drawable/vector_icon_state_list_simple.xml
new file mode 100644
index 0000000..9f08fe8
--- /dev/null
+++ b/tests/VectorDrawableTest/res/drawable/vector_icon_state_list_simple.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:height="64dp"
+        android:width="64dp"
+        android:viewportHeight="24"
+        android:viewportWidth="24" >
+
+    <path
+        android:fillColor="@color/vector_icon_fill_state_list_simple"
+        android:strokeColor="@color/vector_icon_stroke_state_list_simple"
+        android:strokeWidth="3"
+        android:pathData="M16.0,5.0c-1.955.0 -3.83,1.268 -4.5,3.0c-0.67-1.732 -2.547-3.0 -4.5-3.0C4.4570007,5.0 2.5,6.931999 2.5,9.5c0.0,3.529 3.793,6.258 9.0,11.5c5.207-5.242 9.0-7.971 9.0-11.5C20.5,6.931999 18.543,5.0 16.0,5.0z"/>
+
+</vector>
\ No newline at end of file
diff --git a/tests/VectorDrawableTest/res/drawable/vector_icon_state_list_theme.xml b/tests/VectorDrawableTest/res/drawable/vector_icon_state_list_theme.xml
new file mode 100644
index 0000000..b1ed850
--- /dev/null
+++ b/tests/VectorDrawableTest/res/drawable/vector_icon_state_list_theme.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:height="64dp"
+        android:width="64dp"
+        android:viewportHeight="24"
+        android:viewportWidth="24" >
+
+    <path
+        android:fillColor="@color/vector_icon_fill_state_list"
+        android:strokeColor="@color/vector_icon_stroke_state_list"
+        android:strokeWidth="3"
+        android:pathData="M16.0,5.0c-1.955.0 -3.83,1.268 -4.5,3.0c-0.67-1.732 -2.547-3.0 -4.5-3.0C4.4570007,5.0 2.5,6.931999 2.5,9.5c0.0,3.529 3.793,6.258 9.0,11.5c5.207-5.242 9.0-7.971 9.0-11.5C20.5,6.931999 18.543,5.0 16.0,5.0z"/>
+
+</vector>
\ No newline at end of file
diff --git a/tests/VectorDrawableTest/src/com/android/test/dynamic/VectorDrawablePerformance.java b/tests/VectorDrawableTest/src/com/android/test/dynamic/VectorDrawablePerformance.java
index b4a93f6..7172147 100644
--- a/tests/VectorDrawableTest/src/com/android/test/dynamic/VectorDrawablePerformance.java
+++ b/tests/VectorDrawableTest/src/com/android/test/dynamic/VectorDrawablePerformance.java
@@ -35,6 +35,11 @@
 public class VectorDrawablePerformance extends Activity {
     private static final String LOGCAT = "VectorDrawable1";
     protected int[] icon = {
+            R.drawable.vector_icon_gradient_1,
+            R.drawable.vector_icon_gradient_2,
+            R.drawable.vector_icon_gradient_3,
+            R.drawable.vector_icon_state_list_simple,
+            R.drawable.vector_icon_state_list_theme,
             R.drawable.vector_drawable01,
             R.drawable.vector_drawable02,
             R.drawable.vector_drawable03,
@@ -102,7 +107,7 @@
         ScrollView scrollView = new ScrollView(this);
         GridLayout container = new GridLayout(this);
         scrollView.addView(container);
-        container.setColumnCount(5);
+        container.setColumnCount(4);
         Resources res = this.getResources();
         container.setBackgroundColor(0xFF888888);
         VectorDrawable []d = new VectorDrawable[icon.length];
diff --git a/wifi/java/android/net/wifi/WifiConfiguration.java b/wifi/java/android/net/wifi/WifiConfiguration.java
index 274e985..4c22460 100644
--- a/wifi/java/android/net/wifi/WifiConfiguration.java
+++ b/wifi/java/android/net/wifi/WifiConfiguration.java
@@ -704,24 +704,6 @@
      */
     public int numUserTriggeredJoinAttempts;
 
-    /**
-     * @hide
-     * Connect choices
-     *
-     * remember the keys identifying the known WifiConfiguration over which this configuration
-     * was preferred by user or a "WiFi Network Management app", that is,
-     * a WifiManager.CONNECT_NETWORK or SELECT_NETWORK was received while this configuration
-     * was visible to the user:
-     * configKey is : "SSID"-WEP-WPA_PSK-WPA_EAP
-     *
-     * The integer represents the configuration's RSSI at that time (useful?)
-     *
-     * The overall auto-join algorithm make use of past connect choice so as to sort configuration,
-     * the exact algorithm still fluctuating as of 5/7/2014
-     *
-     */
-    public HashMap<String, Integer> connectChoices;
-
     /** @hide
      * Boost given to RSSI on a home network for the purpose of calculating the score
      * This adds stickiness to home networks, as defined by:
@@ -831,6 +813,16 @@
          */
         public static final long INVALID_NETWORK_SELECTION_DISABLE_TIMESTAMP = -1L;
 
+        /**
+         *  This constant indicates the current configuration has connect choice set
+         */
+        private static final int CONNECT_CHOICE_EXISTS = 1;
+
+        /**
+         *  This constant indicates the current configuration does not have connect choice set
+         */
+        private static final int CONNECT_CHOICE_NOT_EXISTS = -1;
+
         // fields for QualityNetwork Selection
         /**
          * Network selection status, should be in one of three status: enable, temporaily disabled
@@ -854,7 +846,128 @@
         private int[] mNetworkSeclectionDisableCounter = new int[NETWORK_SELECTION_DISABLED_MAX];
 
         /**
-         * return current Quality network selection status in String (for debug purpose)
+         * Connect Choice over this configuration
+         *
+         * When current wifi configuration is visible to the user but user explicitly choose to
+         * connect to another network X, the another networks X's configure key will be stored here.
+         * We will consider user has a preference of X over this network. And in the future,
+         * network selection will always give X a higher preference over this configuration.
+         * configKey is : "SSID"-WEP-WPA_PSK-WPA_EAP
+         */
+        private String mConnectChoice;
+
+        /**
+         * The system timestamp when we records the connectChoice. This value is obtained from
+         * System.currentTimeMillis
+         */
+        private long mConnectChoiceTimestamp = INVALID_NETWORK_SELECTION_DISABLE_TIMESTAMP;
+
+        /**
+         * Used to cache the temporary candidate during the network selection procedure. It will be
+         * kept updating once a new scan result has a higher score than current one
+         */
+        private ScanResult mCandidate;
+
+        /**
+         * Used to cache the score of the current temporary candidate during the network
+         * selection procedure.
+         */
+        private int mCandidateScore;
+
+        /**
+         * Indicate whether this network is visible in latest Qualified Network Selection. This
+         * means there is scan result found related to this Configuration and meet the minimum
+         * requirement. The saved network need not join latest Qualified Network Selection. For
+         * example, it is disabled. True means network is visible in latest Qualified Network
+         * Selection and false means network is invisible
+         */
+        private boolean mSeenInLastQualifiedNetworkSelection;
+
+        /**
+         * set whether this network is visible in latest Qualified Network Selection
+         * @param seen value set to candidate
+         */
+        public void setSeenInLastQualifiedNetworkSelection(boolean seen) {
+            mSeenInLastQualifiedNetworkSelection =  seen;
+        }
+
+        /**
+         * get whether this network is visible in latest Qualified Network Selection
+         * @return returns true -- network is visible in latest Qualified Network Selection
+         *         false -- network is invisible in latest Qualified Network Selection
+         */
+        public boolean getSeenInLastQualifiedNetworkSelection() {
+            return mSeenInLastQualifiedNetworkSelection;
+        }
+        /**
+         * set the temporary candidate of current network selection procedure
+         * @param scanCandidate {@link ScanResult} the candidate set to mCandidate
+         */
+        public void setCandidate(ScanResult scanCandidate) {
+            mCandidate = scanCandidate;
+        }
+
+        /**
+         * get the temporary candidate of current network selection procedure
+         * @return  returns {@link ScanResult} temporary candidate of current network selection
+         * procedure
+         */
+        public ScanResult getCandidate() {
+            return mCandidate;
+        }
+
+        /**
+         * set the score of the temporary candidate of current network selection procedure
+         * @param score value set to mCandidateScore
+         */
+        public void setCandidateScore(int score) {
+            mCandidateScore = score;
+        }
+
+        /**
+         * get the score of the temporary candidate of current network selection procedure
+         * @return returns score of the temporary candidate of current network selection procedure
+         */
+        public int getCandidateScore() {
+            return mCandidateScore;
+        }
+
+        /**
+         * get user preferred choice over this configuration
+         *@return returns configKey of user preferred choice over this configuration
+         */
+        public String getConnectChoice() {
+            return mConnectChoice;
+        }
+
+        /**
+         * set user preferred choice over this configuration
+         * @param newConnectChoice, the configKey of user preferred choice over this configuration
+         */
+        public void setConnectChoice(String newConnectChoice) {
+            mConnectChoice = newConnectChoice;
+        }
+
+        /**
+         * get the timeStamp when user select a choice over this configuration
+         * @return returns when current connectChoice is set (time from System.currentTimeMillis)
+         */
+        public long getConnectChoiceTimestamp() {
+            return mConnectChoiceTimestamp;
+        }
+
+        /**
+         * set the timeStamp when user select a choice over this configuration
+         * @param timeStamp, the timestamp set to connectChoiceTimestamp, expected timestamp should
+         *        be obtained from System.currentTimeMillis
+         */
+        public void setConnectChoiceTimestamp(long timeStamp) {
+            mConnectChoiceTimestamp = timeStamp;
+        }
+
+        /**
+         * get current Quality network selection status
+         * @return returns current Quality network selection status in String (for debug purpose)
          */
         public String getNetworkStatusString() {
             return QUALITY_NETWORK_SELECTION_STATUS[mStatus];
@@ -874,6 +987,7 @@
             }
         }
         /**
+         * get current network disable reason
          * @return current network disable reason in String (for debug purpose)
          */
         public String getNetworkDisableReasonString() {
@@ -882,6 +996,7 @@
 
         /**
          * get current network network selection status
+         * @return return current network network selection status
          */
         public int getNetworkSelectionStatus() {
             return mStatus;
@@ -901,12 +1016,14 @@
         }
 
         /**
-         * return whether current network is permanently disabled
+         * @return returns whether current network is permanently disabled
          */
         public boolean isNetworkPermanentlyDisabled() {
             return mStatus == NETWORK_SELECTION_PERMANENTLY_DISABLED;
         }
+
         /**
+         * set current networ work selection status
          * @param status network selection status to set
          */
         public void setNetworkSelectionStatus(int status) {
@@ -914,14 +1031,16 @@
                 mStatus = status;
             }
         }
+
         /**
-         * @return current network's disable reason
+         * @return returns current network's disable reason
          */
         public int getNetworkSelectionDisableReason() {
             return mNetworkSelectionDisableReason;
         }
 
         /**
+         * set Network disable reason
          * @param  reason Network disable reason
          */
         public void setNetworkSelectionDisableReason(int reason) {
@@ -931,12 +1050,17 @@
                 throw new IllegalArgumentException("Illegal reason value: " + reason);
             }
         }
+
         /**
-         * @param reason whether current network is disabled by this reason
+         * check whether network is disabled by this reason
+         * @param reason a specific disable reason
+         * @return true -- network is disabled for this reason
+         *         false -- network is not disabled for this reason
          */
         public boolean isDisabledByReason(int reason) {
             return mNetworkSelectionDisableReason == reason;
         }
+
         /**
          * @param timeStamp Set when current network is disabled in millisecond since January 1,
          * 1970 00:00:00.0 UTC
@@ -946,7 +1070,7 @@
         }
 
         /**
-         * @return Get when current network is disabled in millisecond since January 1,
+         * @return returns when current network is disabled in millisecond since January 1,
          * 1970 00:00:00.0 UTC
          */
         public long getDisableTime() {
@@ -954,6 +1078,7 @@
         }
 
         /**
+         * get the disable counter of a specific reason
          * @param  reason specific failure reason
          * @exception throw IllegalArgumentException for illegal input
          * @return counter number for specific error reason.
@@ -992,6 +1117,7 @@
                 throw new IllegalArgumentException("Illegal reason value: " + reason);
             }
         }
+
         /**
          * clear the counter of a specific failure reason
          * @hide
@@ -1005,6 +1131,7 @@
                 throw new IllegalArgumentException("Illegal reason value: " + reason);
             }
         }
+
         /**
          * clear all the failure reason counters
          */
@@ -1043,6 +1170,8 @@
             }
             mTemporarilyDisabledTimestamp = source.mTemporarilyDisabledTimestamp;
             mNetworkSelectionBSSID = source.mNetworkSelectionBSSID;
+            setConnectChoice(source.getConnectChoice());
+            setConnectChoiceTimestamp(source.getConnectChoiceTimestamp());
         }
 
         public void writeToParcel(Parcel dest) {
@@ -1054,6 +1183,13 @@
             }
             dest.writeLong(getDisableTime());
             dest.writeString(getNetworkSelectionBSSID());
+            if (getConnectChoice() != null) {
+                dest.writeInt(CONNECT_CHOICE_EXISTS);
+                dest.writeString(getConnectChoice());
+                dest.writeLong(getConnectChoiceTimestamp());
+            } else {
+                dest.writeInt(CONNECT_CHOICE_NOT_EXISTS);
+            }
         }
 
         public void readFromParcel(Parcel in) {
@@ -1065,6 +1201,13 @@
             }
             setDisableTime(in.readLong());
             setNetworkSelectionBSSID(in.readString());
+            if (in.readInt() == CONNECT_CHOICE_EXISTS) {
+                setConnectChoice(in.readString());
+                setConnectChoiceTimestamp(in.readLong());
+            } else {
+                setConnectChoice(null);
+                setConnectChoiceTimestamp(INVALID_NETWORK_SELECTION_DISABLE_TIMESTAMP);
+            }
         }
     }
 
@@ -1184,7 +1327,11 @@
                 }
             }
         }
-
+        if (mNetworkSelectionStatus.getConnectChoice() != null) {
+            sbuf.append(" connect choice: ").append(mNetworkSelectionStatus.getConnectChoice());
+            sbuf.append(" connect choice set time: ").append(mNetworkSelectionStatus
+                    .getConnectChoiceTimestamp());
+        }
 
         if (this.numAssociation > 0) {
             sbuf.append(" numAssociation ").append(this.numAssociation).append("\n");
@@ -1336,16 +1483,7 @@
                 sbuf.append('\n');
             }
         }
-        if (this.connectChoices != null) {
-            for(String key : this.connectChoices.keySet()) {
-                Integer choice = this.connectChoices.get(key);
-                if (choice != null) {
-                    sbuf.append(" choice: ").append(key);
-                    sbuf.append(" = ").append(choice);
-                    sbuf.append('\n');
-                }
-            }
-        }
+
         sbuf.append("triggeredLow: ").append(this.numUserTriggeredWifiDisableLowRSSI);
         sbuf.append(" triggeredBad: ").append(this.numUserTriggeredWifiDisableBadRSSI);
         sbuf.append(" triggeredNotHigh: ").append(this.numUserTriggeredWifiDisableNotHighRSSI);
@@ -1632,11 +1770,6 @@
 
             mIpConfiguration = new IpConfiguration(source.mIpConfiguration);
 
-            if ((source.connectChoices != null) && (source.connectChoices.size() > 0)) {
-                connectChoices = new HashMap<String, Integer>();
-                connectChoices.putAll(source.connectChoices);
-            }
-
             if ((source.linkedConfigurations != null)
                     && (source.linkedConfigurations.size() > 0)) {
                 linkedConfigurations = new HashMap<String, Integer>();
diff --git a/wifi/java/android/net/wifi/nan/IWifiNanManager.aidl b/wifi/java/android/net/wifi/nan/IWifiNanManager.aidl
index ff3d29f..ec9e462 100644
--- a/wifi/java/android/net/wifi/nan/IWifiNanManager.aidl
+++ b/wifi/java/android/net/wifi/nan/IWifiNanManager.aidl
@@ -43,7 +43,8 @@
     void publish(int sessionId, in PublishData publishData, in PublishSettings publishSettings);
     void subscribe(int sessionId, in SubscribeData subscribeData,
             in SubscribeSettings subscribeSettings);
-    void sendMessage(int sessionId, int peerId, in byte[] message, int messageLength);
+    void sendMessage(int sessionId, int peerId, in byte[] message, int messageLength,
+            int messageId);
     void stopSession(int sessionId);
     void destroySession(int sessionId);
 }
diff --git a/wifi/java/android/net/wifi/nan/IWifiNanSessionListener.aidl b/wifi/java/android/net/wifi/nan/IWifiNanSessionListener.aidl
index 773f83b..50c34d9 100644
--- a/wifi/java/android/net/wifi/nan/IWifiNanSessionListener.aidl
+++ b/wifi/java/android/net/wifi/nan/IWifiNanSessionListener.aidl
@@ -32,7 +32,7 @@
     void onMatch(int peerId, in byte[] serviceSpecificInfo,
             int serviceSpecificInfoLength, in byte[] matchFilter, int matchFilterLength);
 
-    void onMessageSendSuccess();
-    void onMessageSendFail(int reason);
+    void onMessageSendSuccess(int messageId);
+    void onMessageSendFail(int messageId, int reason);
     void onMessageReceived(int peerId, in byte[] message, int messageLength);
 }
diff --git a/wifi/java/android/net/wifi/nan/WifiNanManager.java b/wifi/java/android/net/wifi/nan/WifiNanManager.java
index 877f993..cb82268 100644
--- a/wifi/java/android/net/wifi/nan/WifiNanManager.java
+++ b/wifi/java/android/net/wifi/nan/WifiNanManager.java
@@ -319,13 +319,14 @@
     /**
      * {@hide}
      */
-    public void sendMessage(int sessionId, int peerId, byte[] message, int messageLength) {
+    public void sendMessage(int sessionId, int peerId, byte[] message, int messageLength,
+            int messageId) {
         try {
             if (VDBG) {
                 Log.v(TAG, "sendMessage(): sessionId=" + sessionId + ", peerId=" + peerId
-                        + ", messageLength=" + messageLength);
+                        + ", messageLength=" + messageLength + ", messageId=" + messageId);
             }
-            mService.sendMessage(sessionId, peerId, message, messageLength);
+            mService.sendMessage(sessionId, peerId, message, messageLength, messageId);
         } catch (RemoteException e) {
             Log.w(TAG, "subscribe RemoteException (FYI - ignoring): " + e);
         }
diff --git a/wifi/java/android/net/wifi/nan/WifiNanSession.java b/wifi/java/android/net/wifi/nan/WifiNanSession.java
index c6b384e..d0a9410 100644
--- a/wifi/java/android/net/wifi/nan/WifiNanSession.java
+++ b/wifi/java/android/net/wifi/nan/WifiNanSession.java
@@ -103,8 +103,11 @@
      * @param message The message to be transmitted.
      * @param messageLength The number of bytes from the {@code message} to be
      *            transmitted.
+     * @param messageId An arbitrary integer used by the caller to identify the
+     *            message. The same integer ID will be returned in the callbacks
+     *            indicated message send success or failure.
      */
-    public void sendMessage(int peerId, byte[] message, int messageLength) {
-        mManager.sendMessage(mSessionId, peerId, message, messageLength);
+    public void sendMessage(int peerId, byte[] message, int messageLength, int messageId) {
+        mManager.sendMessage(mSessionId, peerId, message, messageLength, messageId);
     }
 }
diff --git a/wifi/java/android/net/wifi/nan/WifiNanSessionListener.java b/wifi/java/android/net/wifi/nan/WifiNanSessionListener.java
index c9d08c7..d5e59f0 100644
--- a/wifi/java/android/net/wifi/nan/WifiNanSessionListener.java
+++ b/wifi/java/android/net/wifi/nan/WifiNanSessionListener.java
@@ -210,10 +210,10 @@
                                 msg.getData().getByteArray(MESSAGE_BUNDLE_KEY_MESSAGE2), msg.arg2);
                         break;
                     case LISTEN_MESSAGE_SEND_SUCCESS:
-                        WifiNanSessionListener.this.onMessageSendSuccess();
+                        WifiNanSessionListener.this.onMessageSendSuccess(msg.arg1);
                         break;
                     case LISTEN_MESSAGE_SEND_FAIL:
-                        WifiNanSessionListener.this.onMessageSendFail(msg.arg1);
+                        WifiNanSessionListener.this.onMessageSendFail(msg.arg1, msg.arg2);
                         break;
                     case LISTEN_MESSAGE_RECEIVED:
                         WifiNanSessionListener.this.onMessageReceived(msg.arg2,
@@ -306,7 +306,7 @@
      * {@link WifiNanSessionListener#onMessageSendFail(int)} will be received -
      * never both.
      */
-    public void onMessageSendSuccess() {
+    public void onMessageSendSuccess(int messageId) {
         if (VDBG) Log.v(TAG, "onMessageSendSuccess: called in stub - override if interested");
     }
 
@@ -325,7 +325,7 @@
      * @param reason The failure reason using {@code NanSessionListener.FAIL_*}
      *            codes.
      */
-    public void onMessageSendFail(int reason) {
+    public void onMessageSendFail(int messageId, int reason) {
         if (VDBG) Log.v(TAG, "onMessageSendFail: called in stub - override if interested");
     }
 
@@ -401,19 +401,21 @@
         }
 
         @Override
-        public void onMessageSendSuccess() {
+        public void onMessageSendSuccess(int messageId) {
             if (VDBG) Log.v(TAG, "onMessageSendSuccess");
 
             Message msg = mHandler.obtainMessage(LISTEN_MESSAGE_SEND_SUCCESS);
+            msg.arg1 = messageId;
             mHandler.sendMessage(msg);
         }
 
         @Override
-        public void onMessageSendFail(int reason) {
+        public void onMessageSendFail(int messageId, int reason) {
             if (VDBG) Log.v(TAG, "onMessageSendFail: reason=" + reason);
 
             Message msg = mHandler.obtainMessage(LISTEN_MESSAGE_SEND_FAIL);
-            msg.arg1 = reason;
+            msg.arg1 = messageId;
+            msg.arg2 = reason;
             mHandler.sendMessage(msg);
         }