Merge "WifiManager: startScan throttling"
diff --git a/api/current.txt b/api/current.txt
index c9003b4..8340bbf 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -9340,6 +9340,7 @@
     field public static final java.lang.String WALLPAPER_SERVICE = "wallpaper";
     field public static final java.lang.String WIFI_AWARE_SERVICE = "wifiaware";
     field public static final java.lang.String WIFI_P2P_SERVICE = "wifip2p";
+    field public static final java.lang.String WIFI_RTT_RANGING_SERVICE = "wifirtt";
     field public static final java.lang.String WIFI_SERVICE = "wifi";
     field public static final java.lang.String WINDOW_SERVICE = "window";
   }
@@ -11122,6 +11123,7 @@
     field public static final java.lang.String FEATURE_WIFI_AWARE = "android.hardware.wifi.aware";
     field public static final java.lang.String FEATURE_WIFI_DIRECT = "android.hardware.wifi.direct";
     field public static final java.lang.String FEATURE_WIFI_PASSPOINT = "android.hardware.wifi.passpoint";
+    field public static final java.lang.String FEATURE_WIFI_RTT = "android.hardware.wifi.rtt";
     field public static final int GET_ACTIVITIES = 1; // 0x1
     field public static final int GET_CONFIGURATIONS = 16384; // 0x4000
     field public static final deprecated int GET_DISABLED_COMPONENTS = 512; // 0x200
@@ -15531,6 +15533,7 @@
     field public static final android.hardware.camera2.CameraCharacteristics.Key<java.lang.Float> LENS_INFO_HYPERFOCAL_DISTANCE;
     field public static final android.hardware.camera2.CameraCharacteristics.Key<java.lang.Float> LENS_INFO_MINIMUM_FOCUS_DISTANCE;
     field public static final android.hardware.camera2.CameraCharacteristics.Key<float[]> LENS_INTRINSIC_CALIBRATION;
+    field public static final android.hardware.camera2.CameraCharacteristics.Key<java.lang.Integer> LENS_POSE_REFERENCE;
     field public static final android.hardware.camera2.CameraCharacteristics.Key<float[]> LENS_POSE_ROTATION;
     field public static final android.hardware.camera2.CameraCharacteristics.Key<float[]> LENS_POSE_TRANSLATION;
     field public static final android.hardware.camera2.CameraCharacteristics.Key<float[]> LENS_RADIAL_DISTORTION;
@@ -15603,6 +15606,8 @@
     method public abstract void createReprocessableCaptureSessionByConfigurations(android.hardware.camera2.params.InputConfiguration, java.util.List<android.hardware.camera2.params.OutputConfiguration>, android.hardware.camera2.CameraCaptureSession.StateCallback, android.os.Handler) throws android.hardware.camera2.CameraAccessException;
     method public abstract java.lang.String getId();
     field public static final int TEMPLATE_MANUAL = 6; // 0x6
+    field public static final int TEMPLATE_MOTION_TRACKING_BEST = 8; // 0x8
+    field public static final int TEMPLATE_MOTION_TRACKING_PREVIEW = 7; // 0x7
     field public static final int TEMPLATE_PREVIEW = 1; // 0x1
     field public static final int TEMPLATE_RECORD = 3; // 0x3
     field public static final int TEMPLATE_STILL_CAPTURE = 2; // 0x2
@@ -15705,6 +15710,7 @@
     field public static final int CONTROL_AWB_STATE_SEARCHING = 1; // 0x1
     field public static final int CONTROL_CAPTURE_INTENT_CUSTOM = 0; // 0x0
     field public static final int CONTROL_CAPTURE_INTENT_MANUAL = 6; // 0x6
+    field public static final int CONTROL_CAPTURE_INTENT_MOTION_TRACKING = 7; // 0x7
     field public static final int CONTROL_CAPTURE_INTENT_PREVIEW = 1; // 0x1
     field public static final int CONTROL_CAPTURE_INTENT_STILL_CAPTURE = 2; // 0x2
     field public static final int CONTROL_CAPTURE_INTENT_VIDEO_RECORD = 3; // 0x3
@@ -15771,6 +15777,8 @@
     field public static final int LENS_INFO_FOCUS_DISTANCE_CALIBRATION_UNCALIBRATED = 0; // 0x0
     field public static final int LENS_OPTICAL_STABILIZATION_MODE_OFF = 0; // 0x0
     field public static final int LENS_OPTICAL_STABILIZATION_MODE_ON = 1; // 0x1
+    field public static final int LENS_POSE_REFERENCE_GYROSCOPE = 1; // 0x1
+    field public static final int LENS_POSE_REFERENCE_PRIMARY_CAMERA = 0; // 0x0
     field public static final int LENS_STATE_MOVING = 1; // 0x1
     field public static final int LENS_STATE_STATIONARY = 0; // 0x0
     field public static final int NOISE_REDUCTION_MODE_FAST = 1; // 0x1
@@ -15784,6 +15792,7 @@
     field public static final int REQUEST_AVAILABLE_CAPABILITIES_DEPTH_OUTPUT = 8; // 0x8
     field public static final int REQUEST_AVAILABLE_CAPABILITIES_MANUAL_POST_PROCESSING = 2; // 0x2
     field public static final int REQUEST_AVAILABLE_CAPABILITIES_MANUAL_SENSOR = 1; // 0x1
+    field public static final int REQUEST_AVAILABLE_CAPABILITIES_MOTION_TRACKING = 10; // 0xa
     field public static final int REQUEST_AVAILABLE_CAPABILITIES_PRIVATE_REPROCESSING = 4; // 0x4
     field public static final int REQUEST_AVAILABLE_CAPABILITIES_RAW = 3; // 0x3
     field public static final int REQUEST_AVAILABLE_CAPABILITIES_READ_SENSOR_SETTINGS = 5; // 0x5
@@ -20181,6 +20190,13 @@
     ctor public ICUUncheckedIOException(java.lang.String, java.lang.Throwable);
   }
 
+  public class IllformedLocaleException extends java.lang.RuntimeException {
+    ctor public IllformedLocaleException();
+    ctor public IllformedLocaleException(java.lang.String);
+    ctor public IllformedLocaleException(java.lang.String, int);
+    method public int getErrorIndex();
+  }
+
   public class IndianCalendar extends android.icu.util.Calendar {
     ctor public IndianCalendar();
     ctor public IndianCalendar(android.icu.util.TimeZone);
@@ -27781,6 +27797,55 @@
 
 }
 
+package android.net.wifi.rtt {
+
+  public final class RangingRequest implements android.os.Parcelable {
+    method public int describeContents();
+    method public static int getMaxPeers();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.net.wifi.rtt.RangingRequest> CREATOR;
+  }
+
+  public static final class RangingRequest.Builder {
+    ctor public RangingRequest.Builder();
+    method public android.net.wifi.rtt.RangingRequest.Builder addAccessPoint(android.net.wifi.ScanResult);
+    method public android.net.wifi.rtt.RangingRequest.Builder addAccessPoints(java.util.List<android.net.wifi.ScanResult>);
+    method public android.net.wifi.rtt.RangingRequest.Builder addWifiAwarePeer(android.net.MacAddress);
+    method public android.net.wifi.rtt.RangingRequest.Builder addWifiAwarePeer(android.net.wifi.aware.PeerHandle);
+    method public android.net.wifi.rtt.RangingRequest build();
+  }
+
+  public final class RangingResult implements android.os.Parcelable {
+    method public int describeContents();
+    method public int getDistanceMm();
+    method public int getDistanceStdDevMm();
+    method public android.net.MacAddress getMacAddress();
+    method public android.net.wifi.aware.PeerHandle getPeerHandle();
+    method public long getRangingTimestampUs();
+    method public int getRssi();
+    method public int getStatus();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.net.wifi.rtt.RangingResult> CREATOR;
+    field public static final int STATUS_FAIL = 1; // 0x1
+    field public static final int STATUS_SUCCESS = 0; // 0x0
+  }
+
+  public abstract class RangingResultCallback {
+    ctor public RangingResultCallback();
+    method public abstract void onRangingFailure(int);
+    method public abstract void onRangingResults(java.util.List<android.net.wifi.rtt.RangingResult>);
+    field public static final int STATUS_CODE_FAIL = 1; // 0x1
+    field public static final int STATUS_CODE_FAIL_RTT_NOT_AVAILABLE = 2; // 0x2
+  }
+
+  public class WifiRttManager {
+    method public boolean isAvailable();
+    method public void startRanging(android.net.wifi.rtt.RangingRequest, android.net.wifi.rtt.RangingResultCallback, android.os.Handler);
+    field public static final java.lang.String ACTION_WIFI_RTT_STATE_CHANGED = "android.net.wifi.rtt.action.WIFI_RTT_STATE_CHANGED";
+  }
+
+}
+
 package android.nfc {
 
   public class FormatException extends java.lang.Exception {
@@ -40381,6 +40446,113 @@
 
 package android.telephony {
 
+  public final class AccessNetworkConstants {
+    ctor public AccessNetworkConstants();
+  }
+
+  public static final class AccessNetworkConstants.AccessNetworkType {
+    ctor public AccessNetworkConstants.AccessNetworkType();
+    field public static final int CDMA2000 = 4; // 0x4
+    field public static final int EUTRAN = 3; // 0x3
+    field public static final int GERAN = 1; // 0x1
+    field public static final int IWLAN = 5; // 0x5
+    field public static final int UTRAN = 2; // 0x2
+  }
+
+  public static final class AccessNetworkConstants.EutranBand {
+    ctor public AccessNetworkConstants.EutranBand();
+    field public static final int BAND_1 = 1; // 0x1
+    field public static final int BAND_10 = 10; // 0xa
+    field public static final int BAND_11 = 11; // 0xb
+    field public static final int BAND_12 = 12; // 0xc
+    field public static final int BAND_13 = 13; // 0xd
+    field public static final int BAND_14 = 14; // 0xe
+    field public static final int BAND_17 = 17; // 0x11
+    field public static final int BAND_18 = 18; // 0x12
+    field public static final int BAND_19 = 19; // 0x13
+    field public static final int BAND_2 = 2; // 0x2
+    field public static final int BAND_20 = 20; // 0x14
+    field public static final int BAND_21 = 21; // 0x15
+    field public static final int BAND_22 = 22; // 0x16
+    field public static final int BAND_23 = 23; // 0x17
+    field public static final int BAND_24 = 24; // 0x18
+    field public static final int BAND_25 = 25; // 0x19
+    field public static final int BAND_26 = 26; // 0x1a
+    field public static final int BAND_27 = 27; // 0x1b
+    field public static final int BAND_28 = 28; // 0x1c
+    field public static final int BAND_3 = 3; // 0x3
+    field public static final int BAND_30 = 30; // 0x1e
+    field public static final int BAND_31 = 31; // 0x1f
+    field public static final int BAND_33 = 33; // 0x21
+    field public static final int BAND_34 = 34; // 0x22
+    field public static final int BAND_35 = 35; // 0x23
+    field public static final int BAND_36 = 36; // 0x24
+    field public static final int BAND_37 = 37; // 0x25
+    field public static final int BAND_38 = 38; // 0x26
+    field public static final int BAND_39 = 39; // 0x27
+    field public static final int BAND_4 = 4; // 0x4
+    field public static final int BAND_40 = 40; // 0x28
+    field public static final int BAND_41 = 41; // 0x29
+    field public static final int BAND_42 = 42; // 0x2a
+    field public static final int BAND_43 = 43; // 0x2b
+    field public static final int BAND_44 = 44; // 0x2c
+    field public static final int BAND_45 = 45; // 0x2d
+    field public static final int BAND_46 = 46; // 0x2e
+    field public static final int BAND_47 = 47; // 0x2f
+    field public static final int BAND_48 = 48; // 0x30
+    field public static final int BAND_5 = 5; // 0x5
+    field public static final int BAND_6 = 6; // 0x6
+    field public static final int BAND_65 = 65; // 0x41
+    field public static final int BAND_66 = 66; // 0x42
+    field public static final int BAND_68 = 68; // 0x44
+    field public static final int BAND_7 = 7; // 0x7
+    field public static final int BAND_70 = 70; // 0x46
+    field public static final int BAND_8 = 8; // 0x8
+    field public static final int BAND_9 = 9; // 0x9
+  }
+
+  public static final class AccessNetworkConstants.GeranBand {
+    ctor public AccessNetworkConstants.GeranBand();
+    field public static final int BAND_450 = 3; // 0x3
+    field public static final int BAND_480 = 4; // 0x4
+    field public static final int BAND_710 = 5; // 0x5
+    field public static final int BAND_750 = 6; // 0x6
+    field public static final int BAND_850 = 8; // 0x8
+    field public static final int BAND_DCS1800 = 12; // 0xc
+    field public static final int BAND_E900 = 10; // 0xa
+    field public static final int BAND_ER900 = 14; // 0xe
+    field public static final int BAND_P900 = 9; // 0x9
+    field public static final int BAND_PCS1900 = 13; // 0xd
+    field public static final int BAND_R900 = 11; // 0xb
+    field public static final int BAND_T380 = 1; // 0x1
+    field public static final int BAND_T410 = 2; // 0x2
+    field public static final int BAND_T810 = 7; // 0x7
+  }
+
+  public static final class AccessNetworkConstants.UtranBand {
+    ctor public AccessNetworkConstants.UtranBand();
+    field public static final int BAND_1 = 1; // 0x1
+    field public static final int BAND_10 = 10; // 0xa
+    field public static final int BAND_11 = 11; // 0xb
+    field public static final int BAND_12 = 12; // 0xc
+    field public static final int BAND_13 = 13; // 0xd
+    field public static final int BAND_14 = 14; // 0xe
+    field public static final int BAND_19 = 19; // 0x13
+    field public static final int BAND_2 = 2; // 0x2
+    field public static final int BAND_20 = 20; // 0x14
+    field public static final int BAND_21 = 21; // 0x15
+    field public static final int BAND_22 = 22; // 0x16
+    field public static final int BAND_25 = 25; // 0x19
+    field public static final int BAND_26 = 26; // 0x1a
+    field public static final int BAND_3 = 3; // 0x3
+    field public static final int BAND_4 = 4; // 0x4
+    field public static final int BAND_5 = 5; // 0x5
+    field public static final int BAND_6 = 6; // 0x6
+    field public static final int BAND_7 = 7; // 0x7
+    field public static final int BAND_8 = 8; // 0x8
+    field public static final int BAND_9 = 9; // 0x9
+  }
+
   public class CarrierConfigManager {
     method public android.os.PersistableBundle getConfig();
     method public android.os.PersistableBundle getConfigForSubId(int);
@@ -40905,111 +41077,6 @@
     field public static final android.os.Parcelable.Creator<android.telephony.RadioAccessSpecifier> CREATOR;
   }
 
-  public final class RadioNetworkConstants {
-    ctor public RadioNetworkConstants();
-  }
-
-  public static final class RadioNetworkConstants.EutranBands {
-    ctor public RadioNetworkConstants.EutranBands();
-    field public static final int BAND_1 = 1; // 0x1
-    field public static final int BAND_10 = 10; // 0xa
-    field public static final int BAND_11 = 11; // 0xb
-    field public static final int BAND_12 = 12; // 0xc
-    field public static final int BAND_13 = 13; // 0xd
-    field public static final int BAND_14 = 14; // 0xe
-    field public static final int BAND_17 = 17; // 0x11
-    field public static final int BAND_18 = 18; // 0x12
-    field public static final int BAND_19 = 19; // 0x13
-    field public static final int BAND_2 = 2; // 0x2
-    field public static final int BAND_20 = 20; // 0x14
-    field public static final int BAND_21 = 21; // 0x15
-    field public static final int BAND_22 = 22; // 0x16
-    field public static final int BAND_23 = 23; // 0x17
-    field public static final int BAND_24 = 24; // 0x18
-    field public static final int BAND_25 = 25; // 0x19
-    field public static final int BAND_26 = 26; // 0x1a
-    field public static final int BAND_27 = 27; // 0x1b
-    field public static final int BAND_28 = 28; // 0x1c
-    field public static final int BAND_3 = 3; // 0x3
-    field public static final int BAND_30 = 30; // 0x1e
-    field public static final int BAND_31 = 31; // 0x1f
-    field public static final int BAND_33 = 33; // 0x21
-    field public static final int BAND_34 = 34; // 0x22
-    field public static final int BAND_35 = 35; // 0x23
-    field public static final int BAND_36 = 36; // 0x24
-    field public static final int BAND_37 = 37; // 0x25
-    field public static final int BAND_38 = 38; // 0x26
-    field public static final int BAND_39 = 39; // 0x27
-    field public static final int BAND_4 = 4; // 0x4
-    field public static final int BAND_40 = 40; // 0x28
-    field public static final int BAND_41 = 41; // 0x29
-    field public static final int BAND_42 = 42; // 0x2a
-    field public static final int BAND_43 = 43; // 0x2b
-    field public static final int BAND_44 = 44; // 0x2c
-    field public static final int BAND_45 = 45; // 0x2d
-    field public static final int BAND_46 = 46; // 0x2e
-    field public static final int BAND_47 = 47; // 0x2f
-    field public static final int BAND_48 = 48; // 0x30
-    field public static final int BAND_5 = 5; // 0x5
-    field public static final int BAND_6 = 6; // 0x6
-    field public static final int BAND_65 = 65; // 0x41
-    field public static final int BAND_66 = 66; // 0x42
-    field public static final int BAND_68 = 68; // 0x44
-    field public static final int BAND_7 = 7; // 0x7
-    field public static final int BAND_70 = 70; // 0x46
-    field public static final int BAND_8 = 8; // 0x8
-    field public static final int BAND_9 = 9; // 0x9
-  }
-
-  public static final class RadioNetworkConstants.GeranBands {
-    ctor public RadioNetworkConstants.GeranBands();
-    field public static final int BAND_450 = 3; // 0x3
-    field public static final int BAND_480 = 4; // 0x4
-    field public static final int BAND_710 = 5; // 0x5
-    field public static final int BAND_750 = 6; // 0x6
-    field public static final int BAND_850 = 8; // 0x8
-    field public static final int BAND_DCS1800 = 12; // 0xc
-    field public static final int BAND_E900 = 10; // 0xa
-    field public static final int BAND_ER900 = 14; // 0xe
-    field public static final int BAND_P900 = 9; // 0x9
-    field public static final int BAND_PCS1900 = 13; // 0xd
-    field public static final int BAND_R900 = 11; // 0xb
-    field public static final int BAND_T380 = 1; // 0x1
-    field public static final int BAND_T410 = 2; // 0x2
-    field public static final int BAND_T810 = 7; // 0x7
-  }
-
-  public static final class RadioNetworkConstants.RadioAccessNetworks {
-    ctor public RadioNetworkConstants.RadioAccessNetworks();
-    field public static final int EUTRAN = 3; // 0x3
-    field public static final int GERAN = 1; // 0x1
-    field public static final int UTRAN = 2; // 0x2
-  }
-
-  public static final class RadioNetworkConstants.UtranBands {
-    ctor public RadioNetworkConstants.UtranBands();
-    field public static final int BAND_1 = 1; // 0x1
-    field public static final int BAND_10 = 10; // 0xa
-    field public static final int BAND_11 = 11; // 0xb
-    field public static final int BAND_12 = 12; // 0xc
-    field public static final int BAND_13 = 13; // 0xd
-    field public static final int BAND_14 = 14; // 0xe
-    field public static final int BAND_19 = 19; // 0x13
-    field public static final int BAND_2 = 2; // 0x2
-    field public static final int BAND_20 = 20; // 0x14
-    field public static final int BAND_21 = 21; // 0x15
-    field public static final int BAND_22 = 22; // 0x16
-    field public static final int BAND_25 = 25; // 0x19
-    field public static final int BAND_26 = 26; // 0x1a
-    field public static final int BAND_3 = 3; // 0x3
-    field public static final int BAND_4 = 4; // 0x4
-    field public static final int BAND_5 = 5; // 0x5
-    field public static final int BAND_6 = 6; // 0x6
-    field public static final int BAND_7 = 7; // 0x7
-    field public static final int BAND_8 = 8; // 0x8
-    field public static final int BAND_9 = 9; // 0x9
-  }
-
   public class ServiceState implements android.os.Parcelable {
     ctor public ServiceState();
     ctor public ServiceState(android.telephony.ServiceState);
diff --git a/api/system-current.txt b/api/system-current.txt
index 6a628a8..b2d5a49 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -174,6 +174,7 @@
     field public static final java.lang.String UPDATE_LOCK = "android.permission.UPDATE_LOCK";
     field public static final java.lang.String UPDATE_TIME_ZONE_RULES = "android.permission.UPDATE_TIME_ZONE_RULES";
     field public static final java.lang.String USER_ACTIVITY = "android.permission.USER_ACTIVITY";
+    field public static final java.lang.String USE_RESERVED_DISK = "android.permission.USE_RESERVED_DISK";
     field public static final java.lang.String WRITE_APN_SETTINGS = "android.permission.WRITE_APN_SETTINGS";
     field public static final java.lang.String WRITE_DREAM_STATE = "android.permission.WRITE_DREAM_STATE";
     field public static final java.lang.String WRITE_GSERVICES = "android.permission.WRITE_GSERVICES";
@@ -1449,7 +1450,7 @@
     method public abstract void onMessageReceipt(int, int, android.hardware.location.ContextHubMessage);
   }
 
-  public class ContextHubMessage {
+  public deprecated class ContextHubMessage {
     ctor public ContextHubMessage(int, int, byte[]);
     method public int describeContents();
     method public byte[] getData();
@@ -1580,7 +1581,7 @@
     field public static final android.os.Parcelable.Creator<android.hardware.location.MemoryRegion> CREATOR;
   }
 
-  public class NanoApp {
+  public deprecated class NanoApp {
     ctor public NanoApp();
     ctor public deprecated NanoApp(int, byte[]);
     ctor public NanoApp(long, byte[]);
@@ -1628,7 +1629,7 @@
     field public static final android.os.Parcelable.Creator<android.hardware.location.NanoAppBinary> CREATOR;
   }
 
-  public class NanoAppFilter {
+  public deprecated class NanoAppFilter {
     ctor public NanoAppFilter(long, int, int, long);
     method public int describeContents();
     method public boolean testMatch(android.hardware.location.NanoAppInstanceInfo);
@@ -1643,7 +1644,7 @@
     field public static final int VENDOR_ANY = -1; // 0xffffffff
   }
 
-  public class NanoAppInstanceInfo {
+  public deprecated class NanoAppInstanceInfo {
     ctor public NanoAppInstanceInfo();
     method public int describeContents();
     method public long getAppId();
@@ -1752,6 +1753,15 @@
     field public static final int CLASS_AM_FM = 0; // 0x0
     field public static final int CLASS_DT = 2; // 0x2
     field public static final int CLASS_SAT = 1; // 0x1
+    field public static final int CONFIG_DAB_DAB_LINKING = 6; // 0x6
+    field public static final int CONFIG_DAB_DAB_SOFT_LINKING = 8; // 0x8
+    field public static final int CONFIG_DAB_FM_LINKING = 7; // 0x7
+    field public static final int CONFIG_DAB_FM_SOFT_LINKING = 9; // 0x9
+    field public static final int CONFIG_FORCE_ANALOG = 2; // 0x2
+    field public static final int CONFIG_FORCE_DIGITAL = 3; // 0x3
+    field public static final int CONFIG_FORCE_MONO = 1; // 0x1
+    field public static final int CONFIG_RDS_AF = 4; // 0x4
+    field public static final int CONFIG_RDS_REG = 5; // 0x5
     field public static final int REGION_ITU_1 = 0; // 0x0
     field public static final int REGION_ITU_2 = 1; // 0x1
     field public static final int REGION_JAPAN = 3; // 0x3
@@ -1931,15 +1941,20 @@
     method public abstract void close();
     method public abstract int getConfiguration(android.hardware.radio.RadioManager.BandConfig[]);
     method public abstract boolean getMute();
+    method public java.util.Map<java.lang.String, java.lang.String> getParameters(java.util.List<java.lang.String>);
     method public abstract int getProgramInformation(android.hardware.radio.RadioManager.ProgramInfo[]);
     method public abstract java.util.List<android.hardware.radio.RadioManager.ProgramInfo> getProgramList(java.util.Map<java.lang.String, java.lang.String>);
     method public abstract boolean hasControl();
-    method public abstract boolean isAnalogForced();
+    method public abstract deprecated boolean isAnalogForced();
     method public abstract boolean isAntennaConnected();
+    method public boolean isConfigFlagSet(int);
+    method public boolean isConfigFlagSupported(int);
     method public abstract int scan(int, boolean);
-    method public abstract void setAnalogForced(boolean);
+    method public abstract deprecated void setAnalogForced(boolean);
+    method public void setConfigFlag(int, boolean);
     method public abstract int setConfiguration(android.hardware.radio.RadioManager.BandConfig);
     method public abstract int setMute(boolean);
+    method public java.util.Map<java.lang.String, java.lang.String> setParameters(java.util.Map<java.lang.String, java.lang.String>);
     method public abstract boolean startBackgroundScan();
     method public abstract int step(int, boolean);
     method public abstract deprecated int tune(int, int);
@@ -1965,6 +1980,7 @@
     method public void onEmergencyAnnouncement(boolean);
     method public void onError(int);
     method public deprecated void onMetadataChanged(android.hardware.radio.RadioMetadata);
+    method public void onParametersUpdated(java.util.Map<java.lang.String, java.lang.String>);
     method public void onProgramInfoChanged(android.hardware.radio.RadioManager.ProgramInfo);
     method public void onProgramListChanged();
     method public void onTrafficAnnouncement(boolean);
@@ -3257,6 +3273,52 @@
 
 }
 
+package android.net.wifi.rtt {
+
+  public static final class RangingRequest.Builder {
+    method public android.net.wifi.rtt.RangingRequest.Builder addResponder(android.net.wifi.rtt.ResponderConfig);
+  }
+
+  public final class ResponderConfig implements android.os.Parcelable {
+    ctor public ResponderConfig(android.net.MacAddress, int, boolean, int, int, int, int, int);
+    ctor public ResponderConfig(android.net.wifi.aware.PeerHandle, int, boolean, int, int, int, int, int);
+    method public int describeContents();
+    method public static android.net.wifi.rtt.ResponderConfig fromScanResult(android.net.wifi.ScanResult);
+    method public static android.net.wifi.rtt.ResponderConfig fromWifiAwarePeerHandleWithDefaults(android.net.wifi.aware.PeerHandle);
+    method public static android.net.wifi.rtt.ResponderConfig fromWifiAwarePeerMacAddressWithDefaults(android.net.MacAddress);
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final int CHANNEL_WIDTH_160MHZ = 3; // 0x3
+    field public static final int CHANNEL_WIDTH_20MHZ = 0; // 0x0
+    field public static final int CHANNEL_WIDTH_40MHZ = 1; // 0x1
+    field public static final int CHANNEL_WIDTH_80MHZ = 2; // 0x2
+    field public static final int CHANNEL_WIDTH_80MHZ_PLUS_MHZ = 4; // 0x4
+    field public static final android.os.Parcelable.Creator<android.net.wifi.rtt.ResponderConfig> CREATOR;
+    field public static final int PREAMBLE_HT = 1; // 0x1
+    field public static final int PREAMBLE_LEGACY = 0; // 0x0
+    field public static final int PREAMBLE_VHT = 2; // 0x2
+    field public static final int RESPONDER_AP = 0; // 0x0
+    field public static final int RESPONDER_AWARE = 4; // 0x4
+    field public static final int RESPONDER_P2P_CLIENT = 3; // 0x3
+    field public static final int RESPONDER_P2P_GO = 2; // 0x2
+    field public static final int RESPONDER_STA = 1; // 0x1
+    field public final int centerFreq0;
+    field public final int centerFreq1;
+    field public final int channelWidth;
+    field public final int frequency;
+    field public final android.net.MacAddress macAddress;
+    field public final android.net.wifi.aware.PeerHandle peerHandle;
+    field public final int preamble;
+    field public final int responderType;
+    field public final boolean supports80211mc;
+  }
+
+  public class WifiRttManager {
+    method public void cancelRanging(android.os.WorkSource);
+    method public void startRanging(android.os.WorkSource, android.net.wifi.rtt.RangingRequest, android.net.wifi.rtt.RangingResultCallback, android.os.Handler);
+  }
+
+}
+
 package android.nfc {
 
   public final class NfcAdapter {
diff --git a/api/system-removed.txt b/api/system-removed.txt
index f98d011..a3bf576 100644
--- a/api/system-removed.txt
+++ b/api/system-removed.txt
@@ -142,10 +142,7 @@
   }
 
   public class WifiManager {
-    method public deprecated java.util.List<android.net.wifi.BatchedScanResult> getBatchedScanResults();
     method public android.net.wifi.WifiConnectionStatistics getConnectionStatistics();
-    method public deprecated boolean isBatchedScanSupported();
-    method public deprecated boolean startLocationRestrictedScan(android.os.WorkSource);
   }
 
 }
diff --git a/cmds/incidentd/Android.mk b/cmds/incidentd/Android.mk
index 1cf02d3..8420bc8 100644
--- a/cmds/incidentd/Android.mk
+++ b/cmds/incidentd/Android.mk
@@ -91,7 +91,9 @@
 
 gen_src_dir:=
 
+ifeq ($(BUILD_WITH_INCIDENTD_RC), true)
 LOCAL_INIT_RC := incidentd.rc
+endif
 
 include $(BUILD_EXECUTABLE)
 
diff --git a/cmds/statsd/src/StatsLogProcessor.cpp b/cmds/statsd/src/StatsLogProcessor.cpp
index 991badc..a9e0f23 100644
--- a/cmds/statsd/src/StatsLogProcessor.cpp
+++ b/cmds/statsd/src/StatsLogProcessor.cpp
@@ -82,9 +82,7 @@
 void StatsLogProcessor::onAnomalyAlarmFired(
         const uint64_t timestampNs,
         unordered_set<sp<const AnomalyAlarm>, SpHash<AnomalyAlarm>> anomalySet) {
-    // TODO: This is a thread-safety issue. mMetricsManagers could change under our feet.
-    // TODO: Solution? Lock everything! :(
-    // TODO: Question: Can we replace the other lock (broadcast), or do we need to supplement it?
+    std::lock_guard<std::mutex> lock(mMetricsMutex);
     for (const auto& itr : mMetricsManagers) {
         itr.second->onAnomalyAlarmFired(timestampNs, anomalySet);
     }
@@ -92,11 +90,13 @@
 
 // TODO: what if statsd service restarts? How do we know what logs are already processed before?
 void StatsLogProcessor::OnLogEvent(const LogEvent& msg) {
+    std::lock_guard<std::mutex> lock(mMetricsMutex);
+
     StatsdStats::getInstance().noteAtomLogged(msg.GetTagId(), msg.GetTimestampNs() / NS_PER_SEC);
     // pass the event to metrics managers.
     for (auto& pair : mMetricsManagers) {
         pair.second->onLogEvent(msg);
-        flushIfNecessary(msg.GetTimestampNs(), pair.first, *(pair.second));
+        flushIfNecessaryLocked(msg.GetTimestampNs(), pair.first, *(pair.second));
     }
     // Hard-coded logic to update the isolated uid's in the uid-map.
     // The field numbers need to be currently updated by hand with atoms.proto
@@ -116,6 +116,7 @@
 }
 
 void StatsLogProcessor::OnConfigUpdated(const ConfigKey& key, const StatsdConfig& config) {
+    std::lock_guard<std::mutex> lock(mMetricsMutex);
     ALOGD("Updated configuration for key %s", key.ToString().c_str());
     sp<MetricsManager> newMetricsManager = new MetricsManager(key, config, mTimeBaseSec, mUidMap);
     auto it = mMetricsManagers.find(key);
@@ -142,6 +143,7 @@
 }
 
 size_t StatsLogProcessor::GetMetricsSize(const ConfigKey& key) const {
+    std::lock_guard<std::mutex> lock(mMetricsMutex);
     auto it = mMetricsManagers.find(key);
     if (it == mMetricsManagers.end()) {
         ALOGW("Config source %s does not exist", key.ToString().c_str());
@@ -152,6 +154,7 @@
 
 void StatsLogProcessor::onDumpReport(const ConfigKey& key, const uint64_t& dumpTimeStampNs,
                                      ConfigMetricsReportList* report) {
+    std::lock_guard<std::mutex> lock(mMetricsMutex);
     auto it = mMetricsManagers.find(key);
     if (it == mMetricsManagers.end()) {
         ALOGW("Config source %s does not exist", key.ToString().c_str());
@@ -165,6 +168,7 @@
 }
 
 void StatsLogProcessor::onDumpReport(const ConfigKey& key, vector<uint8_t>* outData) {
+    std::lock_guard<std::mutex> lock(mMetricsMutex);
     auto it = mMetricsManagers.find(key);
     if (it == mMetricsManagers.end()) {
         ALOGW("Config source %s does not exist", key.ToString().c_str());
@@ -173,9 +177,7 @@
 
     // This allows another broadcast to be sent within the rate-limit period if we get close to
     // filling the buffer again soon.
-    mBroadcastTimesMutex.lock();
     mLastBroadcastTimes.erase(key);
-    mBroadcastTimesMutex.unlock();
 
     ProtoOutputStream proto;
 
@@ -224,6 +226,7 @@
 }
 
 void StatsLogProcessor::OnConfigRemoved(const ConfigKey& key) {
+    std::lock_guard<std::mutex> lock(mMetricsMutex);
     auto it = mMetricsManagers.find(key);
     if (it != mMetricsManagers.end()) {
         mMetricsManagers.erase(it);
@@ -231,14 +234,11 @@
     }
     StatsdStats::getInstance().noteConfigRemoved(key);
 
-    std::lock_guard<std::mutex> lock(mBroadcastTimesMutex);
     mLastBroadcastTimes.erase(key);
 }
 
-void StatsLogProcessor::flushIfNecessary(uint64_t timestampNs, const ConfigKey& key,
-                                         MetricsManager& metricsManager) {
-    std::lock_guard<std::mutex> lock(mBroadcastTimesMutex);
-
+void StatsLogProcessor::flushIfNecessaryLocked(
+    uint64_t timestampNs, const ConfigKey& key, MetricsManager& metricsManager) {
     auto lastCheckTime = mLastByteSizeTimes.find(key);
     if (lastCheckTime != mLastByteSizeTimes.end()) {
         if (timestampNs - lastCheckTime->second < StatsdStats::kMinByteSizeCheckPeriodNs) {
@@ -274,6 +274,7 @@
 
 void StatsLogProcessor::WriteDataToDisk() {
     mkdir(STATS_DATA_DIR, S_IRWXU);
+    std::lock_guard<std::mutex> lock(mMetricsMutex);
     for (auto& pair : mMetricsManagers) {
         const ConfigKey& key = pair.first;
         vector<uint8_t> data;
diff --git a/cmds/statsd/src/StatsLogProcessor.h b/cmds/statsd/src/StatsLogProcessor.h
index f62fc4e..b527e27 100644
--- a/cmds/statsd/src/StatsLogProcessor.h
+++ b/cmds/statsd/src/StatsLogProcessor.h
@@ -57,7 +57,7 @@
     void WriteDataToDisk();
 
 private:
-    mutable mutex mBroadcastTimesMutex;
+    mutable mutex mMetricsMutex;
 
     std::unordered_map<ConfigKey, sp<MetricsManager>> mMetricsManagers;
 
@@ -72,8 +72,8 @@
 
     /* Check if we should send a broadcast if approaching memory limits and if we're over, we
      * actually delete the data. */
-    void flushIfNecessary(uint64_t timestampNs, const ConfigKey& key,
-                          MetricsManager& metricsManager);
+    void flushIfNecessaryLocked(uint64_t timestampNs, const ConfigKey& key,
+                                MetricsManager& metricsManager);
 
     // Function used to send a broadcast so that receiver for the config key can call getData
     // to retrieve the stored data.
diff --git a/cmds/statsd/src/anomaly/AnomalyTracker.cpp b/cmds/statsd/src/anomaly/AnomalyTracker.cpp
index 05c68e1..f10b2cf 100644
--- a/cmds/statsd/src/anomaly/AnomalyTracker.cpp
+++ b/cmds/statsd/src/anomaly/AnomalyTracker.cpp
@@ -162,45 +162,23 @@
     return 0;
 }
 
-bool AnomalyTracker::detectAnomaly(const int64_t& currentBucketNum,
-                                   const DimToValMap& currentBucket) {
-    if (currentBucketNum > mMostRecentBucketNum + 1) {
-        addPastBucket(nullptr, currentBucketNum - 1);
-    }
-    for (auto itr = currentBucket.begin(); itr != currentBucket.end(); itr++) {
-        if (itr->second + getSumOverPastBuckets(itr->first) > mAlert.trigger_if_sum_gt()) {
-            return true;
-        }
-    }
-    // In theory, we also need to check the dimsions not in the current bucket. In single-thread
-    // mode, usually we could avoid the following loops.
-    for (auto itr = mSumOverPastBuckets.begin(); itr != mSumOverPastBuckets.end(); itr++) {
-        if (itr->second > mAlert.trigger_if_sum_gt()) {
-            return true;
-        }
-    }
-    return false;
-}
-
 bool AnomalyTracker::detectAnomaly(const int64_t& currentBucketNum, const HashableDimensionKey& key,
                                    const int64_t& currentBucketValue) {
     if (currentBucketNum > mMostRecentBucketNum + 1) {
+        // TODO: This creates a needless 0 entry in mSumOverPastBuckets. Fix this.
         addPastBucket(key, 0, currentBucketNum - 1);
     }
     return mAlert.has_trigger_if_sum_gt()
             && getSumOverPastBuckets(key) + currentBucketValue > mAlert.trigger_if_sum_gt();
 }
 
-void AnomalyTracker::declareAnomaly(const uint64_t& timestampNs) {
-    // TODO: This should also take in the const HashableDimensionKey& key, to pass
-    //       more details to incidentd and to make mRefractoryPeriodEndsSec key-specific.
+void AnomalyTracker::declareAnomaly(const uint64_t& timestampNs, const HashableDimensionKey& key) {
     // TODO: Why receive timestamp? RefractoryPeriod should always be based on real time right now.
-    if (isInRefractoryPeriod(timestampNs)) {
+    if (isInRefractoryPeriod(timestampNs, key)) {
         VLOG("Skipping anomaly declaration since within refractory period");
         return;
     }
-    // TODO(guardrail): Consider guarding against too short refractory periods.
-    mLastAnomalyTimestampNs = timestampNs;
+    mRefractoryPeriodEndsSec[key] = (timestampNs / NS_PER_SEC) + mAlert.refractory_period_secs();
 
     // TODO: If we had access to the bucket_size_millis, consider calling resetStorage()
     // if (mAlert.refractory_period_secs() > mNumOfPastBuckets * bucketSizeNs) { resetStorage(); }
@@ -208,7 +186,7 @@
     if (!mSubscriptions.empty()) {
         if (mAlert.has_id()) {
             ALOGI("An anomaly (%llu) has occurred! Informing subscribers.",mAlert.id());
-            informSubscribers();
+            informSubscribers(key);
         } else {
             ALOGI("An anomaly (with no id) has occurred! Not informing any subscribers.");
         }
@@ -218,6 +196,7 @@
 
     StatsdStats::getInstance().noteAnomalyDeclared(mConfigKey, mAlert.id());
 
+    // TODO: This should also take in the const HashableDimensionKey& key?
     android::util::stats_write(android::util::ANOMALY_DETECTED, mConfigKey.GetUid(),
                                mConfigKey.GetId(), mAlert.id());
 }
@@ -227,24 +206,24 @@
                                              const HashableDimensionKey& key,
                                              const int64_t& currentBucketValue) {
     if (detectAnomaly(currBucketNum, key, currentBucketValue)) {
-        declareAnomaly(timestampNs);
+        declareAnomaly(timestampNs, key);
     }
 }
 
-void AnomalyTracker::detectAndDeclareAnomaly(const uint64_t& timestampNs,
-                                             const int64_t& currBucketNum,
-                                             const DimToValMap& currentBucket) {
-    if (detectAnomaly(currBucketNum, currentBucket)) {
-        declareAnomaly(timestampNs);
+bool AnomalyTracker::isInRefractoryPeriod(const uint64_t& timestampNs,
+                                          const HashableDimensionKey& key) {
+    const auto& it = mRefractoryPeriodEndsSec.find(key);
+    if (it != mRefractoryPeriodEndsSec.end()) {
+        if ((timestampNs / NS_PER_SEC) <= it->second) {
+            return true;
+        } else {
+            mRefractoryPeriodEndsSec.erase(key);
+        }
     }
+    return false;
 }
 
-bool AnomalyTracker::isInRefractoryPeriod(const uint64_t& timestampNs) const {
-    return mLastAnomalyTimestampNs >= 0 &&
-            timestampNs - mLastAnomalyTimestampNs <= mAlert.refractory_period_secs() * NS_PER_SEC;
-}
-
-void AnomalyTracker::informSubscribers() {
+void AnomalyTracker::informSubscribers(const HashableDimensionKey& key) {
     VLOG("informSubscribers called.");
     if (mSubscriptions.empty()) {
         ALOGE("Attempt to call with no subscribers.");
diff --git a/cmds/statsd/src/anomaly/AnomalyTracker.h b/cmds/statsd/src/anomaly/AnomalyTracker.h
index 2d5ab86..472c02c 100644
--- a/cmds/statsd/src/anomaly/AnomalyTracker.h
+++ b/cmds/statsd/src/anomaly/AnomalyTracker.h
@@ -52,17 +52,14 @@
                        const int64_t& bucketNum);
 
     // Returns true if detected anomaly for the existing buckets on one or more dimension keys.
-    bool detectAnomaly(const int64_t& currBucketNum, const DimToValMap& currentBucket);
     bool detectAnomaly(const int64_t& currBucketNum, const HashableDimensionKey& key,
                        const int64_t& currentBucketValue);
 
     // Informs incidentd about the detected alert.
-    void declareAnomaly(const uint64_t& timestampNs);
+    void declareAnomaly(const uint64_t& timestampNs, const HashableDimensionKey& key);
 
     // Detects the alert and informs the incidentd when applicable.
     void detectAndDeclareAnomaly(const uint64_t& timestampNs, const int64_t& currBucketNum,
-                                 const DimToValMap& currentBucket);
-    void detectAndDeclareAnomaly(const uint64_t& timestampNs, const int64_t& currBucketNum,
                                  const HashableDimensionKey& key,
                                  const int64_t& currentBucketValue);
 
@@ -82,9 +79,11 @@
         return mAlert.trigger_if_sum_gt();
     }
 
-    // Helper function to return the timestamp of the last detected anomaly.
-    inline int64_t getLastAnomalyTimestampNs() const {
-        return mLastAnomalyTimestampNs;
+    // Returns the refractory period timestamp (in seconds) for the given key.
+    // If there is no stored refractory period ending timestamp, returns 0.
+    uint32_t getRefractoryPeriodEndsSec(const HashableDimensionKey& key) const {
+        const auto& it = mRefractoryPeriodEndsSec.find(key);
+        return it != mRefractoryPeriodEndsSec.end() ? it->second : 0;
     }
 
     inline int getNumOfPastBuckets() const {
@@ -121,8 +120,11 @@
     // The bucket number of the last added bucket.
     int64_t mMostRecentBucketNum = -1;
 
-    // The timestamp when the last anomaly was declared.
-    int64_t mLastAnomalyTimestampNs = -1;
+    // Map from each dimension to the timestamp that its refractory period (if this anomaly was
+    // declared for that dimension) ends, in seconds. Only anomalies that occur after this period
+    // ends will be declared.
+    // Entries may be, but are not guaranteed to be, removed after the period is finished.
+    unordered_map<HashableDimensionKey, uint32_t> mRefractoryPeriodEndsSec;
 
     void flushPastBuckets(const int64_t& currBucketNum);
 
@@ -133,7 +135,7 @@
     // and remove any items with value 0.
     void subtractBucketFromSum(const shared_ptr<DimToValMap>& bucket);
 
-    bool isInRefractoryPeriod(const uint64_t& timestampNs) const;
+    bool isInRefractoryPeriod(const uint64_t& timestampNs, const HashableDimensionKey& key);
 
     // Calculates the corresponding bucket index within the circular array.
     size_t index(int64_t bucketNum) const;
@@ -142,12 +144,12 @@
     virtual void resetStorage();
 
     // Informs the subscribers that an anomaly has occurred.
-    void informSubscribers();
+    void informSubscribers(const HashableDimensionKey& key);
 
     FRIEND_TEST(AnomalyTrackerTest, TestConsecutiveBuckets);
     FRIEND_TEST(AnomalyTrackerTest, TestSparseBuckets);
     FRIEND_TEST(GaugeMetricProducerTest, TestAnomalyDetection);
-    FRIEND_TEST(CountMetricProducerTest, TestAnomalyDetection);
+    FRIEND_TEST(CountMetricProducerTest, TestAnomalyDetectionUnSliced);
 };
 
 }  // namespace statsd
diff --git a/cmds/statsd/src/anomaly/DurationAnomalyTracker.cpp b/cmds/statsd/src/anomaly/DurationAnomalyTracker.cpp
index d30810f..7576a38 100644
--- a/cmds/statsd/src/anomaly/DurationAnomalyTracker.cpp
+++ b/cmds/statsd/src/anomaly/DurationAnomalyTracker.cpp
@@ -46,7 +46,7 @@
 
     if (itr->second != nullptr &&
         static_cast<uint32_t>(timestampNs / NS_PER_SEC) >= itr->second->timestampSec) {
-        declareAnomaly(timestampNs);
+        declareAnomaly(timestampNs, dimensionKey);
         stopAlarm(dimensionKey);
     }
 }
@@ -55,7 +55,7 @@
                                 const uint64_t& timestampNs) {
 
     uint32_t timestampSec = static_cast<uint32_t>(timestampNs / NS_PER_SEC);
-    if (isInRefractoryPeriod(timestampNs)) {
+    if (isInRefractoryPeriod(timestampNs, dimensionKey)) {
         VLOG("Skipping setting anomaly alarm since it'd fall in the refractory period");
         return;
     }
@@ -104,7 +104,7 @@
 
     // Now declare each of these alarms to have fired.
     for (const auto& kv : matchedAlarms) {
-        declareAnomaly(timestampNs /* TODO: , kv.first */);
+        declareAnomaly(timestampNs, kv.first);
         mAlarms.erase(kv.first);
         firedAlarms.erase(kv.second);  // No one else can also own it, so we're done with it.
     }
diff --git a/cmds/statsd/src/anomaly/DurationAnomalyTracker.h b/cmds/statsd/src/anomaly/DurationAnomalyTracker.h
index 182ce3b..de7093d 100644
--- a/cmds/statsd/src/anomaly/DurationAnomalyTracker.h
+++ b/cmds/statsd/src/anomaly/DurationAnomalyTracker.h
@@ -50,7 +50,7 @@
                                       const uint64_t& timestampNs);
 
     // Declares an anomaly for each alarm in firedAlarms that belongs to this DurationAnomalyTracker
-    // and removes it from firedAlarms. Does NOT remove the alarm from the AnomalyMonitor.
+    // and removes it from firedAlarms.
     // TODO: This will actually be called from a different thread, so make it thread-safe!
     //          This means that almost every function in DurationAnomalyTracker needs to be locked.
     //          But this should be done at the level of StatsLogProcessor, which needs to lock
@@ -70,10 +70,10 @@
     void resetStorage() override;
 
     FRIEND_TEST(OringDurationTrackerTest, TestPredictAnomalyTimestamp);
-    FRIEND_TEST(OringDurationTrackerTest, TestAnomalyDetection);
+    FRIEND_TEST(OringDurationTrackerTest, TestAnomalyDetectionExpiredAlarm);
+    FRIEND_TEST(OringDurationTrackerTest, TestAnomalyDetectionFiredAlarm);
     FRIEND_TEST(MaxDurationTrackerTest, TestAnomalyDetection);
     FRIEND_TEST(MaxDurationTrackerTest, TestAnomalyDetection);
-    FRIEND_TEST(OringDurationTrackerTest, TestAnomalyDetection);
 };
 
 }  // namespace statsd
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index 8637b79..7f77ef7 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -81,6 +81,9 @@
         DropboxErrorChanged dropbox_error_changed = 45;
         AnomalyDetected anomaly_detected = 46;
         AppHook app_hook = 47;
+        AppStartChanged app_start_changed = 48;
+        AppStartCancelChanged app_start_cancel_changed = 49;
+        AppStartFullyDrawnChanged app_start_fully_drawn_changed = 50;
         // TODO: Reorder the numbering so that the most frequent occur events occur in the first 15.
     }
 
@@ -855,6 +858,100 @@
     optional int64 alert_id = 3;
 }
 
+message AppStartChanged {
+    // The uid if available. -1 means not available.
+    optional int32 uid = 1;
+
+    // The app package name.
+    optional string pkg_name = 2;
+
+    enum TransitionType {
+        APP_START_TRANSITION_TYPE_UNKNOWN = 0;
+        WARM = 1;
+        HOT = 2;
+        COLD = 3;
+    }
+    // The transition type.
+    optional TransitionType type = 3;
+
+    // The activity name.
+    optional string activity_name = 4;
+
+    // The name of the calling app. Empty if not set.
+    optional string calling_pkg_name = 5;
+
+    // Whether the app is an instant app.
+    optional bool is_instant_app = 6;
+
+    // Device uptime when activity started.
+    optional int64 activity_start_msec = 7;
+
+    // TODO: Update android/app/ActivityManagerInternal.java constants to depend on our proto enum.
+    enum TransitionReason {
+        APP_START_TRANSITION_REASON_UNKNOWN = 0;
+        SPLASH_SCREEN = 1;
+        WINDOWS_DRAWN = 2;
+        TIMEOUT = 3;
+        SNAPSHOT = 4;
+    }
+    optional TransitionReason reason = 8;
+
+    optional int32 transition_delay_msec = 9;
+    // -1 if not set.
+    optional int32 starting_window_delay_msec = 10;
+    // -1 if not set.
+    optional int32 bind_application_delay_msec = 11;
+    optional int32 windows_drawn_delay_msec = 12;
+
+    // Empty if not set.
+    optional string launch_token = 13;
+
+}
+
+message AppStartCancelChanged {
+    // The uid if available. -1 means not available.
+    optional int32 uid = 1;
+
+    // The app package name.
+    optional string pkg_name = 2;
+
+    enum TransitionType {
+        APP_START_TRANSITION_TYPE_UNKNOWN = 0;
+        WARM = 1;
+        HOT = 2;
+        COLD = 3;
+    }
+    // The transition type.
+    optional TransitionType type = 3;
+
+    // The activity name.
+    optional string activity_name = 4;
+}
+
+message AppStartFullyDrawnChanged {
+    // The uid if available. -1 means not available.
+    optional int32 uid = 1;
+
+    // The app package name.
+    optional string pkg_name = 2;
+
+    enum TransitionType {
+        APP_START_TRANSITION_TYPE_UNKNOWN = 0;
+        WITH_BUNDLE = 1;
+        WITHOUT_BUNDLE = 2;
+    }
+    // The transition type.
+    optional TransitionType type = 3;
+
+    // The activity name.
+    optional string activity_name = 4;
+
+    optional bool transition_process_running = 5;
+
+    // App startup time (until call to Activity#reportFullyDrawn()).
+    optional int64 app_startup_time_ms = 6;
+}
+
 /**
  * Pulls bytes transferred via wifi (Sum of foreground and background usage).
  *
diff --git a/cmds/statsd/src/guardrail/StatsdStats.cpp b/cmds/statsd/src/guardrail/StatsdStats.cpp
index 5842f3c..36dd616 100644
--- a/cmds/statsd/src/guardrail/StatsdStats.cpp
+++ b/cmds/statsd/src/guardrail/StatsdStats.cpp
@@ -85,6 +85,14 @@
     return statsInstance;
 }
 
+void StatsdStats::addToIceBoxLocked(const StatsdStatsReport_ConfigStats& stats) {
+    // The size of mIceBox grows strictly by one at a time. It won't be > kMaxIceBoxSize.
+    if (mIceBox.size() == kMaxIceBoxSize) {
+        mIceBox.pop_front();
+    }
+    mIceBox.push_back(stats);
+}
+
 void StatsdStats::noteConfigReceived(const ConfigKey& key, int metricsCount, int conditionsCount,
                                      int matchersCount, int alertsCount, bool isValid) {
     lock_guard<std::mutex> lock(mLock);
@@ -107,7 +115,7 @@
         mConfigStats[key] = configStats;
     } else {
         configStats.set_deletion_time_sec(nowTimeSec);
-        mIceBox.push_back(configStats);
+        addToIceBoxLocked(configStats);
     }
 }
 
@@ -123,7 +131,7 @@
         mMetricsStats.erase(key);
         mAlertStats.erase(key);
         mConditionStats.erase(key);
-        mIceBox.push_back(it->second);
+        addToIceBoxLocked(it->second);
         mConfigStats.erase(it);
     }
 }
diff --git a/cmds/statsd/src/guardrail/StatsdStats.h b/cmds/statsd/src/guardrail/StatsdStats.h
index 2f10823..52ab253 100644
--- a/cmds/statsd/src/guardrail/StatsdStats.h
+++ b/cmds/statsd/src/guardrail/StatsdStats.h
@@ -21,6 +21,7 @@
 
 #include <gtest/gtest_prod.h>
 #include <log/log_time.h>
+#include <list>
 #include <mutex>
 #include <string>
 #include <vector>
@@ -45,6 +46,9 @@
     const static int kMaxMetricCountPerConfig = 300;
     const static int kMaxMatcherCountPerConfig = 500;
 
+    // The max number of old config stats we keep.
+    const static int kMaxIceBoxSize = 20;
+
     const static int kMaxTimestampCount = 20;
 
     const static int kMaxLogSourceCount = 50;
@@ -202,19 +206,21 @@
     StatsdStatsReport_UidMapStats mUidMapStats;
 
     // The stats about the configs that are still in use.
+    // The map size is capped by kMaxConfigCount.
     std::map<const ConfigKey, StatsdStatsReport_ConfigStats> mConfigStats;
 
     // Stores the stats for the configs that are no longer in use.
-    std::vector<const StatsdStatsReport_ConfigStats> mIceBox;
+    // The size of the vector is capped by kMaxIceBoxSize.
+    std::list<const StatsdStatsReport_ConfigStats> mIceBox;
 
     // Stores the number of output tuple of condition trackers when it's bigger than
     // kDimensionKeySizeSoftLimit. When you see the number is kDimensionKeySizeHardLimit +1,
-    // it means some data has been dropped.
+    // it means some data has been dropped. The map size is capped by kMaxConfigCount.
     std::map<const ConfigKey, std::map<const int64_t, int>> mConditionStats;
 
     // Stores the number of output tuple of metric producers when it's bigger than
     // kDimensionKeySizeSoftLimit. When you see the number is kDimensionKeySizeHardLimit +1,
-    // it means some data has been dropped.
+    // it means some data has been dropped. The map size is capped by kMaxConfigCount.
     std::map<const ConfigKey, std::map<const int64_t, int>> mMetricsStats;
 
     // Stores the number of times a pushed atom is logged.
@@ -223,6 +229,7 @@
     // This is a vector, not a map because it will be accessed A LOT -- for each stats log.
     std::vector<int> mPushedAtomStats;
 
+    // Maps PullAtomId to its stats. The size is capped by the puller atom counts.
     std::map<int, PulledAtomStats> mPulledAtomStats;
 
     // Stores the number of times statsd modified the anomaly alarm registered with
@@ -230,10 +237,10 @@
     int mAnomalyAlarmRegisteredStats = 0;
 
     // Stores the number of times an anomaly detection alert has been declared
-    // (per config, per alert name).
+    // (per config, per alert name). The map size is capped by kMaxConfigCount.
     std::map<const ConfigKey, std::map<const int64_t, int>> mAlertStats;
 
-    // Stores how many times a matcher have been matched.
+    // Stores how many times a matcher have been matched. The map size is capped by kMaxConfigCount.
     std::map<const ConfigKey, std::map<const int64_t, int>> mMatcherStats;
 
     void noteConfigRemovedInternalLocked(const ConfigKey& key);
@@ -249,6 +256,8 @@
 
     void noteBroadcastSent(const ConfigKey& key, int32_t timeSec);
 
+    void addToIceBoxLocked(const StatsdStatsReport_ConfigStats& stats);
+
     FRIEND_TEST(StatsdStatsTest, TestValidConfigAdd);
     FRIEND_TEST(StatsdStatsTest, TestInvalidConfigAdd);
     FRIEND_TEST(StatsdStatsTest, TestConfigRemove);
diff --git a/cmds/statsd/src/metrics/CountMetricProducer.h b/cmds/statsd/src/metrics/CountMetricProducer.h
index 6087ae5..16fc7ee 100644
--- a/cmds/statsd/src/metrics/CountMetricProducer.h
+++ b/cmds/statsd/src/metrics/CountMetricProducer.h
@@ -84,7 +84,7 @@
     FRIEND_TEST(CountMetricProducerTest, TestNonDimensionalEvents);
     FRIEND_TEST(CountMetricProducerTest, TestEventsWithNonSlicedCondition);
     FRIEND_TEST(CountMetricProducerTest, TestEventsWithSlicedCondition);
-    FRIEND_TEST(CountMetricProducerTest, TestAnomalyDetection);
+    FRIEND_TEST(CountMetricProducerTest, TestAnomalyDetectionUnSliced);
 };
 
 }  // namespace statsd
diff --git a/cmds/statsd/src/metrics/DurationMetricProducer.cpp b/cmds/statsd/src/metrics/DurationMetricProducer.cpp
index 3f8a8ff..b546297 100644
--- a/cmds/statsd/src/metrics/DurationMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/DurationMetricProducer.cpp
@@ -117,11 +117,11 @@
         case DurationMetric_AggregationType_SUM:
             return make_unique<OringDurationTracker>(
                     mConfigKey, mMetricId, eventKey, mWizard, mConditionTrackerIndex, mNested,
-                    mCurrentBucketStartTimeNs, mBucketSizeNs, mAnomalyTrackers);
+                    mCurrentBucketStartTimeNs, mBucketSizeNs, mConditionSliced, mAnomalyTrackers);
         case DurationMetric_AggregationType_MAX_SPARSE:
             return make_unique<MaxDurationTracker>(
                     mConfigKey, mMetricId, eventKey, mWizard, mConditionTrackerIndex, mNested,
-                    mCurrentBucketStartTimeNs, mBucketSizeNs, mAnomalyTrackers);
+                    mCurrentBucketStartTimeNs, mBucketSizeNs, mConditionSliced, mAnomalyTrackers);
     }
 }
 
diff --git a/cmds/statsd/src/metrics/duration_helper/DurationTracker.h b/cmds/statsd/src/metrics/duration_helper/DurationTracker.h
index 842581e..371460e 100644
--- a/cmds/statsd/src/metrics/duration_helper/DurationTracker.h
+++ b/cmds/statsd/src/metrics/duration_helper/DurationTracker.h
@@ -62,7 +62,7 @@
 public:
     DurationTracker(const ConfigKey& key, const int64_t& id, const HashableDimensionKey& eventKey,
                     sp<ConditionWizard> wizard, int conditionIndex, bool nesting,
-                    uint64_t currentBucketStartNs, uint64_t bucketSizeNs,
+                    uint64_t currentBucketStartNs, uint64_t bucketSizeNs, bool conditionSliced,
                     const std::vector<sp<DurationAnomalyTracker>>& anomalyTrackers)
         : mConfigKey(key),
           mTrackerId(id),
@@ -74,6 +74,7 @@
           mCurrentBucketStartTimeNs(currentBucketStartNs),
           mDuration(0),
           mCurrentBucketNum(0),
+          mConditionSliced(conditionSliced),
           mAnomalyTrackers(anomalyTrackers){};
 
     virtual ~DurationTracker(){};
@@ -163,10 +164,13 @@
 
     uint64_t mCurrentBucketNum;
 
+    const bool mConditionSliced;
+
     std::vector<sp<DurationAnomalyTracker>> mAnomalyTrackers;
 
     FRIEND_TEST(OringDurationTrackerTest, TestPredictAnomalyTimestamp);
-    FRIEND_TEST(OringDurationTrackerTest, TestAnomalyDetection);
+    FRIEND_TEST(OringDurationTrackerTest, TestAnomalyDetectionExpiredAlarm);
+    FRIEND_TEST(OringDurationTrackerTest, TestAnomalyDetectionFiredAlarm);
 };
 
 }  // namespace statsd
diff --git a/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.cpp b/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.cpp
index 94f98ad..0c99391 100644
--- a/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.cpp
+++ b/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.cpp
@@ -28,9 +28,10 @@
                                        const HashableDimensionKey& eventKey,
                                        sp<ConditionWizard> wizard, int conditionIndex, bool nesting,
                                        uint64_t currentBucketStartNs, uint64_t bucketSizeNs,
+                                       bool conditionSliced,
                                        const vector<sp<DurationAnomalyTracker>>& anomalyTrackers)
     : DurationTracker(key, id, eventKey, wizard, conditionIndex, nesting, currentBucketStartNs,
-                      bucketSizeNs, anomalyTrackers) {
+                      bucketSizeNs, conditionSliced, anomalyTrackers) {
 }
 
 bool MaxDurationTracker::hitGuardRail(const HashableDimensionKey& newKey) {
@@ -63,7 +64,9 @@
     }
 
     DurationInfo& duration = mInfos[key];
-    duration.conditionKeys = conditionKey;
+    if (mConditionSliced) {
+        duration.conditionKeys = conditionKey;
+    }
     VLOG("MaxDuration: key %s start condition %d", key.c_str(), condition);
 
     switch (duration.state) {
diff --git a/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.h b/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.h
index 68c48cb1..5d3c158 100644
--- a/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.h
+++ b/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.h
@@ -31,7 +31,7 @@
     MaxDurationTracker(const ConfigKey& key, const int64_t& id,
                        const HashableDimensionKey& eventKey, sp<ConditionWizard> wizard,
                        int conditionIndex, bool nesting, uint64_t currentBucketStartNs,
-                       uint64_t bucketSizeNs,
+                       uint64_t bucketSizeNs, bool conditionSliced,
                        const std::vector<sp<DurationAnomalyTracker>>& anomalyTrackers);
     void noteStart(const HashableDimensionKey& key, bool condition, const uint64_t eventTime,
                    const ConditionKey& conditionKey) override;
diff --git a/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.cpp b/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.cpp
index c77d0b7..6bf4228 100644
--- a/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.cpp
+++ b/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.cpp
@@ -27,10 +27,10 @@
 OringDurationTracker::OringDurationTracker(
         const ConfigKey& key, const int64_t& id, const HashableDimensionKey& eventKey,
         sp<ConditionWizard> wizard, int conditionIndex, bool nesting, uint64_t currentBucketStartNs,
-        uint64_t bucketSizeNs, const vector<sp<DurationAnomalyTracker>>& anomalyTrackers)
-
+        uint64_t bucketSizeNs, bool conditionSliced,
+        const vector<sp<DurationAnomalyTracker>>& anomalyTrackers)
     : DurationTracker(key, id, eventKey, wizard, conditionIndex, nesting, currentBucketStartNs,
-                      bucketSizeNs, anomalyTrackers),
+                      bucketSizeNs, conditionSliced, anomalyTrackers),
       mStarted(),
       mPaused() {
     mLastStartTime = 0;
@@ -73,7 +73,7 @@
         mPaused[key]++;
     }
 
-    if (mConditionKeyMap.find(key) == mConditionKeyMap.end()) {
+    if (mConditionSliced && mConditionKeyMap.find(key) == mConditionKeyMap.end()) {
         mConditionKeyMap[key] = conditionKey;
     }
 
diff --git a/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.h b/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.h
index 7fe649c..638b7ad 100644
--- a/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.h
+++ b/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.h
@@ -30,7 +30,7 @@
     OringDurationTracker(const ConfigKey& key, const int64_t& id,
                          const HashableDimensionKey& eventKey, sp<ConditionWizard> wizard,
                          int conditionIndex, bool nesting, uint64_t currentBucketStartNs,
-                         uint64_t bucketSizeNs,
+                         uint64_t bucketSizeNs, bool conditionSliced,
                          const std::vector<sp<DurationAnomalyTracker>>& anomalyTrackers);
 
     void noteStart(const HashableDimensionKey& key, bool condition, const uint64_t eventTime,
@@ -67,7 +67,8 @@
     FRIEND_TEST(OringDurationTrackerTest, TestCrossBucketBoundary);
     FRIEND_TEST(OringDurationTrackerTest, TestDurationConditionChange);
     FRIEND_TEST(OringDurationTrackerTest, TestPredictAnomalyTimestamp);
-    FRIEND_TEST(OringDurationTrackerTest, TestAnomalyDetection);
+    FRIEND_TEST(OringDurationTrackerTest, TestAnomalyDetectionExpiredAlarm);
+    FRIEND_TEST(OringDurationTrackerTest, TestAnomalyDetectionFiredAlarm);
 };
 
 }  // namespace statsd
diff --git a/cmds/statsd/tests/StatsLogProcessor_test.cpp b/cmds/statsd/tests/StatsLogProcessor_test.cpp
index 5d053e2..aab5bed 100644
--- a/cmds/statsd/tests/StatsLogProcessor_test.cpp
+++ b/cmds/statsd/tests/StatsLogProcessor_test.cpp
@@ -60,9 +60,9 @@
     // Expect only the first flush to trigger a check for byte size since the last two are
     // rate-limited.
     EXPECT_CALL(mockMetricsManager, byteSize()).Times(1);
-    p.flushIfNecessary(99, key, mockMetricsManager);
-    p.flushIfNecessary(100, key, mockMetricsManager);
-    p.flushIfNecessary(101, key, mockMetricsManager);
+    p.flushIfNecessaryLocked(99, key, mockMetricsManager);
+    p.flushIfNecessaryLocked(100, key, mockMetricsManager);
+    p.flushIfNecessaryLocked(101, key, mockMetricsManager);
 }
 
 TEST(StatsLogProcessorTest, TestRateLimitBroadcast) {
@@ -80,12 +80,12 @@
             .WillRepeatedly(Return(int(StatsdStats::kMaxMetricsBytesPerConfig * .95)));
 
     // Expect only one broadcast despite always returning a size that should trigger broadcast.
-    p.flushIfNecessary(1, key, mockMetricsManager);
+    p.flushIfNecessaryLocked(1, key, mockMetricsManager);
     EXPECT_EQ(1, broadcastCount);
 
     // This next call to flush should not trigger a broadcast.
     p.mLastByteSizeTimes.clear();  // Force another check for byte size.
-    p.flushIfNecessary(2, key, mockMetricsManager);
+    p.flushIfNecessaryLocked(2, key, mockMetricsManager);
     EXPECT_EQ(1, broadcastCount);
 }
 
@@ -106,7 +106,7 @@
     EXPECT_CALL(mockMetricsManager, onDumpReport(_)).Times(1);
 
     // Expect to call the onDumpReport and skip the broadcast.
-    p.flushIfNecessary(1, key, mockMetricsManager);
+    p.flushIfNecessaryLocked(1, key, mockMetricsManager);
     EXPECT_EQ(0, broadcastCount);
 }
 
diff --git a/cmds/statsd/tests/anomaly/AnomalyTracker_test.cpp b/cmds/statsd/tests/anomaly/AnomalyTracker_test.cpp
index 5842bc8..66bfa68 100644
--- a/cmds/statsd/tests/anomaly/AnomalyTracker_test.cpp
+++ b/cmds/statsd/tests/anomaly/AnomalyTracker_test.cpp
@@ -54,11 +54,73 @@
     return bucket;
 }
 
+// Returns the value, for the given key, in that bucket, or 0 if not present.
+int64_t getBucketValue(const std::shared_ptr<DimToValMap>& bucket,
+                       const HashableDimensionKey& key) {
+    const auto& itr = bucket->find(key);
+    if (itr != bucket->end()) {
+        return itr->second;
+    }
+    return 0;
+}
+
+// Returns true if keys in trueList are detected as anomalies and keys in falseList are not.
+bool detectAnomaliesPass(AnomalyTracker& tracker,
+                         const int64_t& bucketNum,
+                         const std::shared_ptr<DimToValMap>& currentBucket,
+                         const std::set<const HashableDimensionKey>& trueList,
+                         const std::set<const HashableDimensionKey>& falseList) {
+    for (HashableDimensionKey key : trueList) {
+        if (!tracker.detectAnomaly(bucketNum, key, getBucketValue(currentBucket, key))) {
+            return false;
+        }
+    }
+    for (HashableDimensionKey key : falseList) {
+        if (tracker.detectAnomaly(bucketNum, key, getBucketValue(currentBucket, key))) {
+            return false;
+        }
+    }
+    return true;
+}
+
+// Calls tracker.detectAndDeclareAnomaly on each key in the bucket.
+void detectAndDeclareAnomalies(AnomalyTracker& tracker,
+                               const int64_t& bucketNum,
+                               const std::shared_ptr<DimToValMap>& bucket,
+                               const int64_t& eventTimestamp) {
+    for (const auto& kv : *bucket) {
+        tracker.detectAndDeclareAnomaly(eventTimestamp, bucketNum, kv.first, kv.second);
+    }
+}
+
+// Asserts that the refractory time for each key in timestamps is the corresponding
+// timestamp (in ns) + refractoryPeriodSec.
+// If a timestamp value is negative, instead asserts that the refractory period is inapplicable
+// (either non-existant or already past).
+void checkRefractoryTimes(AnomalyTracker& tracker,
+                          const int64_t& currTimestampNs,
+                          const int32_t& refractoryPeriodSec,
+                          const std::unordered_map<HashableDimensionKey, int64_t>& timestamps) {
+    for (const auto& kv : timestamps) {
+        if (kv.second < 0) {
+            // Make sure that, if there is a refractory period, it is already past.
+            EXPECT_LT(tracker.getRefractoryPeriodEndsSec(kv.first),
+                    currTimestampNs / NS_PER_SEC + 1)
+                    << "Failure was at currTimestampNs " << currTimestampNs;
+        } else {
+            EXPECT_EQ(tracker.getRefractoryPeriodEndsSec(kv.first),
+                      kv.second / NS_PER_SEC + refractoryPeriodSec)
+                      << "Failure was at currTimestampNs " << currTimestampNs;
+        }
+    }
+}
+
 TEST(AnomalyTrackerTest, TestConsecutiveBuckets) {
     const int64_t bucketSizeNs = 30 * NS_PER_SEC;
+    const int32_t refractoryPeriodSec = 2 * bucketSizeNs / NS_PER_SEC;
     Alert alert;
     alert.set_num_buckets(3);
-    alert.set_refractory_period_secs(2 * bucketSizeNs / NS_PER_SEC);
+    alert.set_refractory_period_secs(refractoryPeriodSec);
     alert.set_trigger_if_sum_gt(2);
 
     AnomalyTracker anomalyTracker(alert, kConfigKey);
@@ -66,26 +128,31 @@
     HashableDimensionKey keyB = getMockDimensionKey(1, "b");
     HashableDimensionKey keyC = getMockDimensionKey(1, "c");
 
-    std::shared_ptr<DimToValMap> bucket0 = MockBucket({{keyA, 1}, {keyB, 2}, {keyC, 1}});
-    int64_t eventTimestamp0 = 10;
-    std::shared_ptr<DimToValMap> bucket1 = MockBucket({{keyA, 1}});
-    int64_t eventTimestamp1 = bucketSizeNs + 11;
-    std::shared_ptr<DimToValMap> bucket2 = MockBucket({{keyB, 1}});
-    int64_t eventTimestamp2 = 2 * bucketSizeNs + 12;
-    std::shared_ptr<DimToValMap> bucket3 = MockBucket({{keyA, 2}});
-    int64_t eventTimestamp3 = 3 * bucketSizeNs + 13;
-    std::shared_ptr<DimToValMap> bucket4 = MockBucket({{keyB, 1}});
-    int64_t eventTimestamp4 = 4 * bucketSizeNs + 14;
-    std::shared_ptr<DimToValMap> bucket5 = MockBucket({{keyA, 2}});
-    int64_t eventTimestamp5 = 5 * bucketSizeNs + 15;
-    std::shared_ptr<DimToValMap> bucket6 = MockBucket({{keyA, 2}});
-    int64_t eventTimestamp6 = 6 * bucketSizeNs + 16;
+    int64_t eventTimestamp0 = 10 * NS_PER_SEC;
+    int64_t eventTimestamp1 = bucketSizeNs + 11 * NS_PER_SEC;
+    int64_t eventTimestamp2 = 2 * bucketSizeNs + 12 * NS_PER_SEC;
+    int64_t eventTimestamp3 = 3 * bucketSizeNs + 13 * NS_PER_SEC;
+    int64_t eventTimestamp4 = 4 * bucketSizeNs + 14 * NS_PER_SEC;
+    int64_t eventTimestamp5 = 5 * bucketSizeNs + 5 * NS_PER_SEC;
+    int64_t eventTimestamp6 = 6 * bucketSizeNs + 16 * NS_PER_SEC;
 
+    std::shared_ptr<DimToValMap> bucket0 = MockBucket({{keyA, 1}, {keyB, 2}, {keyC, 1}});
+    std::shared_ptr<DimToValMap> bucket1 = MockBucket({{keyA, 1}});
+    std::shared_ptr<DimToValMap> bucket2 = MockBucket({{keyB, 1}});
+    std::shared_ptr<DimToValMap> bucket3 = MockBucket({{keyA, 2}});
+    std::shared_ptr<DimToValMap> bucket4 = MockBucket({{keyB, 5}});
+    std::shared_ptr<DimToValMap> bucket5 = MockBucket({{keyA, 2}});
+    std::shared_ptr<DimToValMap> bucket6 = MockBucket({{keyA, 2}});
+
+    // Start time with no events.
     EXPECT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 0u);
     EXPECT_EQ(anomalyTracker.mMostRecentBucketNum, -1LL);
-    EXPECT_FALSE(anomalyTracker.detectAnomaly(0, *bucket0));
-    anomalyTracker.detectAndDeclareAnomaly(eventTimestamp0, 0, *bucket0);
-    EXPECT_EQ(anomalyTracker.mLastAnomalyTimestampNs, -1L);
+
+    // Event from bucket #0 occurs.
+    EXPECT_TRUE(detectAnomaliesPass(anomalyTracker, 0, bucket0, {}, {keyA, keyB, keyC}));
+    detectAndDeclareAnomalies(anomalyTracker, 0, bucket0, eventTimestamp1);
+    checkRefractoryTimes(anomalyTracker, eventTimestamp0, refractoryPeriodSec,
+            {{keyA, -1}, {keyB, -1}, {keyC, -1}});
 
     // Adds past bucket #0
     anomalyTracker.addPastBucket(bucket0, 0);
@@ -94,9 +161,12 @@
     EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyB), 2LL);
     EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyC), 1LL);
     EXPECT_EQ(anomalyTracker.mMostRecentBucketNum, 0LL);
-    EXPECT_FALSE(anomalyTracker.detectAnomaly(1, *bucket1));
-    anomalyTracker.detectAndDeclareAnomaly(eventTimestamp1, 1, *bucket1);
-    EXPECT_EQ(anomalyTracker.mLastAnomalyTimestampNs, -1L);
+
+    // Event from bucket #1 occurs.
+    EXPECT_TRUE(detectAnomaliesPass(anomalyTracker, 1, bucket1, {}, {keyA, keyB, keyC}));
+    detectAndDeclareAnomalies(anomalyTracker, 1, bucket1, eventTimestamp1);
+    checkRefractoryTimes(anomalyTracker, eventTimestamp1, refractoryPeriodSec,
+            {{keyA, -1}, {keyB, -1}, {keyC, -1}});
 
     // Adds past bucket #0 again. The sum does not change.
     anomalyTracker.addPastBucket(bucket0, 0);
@@ -105,9 +175,10 @@
     EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyB), 2LL);
     EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyC), 1LL);
     EXPECT_EQ(anomalyTracker.mMostRecentBucketNum, 0LL);
-    EXPECT_FALSE(anomalyTracker.detectAnomaly(1, *bucket1));
-    anomalyTracker.detectAndDeclareAnomaly(eventTimestamp1 + 1, 1, *bucket1);
-    EXPECT_EQ(anomalyTracker.mLastAnomalyTimestampNs, -1L);
+    EXPECT_TRUE(detectAnomaliesPass(anomalyTracker, 1, bucket1, {}, {keyA, keyB, keyC}));
+    detectAndDeclareAnomalies(anomalyTracker, 1, bucket1, eventTimestamp1 + 1);
+    checkRefractoryTimes(anomalyTracker, eventTimestamp1, refractoryPeriodSec,
+            {{keyA, -1}, {keyB, -1}, {keyC, -1}});
 
     // Adds past bucket #1.
     anomalyTracker.addPastBucket(bucket1, 1);
@@ -116,9 +187,12 @@
     EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyA), 2LL);
     EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyB), 2LL);
     EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyC), 1LL);
-    EXPECT_TRUE(anomalyTracker.detectAnomaly(2, *bucket2));
-    anomalyTracker.detectAndDeclareAnomaly(eventTimestamp2, 2, *bucket2);
-    EXPECT_EQ(anomalyTracker.mLastAnomalyTimestampNs, eventTimestamp2);
+
+    // Event from bucket #2 occurs. New anomaly on keyB.
+    EXPECT_TRUE(detectAnomaliesPass(anomalyTracker, 2, bucket2, {keyB}, {keyA, keyC}));
+    detectAndDeclareAnomalies(anomalyTracker, 2, bucket2, eventTimestamp2);
+    checkRefractoryTimes(anomalyTracker, eventTimestamp2, refractoryPeriodSec,
+            {{keyA, -1}, {keyB, eventTimestamp2}, {keyC, -1}});
 
     // Adds past bucket #1 again. Nothing changes.
     anomalyTracker.addPastBucket(bucket1, 1);
@@ -127,9 +201,11 @@
     EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyA), 2LL);
     EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyB), 2LL);
     EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyC), 1LL);
-    EXPECT_TRUE(anomalyTracker.detectAnomaly(2, *bucket2));
-    anomalyTracker.detectAndDeclareAnomaly(eventTimestamp2 + 1, 2, *bucket2);
-    EXPECT_EQ(anomalyTracker.mLastAnomalyTimestampNs, eventTimestamp2);
+    // Event from bucket #2 occurs (again).
+    EXPECT_TRUE(detectAnomaliesPass(anomalyTracker, 2, bucket2, {keyB}, {keyA, keyC}));
+    detectAndDeclareAnomalies(anomalyTracker, 2, bucket2, eventTimestamp2 + 1);
+    checkRefractoryTimes(anomalyTracker, eventTimestamp2, refractoryPeriodSec,
+            {{keyA, -1}, {keyB, eventTimestamp2}, {keyC, -1}});
 
     // Adds past bucket #2.
     anomalyTracker.addPastBucket(bucket2, 2);
@@ -137,10 +213,12 @@
     EXPECT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 2UL);
     EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyA), 1LL);
     EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyB), 1LL);
-    EXPECT_TRUE(anomalyTracker.detectAnomaly(3, *bucket3));
-    anomalyTracker.detectAndDeclareAnomaly(eventTimestamp3, 3, *bucket3);
-    // Within refractory period.
-    EXPECT_EQ(anomalyTracker.mLastAnomalyTimestampNs, eventTimestamp2);
+
+    // Event from bucket #3 occurs. New anomaly on keyA.
+    EXPECT_TRUE(detectAnomaliesPass(anomalyTracker, 3, bucket3, {keyA}, {keyB, keyC}));
+    detectAndDeclareAnomalies(anomalyTracker, 3, bucket3, eventTimestamp3);
+    checkRefractoryTimes(anomalyTracker, eventTimestamp3, refractoryPeriodSec,
+            {{keyA, eventTimestamp3}, {keyB, eventTimestamp2}, {keyC, -1}});
 
     // Adds bucket #3.
     anomalyTracker.addPastBucket(bucket3, 3L);
@@ -148,37 +226,46 @@
     EXPECT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 2UL);
     EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyA), 2LL);
     EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyB), 1LL);
-    EXPECT_FALSE(anomalyTracker.detectAnomaly(4, *bucket4));
-    anomalyTracker.detectAndDeclareAnomaly(eventTimestamp4, 4, *bucket4);
-    EXPECT_EQ(anomalyTracker.mLastAnomalyTimestampNs, eventTimestamp2);
+
+    // Event from bucket #4 occurs. New anomaly on keyB.
+    EXPECT_TRUE(detectAnomaliesPass(anomalyTracker, 4, bucket4, {keyB}, {keyA, keyC}));
+    detectAndDeclareAnomalies(anomalyTracker, 4, bucket4, eventTimestamp4);
+    checkRefractoryTimes(anomalyTracker, eventTimestamp4, refractoryPeriodSec,
+            {{keyA, eventTimestamp3}, {keyB, eventTimestamp4}, {keyC, -1}});
 
     // Adds bucket #4.
     anomalyTracker.addPastBucket(bucket4, 4);
     EXPECT_EQ(anomalyTracker.mMostRecentBucketNum, 4L);
     EXPECT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 2UL);
     EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyA), 2LL);
-    EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyB), 1LL);
-    EXPECT_TRUE(anomalyTracker.detectAnomaly(5, *bucket5));
-    anomalyTracker.detectAndDeclareAnomaly(eventTimestamp5, 5, *bucket5);
-    EXPECT_EQ(anomalyTracker.mLastAnomalyTimestampNs, eventTimestamp5);
+    EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyB), 5LL);
+
+    // Event from bucket #5 occurs. New anomaly on keyA, which is still in refractory.
+    EXPECT_TRUE(detectAnomaliesPass(anomalyTracker, 5, bucket5, {keyA, keyB}, {keyC}));
+    detectAndDeclareAnomalies(anomalyTracker, 5, bucket5, eventTimestamp5);
+    checkRefractoryTimes(anomalyTracker, eventTimestamp5, refractoryPeriodSec,
+            {{keyA, eventTimestamp3}, {keyB, eventTimestamp4}, {keyC, -1}});
 
     // Adds bucket #5.
     anomalyTracker.addPastBucket(bucket5, 5);
     EXPECT_EQ(anomalyTracker.mMostRecentBucketNum, 5L);
     EXPECT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 2UL);
     EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyA), 2LL);
-    EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyB), 1LL);
-    EXPECT_TRUE(anomalyTracker.detectAnomaly(6, *bucket6));
-    // Within refractory period.
-    anomalyTracker.detectAndDeclareAnomaly(eventTimestamp6, 6, *bucket6);
-    EXPECT_EQ(anomalyTracker.mLastAnomalyTimestampNs, eventTimestamp5);
+    EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyB), 5LL);
+
+    // Event from bucket #6 occurs. New anomaly on keyA, which is now out of refractory.
+    EXPECT_TRUE(detectAnomaliesPass(anomalyTracker, 6, bucket6, {keyA, keyB}, {keyC}));
+    detectAndDeclareAnomalies(anomalyTracker, 6, bucket6, eventTimestamp6);
+    checkRefractoryTimes(anomalyTracker, eventTimestamp6, refractoryPeriodSec,
+            {{keyA, eventTimestamp6}, {keyB, eventTimestamp4}, {keyC, -1}});
 }
 
 TEST(AnomalyTrackerTest, TestSparseBuckets) {
     const int64_t bucketSizeNs = 30 * NS_PER_SEC;
+    const int32_t refractoryPeriodSec = 2 * bucketSizeNs / NS_PER_SEC;
     Alert alert;
     alert.set_num_buckets(3);
-    alert.set_refractory_period_secs(2 * bucketSizeNs / NS_PER_SEC);
+    alert.set_refractory_period_secs(refractoryPeriodSec);
     alert.set_trigger_if_sum_gt(2);
 
     AnomalyTracker anomalyTracker(alert, kConfigKey);
@@ -204,9 +291,10 @@
 
     EXPECT_EQ(anomalyTracker.mMostRecentBucketNum, -1LL);
     EXPECT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 0UL);
-    EXPECT_FALSE(anomalyTracker.detectAnomaly(9, *bucket9));
-    anomalyTracker.detectAndDeclareAnomaly(eventTimestamp1, 9, *bucket9);
-    EXPECT_EQ(anomalyTracker.mLastAnomalyTimestampNs, -1);
+    EXPECT_TRUE(detectAnomaliesPass(anomalyTracker, 9, bucket9, {}, {keyA, keyB, keyC, keyD}));
+    detectAndDeclareAnomalies(anomalyTracker, 9, bucket9, eventTimestamp1);
+    checkRefractoryTimes(anomalyTracker, eventTimestamp1, refractoryPeriodSec,
+            {{keyA, -1}, {keyB, -1}, {keyC, -1}, {keyD, -1}, {keyE, -1}});
 
     // Add past bucket #9
     anomalyTracker.addPastBucket(bucket9, 9);
@@ -215,25 +303,27 @@
     EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyA), 1LL);
     EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyB), 2LL);
     EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyC), 1LL);
-    EXPECT_TRUE(anomalyTracker.detectAnomaly(16, *bucket16));
-    EXPECT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 0UL);
+    EXPECT_TRUE(detectAnomaliesPass(anomalyTracker, 16, bucket16, {keyB}, {keyA, keyC, keyD}));
+    // TODO: after detectAnomaly fix: EXPECT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 0UL);
     EXPECT_EQ(anomalyTracker.mMostRecentBucketNum, 15L);
-    anomalyTracker.detectAndDeclareAnomaly(eventTimestamp2, 16, *bucket16);
-    EXPECT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 0UL);
-    EXPECT_EQ(anomalyTracker.mLastAnomalyTimestampNs, eventTimestamp2);
+    detectAndDeclareAnomalies(anomalyTracker, 16, bucket16, eventTimestamp2);
+    // TODO: after detectAnomaly fix: EXPECT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 0UL);
     EXPECT_EQ(anomalyTracker.mMostRecentBucketNum, 15L);
+    checkRefractoryTimes(anomalyTracker, eventTimestamp2, refractoryPeriodSec,
+            {{keyA, -1}, {keyB, eventTimestamp2}, {keyC, -1}, {keyD, -1}, {keyE, -1}});
 
     // Add past bucket #16
     anomalyTracker.addPastBucket(bucket16, 16);
     EXPECT_EQ(anomalyTracker.mMostRecentBucketNum, 16L);
     EXPECT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 1UL);
     EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyB), 4LL);
-    EXPECT_TRUE(anomalyTracker.detectAnomaly(18, *bucket18));
+    EXPECT_TRUE(detectAnomaliesPass(anomalyTracker, 18, bucket18, {keyB}, {keyA, keyC, keyD}));
     EXPECT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 1UL);
     EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyB), 4LL);
     // Within refractory period.
-    anomalyTracker.detectAndDeclareAnomaly(eventTimestamp3, 18, *bucket18);
-    EXPECT_EQ(anomalyTracker.mLastAnomalyTimestampNs, eventTimestamp2);
+    detectAndDeclareAnomalies(anomalyTracker, 18, bucket18, eventTimestamp3);
+    checkRefractoryTimes(anomalyTracker, eventTimestamp3, refractoryPeriodSec,
+            {{keyA, -1}, {keyB, eventTimestamp2}, {keyC, -1}, {keyD, -1}, {keyE, -1}});
     EXPECT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 1UL);
     EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyB), 4LL);
 
@@ -243,13 +333,14 @@
     EXPECT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 2UL);
     EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyB), 1LL);
     EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyC), 1LL);
-    EXPECT_TRUE(anomalyTracker.detectAnomaly(20, *bucket20));
+    EXPECT_TRUE(detectAnomaliesPass(anomalyTracker, 20, bucket20, {keyB}, {keyA, keyC, keyD}));
     EXPECT_EQ(anomalyTracker.mMostRecentBucketNum, 19L);
     EXPECT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 2UL);
     EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyB), 1LL);
     EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyC), 1LL);
-    anomalyTracker.detectAndDeclareAnomaly(eventTimestamp4, 20, *bucket20);
-    EXPECT_EQ(anomalyTracker.mLastAnomalyTimestampNs, eventTimestamp4);
+    detectAndDeclareAnomalies(anomalyTracker, 20, bucket20, eventTimestamp4);
+    checkRefractoryTimes(anomalyTracker, eventTimestamp4, refractoryPeriodSec,
+            {{keyA, -1}, {keyB, eventTimestamp4}, {keyC, -1}, {keyD, -1}, {keyE, -1}});
 
     // Add bucket #18 again. Nothing changes.
     anomalyTracker.addPastBucket(bucket18, 18);
@@ -257,13 +348,14 @@
     EXPECT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 2UL);
     EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyB), 1LL);
     EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyC), 1LL);
-    EXPECT_TRUE(anomalyTracker.detectAnomaly(20, *bucket20));
+    EXPECT_TRUE(detectAnomaliesPass(anomalyTracker, 20, bucket20, {keyB}, {keyA, keyC, keyD}));
     EXPECT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 2UL);
     EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyB), 1LL);
     EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyC), 1LL);
-    anomalyTracker.detectAndDeclareAnomaly(eventTimestamp4 + 1, 20, *bucket20);
+    detectAndDeclareAnomalies(anomalyTracker, 20, bucket20, eventTimestamp4 + 1);
     // Within refractory period.
-    EXPECT_EQ(anomalyTracker.mLastAnomalyTimestampNs, eventTimestamp4);
+    checkRefractoryTimes(anomalyTracker, eventTimestamp4 + 1, refractoryPeriodSec,
+            {{keyA, -1}, {keyB, eventTimestamp4}, {keyC, -1}, {keyD, -1}, {keyE, -1}});
 
     // Add past bucket #20
     anomalyTracker.addPastBucket(bucket20, 20);
@@ -271,32 +363,37 @@
     EXPECT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 2UL);
     EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyB), 3LL);
     EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyC), 1LL);
-    EXPECT_FALSE(anomalyTracker.detectAnomaly(25, *bucket25));
+    EXPECT_TRUE(detectAnomaliesPass(anomalyTracker, 25, bucket25, {}, {keyA, keyB, keyC, keyD}));
     EXPECT_EQ(anomalyTracker.mMostRecentBucketNum, 24L);
-    EXPECT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 0UL);
-    anomalyTracker.detectAndDeclareAnomaly(eventTimestamp5, 25, *bucket25);
-    EXPECT_EQ(anomalyTracker.mLastAnomalyTimestampNs, eventTimestamp4);
+    // TODO: after detectAnomaly fix: EXPECT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 0UL);
+    detectAndDeclareAnomalies(anomalyTracker, 25, bucket25, eventTimestamp5);
+    checkRefractoryTimes(anomalyTracker, eventTimestamp5, refractoryPeriodSec,
+            {{keyA, -1}, {keyB, eventTimestamp4}, {keyC, -1}, {keyD, -1}, {keyE, -1}});
 
     // Add past bucket #25
     anomalyTracker.addPastBucket(bucket25, 25);
     EXPECT_EQ(anomalyTracker.mMostRecentBucketNum, 25L);
-    EXPECT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 1UL);
+    // TODO: after detectAnomaly fix: EXPECT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 1UL);
     EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyD), 1LL);
-    EXPECT_FALSE(anomalyTracker.detectAnomaly(28, *bucket28));
+    EXPECT_TRUE(detectAnomaliesPass(anomalyTracker, 28, bucket28, {},
+            {keyA, keyB, keyC, keyD, keyE}));
     EXPECT_EQ(anomalyTracker.mMostRecentBucketNum, 27L);
-    EXPECT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 0UL);
-    anomalyTracker.detectAndDeclareAnomaly(eventTimestamp6, 28, *bucket28);
-    EXPECT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 0UL);
-    EXPECT_EQ(anomalyTracker.mLastAnomalyTimestampNs, eventTimestamp4);
+    // TODO: after detectAnomaly fix: EXPECT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 0UL);
+    detectAndDeclareAnomalies(anomalyTracker, 28, bucket28, eventTimestamp6);
+    // TODO: after detectAnomaly fix: EXPECT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 0UL);
+    checkRefractoryTimes(anomalyTracker, eventTimestamp6, refractoryPeriodSec,
+            {{keyA, -1}, {keyB, -1}, {keyC, -1}, {keyD, -1}, {keyE, -1}});
 
     // Updates current bucket #28.
     (*bucket28)[keyE] = 5;
-    EXPECT_TRUE(anomalyTracker.detectAnomaly(28, *bucket28));
+    EXPECT_TRUE(detectAnomaliesPass(anomalyTracker, 28, bucket28, {keyE},
+            {keyA, keyB, keyC, keyD}));
     EXPECT_EQ(anomalyTracker.mMostRecentBucketNum, 27L);
-    EXPECT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 0UL);
-    anomalyTracker.detectAndDeclareAnomaly(eventTimestamp6 + 7, 28, *bucket28);
-    EXPECT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 0UL);
-    EXPECT_EQ(anomalyTracker.mLastAnomalyTimestampNs, eventTimestamp6 + 7);
+    // TODO: after detectAnomaly fix: EXPECT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 0UL);
+    detectAndDeclareAnomalies(anomalyTracker, 28, bucket28, eventTimestamp6 + 7);
+    // TODO: after detectAnomaly fix: EXPECT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 0UL);
+    checkRefractoryTimes(anomalyTracker, eventTimestamp6, refractoryPeriodSec,
+            {{keyA, -1}, {keyB, -1}, {keyC, -1}, {keyD, -1}, {keyE, eventTimestamp6 + 7}});
 }
 
 }  // namespace statsd
diff --git a/cmds/statsd/tests/metrics/CountMetricProducer_test.cpp b/cmds/statsd/tests/metrics/CountMetricProducer_test.cpp
index 768336b..c391513 100644
--- a/cmds/statsd/tests/metrics/CountMetricProducer_test.cpp
+++ b/cmds/statsd/tests/metrics/CountMetricProducer_test.cpp
@@ -191,13 +191,14 @@
     EXPECT_EQ(1LL, bucketInfo.mCount);
 }
 
-TEST(CountMetricProducerTest, TestAnomalyDetection) {
+TEST(CountMetricProducerTest, TestAnomalyDetectionUnSliced) {
     Alert alert;
     alert.set_id(11);
     alert.set_metric_id(1);
     alert.set_trigger_if_sum_gt(2);
     alert.set_num_buckets(2);
-    alert.set_refractory_period_secs(1);
+    const int32_t refPeriodSec = 1;
+    alert.set_refractory_period_secs(refPeriodSec);
 
     int64_t bucketStartTimeNs = 10000000000;
     int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL;
@@ -220,7 +221,7 @@
     LogEvent event4(tagId, bucketStartTimeNs + 3 * bucketSizeNs + 1);
     LogEvent event5(tagId, bucketStartTimeNs + 3 * bucketSizeNs + 2);
     LogEvent event6(tagId, bucketStartTimeNs + 3 * bucketSizeNs + 3);
-    LogEvent event7(tagId, bucketStartTimeNs + 3 * bucketSizeNs + 3 + NS_PER_SEC);
+    LogEvent event7(tagId, bucketStartTimeNs + 3 * bucketSizeNs + 2 * NS_PER_SEC);
 
     // Two events in bucket #0.
     countProducer.onMatchedLogEvent(1 /*log matcher index*/, event1);
@@ -228,13 +229,13 @@
 
     EXPECT_EQ(1UL, countProducer.mCurrentSlicedCounter->size());
     EXPECT_EQ(2L, countProducer.mCurrentSlicedCounter->begin()->second);
-    EXPECT_EQ(anomalyTracker->getLastAnomalyTimestampNs(), -1LL);
+    EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_DIMENSION_KEY), 0U);
 
     // One event in bucket #2. No alarm as bucket #0 is trashed out.
     countProducer.onMatchedLogEvent(1 /*log matcher index*/, event3);
     EXPECT_EQ(1UL, countProducer.mCurrentSlicedCounter->size());
     EXPECT_EQ(1L, countProducer.mCurrentSlicedCounter->begin()->second);
-    EXPECT_EQ(anomalyTracker->getLastAnomalyTimestampNs(), -1LL);
+    EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_DIMENSION_KEY), 0U);
 
     // Two events in bucket #3.
     countProducer.onMatchedLogEvent(1 /*log matcher index*/, event4);
@@ -243,12 +244,14 @@
     EXPECT_EQ(1UL, countProducer.mCurrentSlicedCounter->size());
     EXPECT_EQ(3L, countProducer.mCurrentSlicedCounter->begin()->second);
     // Anomaly at event 6 is within refractory period. The alarm is at event 5 timestamp not event 6
-    EXPECT_EQ(anomalyTracker->getLastAnomalyTimestampNs(), (long long)event5.GetTimestampNs());
+    EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_DIMENSION_KEY),
+            event5.GetTimestampNs() / NS_PER_SEC + refPeriodSec);
 
     countProducer.onMatchedLogEvent(1 /*log matcher index*/, event7);
     EXPECT_EQ(1UL, countProducer.mCurrentSlicedCounter->size());
     EXPECT_EQ(4L, countProducer.mCurrentSlicedCounter->begin()->second);
-    EXPECT_EQ(anomalyTracker->getLastAnomalyTimestampNs(), (long long)event7.GetTimestampNs());
+    EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_DIMENSION_KEY),
+            event7.GetTimestampNs() / NS_PER_SEC + refPeriodSec);
 }
 
 }  // namespace statsd
diff --git a/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp b/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp
index ad9c5a3..82772d8 100644
--- a/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp
+++ b/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp
@@ -201,6 +201,8 @@
     alert.set_metric_id(metricId);
     alert.set_trigger_if_sum_gt(25);
     alert.set_num_buckets(2);
+    const int32_t refPeriodSec = 60;
+    alert.set_refractory_period_secs(refPeriodSec);
     sp<AnomalyTracker> anomalyTracker = gaugeProducer.addAnomalyTracker(alert);
 
     int tagId = 1;
@@ -213,10 +215,10 @@
     EXPECT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size());
     EXPECT_EQ(13L,
         gaugeProducer.mCurrentSlicedBucket->begin()->second->begin()->second.value_int());
-    EXPECT_EQ(anomalyTracker->getLastAnomalyTimestampNs(), -1LL);
+    EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_DIMENSION_KEY), 0U);
 
     std::shared_ptr<LogEvent> event2 =
-            std::make_shared<LogEvent>(tagId, bucketStartTimeNs + bucketSizeNs + 10);
+            std::make_shared<LogEvent>(tagId, bucketStartTimeNs + bucketSizeNs + 20);
     event2->write("some value");
     event2->write(15);
     event2->init();
@@ -225,19 +227,21 @@
     EXPECT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size());
     EXPECT_EQ(15L,
         gaugeProducer.mCurrentSlicedBucket->begin()->second->begin()->second.value_int());
-    EXPECT_EQ(anomalyTracker->getLastAnomalyTimestampNs(), (long long)event2->GetTimestampNs());
+    EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_DIMENSION_KEY),
+            event2->GetTimestampNs() / NS_PER_SEC + refPeriodSec);
 
     std::shared_ptr<LogEvent> event3 =
             std::make_shared<LogEvent>(tagId, bucketStartTimeNs + 2 * bucketSizeNs + 10);
     event3->write("some value");
-    event3->write(24);
+    event3->write(26);
     event3->init();
 
     gaugeProducer.onDataPulled({event3});
     EXPECT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size());
-    EXPECT_EQ(24L,
+    EXPECT_EQ(26L,
         gaugeProducer.mCurrentSlicedBucket->begin()->second->begin()->second.value_int());
-    EXPECT_EQ(anomalyTracker->getLastAnomalyTimestampNs(), (long long)event3->GetTimestampNs());
+    EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_DIMENSION_KEY),
+            event2->GetTimestampNs() / NS_PER_SEC + refPeriodSec);
 
     // The event4 does not have the gauge field. Thus the current bucket value is 0.
     std::shared_ptr<LogEvent> event4 =
@@ -247,7 +251,6 @@
     gaugeProducer.onDataPulled({event4});
     EXPECT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size());
     EXPECT_TRUE(gaugeProducer.mCurrentSlicedBucket->begin()->second->empty());
-    EXPECT_EQ(anomalyTracker->getLastAnomalyTimestampNs(), (long long)event3->GetTimestampNs());
 }
 
 }  // namespace statsd
diff --git a/cmds/statsd/tests/metrics/MaxDurationTracker_test.cpp b/cmds/statsd/tests/metrics/MaxDurationTracker_test.cpp
index 704a466..0772b0d40 100644
--- a/cmds/statsd/tests/metrics/MaxDurationTracker_test.cpp
+++ b/cmds/statsd/tests/metrics/MaxDurationTracker_test.cpp
@@ -50,24 +50,22 @@
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
 
     unordered_map<HashableDimensionKey, vector<DurationBucket>> buckets;
-    ConditionKey conditionKey1;
-    conditionKey1[StringToId("condition")] = conditionKey;
 
     uint64_t bucketStartTimeNs = 10000000000;
     uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
 
     int64_t metricId = 1;
     MaxDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, -1, false, bucketStartTimeNs,
-                               bucketSizeNs, {});
+                               bucketSizeNs, false, {});
 
-    tracker.noteStart(key1, true, bucketStartTimeNs, conditionKey1);
+    tracker.noteStart(key1, true, bucketStartTimeNs, ConditionKey());
     // Event starts again. This would not change anything as it already starts.
-    tracker.noteStart(key1, true, bucketStartTimeNs + 3, conditionKey1);
+    tracker.noteStart(key1, true, bucketStartTimeNs + 3, ConditionKey());
     // Stopped.
     tracker.noteStop(key1, bucketStartTimeNs + 10, false);
 
     // Another event starts in this bucket.
-    tracker.noteStart(key2, true, bucketStartTimeNs + 20, conditionKey1);
+    tracker.noteStart(key2, true, bucketStartTimeNs + 20, ConditionKey());
     tracker.noteStop(key2, bucketStartTimeNs + 40, false /*stop all*/);
 
     tracker.flushIfNeeded(bucketStartTimeNs + bucketSizeNs + 1, &buckets);
@@ -80,20 +78,18 @@
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
 
     unordered_map<HashableDimensionKey, vector<DurationBucket>> buckets;
-    ConditionKey conditionKey1;
-    conditionKey1[StringToId("condition")] = conditionKey;
 
     uint64_t bucketStartTimeNs = 10000000000;
     uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
 
     int64_t metricId = 1;
     MaxDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, -1, false, bucketStartTimeNs,
-                               bucketSizeNs, {});
+                               bucketSizeNs, false, {});
 
-    tracker.noteStart(key1, true, bucketStartTimeNs + 1, conditionKey1);
+    tracker.noteStart(key1, true, bucketStartTimeNs + 1, ConditionKey());
 
     // Another event starts in this bucket.
-    tracker.noteStart(key2, true, bucketStartTimeNs + 20, conditionKey1);
+    tracker.noteStart(key2, true, bucketStartTimeNs + 20, ConditionKey());
     tracker.flushIfNeeded(bucketStartTimeNs + bucketSizeNs + 40, &buckets);
     tracker.noteStopAll(bucketStartTimeNs + bucketSizeNs + 40);
     EXPECT_TRUE(tracker.mInfos.empty());
@@ -112,22 +108,20 @@
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
 
     unordered_map<HashableDimensionKey, vector<DurationBucket>> buckets;
-    ConditionKey conditionKey1;
-    conditionKey1[StringToId("condition")] = conditionKey;
 
     uint64_t bucketStartTimeNs = 10000000000;
     uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
 
     int64_t metricId = 1;
     MaxDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, -1, false, bucketStartTimeNs,
-                               bucketSizeNs, {});
+                               bucketSizeNs, false, {});
 
     // The event starts.
-    tracker.noteStart(DEFAULT_DIMENSION_KEY, true, bucketStartTimeNs + 1, conditionKey1);
+    tracker.noteStart(DEFAULT_DIMENSION_KEY, true, bucketStartTimeNs + 1, ConditionKey());
 
     // Starts again. Does not change anything.
     tracker.noteStart(DEFAULT_DIMENSION_KEY, true, bucketStartTimeNs + bucketSizeNs + 1,
-                      conditionKey1);
+                      ConditionKey());
 
     // The event stops at early 4th bucket.
     tracker.flushIfNeeded(bucketStartTimeNs + (3 * bucketSizeNs) + 20, &buckets);
@@ -144,19 +138,17 @@
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
 
     unordered_map<HashableDimensionKey, vector<DurationBucket>> buckets;
-    ConditionKey conditionKey1;
-    conditionKey1[StringToId("condition")] = conditionKey;
 
     uint64_t bucketStartTimeNs = 10000000000;
     uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
 
     int64_t metricId = 1;
     MaxDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, -1, true, bucketStartTimeNs,
-                               bucketSizeNs, {});
+                               bucketSizeNs, false, {});
 
     // 2 starts
-    tracker.noteStart(DEFAULT_DIMENSION_KEY, true, bucketStartTimeNs + 1, conditionKey1);
-    tracker.noteStart(DEFAULT_DIMENSION_KEY, true, bucketStartTimeNs + 10, conditionKey1);
+    tracker.noteStart(DEFAULT_DIMENSION_KEY, true, bucketStartTimeNs + 1, ConditionKey());
+    tracker.noteStart(DEFAULT_DIMENSION_KEY, true, bucketStartTimeNs + 10, ConditionKey());
     // one stop
     tracker.noteStop(DEFAULT_DIMENSION_KEY, bucketStartTimeNs + 20, false /*stop all*/);
 
@@ -196,7 +188,7 @@
 
     int64_t metricId = 1;
     MaxDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, false, bucketStartTimeNs,
-                               bucketSizeNs, {});
+                               bucketSizeNs, true, {});
     EXPECT_TRUE(tracker.mAnomalyTrackers.empty());
 
     tracker.noteStart(key1, true, eventStartTimeNs, conditionKey1);
@@ -218,31 +210,32 @@
     alert.set_metric_id(metricId);
     alert.set_trigger_if_sum_gt(32 * NS_PER_SEC);
     alert.set_num_buckets(2);
-    alert.set_refractory_period_secs(1);
+    const int32_t refPeriodSec = 1;
+    alert.set_refractory_period_secs(refPeriodSec);
 
     unordered_map<HashableDimensionKey, vector<DurationBucket>> buckets;
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
-    ConditionKey conditionKey1;
-    conditionKey1[StringToId("APP_BACKGROUND")] = conditionKey;
+
     uint64_t bucketStartTimeNs = 10 * NS_PER_SEC;
     uint64_t eventStartTimeNs = bucketStartTimeNs + NS_PER_SEC + 1;
     uint64_t bucketSizeNs = 30 * NS_PER_SEC;
 
     sp<DurationAnomalyTracker> anomalyTracker = new DurationAnomalyTracker(alert, kConfigKey);
     MaxDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, -1, true, bucketStartTimeNs,
-                               bucketSizeNs, {anomalyTracker});
+                               bucketSizeNs, false, {anomalyTracker});
 
-    tracker.noteStart(key1, true, eventStartTimeNs, conditionKey1);
+    tracker.noteStart(key1, true, eventStartTimeNs, ConditionKey());
     tracker.noteStop(key1, eventStartTimeNs + 10, false);
-    EXPECT_EQ(anomalyTracker->mLastAnomalyTimestampNs, -1);
+    EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(eventKey), 0U);
     EXPECT_EQ(10LL, tracker.mDuration);
 
-    tracker.noteStart(key2, true, eventStartTimeNs + 20, conditionKey1);
+    tracker.noteStart(key2, true, eventStartTimeNs + 20, ConditionKey());
     tracker.flushIfNeeded(eventStartTimeNs + 2 * bucketSizeNs + 3 * NS_PER_SEC, &buckets);
     tracker.noteStop(key2, eventStartTimeNs + 2 * bucketSizeNs + 3 * NS_PER_SEC, false);
     EXPECT_EQ((long long)(4 * NS_PER_SEC + 1LL), tracker.mDuration);
-    EXPECT_EQ(anomalyTracker->mLastAnomalyTimestampNs,
-              (long long)(eventStartTimeNs + 2 * bucketSizeNs + 3 * NS_PER_SEC));
+
+    EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(eventKey),
+              (eventStartTimeNs + 2 * bucketSizeNs) / NS_PER_SEC  + 3 + refPeriodSec);
 }
 
 }  // namespace statsd
diff --git a/cmds/statsd/tests/metrics/OringDurationTracker_test.cpp b/cmds/statsd/tests/metrics/OringDurationTracker_test.cpp
index 36cdaae..6b8893e 100644
--- a/cmds/statsd/tests/metrics/OringDurationTracker_test.cpp
+++ b/cmds/statsd/tests/metrics/OringDurationTracker_test.cpp
@@ -47,9 +47,6 @@
 TEST(OringDurationTrackerTest, TestDurationOverlap) {
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
 
-    ConditionKey key1;
-    key1[StringToId("APP_BACKGROUND")] = kConditionKey1;
-
     unordered_map<HashableDimensionKey, vector<DurationBucket>> buckets;
 
     uint64_t bucketStartTimeNs = 10000000000;
@@ -58,11 +55,11 @@
     uint64_t durationTimeNs = 2 * 1000;
 
     OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, false,
-                                 bucketStartTimeNs, bucketSizeNs, {});
+                                 bucketStartTimeNs, bucketSizeNs, false, {});
 
-    tracker.noteStart(kEventKey1, true, eventStartTimeNs, key1);
+    tracker.noteStart(kEventKey1, true, eventStartTimeNs, ConditionKey());
     EXPECT_EQ((long long)eventStartTimeNs, tracker.mLastStartTime);
-    tracker.noteStart(kEventKey1, true, eventStartTimeNs + 10, key1);  // overlapping wl
+    tracker.noteStart(kEventKey1, true, eventStartTimeNs + 10, ConditionKey());  // overlapping wl
     EXPECT_EQ((long long)eventStartTimeNs, tracker.mLastStartTime);
 
     tracker.noteStop(kEventKey1, eventStartTimeNs + durationTimeNs, false);
@@ -76,9 +73,6 @@
 TEST(OringDurationTrackerTest, TestDurationNested) {
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
 
-    ConditionKey key1;
-    key1[StringToId("APP_BACKGROUND")] = kConditionKey1;
-
     unordered_map<HashableDimensionKey, vector<DurationBucket>> buckets;
 
     uint64_t bucketStartTimeNs = 10000000000;
@@ -86,10 +80,10 @@
     uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
 
     OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, true, bucketStartTimeNs,
-                                 bucketSizeNs, {});
+                                 bucketSizeNs, false, {});
 
-    tracker.noteStart(kEventKey1, true, eventStartTimeNs, key1);
-    tracker.noteStart(kEventKey1, true, eventStartTimeNs + 10, key1);  // overlapping wl
+    tracker.noteStart(kEventKey1, true, eventStartTimeNs, ConditionKey());
+    tracker.noteStart(kEventKey1, true, eventStartTimeNs + 10, ConditionKey());  // overlapping wl
 
     tracker.noteStop(kEventKey1, eventStartTimeNs + 2000, false);
     tracker.noteStop(kEventKey1, eventStartTimeNs + 2003, false);
@@ -103,9 +97,6 @@
 TEST(OringDurationTrackerTest, TestStopAll) {
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
 
-    ConditionKey key1;
-    key1[StringToId("APP_BACKGROUND")] = kConditionKey1;
-
     unordered_map<HashableDimensionKey, vector<DurationBucket>> buckets;
 
     uint64_t bucketStartTimeNs = 10000000000;
@@ -113,10 +104,10 @@
     uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
 
     OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, true, bucketStartTimeNs,
-                                 bucketSizeNs, {});
+                                 bucketSizeNs, false, {});
 
-    tracker.noteStart(kEventKey1, true, eventStartTimeNs, key1);
-    tracker.noteStart(kEventKey2, true, eventStartTimeNs + 10, key1);  // overlapping wl
+    tracker.noteStart(kEventKey1, true, eventStartTimeNs, ConditionKey());
+    tracker.noteStart(kEventKey2, true, eventStartTimeNs + 10, ConditionKey());  // overlapping wl
 
     tracker.noteStopAll(eventStartTimeNs + 2003);
 
@@ -129,9 +120,6 @@
 TEST(OringDurationTrackerTest, TestCrossBucketBoundary) {
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
 
-    ConditionKey key1;
-    key1[StringToId("APP_BACKGROUND")] = kConditionKey1;
-
     unordered_map<HashableDimensionKey, vector<DurationBucket>> buckets;
 
     uint64_t bucketStartTimeNs = 10000000000;
@@ -140,12 +128,12 @@
     uint64_t durationTimeNs = 2 * 1000;
 
     OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, true, bucketStartTimeNs,
-                                 bucketSizeNs, {});
+                                 bucketSizeNs, false, {});
 
-    tracker.noteStart(kEventKey1, true, eventStartTimeNs, key1);
+    tracker.noteStart(kEventKey1, true, eventStartTimeNs, ConditionKey());
     EXPECT_EQ((long long)eventStartTimeNs, tracker.mLastStartTime);
     tracker.flushIfNeeded(eventStartTimeNs + 2 * bucketSizeNs, &buckets);
-    tracker.noteStart(kEventKey1, true, eventStartTimeNs + 2 * bucketSizeNs, key1);
+    tracker.noteStart(kEventKey1, true, eventStartTimeNs + 2 * bucketSizeNs, ConditionKey());
     EXPECT_EQ((long long)(bucketStartTimeNs + 2 * bucketSizeNs), tracker.mLastStartTime);
 
     EXPECT_EQ(2u, buckets[eventKey].size());
@@ -178,7 +166,7 @@
     uint64_t durationTimeNs = 2 * 1000;
 
     OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, false,
-                                 bucketStartTimeNs, bucketSizeNs, {});
+                                 bucketStartTimeNs, bucketSizeNs, true, {});
 
     tracker.noteStart(kEventKey1, true, eventStartTimeNs, key1);
 
@@ -211,7 +199,7 @@
     uint64_t durationTimeNs = 2 * 1000;
 
     OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, false,
-                                 bucketStartTimeNs, bucketSizeNs, {});
+                                 bucketStartTimeNs, bucketSizeNs, true, {});
 
     tracker.noteStart(kEventKey1, true, eventStartTimeNs, key1);
     // condition to false; record duration 5n
@@ -243,7 +231,7 @@
     uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
 
     OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, true, bucketStartTimeNs,
-                                 bucketSizeNs, {});
+                                 bucketSizeNs, true, {});
 
     tracker.noteStart(kEventKey1, true, eventStartTimeNs, key1);
     tracker.noteStart(kEventKey1, true, eventStartTimeNs + 2, key1);
@@ -270,18 +258,17 @@
 
     unordered_map<HashableDimensionKey, vector<DurationBucket>> buckets;
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
-    ConditionKey key1;
-    key1[StringToId("APP_BACKGROUND")] = kConditionKey1;
+
     uint64_t bucketStartTimeNs = 10 * NS_PER_SEC;
     uint64_t eventStartTimeNs = bucketStartTimeNs + NS_PER_SEC + 1;
     uint64_t bucketSizeNs = 30 * NS_PER_SEC;
 
     sp<DurationAnomalyTracker> anomalyTracker = new DurationAnomalyTracker(alert, kConfigKey);
     OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, true, bucketStartTimeNs,
-                                 bucketSizeNs, {anomalyTracker});
+                                 bucketSizeNs, true, {anomalyTracker});
 
     // Nothing in the past bucket.
-    tracker.noteStart(DEFAULT_DIMENSION_KEY, true, eventStartTimeNs, key1);
+    tracker.noteStart(DEFAULT_DIMENSION_KEY, true, eventStartTimeNs, ConditionKey());
     EXPECT_EQ((long long)(alert.trigger_if_sum_gt() + eventStartTimeNs),
               tracker.predictAnomalyTimestampNs(*anomalyTracker, eventStartTimeNs));
 
@@ -289,7 +276,7 @@
     EXPECT_EQ(0u, buckets[eventKey].size());
 
     uint64_t event1StartTimeNs = eventStartTimeNs + 10;
-    tracker.noteStart(kEventKey1, true, event1StartTimeNs, key1);
+    tracker.noteStart(kEventKey1, true, event1StartTimeNs, ConditionKey());
     // No past buckets. The anomaly will happen in bucket #0.
     EXPECT_EQ((long long)(event1StartTimeNs + alert.trigger_if_sum_gt() - 3),
               tracker.predictAnomalyTimestampNs(*anomalyTracker, event1StartTimeNs));
@@ -308,7 +295,7 @@
 
     // One past buckets. The anomaly will happen in bucket #1.
     uint64_t event2StartTimeNs = eventStartTimeNs + bucketSizeNs + 15;
-    tracker.noteStart(kEventKey1, true, event2StartTimeNs, key1);
+    tracker.noteStart(kEventKey1, true, event2StartTimeNs, ConditionKey());
     EXPECT_EQ((long long)(event2StartTimeNs + alert.trigger_if_sum_gt() - bucket0Duration -
                           bucket1Duration),
               tracker.predictAnomalyTimestampNs(*anomalyTracker, event2StartTimeNs));
@@ -317,48 +304,111 @@
     // Only one past buckets is applicable. Bucket +0 should be trashed. The anomaly will happen in
     // bucket #2.
     uint64_t event3StartTimeNs = bucketStartTimeNs + 2 * bucketSizeNs - 9 * NS_PER_SEC;
-    tracker.noteStart(kEventKey1, true, event3StartTimeNs, key1);
+    tracker.noteStart(kEventKey1, true, event3StartTimeNs, ConditionKey());
     EXPECT_EQ((long long)(event3StartTimeNs + alert.trigger_if_sum_gt() - bucket1Duration - 1LL),
               tracker.predictAnomalyTimestampNs(*anomalyTracker, event3StartTimeNs));
 }
 
-TEST(OringDurationTrackerTest, TestAnomalyDetection) {
+TEST(OringDurationTrackerTest, TestAnomalyDetectionExpiredAlarm) {
     Alert alert;
     alert.set_id(101);
     alert.set_metric_id(1);
     alert.set_trigger_if_sum_gt(40 * NS_PER_SEC);
     alert.set_num_buckets(2);
-    alert.set_refractory_period_secs(1);
+    const int32_t refPeriodSec = 45;
+    alert.set_refractory_period_secs(refPeriodSec);
 
     unordered_map<HashableDimensionKey, vector<DurationBucket>> buckets;
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
-    ConditionKey key1;
-    key1[StringToId("APP_BACKGROUND")] = kConditionKey1;
+
     uint64_t bucketStartTimeNs = 10 * NS_PER_SEC;
     uint64_t eventStartTimeNs = bucketStartTimeNs + NS_PER_SEC + 1;
     uint64_t bucketSizeNs = 30 * NS_PER_SEC;
 
     sp<DurationAnomalyTracker> anomalyTracker = new DurationAnomalyTracker(alert, kConfigKey);
     OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, true /*nesting*/,
-                                 bucketStartTimeNs, bucketSizeNs, {anomalyTracker});
+                                 bucketStartTimeNs, bucketSizeNs, false, {anomalyTracker});
 
-    tracker.noteStart(DEFAULT_DIMENSION_KEY, true, eventStartTimeNs, key1);
-    tracker.noteStop(DEFAULT_DIMENSION_KEY, eventStartTimeNs + 10, false);
-    EXPECT_EQ(anomalyTracker->mLastAnomalyTimestampNs, -1);
+    tracker.noteStart(kEventKey1, true, eventStartTimeNs, ConditionKey());
+    tracker.noteStop(kEventKey1, eventStartTimeNs + 10, false);
+    EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(eventKey), 0U);
     EXPECT_TRUE(tracker.mStarted.empty());
     EXPECT_EQ(10LL, tracker.mDuration);
 
     EXPECT_EQ(0u, tracker.mStarted.size());
 
-    tracker.noteStart(DEFAULT_DIMENSION_KEY, true, eventStartTimeNs + 20, key1);
+    tracker.noteStart(kEventKey1, true, eventStartTimeNs + 20, ConditionKey());
     EXPECT_EQ(1u, anomalyTracker->mAlarms.size());
     EXPECT_EQ((long long)(51ULL * NS_PER_SEC),
               (long long)(anomalyTracker->mAlarms.begin()->second->timestampSec * NS_PER_SEC));
+    // The alarm is set to fire at 51s, and when it does, an anomaly would be declared. However,
+    // because this is a unit test, the alarm won't actually fire at all. Since the alarm fails
+    // to fire in time, the anomaly is instead caught when noteStop is called, at around 71s.
     tracker.flushIfNeeded(eventStartTimeNs + 2 * bucketSizeNs + 25, &buckets);
-    tracker.noteStop(DEFAULT_DIMENSION_KEY, eventStartTimeNs + 2 * bucketSizeNs + 25, false);
+    tracker.noteStop(kEventKey1, eventStartTimeNs + 2 * bucketSizeNs + 25, false);
     EXPECT_EQ(anomalyTracker->getSumOverPastBuckets(eventKey), (long long)(bucketSizeNs));
-    EXPECT_EQ((long long)(eventStartTimeNs + 2 * bucketSizeNs + 25),
-              anomalyTracker->mLastAnomalyTimestampNs);
+    EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(eventKey),
+              (eventStartTimeNs + 2 * bucketSizeNs + 25) / NS_PER_SEC + refPeriodSec);
+}
+
+TEST(OringDurationTrackerTest, TestAnomalyDetectionFiredAlarm) {
+    Alert alert;
+    alert.set_id(101);
+    alert.set_metric_id(1);
+    alert.set_trigger_if_sum_gt(40 * NS_PER_SEC);
+    alert.set_num_buckets(2);
+    const int32_t refPeriodSec = 45;
+    alert.set_refractory_period_secs(refPeriodSec);
+
+    unordered_map<HashableDimensionKey, vector<DurationBucket>> buckets;
+    sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
+    ConditionKey conkey;
+    conkey[StringToId("APP_BACKGROUND")] = kConditionKey1;
+    uint64_t bucketStartTimeNs = 10 * NS_PER_SEC;
+    uint64_t eventStartTimeNs = bucketStartTimeNs + NS_PER_SEC + 1;
+    uint64_t bucketSizeNs = 30 * NS_PER_SEC;
+
+    sp<DurationAnomalyTracker> anomalyTracker = new DurationAnomalyTracker(alert, kConfigKey);
+    OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, true /*nesting*/,
+                                 bucketStartTimeNs, bucketSizeNs, false, {anomalyTracker});
+
+    tracker.noteStart(kEventKey1, true, 15 * NS_PER_SEC, conkey); // start key1
+    EXPECT_EQ(1u, anomalyTracker->mAlarms.size());
+    sp<const AnomalyAlarm> alarm = anomalyTracker->mAlarms.begin()->second;
+    EXPECT_EQ((long long)(55ULL * NS_PER_SEC), (long long)(alarm->timestampSec * NS_PER_SEC));
+    EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(eventKey), 0U);
+
+    tracker.noteStop(kEventKey1, 17 * NS_PER_SEC, false); // stop key1 (2 seconds later)
+    EXPECT_EQ(0u, anomalyTracker->mAlarms.size());
+    EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(eventKey), 0U);
+
+    tracker.noteStart(kEventKey1, true, 22 * NS_PER_SEC, conkey); // start key1 again
+    EXPECT_EQ(1u, anomalyTracker->mAlarms.size());
+    alarm = anomalyTracker->mAlarms.begin()->second;
+    EXPECT_EQ((long long)(60ULL * NS_PER_SEC), (long long)(alarm->timestampSec * NS_PER_SEC));
+    EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(eventKey), 0U);
+
+    tracker.noteStart(kEventKey2, true, 32 * NS_PER_SEC, conkey); // start key2
+    EXPECT_EQ(1u, anomalyTracker->mAlarms.size());
+    alarm = anomalyTracker->mAlarms.begin()->second;
+    EXPECT_EQ((long long)(60ULL * NS_PER_SEC), (long long)(alarm->timestampSec * NS_PER_SEC));
+    EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(eventKey), 0U);
+
+    tracker.noteStop(kEventKey1, 47 * NS_PER_SEC, false); // stop key1
+    EXPECT_EQ(1u, anomalyTracker->mAlarms.size());
+    alarm = anomalyTracker->mAlarms.begin()->second;
+    EXPECT_EQ((long long)(60ULL * NS_PER_SEC), (long long)(alarm->timestampSec * NS_PER_SEC));
+    EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(eventKey), 0U);
+
+    // Now, at 60s, which is 38s after key1 started again, we have reached 40s of 'on' time.
+    std::unordered_set<sp<const AnomalyAlarm>, SpHash<AnomalyAlarm>> firedAlarms({alarm});
+    anomalyTracker->informAlarmsFired(62 * NS_PER_SEC, firedAlarms);
+    EXPECT_EQ(0u, anomalyTracker->mAlarms.size());
+    EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(eventKey), 62U + refPeriodSec);
+
+    tracker.noteStop(kEventKey2, 69 * NS_PER_SEC, false); // stop key2
+    EXPECT_EQ(0u, anomalyTracker->mAlarms.size());
+    EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(eventKey), 62U + refPeriodSec);
 }
 
 }  // namespace statsd
diff --git a/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp b/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp
index 459da01..fff3dbf 100644
--- a/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp
+++ b/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp
@@ -249,7 +249,8 @@
     alert.set_metric_id(metricId);
     alert.set_trigger_if_sum_gt(130);
     alert.set_num_buckets(2);
-    alert.set_refractory_period_secs(3);
+    const int32_t refPeriodSec = 3;
+    alert.set_refractory_period_secs(refPeriodSec);
 
     ValueMetric metric;
     metric.set_id(metricId);
@@ -297,23 +298,28 @@
     // Two events in bucket #0.
     valueProducer.onMatchedLogEvent(1 /*log matcher index*/, *event1);
     valueProducer.onMatchedLogEvent(1 /*log matcher index*/, *event2);
-    EXPECT_EQ(anomalyTracker->getLastAnomalyTimestampNs(), -1LL); // Value sum == 30 <= 130.
+    // Value sum == 30 <= 130.
+    EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_DIMENSION_KEY), 0U);
 
     // One event in bucket #2. No alarm as bucket #0 is trashed out.
     valueProducer.onMatchedLogEvent(1 /*log matcher index*/, *event3);
-    EXPECT_EQ(anomalyTracker->getLastAnomalyTimestampNs(), -1LL); // Value sum == 130 <= 130.
+    // Value sum == 130 <= 130.
+    EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_DIMENSION_KEY), 0U);
 
     // Three events in bucket #3.
     valueProducer.onMatchedLogEvent(1 /*log matcher index*/, *event4);
     // Anomaly at event 4 since Value sum == 131 > 130!
-    EXPECT_EQ(anomalyTracker->getLastAnomalyTimestampNs(), (long long)event4->GetTimestampNs());
+    EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_DIMENSION_KEY),
+            event4->GetTimestampNs() / NS_PER_SEC + refPeriodSec);
     valueProducer.onMatchedLogEvent(1 /*log matcher index*/, *event5);
     // Event 5 is within 3 sec refractory period. Thus last alarm timestamp is still event4.
-    EXPECT_EQ(anomalyTracker->getLastAnomalyTimestampNs(), (long long)event4->GetTimestampNs());
+    EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_DIMENSION_KEY),
+            event4->GetTimestampNs() / NS_PER_SEC + refPeriodSec);
 
     valueProducer.onMatchedLogEvent(1 /*log matcher index*/, *event6);
     // Anomaly at event 6 since Value sum == 160 > 130 and after refractory period.
-    EXPECT_EQ(anomalyTracker->getLastAnomalyTimestampNs(), (long long)event6->GetTimestampNs());
+    EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_DIMENSION_KEY),
+            event6->GetTimestampNs() / NS_PER_SEC + refPeriodSec);
 }
 
 }  // namespace statsd
diff --git a/core/java/android/annotation/SystemApi.java b/core/java/android/annotation/SystemApi.java
index 55028eb..e96ff01 100644
--- a/core/java/android/annotation/SystemApi.java
+++ b/core/java/android/annotation/SystemApi.java
@@ -39,6 +39,6 @@
  * @hide
  */
 @Target({TYPE, FIELD, METHOD, CONSTRUCTOR, ANNOTATION_TYPE, PACKAGE})
-@Retention(RetentionPolicy.SOURCE)
+@Retention(RetentionPolicy.RUNTIME)
 public @interface SystemApi {
 }
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 85c3be8..046a128 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -5994,8 +5994,15 @@
         /**
          * Sets the title to be displayed on this conversation. May be set to {@code null}.
          *
-         * @param conversationTitle A name for the conversation, or {@code null}
-         * @return this object for method chaining.
+         * <p>This API's behavior was changed in SDK version {@link Build.VERSION_CODES#P}. If your
+         * application's target version is less than {@link Build.VERSION_CODES#P}, setting a
+         * conversation title to a non-null value will make {@link #isGroupConversation()} return
+         * {@code true} and passing {@code null} will make it return {@code false}. In
+         * {@link Build.VERSION_CODES#P} and beyond, use {@link #setGroupConversation(boolean)}
+         * to set group conversation status.
+         *
+         * @param conversationTitle Title displayed for this conversation
+         * @return this object for method chaining
          */
         public MessagingStyle setConversationTitle(@Nullable CharSequence conversationTitle) {
             mConversationTitle = conversationTitle;
@@ -6083,6 +6090,7 @@
 
         /**
          * Sets whether this conversation notification represents a group.
+         *
          * @param isGroupConversation {@code true} if the conversation represents a group,
          * {@code false} otherwise.
          * @return this object for method chaining
@@ -6093,9 +6101,27 @@
         }
 
         /**
-         * Returns {@code true} if this notification represents a group conversation.
+         * Returns {@code true} if this notification represents a group conversation, otherwise
+         * {@code false}.
+         *
+         * <p> If the application that generated this {@link MessagingStyle} targets an SDK version
+         * less than {@link Build.VERSION_CODES#P}, this method becomes dependent on whether or
+         * not the conversation title is set; returning {@code true} if the conversation title is
+         * a non-null value, or {@code false} otherwise. From {@link Build.VERSION_CODES#P} forward,
+         * this method returns what's set by {@link #setGroupConversation(boolean)} allowing for
+         * named, non-group conversations.
+         *
+         * @see #setConversationTitle(CharSequence)
          */
         public boolean isGroupConversation() {
+            // When target SDK version is < P, a non-null conversation title dictates if this is
+            // as group conversation.
+            if (mBuilder != null
+                    && mBuilder.mContext.getApplicationInfo().targetSdkVersion
+                            < Build.VERSION_CODES.P) {
+                return mConversationTitle != null;
+            }
+
             return mIsGroupConversation;
         }
 
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 0b74741..76c078e 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -7518,7 +7518,8 @@
     }
 
     /**
-     * Called by a device owner to disable the keyguard altogether.
+     * Called by a device owner or profile owner of secondary users that is affiliated with the
+     * device to disable the keyguard altogether.
      * <p>
      * Setting the keyguard to disabled has the same effect as choosing "None" as the screen lock
      * type. However, this call has no effect if a password, pin or pattern is currently set. If a
@@ -7533,7 +7534,10 @@
      * @param disabled {@code true} disables the keyguard, {@code false} reenables it.
      * @return {@code false} if attempting to disable the keyguard while a lock password was in
      *         place. {@code true} otherwise.
-     * @throws SecurityException if {@code admin} is not a device owner.
+     * @throws SecurityException if {@code admin} is not the device owner, or a profile owner of
+     * secondary user that is affiliated with the device.
+     * @see #isAffiliatedUser
+     * @see #getSecondaryUsers
      */
     public boolean setKeyguardDisabled(@NonNull ComponentName admin, boolean disabled) {
         throwIfParentInstance("setKeyguardDisabled");
@@ -7545,9 +7549,9 @@
     }
 
     /**
-     * Called by device owner to disable the status bar. Disabling the status bar blocks
-     * notifications, quick settings and other screen overlays that allow escaping from a single use
-     * device.
+     * Called by device owner or profile owner of secondary users  that is affiliated with the
+     * device to disable the status bar. Disabling the status bar blocks notifications, quick
+     * settings and other screen overlays that allow escaping from a single use device.
      * <p>
      * <strong>Note:</strong> This method has no effect for LockTask mode. The behavior of the
      * status bar in LockTask mode can be configured with
@@ -7558,7 +7562,10 @@
      * @param admin Which {@link DeviceAdminReceiver} this request is associated with.
      * @param disabled {@code true} disables the status bar, {@code false} reenables it.
      * @return {@code false} if attempting to disable the status bar failed. {@code true} otherwise.
-     * @throws SecurityException if {@code admin} is not a device owner.
+     * @throws SecurityException if {@code admin} is not the device owner, or a profile owner of
+     * secondary user that is affiliated with the device.
+     * @see #isAffiliatedUser
+     * @see #getSecondaryUsers
      */
     public boolean setStatusBarDisabled(@NonNull ComponentName admin, boolean disabled) {
         throwIfParentInstance("setStatusBarDisabled");
diff --git a/core/java/android/app/usage/UsageStatsManagerInternal.java b/core/java/android/app/usage/UsageStatsManagerInternal.java
index 4b4fe72..93e14de 100644
--- a/core/java/android/app/usage/UsageStatsManagerInternal.java
+++ b/core/java/android/app/usage/UsageStatsManagerInternal.java
@@ -16,6 +16,7 @@
 
 package android.app.usage;
 
+import android.annotation.UserIdInt;
 import android.app.usage.UsageStatsManager.StandbyBuckets;
 import android.content.ComponentName;
 import android.content.res.Configuration;
@@ -37,7 +38,7 @@
      * @param eventType The event that occurred. Valid values can be found at
      * {@link UsageEvents}
      */
-    public abstract void reportEvent(ComponentName component, int userId, int eventType);
+    public abstract void reportEvent(ComponentName component, @UserIdInt int userId, int eventType);
 
     /**
      * Reports an event to the UsageStatsManager.
@@ -47,14 +48,14 @@
      * @param eventType The event that occurred. Valid values can be found at
      * {@link UsageEvents}
      */
-    public abstract void reportEvent(String packageName, int userId, int eventType);
+    public abstract void reportEvent(String packageName, @UserIdInt int userId, int eventType);
 
     /**
      * Reports a configuration change to the UsageStatsManager.
      *
      * @param config The new device configuration.
      */
-    public abstract void reportConfigurationChange(Configuration config, int userId);
+    public abstract void reportConfigurationChange(Configuration config, @UserIdInt int userId);
 
     /**
      * Reports that an action equivalent to a ShortcutInfo is taken by the user.
@@ -65,7 +66,8 @@
      *
      * @see android.content.pm.ShortcutManager#reportShortcutUsed(String)
      */
-    public abstract void reportShortcutUsage(String packageName, String shortcutId, int userId);
+    public abstract void reportShortcutUsage(String packageName, String shortcutId,
+            @UserIdInt int userId);
 
     /**
      * Reports that a content provider has been accessed by a foreground app.
@@ -73,7 +75,8 @@
      * @param pkgName The package name of the content provider
      * @param userId The user in which the content provider was accessed.
      */
-    public abstract void reportContentProviderUsage(String name, String pkgName, int userId);
+    public abstract void reportContentProviderUsage(String name, String pkgName,
+            @UserIdInt int userId);
 
     /**
      * Prepares the UsageStatsService for shutdown.
@@ -89,7 +92,7 @@
      * @param userId
      * @return
      */
-    public abstract boolean isAppIdle(String packageName, int uidForAppId, int userId);
+    public abstract boolean isAppIdle(String packageName, int uidForAppId, @UserIdInt int userId);
 
     /**
      * Returns the app standby bucket that the app is currently in.  This accessor does
@@ -101,15 +104,15 @@
      * @return the AppStandby bucket code the app currently resides in.  If the app is
      *     unknown in the given user, STANDBY_BUCKET_NEVER is returned.
      */
-    @StandbyBuckets public abstract int getAppStandbyBucket(String packageName, int userId,
-            long nowElapsed);
+    @StandbyBuckets public abstract int getAppStandbyBucket(String packageName,
+            @UserIdInt int userId, long nowElapsed);
 
     /**
      * Returns all of the uids for a given user where all packages associating with that uid
      * are in the app idle state -- there are no associated apps that are not idle.  This means
      * all of the returned uids can be safely considered app idle.
      */
-    public abstract int[] getIdleUidsForUser(int userId);
+    public abstract int[] getIdleUidsForUser(@UserIdInt int userId);
 
     /**
      * @return True if currently app idle parole mode is on.  This means all idle apps are allow to
@@ -134,8 +137,8 @@
     public static abstract class AppIdleStateChangeListener {
 
         /** Callback to inform listeners that the idle state has changed to a new bucket. */
-        public abstract void onAppIdleStateChanged(String packageName, int userId, boolean idle,
-                                                   int bucket);
+        public abstract void onAppIdleStateChanged(String packageName, @UserIdInt int userId,
+                boolean idle, int bucket);
 
         /**
          * Callback to inform listeners that the parole state has changed. This means apps are
@@ -144,10 +147,16 @@
         public abstract void onParoleStateChanged(boolean isParoleOn);
     }
 
-    /*  Backup/Restore API */
-    public abstract byte[] getBackupPayload(int user, String key);
+    /**  Backup/Restore API */
+    public abstract byte[] getBackupPayload(@UserIdInt int userId, String key);
 
-    public abstract void applyRestoredPayload(int user, String key, byte[] payload);
+    /**
+     * ?
+     * @param userId
+     * @param key
+     * @param payload
+     */
+    public abstract void applyRestoredPayload(@UserIdInt int userId, String key, byte[] payload);
 
     /**
      * Return usage stats.
@@ -155,6 +164,29 @@
      * @param obfuscateInstantApps whether instant app package names need to be obfuscated in the
      *     result.
      */
-    public abstract List<UsageStats> queryUsageStatsForUser(
-            int userId, int interval, long beginTime, long endTime, boolean obfuscateInstantApps);
+    public abstract List<UsageStats> queryUsageStatsForUser(@UserIdInt int userId, int interval,
+            long beginTime, long endTime, boolean obfuscateInstantApps);
+
+    /**
+     * Used to persist the last time a job was run for this app, in order to make decisions later
+     * whether a job should be deferred until later. The time passed in should be in elapsed
+     * realtime since boot.
+     * @param packageName the app that executed a job.
+     * @param userId the user associated with the job.
+     * @param elapsedRealtime the time when the job was executed, in elapsed realtime millis since
+     *                        boot.
+     */
+    public abstract void setLastJobRunTime(String packageName, @UserIdInt int userId,
+            long elapsedRealtime);
+
+    /**
+     * Returns the time in millis since a job was executed for this app, in elapsed realtime
+     * timebase. This value can be larger than the current elapsed realtime if the job was executed
+     * before the device was rebooted. The default value is {@link Long#MAX_VALUE}.
+     * @param packageName the app you're asking about.
+     * @param userId the user associated with the job.
+     * @return the time in millis since a job was last executed for the app, provided it was
+     * indicated here before by a call to {@link #setLastJobRunTime(String, int, long)}.
+     */
+    public abstract long getTimeSinceLastJobRun(String packageName, @UserIdInt int userId);
 }
diff --git a/core/java/android/bluetooth/BluetoothHeadset.java b/core/java/android/bluetooth/BluetoothHeadset.java
index 838d315..55a6b4c 100644
--- a/core/java/android/bluetooth/BluetoothHeadset.java
+++ b/core/java/android/bluetooth/BluetoothHeadset.java
@@ -16,6 +16,7 @@
 
 package android.bluetooth;
 
+import android.annotation.Nullable;
 import android.annotation.RequiresPermission;
 import android.annotation.SdkConstant;
 import android.annotation.SdkConstant.SdkConstantType;
@@ -93,6 +94,23 @@
     public static final String ACTION_AUDIO_STATE_CHANGED =
             "android.bluetooth.headset.profile.action.AUDIO_STATE_CHANGED";
 
+    /**
+     * Intent used to broadcast the selection of a connected device as active.
+     *
+     * <p>This intent will have one extra:
+     * <ul>
+     * <li> {@link BluetoothDevice#EXTRA_DEVICE} - The remote device. It can
+     * be null if no device is active. </li>
+     * </ul>
+     *
+     * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission to
+     * receive.
+     *
+     * @hide
+     */
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    public static final String ACTION_ACTIVE_DEVICE_CHANGED =
+            "android.bluetooth.headset.profile.action.ACTIVE_DEVICE_CHANGED";
 
     /**
      * Intent used to broadcast that the headset has posted a
@@ -983,6 +1001,76 @@
     }
 
     /**
+     * Select a connected device as active.
+     *
+     * The active device selection is per profile. An active device's
+     * purpose is profile-specific. For example, in HFP and HSP profiles,
+     * it is the device used for phone call audio. If a remote device is not
+     * connected, it cannot be selected as active.
+     *
+     * <p> This API returns false in scenarios like the profile on the
+     * device is not connected or Bluetooth is not turned on.
+     * When this API returns true, it is guaranteed that the
+     * {@link #ACTION_ACTIVE_DEVICE_CHANGED} intent will be broadcasted
+     * with the active device.
+     *
+     * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}
+     * permission.
+     *
+     * @param device Remote Bluetooth Device, could be null if phone call audio should not be
+     * streamed to a headset
+     * @return false on immediate error, true otherwise
+     * @hide
+     */
+    @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADMIN)
+    public boolean setActiveDevice(@Nullable BluetoothDevice device) {
+        if (DBG) {
+            Log.d(TAG, "setActiveDevice: " + device);
+        }
+        final IBluetoothHeadset service = mService;
+        if (service != null && isEnabled() && (device == null || isValidDevice(device))) {
+            try {
+                return service.setActiveDevice(device);
+            } catch (RemoteException e) {
+                Log.e(TAG, Log.getStackTraceString(new Throwable()));
+            }
+        }
+        if (service == null) {
+            Log.w(TAG, "Proxy not attached to service");
+        }
+        return false;
+    }
+
+    /**
+     * Get the connected device that is active.
+     *
+     * <p>Requires {@link android.Manifest.permission#BLUETOOTH}
+     * permission.
+     *
+     * @return the connected device that is active or null if no device
+     * is active.
+     * @hide
+     */
+    @RequiresPermission(android.Manifest.permission.BLUETOOTH)
+    public BluetoothDevice getActiveDevice() {
+        if (VDBG) {
+            Log.d(TAG, "getActiveDevice");
+        }
+        final IBluetoothHeadset service = mService;
+        if (service != null && isEnabled()) {
+            try {
+                return service.getActiveDevice();
+            } catch (RemoteException e) {
+                Log.e(TAG, Log.getStackTraceString(new Throwable()));
+            }
+        }
+        if (service == null) {
+            Log.w(TAG, "Proxy not attached to service");
+        }
+        return null;
+    }
+
+    /**
      * check if in-band ringing is supported for this platform.
      *
      * @return true if in-band ringing is supported false if in-band ringing is not supported
diff --git a/core/java/android/bluetooth/BluetoothProfile.java b/core/java/android/bluetooth/BluetoothProfile.java
index df2028a..41cf809 100644
--- a/core/java/android/bluetooth/BluetoothProfile.java
+++ b/core/java/android/bluetooth/BluetoothProfile.java
@@ -157,12 +157,19 @@
     public static final int HID_DEVICE = 19;
 
     /**
+     * Object Push Profile (OPP)
+     *
+     * @hide
+     */
+    public static final int OPP = 20;
+
+    /**
      * Max profile ID. This value should be updated whenever a new profile is added to match
      * the largest value assigned to a profile.
      *
      * @hide
      */
-    public static final int MAX_PROFILE_ID = 19;
+    public static final int MAX_PROFILE_ID = 20;
 
     /**
      * Default priority for devices that we try to auto-connect to and
diff --git a/core/java/android/content/ClipData.java b/core/java/android/content/ClipData.java
index 9323261..94e1e2d 100644
--- a/core/java/android/content/ClipData.java
+++ b/core/java/android/content/ClipData.java
@@ -34,16 +34,18 @@
 import android.text.TextUtils;
 import android.text.style.URLSpan;
 import android.util.Log;
+import android.util.proto.ProtoOutputStream;
 
 import com.android.internal.util.ArrayUtils;
 
+import libcore.io.IoUtils;
+
 import java.io.FileInputStream;
 import java.io.FileNotFoundException;
 import java.io.IOException;
 import java.io.InputStreamReader;
 import java.util.ArrayList;
 import java.util.List;
-import libcore.io.IoUtils;
 
 /**
  * Representation of a clipped data on the clipboard.
@@ -665,6 +667,25 @@
                 b.append("NULL");
             }
         }
+
+        /** @hide */
+        public void writeToProto(ProtoOutputStream proto, long fieldId) {
+            final long token = proto.start(fieldId);
+
+            if (mHtmlText != null) {
+                proto.write(ClipDataProto.Item.HTML_TEXT, mHtmlText);
+            } else if (mText != null) {
+                proto.write(ClipDataProto.Item.TEXT, mText.toString());
+            } else if (mUri != null) {
+                proto.write(ClipDataProto.Item.URI, mUri.toString());
+            } else if (mIntent != null) {
+                mIntent.writeToProto(proto, ClipDataProto.Item.INTENT, true, true, true, true);
+            } else {
+                proto.write(ClipDataProto.Item.NOTHING, true);
+            }
+
+            proto.end(token);
+        }
     }
 
     /**
@@ -1048,6 +1069,26 @@
     }
 
     /** @hide */
+    public void writeToProto(ProtoOutputStream proto, long fieldId) {
+        final long token = proto.start(fieldId);
+
+        if (mClipDescription != null) {
+            mClipDescription.writeToProto(proto, ClipDataProto.DESCRIPTION);
+        }
+        if (mIcon != null) {
+            final long iToken = proto.start(ClipDataProto.ICON);
+            proto.write(ClipDataProto.Icon.WIDTH, mIcon.getWidth());
+            proto.write(ClipDataProto.Icon.HEIGHT, mIcon.getHeight());
+            proto.end(iToken);
+        }
+        for (int i = 0; i < mItems.size(); i++) {
+            mItems.get(i).writeToProto(proto, ClipDataProto.ITEMS);
+        }
+
+        proto.end(token);
+    }
+
+    /** @hide */
     public void collectUris(List<Uri> out) {
         for (int i = 0; i < mItems.size(); ++i) {
             ClipData.Item item = getItemAt(i);
diff --git a/core/java/android/content/ClipDescription.java b/core/java/android/content/ClipDescription.java
index 8e30fd6..19295fc 100644
--- a/core/java/android/content/ClipDescription.java
+++ b/core/java/android/content/ClipDescription.java
@@ -21,6 +21,7 @@
 import android.os.PersistableBundle;
 import android.text.TextUtils;
 import android.util.TimeUtils;
+import android.util.proto.ProtoOutputStream;
 
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -337,6 +338,28 @@
         return !first;
     }
 
+    /** @hide */
+    public void writeToProto(ProtoOutputStream proto, long fieldId) {
+        final long token = proto.start(fieldId);
+
+        final int size = mMimeTypes.size();
+        for (int i = 0; i < size; i++) {
+            proto.write(ClipDescriptionProto.MIME_TYPES, mMimeTypes.get(i));
+        }
+
+        if (mLabel != null) {
+            proto.write(ClipDescriptionProto.LABEL, mLabel.toString());
+        }
+        if (mExtras != null) {
+            mExtras.writeToProto(proto, ClipDescriptionProto.EXTRAS);
+        }
+        if (mTimeStamp > 0) {
+            proto.write(ClipDescriptionProto.TIMESTAMP_MS, mTimeStamp);
+        }
+
+        proto.end(token);
+    }
+
     @Override
     public int describeContents() {
         return 0;
diff --git a/core/java/android/content/ComponentName.java b/core/java/android/content/ComponentName.java
index 0d36bdd..ead6c25 100644
--- a/core/java/android/content/ComponentName.java
+++ b/core/java/android/content/ComponentName.java
@@ -284,9 +284,11 @@
     }
 
     /** Put this here so that individual services don't have to reimplement this. @hide */
-    public void toProto(ProtoOutputStream proto) {
+    public void writeToProto(ProtoOutputStream proto, long fieldId) {
+        final long token = proto.start(fieldId);
         proto.write(ComponentNameProto.PACKAGE_NAME, mPackage);
         proto.write(ComponentNameProto.CLASS_NAME, mClass);
+        proto.end(token);
     }
 
     @Override
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index c5c6aa2..06f9171 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -2851,10 +2851,12 @@
      *          {@link #BIND_NOT_FOREGROUND}, {@link #BIND_ABOVE_CLIENT},
      *          {@link #BIND_ALLOW_OOM_MANAGEMENT}, or
      *          {@link #BIND_WAIVE_PRIORITY}.
-     * @return If you have successfully bound to the service, {@code true} is returned;
-     *         {@code false} is returned if the connection is not made so you will not
-     *         receive the service object. You should still call {@link #unbindService}
-     *         to release the connection even if this method returned {@code false}.
+     * @return {@code true} if the system is in the process of bringing up a
+     *         service that your client has permission to bind to; {@code false}
+     *         if the system couldn't find the service or if your client doesn't
+     *         have permission to bind to it. If this value is {@code true}, you
+     *         should later call {@link #unbindService} to release the
+     *         connection.
      *
      * @throws SecurityException If the caller does not have permission to access the service
      * or the service can not be found.
@@ -3530,7 +3532,6 @@
      *
      * @see #getSystemService
      * @see android.net.wifi.rtt.WifiRttManager
-     * @hide
      */
     public static final String WIFI_RTT_RANGING_SERVICE = "wifirtt";
 
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index e940769..6e99709 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -9410,6 +9410,12 @@
     }
 
     /** @hide */
+    public void writeToProto(ProtoOutputStream proto, long fieldId) {
+        // Same input parameters that toString() gives to toShortString().
+        writeToProto(proto, fieldId, true, true, true, false);
+    }
+
+    /** @hide */
     public void writeToProto(ProtoOutputStream proto, long fieldId, boolean secure, boolean comp,
             boolean extras, boolean clip) {
         long token = proto.start(fieldId);
diff --git a/core/java/android/content/ServiceConnection.java b/core/java/android/content/ServiceConnection.java
index c16dbbe..21398f6 100644
--- a/core/java/android/content/ServiceConnection.java
+++ b/core/java/android/content/ServiceConnection.java
@@ -31,6 +31,11 @@
      * the {@link android.os.IBinder} of the communication channel to the
      * Service.
      *
+     * <p class="note"><b>Note:</b> If the system has started to bind your
+     * client app to a service, it's possible that your app will never receive
+     * this callback. Your app won't receive a callback if there's an issue with
+     * the service, such as the service crashing while being created.
+     *
      * @param name The concrete component name of the service that has
      * been connected.
      *
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 5f82c2a..6cd4285 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -2327,8 +2327,6 @@
     /**
      * Feature for {@link #getSystemAvailableFeatures} and
      * {@link #hasSystemFeature}: The device supports Wi-Fi RTT (IEEE 802.11mc).
-     *
-     * @hide RTT_API
      */
     @SdkConstant(SdkConstantType.FEATURE)
     public static final String FEATURE_WIFI_RTT = "android.hardware.wifi.rtt";
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index 367c39d..77eb57f2 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -85,6 +85,7 @@
 import android.util.Slog;
 import android.util.SparseArray;
 import android.util.TypedValue;
+import android.util.apk.ApkSignatureSchemeV2Verifier;
 import android.util.apk.ApkSignatureVerifier;
 import android.view.Gravity;
 
@@ -111,7 +112,8 @@
 import java.security.KeyFactory;
 import java.security.NoSuchAlgorithmException;
 import java.security.PublicKey;
-import java.security.cert.CertificateException;
+import java.security.cert.Certificate;
+import java.security.cert.CertificateEncodingException;
 import java.security.spec.EncodedKeySpec;
 import java.security.spec.InvalidKeySpecException;
 import java.security.spec.X509EncodedKeySpec;
@@ -156,6 +158,10 @@
     private static final boolean MULTI_PACKAGE_APK_ENABLED = Build.IS_DEBUGGABLE &&
             SystemProperties.getBoolean(PROPERTY_CHILD_PACKAGES_ENABLED, false);
 
+    public static final int APK_SIGNING_UNKNOWN = 0;
+    public static final int APK_SIGNING_V1 = 1;
+    public static final int APK_SIGNING_V2 = 2;
+
     private static final float DEFAULT_PRE_O_MAX_ASPECT_RATIO = 1.86f;
 
     // TODO: switch outError users to PackageParserException
@@ -471,7 +477,8 @@
         public final int revisionCode;
         public final int installLocation;
         public final VerifierInfo[] verifiers;
-        public final SigningDetails signingDetails;
+        public final Signature[] signatures;
+        public final Certificate[][] certificates;
         public final boolean coreApp;
         public final boolean debuggable;
         public final boolean multiArch;
@@ -479,11 +486,10 @@
         public final boolean extractNativeLibs;
         public final boolean isolatedSplits;
 
-        public ApkLite(String codePath, String packageName, String splitName,
-                boolean isFeatureSplit,
+        public ApkLite(String codePath, String packageName, String splitName, boolean isFeatureSplit,
                 String configForSplit, String usesSplitName, int versionCode, int versionCodeMajor,
                 int revisionCode, int installLocation, List<VerifierInfo> verifiers,
-                SigningDetails signingDetails, boolean coreApp,
+                Signature[] signatures, Certificate[][] certificates, boolean coreApp,
                 boolean debuggable, boolean multiArch, boolean use32bitAbi,
                 boolean extractNativeLibs, boolean isolatedSplits) {
             this.codePath = codePath;
@@ -496,8 +502,9 @@
             this.versionCodeMajor = versionCodeMajor;
             this.revisionCode = revisionCode;
             this.installLocation = installLocation;
-            this.signingDetails = signingDetails;
             this.verifiers = verifiers.toArray(new VerifierInfo[verifiers.size()]);
+            this.signatures = signatures;
+            this.certificates = certificates;
             this.coreApp = coreApp;
             this.debuggable = debuggable;
             this.multiArch = multiArch;
@@ -800,10 +807,10 @@
             }
         }
         if ((flags&PackageManager.GET_SIGNATURES) != 0) {
-            if (p.mSigningDetails.hasSignatures()) {
-                int numberOfSigs = p.mSigningDetails.signatures.length;
-                pi.signatures = new Signature[numberOfSigs];
-                System.arraycopy(p.mSigningDetails.signatures, 0, pi.signatures, 0, numberOfSigs);
+           int N = (p.mSignatures != null) ? p.mSignatures.length : 0;
+           if (N > 0) {
+                pi.signatures = new Signature[N];
+                System.arraycopy(p.mSignatures, 0, pi.signatures, 0, N);
             }
         }
         return pi;
@@ -1342,7 +1349,7 @@
             pkg.setVolumeUuid(volumeUuid);
             pkg.setApplicationVolumeUuid(volumeUuid);
             pkg.setBaseCodePath(apkPath);
-            pkg.setSigningDetails(SigningDetails.UNKNOWN);
+            pkg.setSignatures(null);
 
             return pkg;
 
@@ -1462,19 +1469,57 @@
         return pkg;
     }
 
-    /** Parses the public keys from the set of signatures. */
-    public static ArraySet<PublicKey> toSigningKeys(Signature[] signatures)
-            throws CertificateException {
-        ArraySet<PublicKey> keys = new ArraySet<>(signatures.length);
-        for (int i = 0; i < signatures.length; i++) {
-            keys.add(signatures[i].getPublicKey());
+    public static int getApkSigningVersion(Package pkg) {
+        try {
+            if (ApkSignatureSchemeV2Verifier.hasSignature(pkg.baseCodePath)) {
+                return APK_SIGNING_V2;
+            }
+            return APK_SIGNING_V1;
+        } catch (IOException e) {
         }
-        return keys;
+        return APK_SIGNING_UNKNOWN;
+    }
+
+    /**
+     * Populates the correct packages fields with the given certificates.
+     * <p>
+     * This is useful when we've already processed the certificates [such as during package
+     * installation through an installer session]. We don't re-process the archive and
+     * simply populate the correct fields.
+     */
+    public static void populateCertificates(Package pkg, Certificate[][] certificates)
+            throws PackageParserException {
+        pkg.mCertificates = null;
+        pkg.mSignatures = null;
+        pkg.mSigningKeys = null;
+
+        pkg.mCertificates = certificates;
+        try {
+            pkg.mSignatures = ApkSignatureVerifier.convertToSignatures(certificates);
+        } catch (CertificateEncodingException e) {
+            // certificates weren't encoded properly; something went wrong
+            throw new PackageParserException(INSTALL_PARSE_FAILED_NO_CERTIFICATES,
+                    "Failed to collect certificates from " + pkg.baseCodePath, e);
+        }
+        pkg.mSigningKeys = new ArraySet<>(certificates.length);
+        for (int i = 0; i < certificates.length; i++) {
+            Certificate[] signerCerts = certificates[i];
+            Certificate signerCert = signerCerts[0];
+            pkg.mSigningKeys.add(signerCert.getPublicKey());
+        }
+        // add signatures to child packages
+        final int childCount = (pkg.childPackages != null) ? pkg.childPackages.size() : 0;
+        for (int i = 0; i < childCount; i++) {
+            Package childPkg = pkg.childPackages.get(i);
+            childPkg.mCertificates = pkg.mCertificates;
+            childPkg.mSignatures = pkg.mSignatures;
+            childPkg.mSigningKeys = pkg.mSigningKeys;
+        }
     }
 
     /**
      * Collect certificates from all the APKs described in the given package,
-     * populating {@link Package#mSigningDetails}. Also asserts that all APK
+     * populating {@link Package#mSignatures}. Also asserts that all APK
      * contents are signed correctly and consistently.
      */
     public static void collectCertificates(Package pkg, @ParseFlags int parseFlags)
@@ -1483,13 +1528,17 @@
         final int childCount = (pkg.childPackages != null) ? pkg.childPackages.size() : 0;
         for (int i = 0; i < childCount; i++) {
             Package childPkg = pkg.childPackages.get(i);
-            childPkg.mSigningDetails = pkg.mSigningDetails;
+            childPkg.mCertificates = pkg.mCertificates;
+            childPkg.mSignatures = pkg.mSignatures;
+            childPkg.mSigningKeys = pkg.mSigningKeys;
         }
     }
 
     private static void collectCertificatesInternal(Package pkg, @ParseFlags int parseFlags)
             throws PackageParserException {
-        pkg.mSigningDetails = SigningDetails.UNKNOWN;
+        pkg.mCertificates = null;
+        pkg.mSignatures = null;
+        pkg.mSigningKeys = null;
 
         Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "collectCertificates");
         try {
@@ -1509,12 +1558,12 @@
             throws PackageParserException {
         final String apkPath = apkFile.getAbsolutePath();
 
-        int minSignatureScheme = SigningDetails.SignatureSchemeVersion.JAR;
+        int minSignatureScheme = ApkSignatureVerifier.VERSION_JAR_SIGNATURE_SCHEME;
         if (pkg.applicationInfo.isStaticSharedLibrary()) {
             // must use v2 signing scheme
-            minSignatureScheme = SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V2;
+            minSignatureScheme = ApkSignatureVerifier.VERSION_APK_SIGNATURE_SCHEME_V2;
         }
-        SigningDetails verified;
+        ApkSignatureVerifier.Result verified;
         if ((parseFlags & PARSE_IS_SYSTEM_DIR) != 0) {
             // systemDir APKs are already trusted, save time by not verifying
             verified = ApkSignatureVerifier.plsCertsNoVerifyOnlyCerts(
@@ -1523,7 +1572,7 @@
             verified = ApkSignatureVerifier.verify(apkPath, minSignatureScheme);
         }
         if (verified.signatureSchemeVersion
-                < SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V2) {
+                < ApkSignatureVerifier.VERSION_APK_SIGNATURE_SCHEME_V2) {
             // TODO (b/68860689): move this logic to packagemanagerserivce
             if ((parseFlags & PARSE_IS_EPHEMERAL) != 0) {
                 throw new PackageParserException(INSTALL_PARSE_FAILED_NO_CERTIFICATES,
@@ -1534,10 +1583,17 @@
         // Verify that entries are signed consistently with the first pkg
         // we encountered. Note that for splits, certificates may have
         // already been populated during an earlier parse of a base APK.
-        if (pkg.mSigningDetails == SigningDetails.UNKNOWN) {
-            pkg.mSigningDetails = verified;
+        if (pkg.mCertificates == null) {
+            pkg.mCertificates = verified.certs;
+            pkg.mSignatures = verified.sigs;
+            pkg.mSigningKeys = new ArraySet<>(verified.certs.length);
+            for (int i = 0; i < verified.certs.length; i++) {
+                Certificate[] signerCerts = verified.certs[i];
+                Certificate signerCert = signerCerts[0];
+                pkg.mSigningKeys.add(signerCert.getPublicKey());
+            }
         } else {
-            if (!Signature.areExactMatch(pkg.mSigningDetails.signatures, verified.signatures)) {
+            if (!Signature.areExactMatch(pkg.mSignatures, verified.sigs)) {
                 throw new PackageParserException(
                         INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES,
                         apkPath + " has mismatched certificates");
@@ -1599,7 +1655,8 @@
 
             parser = assets.openXmlResourceParser(cookie, ANDROID_MANIFEST_FILENAME);
 
-            final SigningDetails signingDetails;
+            final Signature[] signatures;
+            final Certificate[][] certificates;
             if ((flags & PARSE_COLLECT_CERTIFICATES) != 0) {
                 // TODO: factor signature related items out of Package object
                 final Package tempPkg = new Package((String) null);
@@ -1609,13 +1666,15 @@
                 } finally {
                     Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
                 }
-                signingDetails = tempPkg.mSigningDetails;
+                signatures = tempPkg.mSignatures;
+                certificates = tempPkg.mCertificates;
             } else {
-                signingDetails = SigningDetails.UNKNOWN;
+                signatures = null;
+                certificates = null;
             }
 
             final AttributeSet attrs = parser;
-            return parseApkLite(apkPath, parser, attrs, signingDetails);
+            return parseApkLite(apkPath, parser, attrs, signatures, certificates);
 
         } catch (XmlPullParserException | IOException | RuntimeException e) {
             Slog.w(TAG, "Failed to parse " + apkPath, e);
@@ -1702,7 +1761,7 @@
     }
 
     private static ApkLite parseApkLite(String codePath, XmlPullParser parser, AttributeSet attrs,
-            SigningDetails signingDetails)
+            Signature[] signatures, Certificate[][] certificates)
             throws IOException, XmlPullParserException, PackageParserException {
         final Pair<String, String> packageSplit = parsePackageSplitNames(parser, attrs);
 
@@ -1795,7 +1854,7 @@
 
         return new ApkLite(codePath, packageSplit.first, packageSplit.second, isFeatureSplit,
                 configForSplit, usesSplitName, versionCode, versionCodeMajor, revisionCode,
-                installLocation, verifiers, signingDetails, coreApp, debuggable,
+                installLocation, verifiers, signatures, certificates, coreApp, debuggable,
                 multiArch, use32bitAbi, extractNativeLibs, isolatedSplits);
     }
 
@@ -5675,117 +5734,6 @@
         return true;
     }
 
-    /** A container for signing-related data of an application package. */
-    public static final class SigningDetails implements Parcelable {
-
-        @IntDef({SigningDetails.SignatureSchemeVersion.UNKNOWN,
-                SigningDetails.SignatureSchemeVersion.JAR,
-                SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V2,
-                SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3})
-        public @interface SignatureSchemeVersion {
-            int UNKNOWN = 0;
-            int JAR = 1;
-            int SIGNING_BLOCK_V2 = 2;
-            int SIGNING_BLOCK_V3 = 3;
-        }
-
-        @Nullable
-        public final Signature[] signatures;
-        @SignatureSchemeVersion
-        public final int signatureSchemeVersion;
-        @Nullable
-        public final ArraySet<PublicKey> publicKeys;
-
-        /** A representation of unknown signing details. Use instead of null. */
-        public static final SigningDetails UNKNOWN =
-                new SigningDetails(null, SignatureSchemeVersion.UNKNOWN, null);
-
-        @VisibleForTesting
-        public SigningDetails(Signature[] signatures,
-                @SignatureSchemeVersion int signatureSchemeVersion,
-                ArraySet<PublicKey> keys) {
-            this.signatures = signatures;
-            this.signatureSchemeVersion = signatureSchemeVersion;
-            this.publicKeys = keys;
-        }
-
-        public SigningDetails(Signature[] signatures,
-                @SignatureSchemeVersion int signatureSchemeVersion)
-                throws CertificateException {
-            this(signatures, signatureSchemeVersion, toSigningKeys(signatures));
-        }
-
-        /** Returns true if the signing details have one or more signatures. */
-        public boolean hasSignatures() {
-            return signatures != null && signatures.length > 0;
-        }
-
-        /** Returns true if the signatures in this and other match exactly. */
-        public boolean signaturesMatchExactly(SigningDetails other) {
-            return Signature.areExactMatch(this.signatures, other.signatures);
-        }
-
-        @Override
-        public int describeContents() {
-            return 0;
-        }
-
-        @Override
-        public void writeToParcel(Parcel dest, int flags) {
-            boolean isUnknown = UNKNOWN == this;
-            dest.writeBoolean(isUnknown);
-            if (isUnknown) {
-                return;
-            }
-            dest.writeTypedArray(this.signatures, flags);
-            dest.writeInt(this.signatureSchemeVersion);
-            dest.writeArraySet(this.publicKeys);
-        }
-
-        protected SigningDetails(Parcel in) {
-            final ClassLoader boot = Object.class.getClassLoader();
-            this.signatures = in.createTypedArray(Signature.CREATOR);
-            this.signatureSchemeVersion = in.readInt();
-            this.publicKeys = (ArraySet<PublicKey>) in.readArraySet(boot);
-        }
-
-        public static final Creator<SigningDetails> CREATOR = new Creator<SigningDetails>() {
-            @Override
-            public SigningDetails createFromParcel(Parcel source) {
-                if (source.readBoolean()) {
-                    return UNKNOWN;
-                }
-                return new SigningDetails(source);
-            }
-
-            @Override
-            public SigningDetails[] newArray(int size) {
-                return new SigningDetails[size];
-            }
-        };
-
-        @Override
-        public boolean equals(Object o) {
-            if (this == o) return true;
-            if (!(o instanceof SigningDetails)) return false;
-
-            SigningDetails that = (SigningDetails) o;
-
-            if (signatureSchemeVersion != that.signatureSchemeVersion) return false;
-            if (!Signature.areExactMatch(signatures, that.signatures)) return false;
-            return publicKeys != null ? publicKeys.equals(that.publicKeys)
-                    : that.publicKeys == null;
-        }
-
-        @Override
-        public int hashCode() {
-            int result = +Arrays.hashCode(signatures);
-            result = 31 * result + signatureSchemeVersion;
-            result = 31 * result + (publicKeys != null ? publicKeys.hashCode() : 0);
-            return result;
-        }
-    }
-
     /**
      * Representation of a full package parsed from APK files on disk. A package
      * consists of a single base APK, and zero or more split APKs.
@@ -5892,7 +5840,8 @@
         public int mSharedUserLabel;
 
         // Signatures that were read from the package.
-        @NonNull public SigningDetails mSigningDetails = SigningDetails.UNKNOWN;
+        public Signature[] mSignatures;
+        public Certificate[][] mCertificates;
 
         // For use by package manager service for quick lookup of
         // preferred up order.
@@ -5944,6 +5893,7 @@
         /**
          * Data used to feed the KeySetManagerService
          */
+        public ArraySet<PublicKey> mSigningKeys;
         public ArraySet<String> mUpgradeKeySets;
         public ArrayMap<String, ArraySet<PublicKey>> mKeySetMapping;
 
@@ -6087,13 +6037,12 @@
             }
         }
 
-        /** Sets signing details on the package and any of its children. */
-        public void setSigningDetails(@NonNull SigningDetails signingDetails) {
-            mSigningDetails = signingDetails;
+        public void setSignatures(Signature[] signatures) {
+            this.mSignatures = signatures;
             if (childPackages != null) {
                 final int packageCount = childPackages.size();
                 for (int i = 0; i < packageCount; i++) {
-                    childPackages.get(i).mSigningDetails = signingDetails;
+                    childPackages.get(i).mSignatures = signatures;
                 }
             }
         }
@@ -6399,7 +6348,8 @@
             }
             mSharedUserLabel = dest.readInt();
 
-            mSigningDetails = dest.readParcelable(boot);
+            mSignatures = (Signature[]) dest.readParcelableArray(boot, Signature.class);
+            mCertificates = (Certificate[][]) dest.readSerializable();
 
             mPreferredOrder = dest.readInt();
 
@@ -6439,6 +6389,7 @@
             mTrustedOverlay = (dest.readInt() == 1);
             mCompileSdkVersion = dest.readInt();
             mCompileSdkVersionCodename = dest.readString();
+            mSigningKeys = (ArraySet<PublicKey>) dest.readArraySet(boot);
             mUpgradeKeySets = (ArraySet<String>) dest.readArraySet(boot);
 
             mKeySetMapping = readKeySetMapping(dest);
@@ -6538,7 +6489,8 @@
             dest.writeString(mSharedUserId);
             dest.writeInt(mSharedUserLabel);
 
-            dest.writeParcelable(mSigningDetails, flags);
+            dest.writeParcelableArray(mSignatures, flags);
+            dest.writeSerializable(mCertificates);
 
             dest.writeInt(mPreferredOrder);
 
@@ -6563,6 +6515,7 @@
             dest.writeInt(mTrustedOverlay ? 1 : 0);
             dest.writeInt(mCompileSdkVersion);
             dest.writeString(mCompileSdkVersionCodename);
+            dest.writeArraySet(mSigningKeys);
             dest.writeArraySet(mUpgradeKeySets);
             writeKeySetMapping(dest, mKeySetMapping);
             dest.writeString(cpuAbiOverride);
diff --git a/core/java/android/hardware/camera2/CameraCharacteristics.java b/core/java/android/hardware/camera2/CameraCharacteristics.java
index 5a63896..1201ef4 100644
--- a/core/java/android/hardware/camera2/CameraCharacteristics.java
+++ b/core/java/android/hardware/camera2/CameraCharacteristics.java
@@ -1149,36 +1149,33 @@
     /**
      * <p>Position of the camera optical center.</p>
      * <p>The position of the camera device's lens optical center,
-     * as a three-dimensional vector <code>(x,y,z)</code>, relative to the
-     * optical center of the largest camera device facing in the
-     * same direction as this camera, in the {@link android.hardware.SensorEvent Android sensor coordinate
-     * axes}. Note that only the axis definitions are shared with
-     * the sensor coordinate system, but not the origin.</p>
-     * <p>If this device is the largest or only camera device with a
-     * given facing, then this position will be <code>(0, 0, 0)</code>; a
-     * camera device with a lens optical center located 3 cm from
-     * the main sensor along the +X axis (to the right from the
-     * user's perspective) will report <code>(0.03, 0, 0)</code>.</p>
-     * <p>To transform a pixel coordinates between two cameras
-     * facing the same direction, first the source camera
-     * {@link CameraCharacteristics#LENS_RADIAL_DISTORTION android.lens.radialDistortion} must be corrected for.  Then
-     * the source camera {@link CameraCharacteristics#LENS_INTRINSIC_CALIBRATION android.lens.intrinsicCalibration} needs
-     * to be applied, followed by the {@link CameraCharacteristics#LENS_POSE_ROTATION android.lens.poseRotation}
-     * of the source camera, the translation of the source camera
-     * relative to the destination camera, the
-     * {@link CameraCharacteristics#LENS_POSE_ROTATION android.lens.poseRotation} of the destination camera, and
-     * finally the inverse of {@link CameraCharacteristics#LENS_INTRINSIC_CALIBRATION android.lens.intrinsicCalibration}
-     * of the destination camera. This obtains a
-     * radial-distortion-free coordinate in the destination
-     * camera pixel coordinates.</p>
-     * <p>To compare this against a real image from the destination
-     * camera, the destination camera image then needs to be
-     * corrected for radial distortion before comparison or
-     * sampling.</p>
+     * as a three-dimensional vector <code>(x,y,z)</code>.</p>
+     * <p>Prior to Android P, or when {@link CameraCharacteristics#LENS_POSE_REFERENCE android.lens.poseReference} is PRIMARY_CAMERA, this position
+     * is relative to the optical center of the largest camera device facing in the same
+     * direction as this camera, in the {@link android.hardware.SensorEvent Android sensor
+     * coordinate axes}. Note that only the axis definitions are shared with the sensor
+     * coordinate system, but not the origin.</p>
+     * <p>If this device is the largest or only camera device with a given facing, then this
+     * position will be <code>(0, 0, 0)</code>; a camera device with a lens optical center located 3 cm
+     * from the main sensor along the +X axis (to the right from the user's perspective) will
+     * report <code>(0.03, 0, 0)</code>.</p>
+     * <p>To transform a pixel coordinates between two cameras facing the same direction, first
+     * the source camera {@link CameraCharacteristics#LENS_RADIAL_DISTORTION android.lens.radialDistortion} must be corrected for.  Then the source
+     * camera {@link CameraCharacteristics#LENS_INTRINSIC_CALIBRATION android.lens.intrinsicCalibration} needs to be applied, followed by the
+     * {@link CameraCharacteristics#LENS_POSE_ROTATION android.lens.poseRotation} of the source camera, the translation of the source camera
+     * relative to the destination camera, the {@link CameraCharacteristics#LENS_POSE_ROTATION android.lens.poseRotation} of the destination
+     * camera, and finally the inverse of {@link CameraCharacteristics#LENS_INTRINSIC_CALIBRATION android.lens.intrinsicCalibration} of the destination
+     * camera. This obtains a radial-distortion-free coordinate in the destination camera pixel
+     * coordinates.</p>
+     * <p>To compare this against a real image from the destination camera, the destination camera
+     * image then needs to be corrected for radial distortion before comparison or sampling.</p>
+     * <p>When {@link CameraCharacteristics#LENS_POSE_REFERENCE android.lens.poseReference} is GYROSCOPE, then this position is relative to
+     * the center of the primary gyroscope on the device.</p>
      * <p><b>Units</b>: Meters</p>
      * <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
      *
      * @see CameraCharacteristics#LENS_INTRINSIC_CALIBRATION
+     * @see CameraCharacteristics#LENS_POSE_REFERENCE
      * @see CameraCharacteristics#LENS_POSE_ROTATION
      * @see CameraCharacteristics#LENS_RADIAL_DISTORTION
      */
@@ -1289,6 +1286,28 @@
             new Key<float[]>("android.lens.radialDistortion", float[].class);
 
     /**
+     * <p>The origin for {@link CameraCharacteristics#LENS_POSE_TRANSLATION android.lens.poseTranslation}.</p>
+     * <p>Different calibration methods and use cases can produce better or worse results
+     * depending on the selected coordinate origin.</p>
+     * <p>For devices designed to support the MOTION_TRACKING capability, the GYROSCOPE origin
+     * makes device calibration and later usage by applications combining camera and gyroscope
+     * information together simpler.</p>
+     * <p><b>Possible values:</b>
+     * <ul>
+     *   <li>{@link #LENS_POSE_REFERENCE_PRIMARY_CAMERA PRIMARY_CAMERA}</li>
+     *   <li>{@link #LENS_POSE_REFERENCE_GYROSCOPE GYROSCOPE}</li>
+     * </ul></p>
+     * <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
+     *
+     * @see CameraCharacteristics#LENS_POSE_TRANSLATION
+     * @see #LENS_POSE_REFERENCE_PRIMARY_CAMERA
+     * @see #LENS_POSE_REFERENCE_GYROSCOPE
+     */
+    @PublicKey
+    public static final Key<Integer> LENS_POSE_REFERENCE =
+            new Key<Integer>("android.lens.poseReference", int.class);
+
+    /**
      * <p>List of noise reduction modes for {@link CaptureRequest#NOISE_REDUCTION_MODE android.noiseReduction.mode} that are supported
      * by this camera device.</p>
      * <p>Full-capability camera devices will always support OFF and FAST.</p>
@@ -1559,6 +1578,7 @@
      *   <li>{@link #REQUEST_AVAILABLE_CAPABILITIES_YUV_REPROCESSING YUV_REPROCESSING}</li>
      *   <li>{@link #REQUEST_AVAILABLE_CAPABILITIES_DEPTH_OUTPUT DEPTH_OUTPUT}</li>
      *   <li>{@link #REQUEST_AVAILABLE_CAPABILITIES_CONSTRAINED_HIGH_SPEED_VIDEO CONSTRAINED_HIGH_SPEED_VIDEO}</li>
+     *   <li>{@link #REQUEST_AVAILABLE_CAPABILITIES_MOTION_TRACKING MOTION_TRACKING}</li>
      * </ul></p>
      * <p>This key is available on all devices.</p>
      *
@@ -1573,6 +1593,7 @@
      * @see #REQUEST_AVAILABLE_CAPABILITIES_YUV_REPROCESSING
      * @see #REQUEST_AVAILABLE_CAPABILITIES_DEPTH_OUTPUT
      * @see #REQUEST_AVAILABLE_CAPABILITIES_CONSTRAINED_HIGH_SPEED_VIDEO
+     * @see #REQUEST_AVAILABLE_CAPABILITIES_MOTION_TRACKING
      */
     @PublicKey
     public static final Key<int[]> REQUEST_AVAILABLE_CAPABILITIES =
@@ -1643,7 +1664,7 @@
      * time-consuming hardware re-configuration or internal camera pipeline
      * change. For performance reasons we advise clients to pass their initial
      * values as part of
-     * {@link SessionConfiguration#setSessionParameters }.i
+     * {@link SessionConfiguration#setSessionParameters }.
      * Once the camera capture session is enabled it is also recommended to avoid
      * changing them from their initial values set in
      * {@link SessionConfiguration#setSessionParameters }.
diff --git a/core/java/android/hardware/camera2/CameraDevice.java b/core/java/android/hardware/camera2/CameraDevice.java
index 87e503d..ce1fba7 100644
--- a/core/java/android/hardware/camera2/CameraDevice.java
+++ b/core/java/android/hardware/camera2/CameraDevice.java
@@ -144,6 +144,37 @@
      */
     public static final int TEMPLATE_MANUAL = 6;
 
+    /**
+     * A template for selecting camera parameters that match TEMPLATE_PREVIEW as closely as
+     * possible while improving the camera output for motion tracking use cases.
+     *
+     * <p>This template is best used by applications that are frequently switching between motion
+     * tracking use cases and regular still capture use cases, to minimize the IQ changes
+     * when swapping use cases.</p>
+     *
+     * <p>This template is guaranteed to be supported on camera devices that support the
+     * {@link CameraMetadata#REQUEST_AVAILABLE_CAPABILITIES_MOTION_TRACKING MOTION_TRACKING}
+     * capability.</p>
+     *
+     * @see #createCaptureRequest
+     */
+    public static final int TEMPLATE_MOTION_TRACKING_PREVIEW = 7;
+
+    /**
+     * A template for selecting camera parameters that maximize the quality of camera output for
+     * motion tracking use cases.
+     *
+     * <p>This template is best used by applications dedicated to motion tracking applications,
+     * which aren't concerned about fast switches between motion tracking and other use cases.</p>
+     *
+     * <p>This template is guaranteed to be supported on camera devices that support the
+     * {@link CameraMetadata#REQUEST_AVAILABLE_CAPABILITIES_MOTION_TRACKING MOTION_TRACKING}
+     * capability.</p>
+     *
+     * @see #createCaptureRequest
+     */
+    public static final int TEMPLATE_MOTION_TRACKING_BEST = 8;
+
      /** @hide */
      @Retention(RetentionPolicy.SOURCE)
      @IntDef(prefix = {"TEMPLATE_"}, value =
@@ -386,6 +417,24 @@
      * </table><br>
      * </p>
      *
+     * <p>MOTION_TRACKING-capability ({@link CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES}
+     * includes
+     * {@link CameraMetadata#REQUEST_AVAILABLE_CAPABILITIES_MOTION_TRACKING MOTION_TRACKING})
+     * devices support at least the below stream combinations in addition to those for
+     * {@link CameraMetadata#INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED LIMITED} devices. The
+     * {@code FULL FOV 640} entry means that the device will support a resolution that's 640 pixels
+     * wide, with the height set so that the resolution aspect ratio matches the MAXIMUM output
+     * aspect ratio.  So for a device with a 4:3 image sensor, this will be 640x480, and for a
+     * device with a 16:9 sensor, this will be 640x360, and so on.
+     *
+     * <table>
+     * <tr><th colspan="7">MOTION_TRACKING-capability additional guaranteed configurations</th></tr>
+     * <tr><th colspan="2" id="rb">Target 1</th><th colspan="2" id="rb">Target 2</th><th colspan="2" id="rb">Target 3</th><th rowspan="2">Sample use case(s)</th> </tr>
+     * <tr><th>Type</th><th id="rb">Max size</th><th>Type</th><th id="rb">Max size</th><th>Type</th><th id="rb">Max size</th></tr>
+     * <tr> <td>{@code PRIV}</td><td id="rb">{@code PREVIEW}</td> <td>{@code YUV }</td><td id="rb">{@code FULL FOV 640}</td> <td>{@code YUV }</td><td id="rb">{@code MAXIMUM}</td> <td>Live preview with a tracking YUV output and a maximum-resolution YUV for still captures.</td> </tr>
+     * </table><br>
+     * </p>
+     *
      * <p>BURST-capability ({@link CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES} includes
      * {@link CameraMetadata#REQUEST_AVAILABLE_CAPABILITIES_BURST_CAPTURE BURST_CAPTURE}) devices
      * support at least the below stream combinations in addition to those for
diff --git a/core/java/android/hardware/camera2/CameraMetadata.java b/core/java/android/hardware/camera2/CameraMetadata.java
index cb11d0f..1c7f289 100644
--- a/core/java/android/hardware/camera2/CameraMetadata.java
+++ b/core/java/android/hardware/camera2/CameraMetadata.java
@@ -336,6 +336,30 @@
     public static final int LENS_FACING_EXTERNAL = 2;
 
     //
+    // Enumeration values for CameraCharacteristics#LENS_POSE_REFERENCE
+    //
+
+    /**
+     * <p>The value of {@link CameraCharacteristics#LENS_POSE_TRANSLATION android.lens.poseTranslation} is relative to the optical center of
+     * the largest camera device facing the same direction as this camera.</p>
+     * <p>This default value for API levels before Android P.</p>
+     *
+     * @see CameraCharacteristics#LENS_POSE_TRANSLATION
+     * @see CameraCharacteristics#LENS_POSE_REFERENCE
+     */
+    public static final int LENS_POSE_REFERENCE_PRIMARY_CAMERA = 0;
+
+    /**
+     * <p>The value of {@link CameraCharacteristics#LENS_POSE_TRANSLATION android.lens.poseTranslation} is relative to the position of the
+     * primary gyroscope of this Android device.</p>
+     * <p>This is the value reported by all devices that support the MOTION_TRACKING capability.</p>
+     *
+     * @see CameraCharacteristics#LENS_POSE_TRANSLATION
+     * @see CameraCharacteristics#LENS_POSE_REFERENCE
+     */
+    public static final int LENS_POSE_REFERENCE_GYROSCOPE = 1;
+
+    //
     // Enumeration values for CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES
     //
 
@@ -665,6 +689,7 @@
      * </ul>
      * </li>
      * <li>The {@link CameraCharacteristics#DEPTH_DEPTH_IS_EXCLUSIVE android.depth.depthIsExclusive} entry is listed by this device.</li>
+     * <li>As of Android P, the {@link CameraCharacteristics#LENS_POSE_REFERENCE android.lens.poseReference} entry is listed by this device.</li>
      * <li>A LIMITED camera with only the DEPTH_OUTPUT capability does not have to support
      *   normal YUV_420_888, JPEG, and PRIV-format outputs. It only has to support the DEPTH16
      *   format.</li>
@@ -680,6 +705,7 @@
      * @see CameraCharacteristics#DEPTH_DEPTH_IS_EXCLUSIVE
      * @see CameraCharacteristics#LENS_FACING
      * @see CameraCharacteristics#LENS_INTRINSIC_CALIBRATION
+     * @see CameraCharacteristics#LENS_POSE_REFERENCE
      * @see CameraCharacteristics#LENS_POSE_ROTATION
      * @see CameraCharacteristics#LENS_POSE_TRANSLATION
      * @see CameraCharacteristics#LENS_RADIAL_DISTORTION
@@ -774,6 +800,51 @@
      */
     public static final int REQUEST_AVAILABLE_CAPABILITIES_CONSTRAINED_HIGH_SPEED_VIDEO = 9;
 
+    /**
+     * <p>The device supports controls and metadata required for accurate motion tracking for
+     * use cases such as augmented reality, electronic image stabilization, and so on.</p>
+     * <p>This means this camera device has accurate optical calibration and timestamps relative
+     * to the inertial sensors.</p>
+     * <p>This capability requires the camera device to support the following:</p>
+     * <ul>
+     * <li>Capture request templates {@link android.hardware.camera2.CameraDevice#TEMPLATE_MOTION_TRACKING_PREVIEW } and {@link android.hardware.camera2.CameraDevice#TEMPLATE_MOTION_TRACKING_BEST } are defined.</li>
+     * <li>The stream configurations listed in {@link android.hardware.camera2.CameraDevice#createCaptureSession } for MOTION_TRACKING are
+     *   supported, either at 30 or 60fps maximum frame rate.</li>
+     * <li>The following camera characteristics and capture result metadata are provided:<ul>
+     * <li>{@link CameraCharacteristics#LENS_INTRINSIC_CALIBRATION android.lens.intrinsicCalibration}</li>
+     * <li>{@link CameraCharacteristics#LENS_RADIAL_DISTORTION android.lens.radialDistortion}</li>
+     * <li>{@link CameraCharacteristics#LENS_POSE_ROTATION android.lens.poseRotation}</li>
+     * <li>{@link CameraCharacteristics#LENS_POSE_TRANSLATION android.lens.poseTranslation}</li>
+     * <li>{@link CameraCharacteristics#LENS_POSE_REFERENCE android.lens.poseReference} with value GYROSCOPE</li>
+     * </ul>
+     * </li>
+     * <li>The {@link CameraCharacteristics#SENSOR_INFO_TIMESTAMP_SOURCE android.sensor.info.timestampSource} field has value <code>REALTIME</code>. When compared to
+     *   timestamps from the device's gyroscopes, the clock difference for events occuring at
+     *   the same actual time instant will be less than 1 ms.</li>
+     * <li>The value of the {@link CaptureResult#SENSOR_ROLLING_SHUTTER_SKEW android.sensor.rollingShutterSkew} field is accurate to within 1 ms.</li>
+     * <li>The value of {@link CaptureRequest#SENSOR_EXPOSURE_TIME android.sensor.exposureTime} is guaranteed to be available in the
+     *   capture result.</li>
+     * <li>The {@link CaptureRequest#CONTROL_CAPTURE_INTENT android.control.captureIntent} control supports MOTION_TRACKING to limit maximum
+     *   exposure to 20 milliseconds.</li>
+     * <li>The stream configurations required for MOTION_TRACKING (listed at {@link android.hardware.camera2.CameraDevice#createCaptureSession }) can operate at least at
+     *   30fps; optionally, they can operate at 60fps, and '[60, 60]' is listed in
+     *   {@link CameraCharacteristics#CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES android.control.aeAvailableTargetFpsRanges}.</li>
+     * </ul>
+     *
+     * @see CameraCharacteristics#CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES
+     * @see CaptureRequest#CONTROL_CAPTURE_INTENT
+     * @see CameraCharacteristics#LENS_INTRINSIC_CALIBRATION
+     * @see CameraCharacteristics#LENS_POSE_REFERENCE
+     * @see CameraCharacteristics#LENS_POSE_ROTATION
+     * @see CameraCharacteristics#LENS_POSE_TRANSLATION
+     * @see CameraCharacteristics#LENS_RADIAL_DISTORTION
+     * @see CaptureRequest#SENSOR_EXPOSURE_TIME
+     * @see CameraCharacteristics#SENSOR_INFO_TIMESTAMP_SOURCE
+     * @see CaptureResult#SENSOR_ROLLING_SHUTTER_SKEW
+     * @see CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES
+     */
+    public static final int REQUEST_AVAILABLE_CAPABILITIES_MOTION_TRACKING = 10;
+
     //
     // Enumeration values for CameraCharacteristics#SCALER_CROPPING_TYPE
     //
@@ -1661,6 +1732,16 @@
      */
     public static final int CONTROL_CAPTURE_INTENT_MANUAL = 6;
 
+    /**
+     * <p>This request is for a motion tracking use case, where
+     * the application will use camera and inertial sensor data to
+     * locate and track objects in the world.</p>
+     * <p>The camera device auto-exposure routine will limit the exposure time
+     * of the camera to no more than 20 milliseconds, to minimize motion blur.</p>
+     * @see CaptureRequest#CONTROL_CAPTURE_INTENT
+     */
+    public static final int CONTROL_CAPTURE_INTENT_MOTION_TRACKING = 7;
+
     //
     // Enumeration values for CaptureRequest#CONTROL_EFFECT_MODE
     //
diff --git a/core/java/android/hardware/camera2/CaptureRequest.java b/core/java/android/hardware/camera2/CaptureRequest.java
index 77da2a5..cf27c70 100644
--- a/core/java/android/hardware/camera2/CaptureRequest.java
+++ b/core/java/android/hardware/camera2/CaptureRequest.java
@@ -1487,10 +1487,13 @@
      * strategy.</p>
      * <p>This control (except for MANUAL) is only effective if
      * <code>{@link CaptureRequest#CONTROL_MODE android.control.mode} != OFF</code> and any 3A routine is active.</p>
-     * <p>ZERO_SHUTTER_LAG will be supported if {@link CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES android.request.availableCapabilities}
-     * contains PRIVATE_REPROCESSING or YUV_REPROCESSING. MANUAL will be supported if
-     * {@link CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES android.request.availableCapabilities} contains MANUAL_SENSOR. Other intent values are
-     * always supported.</p>
+     * <p>All intents are supported by all devices, except that:
+     *   * ZERO_SHUTTER_LAG will be supported if {@link CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES android.request.availableCapabilities} contains
+     * PRIVATE_REPROCESSING or YUV_REPROCESSING.
+     *   * MANUAL will be supported if {@link CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES android.request.availableCapabilities} contains
+     * MANUAL_SENSOR.
+     *   * MOTION_TRACKING will be supported if {@link CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES android.request.availableCapabilities} contains
+     * MOTION_TRACKING.</p>
      * <p><b>Possible values:</b>
      * <ul>
      *   <li>{@link #CONTROL_CAPTURE_INTENT_CUSTOM CUSTOM}</li>
@@ -1500,6 +1503,7 @@
      *   <li>{@link #CONTROL_CAPTURE_INTENT_VIDEO_SNAPSHOT VIDEO_SNAPSHOT}</li>
      *   <li>{@link #CONTROL_CAPTURE_INTENT_ZERO_SHUTTER_LAG ZERO_SHUTTER_LAG}</li>
      *   <li>{@link #CONTROL_CAPTURE_INTENT_MANUAL MANUAL}</li>
+     *   <li>{@link #CONTROL_CAPTURE_INTENT_MOTION_TRACKING MOTION_TRACKING}</li>
      * </ul></p>
      * <p>This key is available on all devices.</p>
      *
@@ -1512,6 +1516,7 @@
      * @see #CONTROL_CAPTURE_INTENT_VIDEO_SNAPSHOT
      * @see #CONTROL_CAPTURE_INTENT_ZERO_SHUTTER_LAG
      * @see #CONTROL_CAPTURE_INTENT_MANUAL
+     * @see #CONTROL_CAPTURE_INTENT_MOTION_TRACKING
      */
     @PublicKey
     public static final Key<Integer> CONTROL_CAPTURE_INTENT =
diff --git a/core/java/android/hardware/camera2/CaptureResult.java b/core/java/android/hardware/camera2/CaptureResult.java
index 6d7b06f..b6b0c90 100644
--- a/core/java/android/hardware/camera2/CaptureResult.java
+++ b/core/java/android/hardware/camera2/CaptureResult.java
@@ -1754,10 +1754,13 @@
      * strategy.</p>
      * <p>This control (except for MANUAL) is only effective if
      * <code>{@link CaptureRequest#CONTROL_MODE android.control.mode} != OFF</code> and any 3A routine is active.</p>
-     * <p>ZERO_SHUTTER_LAG will be supported if {@link CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES android.request.availableCapabilities}
-     * contains PRIVATE_REPROCESSING or YUV_REPROCESSING. MANUAL will be supported if
-     * {@link CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES android.request.availableCapabilities} contains MANUAL_SENSOR. Other intent values are
-     * always supported.</p>
+     * <p>All intents are supported by all devices, except that:
+     *   * ZERO_SHUTTER_LAG will be supported if {@link CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES android.request.availableCapabilities} contains
+     * PRIVATE_REPROCESSING or YUV_REPROCESSING.
+     *   * MANUAL will be supported if {@link CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES android.request.availableCapabilities} contains
+     * MANUAL_SENSOR.
+     *   * MOTION_TRACKING will be supported if {@link CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES android.request.availableCapabilities} contains
+     * MOTION_TRACKING.</p>
      * <p><b>Possible values:</b>
      * <ul>
      *   <li>{@link #CONTROL_CAPTURE_INTENT_CUSTOM CUSTOM}</li>
@@ -1767,6 +1770,7 @@
      *   <li>{@link #CONTROL_CAPTURE_INTENT_VIDEO_SNAPSHOT VIDEO_SNAPSHOT}</li>
      *   <li>{@link #CONTROL_CAPTURE_INTENT_ZERO_SHUTTER_LAG ZERO_SHUTTER_LAG}</li>
      *   <li>{@link #CONTROL_CAPTURE_INTENT_MANUAL MANUAL}</li>
+     *   <li>{@link #CONTROL_CAPTURE_INTENT_MOTION_TRACKING MOTION_TRACKING}</li>
      * </ul></p>
      * <p>This key is available on all devices.</p>
      *
@@ -1779,6 +1783,7 @@
      * @see #CONTROL_CAPTURE_INTENT_VIDEO_SNAPSHOT
      * @see #CONTROL_CAPTURE_INTENT_ZERO_SHUTTER_LAG
      * @see #CONTROL_CAPTURE_INTENT_MANUAL
+     * @see #CONTROL_CAPTURE_INTENT_MOTION_TRACKING
      */
     @PublicKey
     public static final Key<Integer> CONTROL_CAPTURE_INTENT =
@@ -2761,36 +2766,33 @@
     /**
      * <p>Position of the camera optical center.</p>
      * <p>The position of the camera device's lens optical center,
-     * as a three-dimensional vector <code>(x,y,z)</code>, relative to the
-     * optical center of the largest camera device facing in the
-     * same direction as this camera, in the {@link android.hardware.SensorEvent Android sensor coordinate
-     * axes}. Note that only the axis definitions are shared with
-     * the sensor coordinate system, but not the origin.</p>
-     * <p>If this device is the largest or only camera device with a
-     * given facing, then this position will be <code>(0, 0, 0)</code>; a
-     * camera device with a lens optical center located 3 cm from
-     * the main sensor along the +X axis (to the right from the
-     * user's perspective) will report <code>(0.03, 0, 0)</code>.</p>
-     * <p>To transform a pixel coordinates between two cameras
-     * facing the same direction, first the source camera
-     * {@link CameraCharacteristics#LENS_RADIAL_DISTORTION android.lens.radialDistortion} must be corrected for.  Then
-     * the source camera {@link CameraCharacteristics#LENS_INTRINSIC_CALIBRATION android.lens.intrinsicCalibration} needs
-     * to be applied, followed by the {@link CameraCharacteristics#LENS_POSE_ROTATION android.lens.poseRotation}
-     * of the source camera, the translation of the source camera
-     * relative to the destination camera, the
-     * {@link CameraCharacteristics#LENS_POSE_ROTATION android.lens.poseRotation} of the destination camera, and
-     * finally the inverse of {@link CameraCharacteristics#LENS_INTRINSIC_CALIBRATION android.lens.intrinsicCalibration}
-     * of the destination camera. This obtains a
-     * radial-distortion-free coordinate in the destination
-     * camera pixel coordinates.</p>
-     * <p>To compare this against a real image from the destination
-     * camera, the destination camera image then needs to be
-     * corrected for radial distortion before comparison or
-     * sampling.</p>
+     * as a three-dimensional vector <code>(x,y,z)</code>.</p>
+     * <p>Prior to Android P, or when {@link CameraCharacteristics#LENS_POSE_REFERENCE android.lens.poseReference} is PRIMARY_CAMERA, this position
+     * is relative to the optical center of the largest camera device facing in the same
+     * direction as this camera, in the {@link android.hardware.SensorEvent Android sensor
+     * coordinate axes}. Note that only the axis definitions are shared with the sensor
+     * coordinate system, but not the origin.</p>
+     * <p>If this device is the largest or only camera device with a given facing, then this
+     * position will be <code>(0, 0, 0)</code>; a camera device with a lens optical center located 3 cm
+     * from the main sensor along the +X axis (to the right from the user's perspective) will
+     * report <code>(0.03, 0, 0)</code>.</p>
+     * <p>To transform a pixel coordinates between two cameras facing the same direction, first
+     * the source camera {@link CameraCharacteristics#LENS_RADIAL_DISTORTION android.lens.radialDistortion} must be corrected for.  Then the source
+     * camera {@link CameraCharacteristics#LENS_INTRINSIC_CALIBRATION android.lens.intrinsicCalibration} needs to be applied, followed by the
+     * {@link CameraCharacteristics#LENS_POSE_ROTATION android.lens.poseRotation} of the source camera, the translation of the source camera
+     * relative to the destination camera, the {@link CameraCharacteristics#LENS_POSE_ROTATION android.lens.poseRotation} of the destination
+     * camera, and finally the inverse of {@link CameraCharacteristics#LENS_INTRINSIC_CALIBRATION android.lens.intrinsicCalibration} of the destination
+     * camera. This obtains a radial-distortion-free coordinate in the destination camera pixel
+     * coordinates.</p>
+     * <p>To compare this against a real image from the destination camera, the destination camera
+     * image then needs to be corrected for radial distortion before comparison or sampling.</p>
+     * <p>When {@link CameraCharacteristics#LENS_POSE_REFERENCE android.lens.poseReference} is GYROSCOPE, then this position is relative to
+     * the center of the primary gyroscope on the device.</p>
      * <p><b>Units</b>: Meters</p>
      * <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
      *
      * @see CameraCharacteristics#LENS_INTRINSIC_CALIBRATION
+     * @see CameraCharacteristics#LENS_POSE_REFERENCE
      * @see CameraCharacteristics#LENS_POSE_ROTATION
      * @see CameraCharacteristics#LENS_RADIAL_DISTORTION
      */
diff --git a/core/java/android/hardware/location/ContextHubMessage.java b/core/java/android/hardware/location/ContextHubMessage.java
index bca2ae6..2a4ad00 100644
--- a/core/java/android/hardware/location/ContextHubMessage.java
+++ b/core/java/android/hardware/location/ContextHubMessage.java
@@ -19,14 +19,20 @@
 import android.annotation.SystemApi;
 import android.os.Parcel;
 import android.os.Parcelable;
-import android.util.Log;
 
 import java.util.Arrays;
 
 /**
+ * @deprecated Use {@link android.hardware.location.NanoAppMessage} instead to send messages with
+ *             {@link android.hardware.location.ContextHubClient#sendMessageToNanoApp(
+ *             NanoAppMessage)} and receive messages with
+ *             {@link android.hardware.location.ContextHubClientCallback#onMessageFromNanoApp(
+ *             ContextHubClient, NanoAppMessage)}.
+ *
  * @hide
  */
 @SystemApi
+@Deprecated
 public class ContextHubMessage {
     private int mType;
     private int mVersion;
@@ -34,7 +40,6 @@
 
     private static final String TAG = "ContextHubMessage";
 
-
     /**
      * Get the message type
      *
diff --git a/core/java/android/hardware/location/NanoApp.java b/core/java/android/hardware/location/NanoApp.java
index 0465def..b5c01ec 100644
--- a/core/java/android/hardware/location/NanoApp.java
+++ b/core/java/android/hardware/location/NanoApp.java
@@ -28,9 +28,14 @@
  * Nano apps are expected to be used only by bundled apps only
  * at this time.
  *
+ * @deprecated Use {@link android.hardware.location.NanoAppBinary} instead to load a nanoapp with
+ *             {@link android.hardware.location.ContextHubManager#loadNanoApp(
+ *             ContextHubInfo, NanoAppBinary)}.
+ *
  * @hide
  */
 @SystemApi
+@Deprecated
 public class NanoApp {
     private final String TAG = "NanoApp";
 
diff --git a/core/java/android/hardware/location/NanoAppFilter.java b/core/java/android/hardware/location/NanoAppFilter.java
index 5ccf546..75a96ee 100644
--- a/core/java/android/hardware/location/NanoAppFilter.java
+++ b/core/java/android/hardware/location/NanoAppFilter.java
@@ -16,15 +16,18 @@
 
 package android.hardware.location;
 
-
 import android.annotation.SystemApi;
 import android.os.Parcel;
 import android.os.Parcelable;
 
 /**
+ * @deprecated Use {@link android.hardware.location.ContextHubManager#queryNanoApps(ContextHubInfo)}
+ *             to find loaded nanoapps, which doesn't require using this class as a parameter.
+ *
  * @hide
  */
 @SystemApi
+@Deprecated
 public class NanoAppFilter {
 
     private static final String TAG = "NanoAppFilter";
diff --git a/core/java/android/hardware/location/NanoAppInstanceInfo.java b/core/java/android/hardware/location/NanoAppInstanceInfo.java
index c00819b..f1926eaa 100644
--- a/core/java/android/hardware/location/NanoAppInstanceInfo.java
+++ b/core/java/android/hardware/location/NanoAppInstanceInfo.java
@@ -28,9 +28,12 @@
  *
  * TODO(b/69270990) Remove this class once the old API is deprecated.
  *
+ * @deprecated Use {@link android.hardware.location.NanoAppState} instead.
+ *
  * @hide
  */
 @SystemApi
+@Deprecated
 public class NanoAppInstanceInfo {
     private String mPublisher = "Unknown";
     private String mName = "Unknown";
diff --git a/core/java/android/hardware/radio/ITuner.aidl b/core/java/android/hardware/radio/ITuner.aidl
index 18287fa..ca38076 100644
--- a/core/java/android/hardware/radio/ITuner.aidl
+++ b/core/java/android/hardware/radio/ITuner.aidl
@@ -82,17 +82,9 @@
      */
     List<RadioManager.ProgramInfo> getProgramList(in Map vendorFilter);
 
-    /**
-     * @throws IllegalStateException if the switch is not supported at current
-     *         configuration.
-     */
-    boolean isAnalogForced();
-
-    /**
-     * @throws IllegalStateException if the switch is not supported at current
-     *         configuration.
-     */
-    void setAnalogForced(boolean isForced);
+    boolean isConfigFlagSupported(int flag);
+    boolean isConfigFlagSet(int flag);
+    void setConfigFlag(int flag, boolean value);
 
     /**
      * @param parameters Vendor-specific key-value pairs, must be Map<String, String>
diff --git a/core/java/android/hardware/radio/RadioManager.java b/core/java/android/hardware/radio/RadioManager.java
index 6e383ae..b740f14 100644
--- a/core/java/android/hardware/radio/RadioManager.java
+++ b/core/java/android/hardware/radio/RadioManager.java
@@ -120,6 +120,71 @@
      * @see BandDescriptor */
     public static final int REGION_KOREA  = 4;
 
+    /**
+     * Forces mono audio stream reception.
+     *
+     * Analog broadcasts can recover poor reception conditions by jointing
+     * stereo channels into one. Mainly for, but not limited to AM/FM.
+     */
+    public static final int CONFIG_FORCE_MONO = 1;
+    /**
+     * Forces the analog playback for the supporting radio technology.
+     *
+     * User may disable digital playback for FM HD Radio or hybrid FM/DAB with
+     * this option. This is purely user choice, ie. does not reflect digital-
+     * analog handover state managed from the HAL implementation side.
+     *
+     * Some radio technologies may not support this, ie. DAB.
+     */
+    public static final int CONFIG_FORCE_ANALOG = 2;
+    /**
+     * Forces the digital playback for the supporting radio technology.
+     *
+     * User may disable digital-analog handover that happens with poor
+     * reception conditions. With digital forced, the radio will remain silent
+     * instead of switching to analog channel if it's available. This is purely
+     * user choice, it does not reflect the actual state of handover.
+     */
+    public static final int CONFIG_FORCE_DIGITAL = 3;
+    /**
+     * RDS Alternative Frequencies.
+     *
+     * If set and the currently tuned RDS station broadcasts on multiple
+     * channels, radio tuner automatically switches to the best available
+     * alternative.
+     */
+    public static final int CONFIG_RDS_AF = 4;
+    /**
+     * RDS region-specific program lock-down.
+     *
+     * Allows user to lock to the current region as they move into the
+     * other region.
+     */
+    public static final int CONFIG_RDS_REG = 5;
+    /** Enables DAB-DAB hard- and implicit-linking (the same content). */
+    public static final int CONFIG_DAB_DAB_LINKING = 6;
+    /** Enables DAB-FM hard- and implicit-linking (the same content). */
+    public static final int CONFIG_DAB_FM_LINKING = 7;
+    /** Enables DAB-DAB soft-linking (related content). */
+    public static final int CONFIG_DAB_DAB_SOFT_LINKING = 8;
+    /** Enables DAB-FM soft-linking (related content). */
+    public static final int CONFIG_DAB_FM_SOFT_LINKING = 9;
+
+    /** @hide */
+    @IntDef(prefix = { "CONFIG_" }, value = {
+        CONFIG_FORCE_MONO,
+        CONFIG_FORCE_ANALOG,
+        CONFIG_FORCE_DIGITAL,
+        CONFIG_RDS_AF,
+        CONFIG_RDS_REG,
+        CONFIG_DAB_DAB_LINKING,
+        CONFIG_DAB_FM_LINKING,
+        CONFIG_DAB_DAB_SOFT_LINKING,
+        CONFIG_DAB_FM_SOFT_LINKING,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface ConfigFlag {}
+
     private static void writeStringMap(@NonNull Parcel dest, @NonNull Map<String, String> map) {
         dest.writeInt(map.size());
         for (Map.Entry<String, String> entry : map.entrySet()) {
diff --git a/core/java/android/hardware/radio/RadioTuner.java b/core/java/android/hardware/radio/RadioTuner.java
index e93fd5f..0d367e7 100644
--- a/core/java/android/hardware/radio/RadioTuner.java
+++ b/core/java/android/hardware/radio/RadioTuner.java
@@ -290,7 +290,9 @@
      * @throws IllegalStateException if the switch is not supported at current
      *         configuration.
      * @return {@code true} if analog is forced, {@code false} otherwise.
+     * @deprecated Use {@link isConfigFlagSet(int)} instead.
      */
+    @Deprecated
     public abstract boolean isAnalogForced();
 
     /**
@@ -305,10 +307,50 @@
      * @param isForced {@code true} to force analog, {@code false} for a default behaviour.
      * @throws IllegalStateException if the switch is not supported at current
      *         configuration.
+     * @deprecated Use {@link setConfigFlag(int, boolean)} instead.
      */
+    @Deprecated
     public abstract void setAnalogForced(boolean isForced);
 
     /**
+     * Checks, if a given config flag is supported
+     *
+     * @param flag Flag to check.
+     * @return True, if the flag is supported.
+     */
+    public boolean isConfigFlagSupported(@RadioManager.ConfigFlag int flag) {
+        return false;
+    }
+
+    /**
+     * Fetches the current setting of a given config flag.
+     *
+     * The success/failure result is consistent with isConfigFlagSupported.
+     *
+     * @param flag Flag to fetch.
+     * @return The current value of the flag.
+     * @throws IllegalStateException if the flag is not applicable right now.
+     * @throws UnsupportedOperationException if the flag is not supported at all.
+     */
+    public boolean isConfigFlagSet(@RadioManager.ConfigFlag int flag) {
+        throw new UnsupportedOperationException();
+    }
+
+    /**
+     * Sets the config flag.
+     *
+     * The success/failure result is consistent with isConfigFlagSupported.
+     *
+     * @param flag Flag to set.
+     * @param value The new value of a given flag.
+     * @throws IllegalStateException if the flag is not applicable right now.
+     * @throws UnsupportedOperationException if the flag is not supported at all.
+     */
+    public void setConfigFlag(@RadioManager.ConfigFlag int flag, boolean value) {
+        throw new UnsupportedOperationException();
+    }
+
+    /**
      * Generic method for setting vendor-specific parameter values.
      * The framework does not interpret the parameters, they are passed
      * in an opaque manner between a vendor application and HAL.
@@ -316,6 +358,7 @@
      * Framework does not make any assumptions on the keys or values, other than
      * ones stated in VendorKeyValue documentation (a requirement of key
      * prefixes).
+     * See VendorKeyValue at hardware/interfaces/broadcastradio/2.0/types.hal.
      *
      * For each pair in the result map, the key will be one of the keys
      * contained in the input (possibly with wildcards expanded), and the value
@@ -332,10 +375,11 @@
      *
      * @param parameters Vendor-specific key-value pairs.
      * @return Operation completion status for parameters being set.
-     * @hide FutureFeature
      */
-    public abstract @NonNull Map<String, String>
-            setParameters(@NonNull Map<String, String> parameters);
+    public @NonNull Map<String, String>
+            setParameters(@NonNull Map<String, String> parameters) {
+        throw new UnsupportedOperationException();
+    }
 
     /**
      * Generic method for retrieving vendor-specific parameter values.
@@ -355,10 +399,11 @@
      *
      * @param keys Parameter keys to fetch.
      * @return Vendor-specific key-value pairs.
-     * @hide FutureFeature
      */
-    public abstract @NonNull Map<String, String>
-            getParameters(@NonNull List<String> keys);
+    public @NonNull Map<String, String>
+            getParameters(@NonNull List<String> keys) {
+        throw new UnsupportedOperationException();
+    }
 
     /**
      * Get current antenna connection state for current configuration.
@@ -494,7 +539,6 @@
          * asynchronously.
          *
          * @param parameters Vendor-specific key-value pairs.
-         * @hide FutureFeature
          */
         public void onParametersUpdated(@NonNull Map<String, String> parameters) {}
     }
diff --git a/core/java/android/hardware/radio/TunerAdapter.java b/core/java/android/hardware/radio/TunerAdapter.java
index a8a896a..8ad609d 100644
--- a/core/java/android/hardware/radio/TunerAdapter.java
+++ b/core/java/android/hardware/radio/TunerAdapter.java
@@ -236,17 +236,36 @@
 
     @Override
     public boolean isAnalogForced() {
+        return isConfigFlagSet(RadioManager.CONFIG_FORCE_ANALOG);
+    }
+
+    @Override
+    public void setAnalogForced(boolean isForced) {
+        setConfigFlag(RadioManager.CONFIG_FORCE_ANALOG, isForced);
+    }
+
+    @Override
+    public boolean isConfigFlagSupported(@RadioManager.ConfigFlag int flag) {
         try {
-            return mTuner.isAnalogForced();
+            return mTuner.isConfigFlagSupported(flag);
         } catch (RemoteException e) {
             throw new RuntimeException("service died", e);
         }
     }
 
     @Override
-    public void setAnalogForced(boolean isForced) {
+    public boolean isConfigFlagSet(@RadioManager.ConfigFlag int flag) {
         try {
-            mTuner.setAnalogForced(isForced);
+            return mTuner.isConfigFlagSet(flag);
+        } catch (RemoteException e) {
+            throw new RuntimeException("service died", e);
+        }
+    }
+
+    @Override
+    public void setConfigFlag(@RadioManager.ConfigFlag int flag, boolean value) {
+        try {
+            mTuner.setConfigFlag(flag, value);
         } catch (RemoteException e) {
             throw new RuntimeException("service died", e);
         }
diff --git a/core/java/android/net/IpSecAlgorithm.java b/core/java/android/net/IpSecAlgorithm.java
index f82627b..7d752e8 100644
--- a/core/java/android/net/IpSecAlgorithm.java
+++ b/core/java/android/net/IpSecAlgorithm.java
@@ -231,6 +231,31 @@
         }
     }
 
+    /** @hide */
+    public boolean isAuthentication() {
+        switch (getName()) {
+            // Fallthrough
+            case AUTH_HMAC_MD5:
+            case AUTH_HMAC_SHA1:
+            case AUTH_HMAC_SHA256:
+            case AUTH_HMAC_SHA384:
+            case AUTH_HMAC_SHA512:
+                return true;
+            default:
+                return false;
+        }
+    }
+
+    /** @hide */
+    public boolean isEncryption() {
+        return getName().equals(CRYPT_AES_CBC);
+    }
+
+    /** @hide */
+    public boolean isAead() {
+        return getName().equals(AUTH_CRYPT_AES_GCM);
+    }
+
     @Override
     public String toString() {
         return new StringBuilder()
diff --git a/core/java/android/net/Network.java b/core/java/android/net/Network.java
index 903b602..1a3ce91 100644
--- a/core/java/android/net/Network.java
+++ b/core/java/android/net/Network.java
@@ -21,6 +21,7 @@
 import android.system.ErrnoException;
 import android.system.Os;
 import android.system.OsConstants;
+import android.util.proto.ProtoOutputStream;
 
 import com.android.okhttp.internalandroidapi.Dns;
 import com.android.okhttp.internalandroidapi.HttpURLConnectionFactory;
@@ -402,4 +403,11 @@
     public String toString() {
         return Integer.toString(netId);
     }
+
+    /** @hide */
+    public void writeToProto(ProtoOutputStream proto, long fieldId) {
+        final long token = proto.start(fieldId);
+        proto.write(NetworkProto.NET_ID, netId);
+        proto.end(token);
+    }
 }
diff --git a/core/java/android/net/NetworkCapabilities.java b/core/java/android/net/NetworkCapabilities.java
index f468e5d..8b03fa8 100644
--- a/core/java/android/net/NetworkCapabilities.java
+++ b/core/java/android/net/NetworkCapabilities.java
@@ -20,6 +20,7 @@
 import android.net.ConnectivityManager.NetworkCallback;
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.util.proto.ProtoOutputStream;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.BitUtils;
@@ -1016,6 +1017,31 @@
         return "[" + transports + capabilities + upBand + dnBand + specifier + signalStrength + "]";
     }
 
+    /** @hide */
+    public void writeToProto(ProtoOutputStream proto, long fieldId) {
+        final long token = proto.start(fieldId);
+
+        for (int transport : getTransportTypes()) {
+            proto.write(NetworkCapabilitiesProto.TRANSPORTS, transport);
+        }
+
+        for (int capability : getCapabilities()) {
+            proto.write(NetworkCapabilitiesProto.CAPABILITIES, capability);
+        }
+
+        proto.write(NetworkCapabilitiesProto.LINK_UP_BANDWIDTH_KBPS, mLinkUpBandwidthKbps);
+        proto.write(NetworkCapabilitiesProto.LINK_DOWN_BANDWIDTH_KBPS, mLinkDownBandwidthKbps);
+
+        if (mNetworkSpecifier != null) {
+            proto.write(NetworkCapabilitiesProto.NETWORK_SPECIFIER, mNetworkSpecifier.toString());
+        }
+
+        proto.write(NetworkCapabilitiesProto.CAN_REPORT_SIGNAL_STRENGTH, hasSignalStrength());
+        proto.write(NetworkCapabilitiesProto.SIGNAL_STRENGTH, mSignalStrength);
+
+        proto.end(token);
+    }
+
     /**
      * @hide
      */
diff --git a/core/java/android/net/NetworkRequest.java b/core/java/android/net/NetworkRequest.java
index 97ded2d..a072409 100644
--- a/core/java/android/net/NetworkRequest.java
+++ b/core/java/android/net/NetworkRequest.java
@@ -20,6 +20,7 @@
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.text.TextUtils;
+import android.util.proto.ProtoOutputStream;
 
 import java.util.Objects;
 
@@ -389,6 +390,35 @@
                 ", " + networkCapabilities.toString() + " ]";
     }
 
+    private int typeToProtoEnum(Type t) {
+        switch (t) {
+            case NONE:
+                return NetworkRequestProto.TYPE_NONE;
+            case LISTEN:
+                return NetworkRequestProto.TYPE_LISTEN;
+            case TRACK_DEFAULT:
+                return NetworkRequestProto.TYPE_TRACK_DEFAULT;
+            case REQUEST:
+                return NetworkRequestProto.TYPE_REQUEST;
+            case BACKGROUND_REQUEST:
+                return NetworkRequestProto.TYPE_BACKGROUND_REQUEST;
+            default:
+                return NetworkRequestProto.TYPE_UNKNOWN;
+        }
+    }
+
+    /** @hide */
+    public void writeToProto(ProtoOutputStream proto, long fieldId) {
+        final long token = proto.start(fieldId);
+
+        proto.write(NetworkRequestProto.TYPE, typeToProtoEnum(type));
+        proto.write(NetworkRequestProto.REQUEST_ID, requestId);
+        proto.write(NetworkRequestProto.LEGACY_TYPE, legacyType);
+        networkCapabilities.writeToProto(proto, NetworkRequestProto.NETWORK_CAPABILITIES);
+
+        proto.end(token);
+    }
+
     public boolean equals(Object obj) {
         if (obj instanceof NetworkRequest == false) return false;
         NetworkRequest that = (NetworkRequest)obj;
diff --git a/core/java/android/os/Binder.java b/core/java/android/os/Binder.java
index 33470f3..eb264d6d 100644
--- a/core/java/android/os/Binder.java
+++ b/core/java/android/os/Binder.java
@@ -805,7 +805,7 @@
         /**
          * Return the total number of pairs in the map.
          */
-        int size() {
+        private int size() {
             int size = 0;
             for (ArrayList<WeakReference<BinderProxy>> a : mMainIndexValues) {
                 if (a != null) {
@@ -816,6 +816,24 @@
         }
 
         /**
+         * Return the total number of pairs in the map containing values that have
+         * not been cleared. More expensive than the above size function.
+         */
+        private int unclearedSize() {
+            int size = 0;
+            for (ArrayList<WeakReference<BinderProxy>> a : mMainIndexValues) {
+                if (a != null) {
+                    for (WeakReference<BinderProxy> ref : a) {
+                        if (ref.get() != null) {
+                            ++size;
+                        }
+                    }
+                }
+            }
+            return size;
+        }
+
+        /**
          * Remove ith entry from the hash bucket indicated by hash.
          */
         private void remove(int hash, int index) {
@@ -908,17 +926,31 @@
                 Log.v(Binder.TAG, "BinderProxy map growth! bucket size = " + size
                         + " total = " + totalSize);
                 mWarnBucketSize += WARN_INCREMENT;
-                if (Build.IS_DEBUGGABLE && totalSize > CRASH_AT_SIZE) {
-                    diagnosticCrash();
+                if (Build.IS_DEBUGGABLE && totalSize >= CRASH_AT_SIZE) {
+                    // Use the number of uncleared entries to determine whether we should
+                    // really report a histogram and crash. We don't want to fundamentally
+                    // change behavior for a debuggable process, so we GC only if we are
+                    // about to crash.
+                    final int totalUnclearedSize = unclearedSize();
+                    if (totalUnclearedSize >= CRASH_AT_SIZE) {
+                        dumpProxyInterfaceCounts();
+                        Runtime.getRuntime().gc();
+                        throw new AssertionError("Binder ProxyMap has too many entries: "
+                                + totalSize + " (total), " + totalUnclearedSize + " (uncleared), "
+                                + unclearedSize() + " (uncleared after GC). BinderProxy leak?");
+                    } else if (totalSize > 3 * totalUnclearedSize / 2) {
+                        Log.v(Binder.TAG, "BinderProxy map has many cleared entries: "
+                                + (totalSize - totalUnclearedSize) + " of " + totalSize
+                                + " are cleared");
+                    }
                 }
             }
         }
 
         /**
-         * Dump a histogram to the logcat, then throw an assertion error. Used to diagnose
-         * abnormally large proxy maps.
+         * Dump a histogram to the logcat. Used to diagnose abnormally large proxy maps.
          */
-        private void diagnosticCrash() {
+        private void dumpProxyInterfaceCounts() {
             Map<String, Integer> counts = new HashMap<>();
             for (ArrayList<WeakReference<BinderProxy>> a : mMainIndexValues) {
                 if (a != null) {
@@ -953,11 +985,6 @@
                 Log.v(Binder.TAG, " #" + (i + 1) + ": " + sorted[i].getKey() + " x"
                         + sorted[i].getValue());
             }
-
-            // Now throw an assertion.
-            final int totalSize = size();
-            throw new AssertionError("Binder ProxyMap has too many entries: " + totalSize
-                    + ". BinderProxy leak?");
         }
 
         // Corresponding ArrayLists in the following two arrays always have the same size.
diff --git a/core/java/android/os/Bundle.java b/core/java/android/os/Bundle.java
index c58153a..7ae5a67 100644
--- a/core/java/android/os/Bundle.java
+++ b/core/java/android/os/Bundle.java
@@ -21,6 +21,7 @@
 import android.util.Size;
 import android.util.SizeF;
 import android.util.SparseArray;
+import android.util.proto.ProtoOutputStream;
 
 import com.android.internal.annotations.VisibleForTesting;
 
@@ -1272,4 +1273,21 @@
         }
         return mMap.toString();
     }
+
+    /** @hide */
+    public void writeToProto(ProtoOutputStream proto, long fieldId) {
+        final long token = proto.start(fieldId);
+
+        if (mParcelledData != null) {
+            if (isEmptyParcel()) {
+                proto.write(BundleProto.PARCELLED_DATA_SIZE, 0);
+            } else {
+                proto.write(BundleProto.PARCELLED_DATA_SIZE, mParcelledData.dataSize());
+            }
+        } else {
+            proto.write(BundleProto.MAP_DATA, mMap.toString());
+        }
+
+        proto.end(token);
+    }
 }
diff --git a/core/java/android/os/IUserManager.aidl b/core/java/android/os/IUserManager.aidl
index f643c57..01d6b02 100644
--- a/core/java/android/os/IUserManager.aidl
+++ b/core/java/android/os/IUserManager.aidl
@@ -98,4 +98,6 @@
     boolean isUserNameSet(int userHandle);
     boolean hasRestrictedProfiles();
     boolean trySetQuietModeEnabled(String callingPackage, boolean enableQuietMode, int userHandle, in IntentSender target);
+    long getUserStartRealtime();
+    long getUserUnlockRealtime();
 }
diff --git a/core/java/android/os/PersistableBundle.java b/core/java/android/os/PersistableBundle.java
index 3ed5b17..40eceb8 100644
--- a/core/java/android/os/PersistableBundle.java
+++ b/core/java/android/os/PersistableBundle.java
@@ -18,6 +18,7 @@
 
 import android.annotation.Nullable;
 import android.util.ArrayMap;
+import android.util.proto.ProtoOutputStream;
 
 import com.android.internal.util.XmlUtils;
 
@@ -321,4 +322,21 @@
         }
         return mMap.toString();
     }
+
+    /** @hide */
+    public void writeToProto(ProtoOutputStream proto, long fieldId) {
+        final long token = proto.start(fieldId);
+
+        if (mParcelledData != null) {
+            if (isEmptyParcel()) {
+                proto.write(PersistableBundleProto.PARCELLED_DATA_SIZE, 0);
+            } else {
+                proto.write(PersistableBundleProto.PARCELLED_DATA_SIZE, mParcelledData.dataSize());
+            }
+        } else {
+            proto.write(PersistableBundleProto.MAP_DATA, mMap.toString());
+        }
+
+        proto.end(token);
+    }
 }
diff --git a/core/java/android/os/Seccomp.java b/core/java/android/os/Seccomp.java
index f14e93f..335e44b 100644
--- a/core/java/android/os/Seccomp.java
+++ b/core/java/android/os/Seccomp.java
@@ -20,5 +20,6 @@
  * @hide
  */
 public final class Seccomp {
-    public static final native void setPolicy();
+    public static native void setSystemServerPolicy();
+    public static native void setAppPolicy();
 }
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index 38993b7..44238df 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -1392,6 +1392,34 @@
     }
 
     /**
+     * Return the time when the calling user started in elapsed milliseconds since boot,
+     * or 0 if not started.
+     *
+     * @hide
+     */
+    public long getUserStartRealtime() {
+        try {
+            return mService.getUserStartRealtime();
+        } catch (RemoteException re) {
+            throw re.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Return the time when the calling user was unlocked elapsed milliseconds since boot,
+     * or 0 if not unlocked.
+     *
+     * @hide
+     */
+    public long getUserUnlockRealtime() {
+        try {
+            return mService.getUserUnlockRealtime();
+        } catch (RemoteException re) {
+            throw re.rethrowFromSystemServer();
+        }
+    }
+
+    /**
      * Returns the UserInfo object describing a specific user.
      * Requires {@link android.Manifest.permission#MANAGE_USERS} permission.
      * @param userHandle the user handle of the user whose information is being requested.
diff --git a/core/java/android/os/WorkSource.java b/core/java/android/os/WorkSource.java
index 401b4a3..bb043b0 100644
--- a/core/java/android/os/WorkSource.java
+++ b/core/java/android/os/WorkSource.java
@@ -407,11 +407,11 @@
     }
 
     public boolean remove(WorkSource other) {
-        if (mNum <= 0 || other.mNum <= 0) {
+        if (isEmpty() || other.isEmpty()) {
             return false;
         }
 
-        boolean uidRemoved = false;
+        boolean uidRemoved;
         if (mNames == null && other.mNames == null) {
             uidRemoved = removeUids(other);
         } else {
@@ -427,13 +427,8 @@
         }
 
         boolean chainRemoved = false;
-        if (other.mChains != null) {
-            if (mChains != null) {
-                chainRemoved = mChains.removeAll(other.mChains);
-            }
-        } else if (mChains != null) {
-            mChains.clear();
-            chainRemoved = true;
+        if (other.mChains != null && mChains != null) {
+            chainRemoved = mChains.removeAll(other.mChains);
         }
 
         return uidRemoved || chainRemoved;
diff --git a/core/java/android/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java
index 9de1223..f4deeed 100644
--- a/core/java/android/os/storage/StorageManager.java
+++ b/core/java/android/os/storage/StorageManager.java
@@ -114,6 +114,8 @@
     /** {@hide} */
     public static final String PROP_HAS_ADOPTABLE = "vold.has_adoptable";
     /** {@hide} */
+    public static final String PROP_HAS_RESERVED = "vold.has_reserved";
+    /** {@hide} */
     public static final String PROP_FORCE_ADOPTABLE = "persist.fw.force_adoptable";
     /** {@hide} */
     public static final String PROP_EMULATE_FBE = "persist.sys.emulate_fbe";
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 009fc39..2ec4906 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -7191,13 +7191,6 @@
         public static final String QS_TILES = "sysui_qs_tiles";
 
         /**
-         * Whether preloaded APKs have been installed for the user.
-         * @hide
-         */
-        public static final String DEMO_USER_SETUP_COMPLETE
-                = "demo_user_setup_complete";
-
-        /**
          * Specifies whether the web action API is enabled.
          *
          * @hide
@@ -8711,6 +8704,8 @@
 
         /**
          * Whether soft AP will shut down after a timeout period when no devices are connected.
+         *
+         * Type: int (0 for false, 1 for true)
          * @hide
          */
         public static final String SOFT_AP_TIMEOUT_ENABLED = "soft_ap_timeout_enabled";
@@ -9782,6 +9777,22 @@
         public static final String TEXT_CLASSIFIER_CONSTANTS = "text_classifier_constants";
 
         /**
+         * BatteryStats specific settings.
+         * This is encoded as a key=value list, separated by commas. Ex: "foo=1,bar=true"
+         *
+         * The following keys are supported:
+         * <pre>
+         * track_cpu_times_by_proc_state (boolean)
+         * </pre>
+         *
+         * <p>
+         * Type: string
+         * @hide
+         * see also com.android.internal.os.BatteryStatsImpl.Constants
+         */
+        public static final String BATTERY_STATS_CONSTANTS = "battery_stats_constants";
+
+        /**
          * Whether or not App Standby feature is enabled. This controls throttling of apps
          * based on usage patterns and predictions.
          * Type: int (0 for false, 1 for true)
@@ -9791,6 +9802,14 @@
         public static final java.lang.String APP_STANDBY_ENABLED = "app_standby_enabled";
 
         /**
+         * Whether or not Network Watchlist feature is enabled.
+         * Type: int (0 for false, 1 for true)
+         * Default: 0
+         * @hide
+         */
+        public static final String NETWORK_WATCHLIST_ENABLED = "network_watchlist_enabled";
+
+        /**
          * Get the key that retrieves a bluetooth headset's priority.
          * @hide
          */
diff --git a/core/java/android/security/recoverablekeystore/KeyEntryRecoveryData.java b/core/java/android/security/recoverablekeystore/KeyEntryRecoveryData.java
index 80f5aa7..5f56c91 100644
--- a/core/java/android/security/recoverablekeystore/KeyEntryRecoveryData.java
+++ b/core/java/android/security/recoverablekeystore/KeyEntryRecoveryData.java
@@ -22,13 +22,12 @@
 
 import com.android.internal.util.Preconditions;
 
-
 /**
  * Helper class with data necessary recover a single application key, given a recovery key.
  *
  * <ul>
- * <li>Alias - Keystore alias of the key.
- * <li>Encrypted key material.
+ *   <li>Alias - Keystore alias of the key.
+ *   <li>Encrypted key material.
  * </ul>
  *
  * Note that Application info is not included. Recovery Agent can only make its own keys
@@ -37,49 +36,48 @@
  * @hide
  */
 public final class KeyEntryRecoveryData implements Parcelable {
-    private final byte[] mAlias;
+    private final String mAlias;
     // The only supported format is AES-256 symmetric key.
     private final byte[] mEncryptedKeyMaterial;
 
-    public KeyEntryRecoveryData(@NonNull byte[] alias, @NonNull byte[] encryptedKeyMaterial) {
+    public KeyEntryRecoveryData(@NonNull String alias, @NonNull byte[] encryptedKeyMaterial) {
         mAlias = Preconditions.checkNotNull(alias);
         mEncryptedKeyMaterial = Preconditions.checkNotNull(encryptedKeyMaterial);
     }
 
     /**
      * Application-specific alias of the key.
+     *
      * @see java.security.KeyStore.aliases
      */
-    public @NonNull byte[] getAlias() {
+    public @NonNull String getAlias() {
         return mAlias;
     }
 
-    /**
-     * Encrypted key material encrypted by recovery key.
-     */
+    /** Encrypted key material encrypted by recovery key. */
     public @NonNull byte[] getEncryptedKeyMaterial() {
         return mEncryptedKeyMaterial;
     }
 
     public static final Parcelable.Creator<KeyEntryRecoveryData> CREATOR =
             new Parcelable.Creator<KeyEntryRecoveryData>() {
-        public KeyEntryRecoveryData createFromParcel(Parcel in) {
-                return new KeyEntryRecoveryData(in);
-        }
+                public KeyEntryRecoveryData createFromParcel(Parcel in) {
+                    return new KeyEntryRecoveryData(in);
+                }
 
-        public KeyEntryRecoveryData[] newArray(int length) {
-            return new KeyEntryRecoveryData[length];
-        }
-    };
+                public KeyEntryRecoveryData[] newArray(int length) {
+                    return new KeyEntryRecoveryData[length];
+                }
+            };
 
     @Override
     public void writeToParcel(Parcel out, int flags) {
-        out.writeByteArray(mAlias);
+        out.writeString(mAlias);
         out.writeByteArray(mEncryptedKeyMaterial);
     }
 
     protected KeyEntryRecoveryData(Parcel in) {
-        mAlias = in.createByteArray();
+        mAlias = in.readString();
         mEncryptedKeyMaterial = in.createByteArray();
     }
 
diff --git a/core/java/android/text/DynamicLayout.java b/core/java/android/text/DynamicLayout.java
index 6bca37a..18431ca 100644
--- a/core/java/android/text/DynamicLayout.java
+++ b/core/java/android/text/DynamicLayout.java
@@ -1096,6 +1096,11 @@
 
         public void onSpanChanged(Spannable s, Object o, int start, int end, int nstart, int nend) {
             if (o instanceof UpdateLayout) {
+                if (start > end) {
+                    // Bug: 67926915 start cannot be determined, fallback to reflow from start
+                    // instead of causing an exception
+                    start = 0;
+                }
                 reflow(s, start, end - start, end - start);
                 reflow(s, nstart, nend - nstart, nend - nstart);
             }
diff --git a/core/java/android/util/KeyValueListParser.java b/core/java/android/util/KeyValueListParser.java
index 0a00794..7eef63e 100644
--- a/core/java/android/util/KeyValueListParser.java
+++ b/core/java/android/util/KeyValueListParser.java
@@ -17,6 +17,9 @@
 
 import android.text.TextUtils;
 
+import java.time.Duration;
+import java.time.format.DateTimeParseException;
+
 /**
  * Parses a list of key=value pairs, separated by some delimiter, and puts the results in
  * an internal Map. Values can be then queried by key, or if not found, a default value
@@ -189,4 +192,24 @@
     public String keyAt(int index) {
         return mValues.keyAt(index);
     }
+
+    /**
+     * {@hide}
+     * Parse a duration in millis based on java.time.Duration or just a number (millis)
+     */
+    public long getDurationMillis(String key, long def) {
+        String value = mValues.get(key);
+        if (value != null) {
+            try {
+                if (value.startsWith("P") || value.startsWith("p")) {
+                    return Duration.parse(value).toMillis();
+                } else {
+                    return Long.parseLong(value);
+                }
+            } catch (NumberFormatException | DateTimeParseException e) {
+                // fallthrough
+            }
+        }
+        return def;
+    }
 }
diff --git a/core/java/android/util/apk/ApkSignatureVerifier.java b/core/java/android/util/apk/ApkSignatureVerifier.java
index 555c474..8146729 100644
--- a/core/java/android/util/apk/ApkSignatureVerifier.java
+++ b/core/java/android/util/apk/ApkSignatureVerifier.java
@@ -25,7 +25,6 @@
 
 import android.content.pm.PackageParser;
 import android.content.pm.PackageParser.PackageParserException;
-import android.content.pm.PackageParser.SigningDetails.SignatureSchemeVersion;
 import android.content.pm.Signature;
 import android.os.Trace;
 import android.util.jar.StrictJarFile;
@@ -53,6 +52,10 @@
  */
 public class ApkSignatureVerifier {
 
+    public static final int VERSION_JAR_SIGNATURE_SCHEME = 1;
+    public static final int VERSION_APK_SIGNATURE_SCHEME_V2 = 2;
+    public static final int VERSION_APK_SIGNATURE_SCHEME_V3 = 3;
+
     private static final AtomicReference<byte[]> sBuffer = new AtomicReference<>();
 
     /**
@@ -60,11 +63,10 @@
      *
      * @throws PackageParserException if the APK's signature failed to verify.
      */
-    public static PackageParser.SigningDetails verify(String apkPath,
-            @SignatureSchemeVersion int minSignatureSchemeVersion)
+    public static Result verify(String apkPath, int minSignatureSchemeVersion)
             throws PackageParserException {
 
-        if (minSignatureSchemeVersion > SignatureSchemeVersion.SIGNING_BLOCK_V3) {
+        if (minSignatureSchemeVersion > VERSION_APK_SIGNATURE_SCHEME_V3) {
             // V3 and before are older than the requested minimum signing version
             throw new PackageParserException(INSTALL_PARSE_FAILED_NO_CERTIFICATES,
                     "No signature found in package of version " + minSignatureSchemeVersion
@@ -78,11 +80,10 @@
                     ApkSignatureSchemeV3Verifier.verify(apkPath);
             Certificate[][] signerCerts = new Certificate[][] { vSigner.certs };
             Signature[] signerSigs = convertToSignatures(signerCerts);
-            return new PackageParser.SigningDetails(signerSigs,
-                    SignatureSchemeVersion.SIGNING_BLOCK_V3);
+            return new Result(signerCerts, signerSigs, VERSION_APK_SIGNATURE_SCHEME_V3);
         } catch (SignatureNotFoundException e) {
             // not signed with v2, try older if allowed
-            if (minSignatureSchemeVersion >= SignatureSchemeVersion.SIGNING_BLOCK_V3) {
+            if (minSignatureSchemeVersion >= VERSION_APK_SIGNATURE_SCHEME_V3) {
                 throw new PackageParserException(INSTALL_PARSE_FAILED_NO_CERTIFICATES,
                         "No APK Signature Scheme v3 signature in package " + apkPath, e);
             }
@@ -96,7 +97,7 @@
         }
 
         // redundant, protective version check
-        if (minSignatureSchemeVersion > SignatureSchemeVersion.SIGNING_BLOCK_V2) {
+        if (minSignatureSchemeVersion > VERSION_APK_SIGNATURE_SCHEME_V2) {
             // V2 and before are older than the requested minimum signing version
             throw new PackageParserException(INSTALL_PARSE_FAILED_NO_CERTIFICATES,
                     "No signature found in package of version " + minSignatureSchemeVersion
@@ -109,11 +110,10 @@
             Certificate[][] signerCerts = ApkSignatureSchemeV2Verifier.verify(apkPath);
             Signature[] signerSigs = convertToSignatures(signerCerts);
 
-            return new PackageParser.SigningDetails(
-                    signerSigs, SignatureSchemeVersion.SIGNING_BLOCK_V2);
+            return new Result(signerCerts, signerSigs, VERSION_APK_SIGNATURE_SCHEME_V2);
         } catch (SignatureNotFoundException e) {
             // not signed with v2, try older if allowed
-            if (minSignatureSchemeVersion >= SignatureSchemeVersion.SIGNING_BLOCK_V2) {
+            if (minSignatureSchemeVersion >= VERSION_APK_SIGNATURE_SCHEME_V2) {
                 throw new PackageParserException(INSTALL_PARSE_FAILED_NO_CERTIFICATES,
                         "No APK Signature Scheme v2 signature in package " + apkPath, e);
             }
@@ -127,7 +127,7 @@
         }
 
         // redundant, protective version check
-        if (minSignatureSchemeVersion > SignatureSchemeVersion.JAR) {
+        if (minSignatureSchemeVersion > VERSION_JAR_SIGNATURE_SCHEME) {
             // V1 and is older than the requested minimum signing version
             throw new PackageParserException(INSTALL_PARSE_FAILED_NO_CERTIFICATES,
                     "No signature found in package of version " + minSignatureSchemeVersion
@@ -145,8 +145,7 @@
      *
      * @throws PackageParserException if there was a problem collecting certificates
      */
-    private static PackageParser.SigningDetails verifyV1Signature(
-            String apkPath, boolean verifyFull)
+    private static Result verifyV1Signature(String apkPath, boolean verifyFull)
             throws PackageParserException {
         StrictJarFile jarFile = null;
 
@@ -212,7 +211,7 @@
                     }
                 }
             }
-            return new PackageParser.SigningDetails(lastSigs, SignatureSchemeVersion.JAR);
+            return new Result(lastCerts, lastSigs, VERSION_JAR_SIGNATURE_SCHEME);
         } catch (GeneralSecurityException e) {
             throw new PackageParserException(INSTALL_PARSE_FAILED_CERTIFICATE_ENCODING,
                     "Failed to collect certificates from " + apkPath, e);
@@ -290,11 +289,10 @@
      * @throws PackageParserException if the APK's signature failed to verify.
      * or greater is not found, except in the case of no JAR signature.
      */
-    public static PackageParser.SigningDetails plsCertsNoVerifyOnlyCerts(
-            String apkPath, int minSignatureSchemeVersion)
+    public static Result plsCertsNoVerifyOnlyCerts(String apkPath, int minSignatureSchemeVersion)
             throws PackageParserException {
 
-        if (minSignatureSchemeVersion > SignatureSchemeVersion.SIGNING_BLOCK_V3) {
+        if (minSignatureSchemeVersion > VERSION_APK_SIGNATURE_SCHEME_V3) {
             // V3 and before are older than the requested minimum signing version
             throw new PackageParserException(INSTALL_PARSE_FAILED_NO_CERTIFICATES,
                     "No signature found in package of version " + minSignatureSchemeVersion
@@ -308,11 +306,10 @@
                     ApkSignatureSchemeV3Verifier.plsCertsNoVerifyOnlyCerts(apkPath);
             Certificate[][] signerCerts = new Certificate[][] { vSigner.certs };
             Signature[] signerSigs = convertToSignatures(signerCerts);
-            return new PackageParser.SigningDetails(signerSigs,
-                    SignatureSchemeVersion.SIGNING_BLOCK_V3);
+            return new Result(signerCerts, signerSigs, VERSION_APK_SIGNATURE_SCHEME_V3);
         } catch (SignatureNotFoundException e) {
             // not signed with v2, try older if allowed
-            if (minSignatureSchemeVersion >= SignatureSchemeVersion.SIGNING_BLOCK_V3) {
+            if (minSignatureSchemeVersion >= VERSION_APK_SIGNATURE_SCHEME_V3) {
                 throw new PackageParserException(INSTALL_PARSE_FAILED_NO_CERTIFICATES,
                         "No APK Signature Scheme v3 signature in package " + apkPath, e);
             }
@@ -326,7 +323,7 @@
         }
 
         // redundant, protective version check
-        if (minSignatureSchemeVersion > SignatureSchemeVersion.SIGNING_BLOCK_V2) {
+        if (minSignatureSchemeVersion > VERSION_APK_SIGNATURE_SCHEME_V2) {
             // V2 and before are older than the requested minimum signing version
             throw new PackageParserException(INSTALL_PARSE_FAILED_NO_CERTIFICATES,
                     "No signature found in package of version " + minSignatureSchemeVersion
@@ -339,11 +336,10 @@
             Certificate[][] signerCerts =
                     ApkSignatureSchemeV2Verifier.plsCertsNoVerifyOnlyCerts(apkPath);
             Signature[] signerSigs = convertToSignatures(signerCerts);
-            return new PackageParser.SigningDetails(signerSigs,
-                    SignatureSchemeVersion.SIGNING_BLOCK_V2);
+            return new Result(signerCerts, signerSigs, VERSION_APK_SIGNATURE_SCHEME_V2);
         } catch (SignatureNotFoundException e) {
             // not signed with v2, try older if allowed
-            if (minSignatureSchemeVersion >= SignatureSchemeVersion.SIGNING_BLOCK_V2) {
+            if (minSignatureSchemeVersion >= VERSION_APK_SIGNATURE_SCHEME_V2) {
                 throw new PackageParserException(INSTALL_PARSE_FAILED_NO_CERTIFICATES,
                         "No APK Signature Scheme v2 signature in package " + apkPath, e);
             }
@@ -357,7 +353,7 @@
         }
 
         // redundant, protective version check
-        if (minSignatureSchemeVersion > SignatureSchemeVersion.JAR) {
+        if (minSignatureSchemeVersion > VERSION_JAR_SIGNATURE_SCHEME) {
             // V1 and is older than the requested minimum signing version
             throw new PackageParserException(INSTALL_PARSE_FAILED_NO_CERTIFICATES,
                     "No signature found in package of version " + minSignatureSchemeVersion
@@ -367,4 +363,19 @@
         // v2 didn't work, try jarsigner
         return verifyV1Signature(apkPath, false);
     }
+
+    /**
+     * Result of a successful APK verification operation.
+     */
+    public static class Result {
+        public final Certificate[][] certs;
+        public final Signature[] sigs;
+        public final int signatureSchemeVersion;
+
+        public Result(Certificate[][] certs, Signature[] sigs, int signingVersion) {
+            this.certs = certs;
+            this.sigs = sigs;
+            this.signatureSchemeVersion = signingVersion;
+        }
+    }
 }
diff --git a/core/java/android/view/Choreographer.java b/core/java/android/view/Choreographer.java
index ba6b6cf..33fbf73 100644
--- a/core/java/android/view/Choreographer.java
+++ b/core/java/android/view/Choreographer.java
@@ -235,6 +235,8 @@
         for (int i = 0; i <= CALLBACK_LAST; i++) {
             mCallbackQueues[i] = new CallbackQueue();
         }
+        // b/68769804: For low FPS experiments.
+        setFPSDivisor(SystemProperties.getInt(ThreadedRenderer.DEBUG_FPS_DIVISOR, 1));
     }
 
     private static float getRefreshRate() {
@@ -605,6 +607,7 @@
     void setFPSDivisor(int divisor) {
         if (divisor <= 0) divisor = 1;
         mFPSDivisor = divisor;
+        ThreadedRenderer.setFPSDivisor(divisor);
     }
 
     void doFrame(long frameTimeNanos, int frame) {
diff --git a/core/java/android/view/Surface.java b/core/java/android/view/Surface.java
index a417a4a..1f7f8b9 100644
--- a/core/java/android/view/Surface.java
+++ b/core/java/android/view/Surface.java
@@ -182,6 +182,11 @@
      * SurfaceTexture}, which can attach them to an OpenGL ES texture via {@link
      * SurfaceTexture#updateTexImage}.
      *
+     * Please note that holding onto the Surface created here is not enough to
+     * keep the provided SurfaceTexture from being reclaimed.  In that sense,
+     * the Surface will act like a
+     * {@link java.lang.ref.WeakReference weak reference} to the SurfaceTexture.
+     *
      * @param surfaceTexture The {@link SurfaceTexture} that is updated by this
      * Surface.
      * @throws OutOfResourcesException if the surface could not be created.
diff --git a/core/java/android/view/ThreadedRenderer.java b/core/java/android/view/ThreadedRenderer.java
index 6a8f8b1..8b730f2 100644
--- a/core/java/android/view/ThreadedRenderer.java
+++ b/core/java/android/view/ThreadedRenderer.java
@@ -969,8 +969,6 @@
             mInitialized = true;
             mAppContext = context.getApplicationContext();
 
-            // b/68769804: For low FPS experiments.
-            setFPSDivisor(SystemProperties.getInt(DEBUG_FPS_DIVISOR, 1));
             initSched(renderProxy);
             initGraphicsStats();
         }
@@ -1025,9 +1023,7 @@
 
     /** b/68769804: For low FPS experiments. */
     public static void setFPSDivisor(int divisor) {
-        if (divisor <= 0) divisor = 1;
-        Choreographer.getInstance().setFPSDivisor(divisor);
-        nHackySetRTAnimationsEnabled(divisor == 1);
+        nHackySetRTAnimationsEnabled(divisor <= 1);
     }
 
     /** Not actually public - internal use only. This doc to make lint happy */
diff --git a/core/java/android/webkit/FindAddress.java b/core/java/android/webkit/FindAddress.java
new file mode 100644
index 0000000..31b2427
--- /dev/null
+++ b/core/java/android/webkit/FindAddress.java
@@ -0,0 +1,478 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.webkit;
+
+import java.util.Locale;
+import java.util.regex.MatchResult;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * Java implementation of legacy WebView.findAddress algorithm.
+ *
+ * @hide
+ */
+class FindAddress {
+    static class ZipRange {
+        int mLow;
+        int mHigh;
+        int mException1;
+        int mException2;
+        ZipRange(int low, int high, int exception1, int exception2) {
+            mLow = low;
+            mHigh = high;
+            mException1 = exception1;
+            mException2 = exception1;
+        }
+        boolean matches(String zipCode) {
+            int prefix = Integer.parseInt(zipCode.substring(0, 2));
+            return (mLow <= prefix && prefix <= mHigh) || prefix == mException1
+                    || prefix == mException2;
+        }
+    }
+
+    // Addresses consist of at least this many words, not including state and zip code.
+    private static final int MIN_ADDRESS_WORDS = 4;
+
+    // Adddresses consist of at most this many words, not including state and zip code.
+    private static final int MAX_ADDRESS_WORDS = 14;
+
+    // Addresses consist of at most this many lines.
+    private static final int MAX_ADDRESS_LINES = 5;
+
+    // No words in an address are longer than this many characters.
+    private static final int kMaxAddressNameWordLength = 25;
+
+    // Location name should be in the first MAX_LOCATION_NAME_DISTANCE words
+    private static final int MAX_LOCATION_NAME_DISTANCE = 5;
+
+    private static final ZipRange[] sStateZipCodeRanges = {
+            new ZipRange(99, 99, -1, -1), // AK Alaska.
+            new ZipRange(35, 36, -1, -1), // AL Alabama.
+            new ZipRange(71, 72, -1, -1), // AR Arkansas.
+            new ZipRange(96, 96, -1, -1), // AS American Samoa.
+            new ZipRange(85, 86, -1, -1), // AZ Arizona.
+            new ZipRange(90, 96, -1, -1), // CA California.
+            new ZipRange(80, 81, -1, -1), // CO Colorado.
+            new ZipRange(6, 6, -1, -1), // CT Connecticut.
+            new ZipRange(20, 20, -1, -1), // DC District of Columbia.
+            new ZipRange(19, 19, -1, -1), // DE Delaware.
+            new ZipRange(32, 34, -1, -1), // FL Florida.
+            new ZipRange(96, 96, -1, -1), // FM Federated States of Micronesia.
+            new ZipRange(30, 31, -1, -1), // GA Georgia.
+            new ZipRange(96, 96, -1, -1), // GU Guam.
+            new ZipRange(96, 96, -1, -1), // HI Hawaii.
+            new ZipRange(50, 52, -1, -1), // IA Iowa.
+            new ZipRange(83, 83, -1, -1), // ID Idaho.
+            new ZipRange(60, 62, -1, -1), // IL Illinois.
+            new ZipRange(46, 47, -1, -1), // IN Indiana.
+            new ZipRange(66, 67, 73, -1), // KS Kansas.
+            new ZipRange(40, 42, -1, -1), // KY Kentucky.
+            new ZipRange(70, 71, -1, -1), // LA Louisiana.
+            new ZipRange(1, 2, -1, -1), // MA Massachusetts.
+            new ZipRange(20, 21, -1, -1), // MD Maryland.
+            new ZipRange(3, 4, -1, -1), // ME Maine.
+            new ZipRange(96, 96, -1, -1), // MH Marshall Islands.
+            new ZipRange(48, 49, -1, -1), // MI Michigan.
+            new ZipRange(55, 56, -1, -1), // MN Minnesota.
+            new ZipRange(63, 65, -1, -1), // MO Missouri.
+            new ZipRange(96, 96, -1, -1), // MP Northern Mariana Islands.
+            new ZipRange(38, 39, -1, -1), // MS Mississippi.
+            new ZipRange(55, 56, -1, -1), // MT Montana.
+            new ZipRange(27, 28, -1, -1), // NC North Carolina.
+            new ZipRange(58, 58, -1, -1), // ND North Dakota.
+            new ZipRange(68, 69, -1, -1), // NE Nebraska.
+            new ZipRange(3, 4, -1, -1), // NH New Hampshire.
+            new ZipRange(7, 8, -1, -1), // NJ New Jersey.
+            new ZipRange(87, 88, 86, -1), // NM New Mexico.
+            new ZipRange(88, 89, 96, -1), // NV Nevada.
+            new ZipRange(10, 14, 0, 6), // NY New York.
+            new ZipRange(43, 45, -1, -1), // OH Ohio.
+            new ZipRange(73, 74, -1, -1), // OK Oklahoma.
+            new ZipRange(97, 97, -1, -1), // OR Oregon.
+            new ZipRange(15, 19, -1, -1), // PA Pennsylvania.
+            new ZipRange(6, 6, 0, 9), // PR Puerto Rico.
+            new ZipRange(96, 96, -1, -1), // PW Palau.
+            new ZipRange(2, 2, -1, -1), // RI Rhode Island.
+            new ZipRange(29, 29, -1, -1), // SC South Carolina.
+            new ZipRange(57, 57, -1, -1), // SD South Dakota.
+            new ZipRange(37, 38, -1, -1), // TN Tennessee.
+            new ZipRange(75, 79, 87, 88), // TX Texas.
+            new ZipRange(84, 84, -1, -1), // UT Utah.
+            new ZipRange(22, 24, 20, -1), // VA Virginia.
+            new ZipRange(6, 9, -1, -1), // VI Virgin Islands.
+            new ZipRange(5, 5, -1, -1), // VT Vermont.
+            new ZipRange(98, 99, -1, -1), // WA Washington.
+            new ZipRange(53, 54, -1, -1), // WI Wisconsin.
+            new ZipRange(24, 26, -1, -1), // WV West Virginia.
+            new ZipRange(82, 83, -1, -1) // WY Wyoming.
+    };
+
+    // Newlines
+    private static final String NL = "\n\u000B\u000C\r\u0085\u2028\u2029";
+
+    // Space characters
+    private static final String SP = "\u0009\u0020\u00A0\u1680\u2000\u2001"
+            + "\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200A\u202F"
+            + "\u205F\u3000";
+
+    // Whitespace
+    private static final String WS = SP + NL;
+
+    // Characters that are considered word delimiters.
+    private static final String WORD_DELIM = ",*\u2022" + WS;
+
+    // Lookahead for word end.
+    private static final String WORD_END = "(?=[" + WORD_DELIM + "]|$)";
+
+    // Address words are a sequence of non-delimiter characters.
+    private static final Pattern sWordRe =
+            Pattern.compile("[^" + WORD_DELIM + "]+" + WORD_END, Pattern.CASE_INSENSITIVE);
+
+    // Characters that are considered suffix delimiters for house numbers.
+    private static final String HOUSE_POST_DELIM = ",\"'" + WS;
+
+    // Lookahead for house end.
+    private static final String HOUSE_END = "(?=[" + HOUSE_POST_DELIM + "]|$)";
+
+    // Characters that are considered prefix delimiters for house numbers.
+    private static final String HOUSE_PRE_DELIM = ":" + HOUSE_POST_DELIM;
+
+    // A house number component is "one" or a number, optionally
+    // followed by a single alphabetic character, or
+    private static final String HOUSE_COMPONENT = "(?:one|\\d+([a-z](?=[^a-z]|$)|st|nd|rd|th)?)";
+
+    // House numbers are a repetition of |HOUSE_COMPONENT|, separated by -, and followed by
+    // a delimiter character.
+    private static final Pattern sHouseNumberRe =
+            Pattern.compile(HOUSE_COMPONENT + "(?:-" + HOUSE_COMPONENT + ")*" + HOUSE_END,
+                    Pattern.CASE_INSENSITIVE);
+
+    // XXX: do we want to accept whitespace other than 0x20 in state names?
+    private static final Pattern sStateRe = Pattern.compile("(?:"
+                    + "(ak|alaska)|"
+                    + "(al|alabama)|"
+                    + "(ar|arkansas)|"
+                    + "(as|american[" + SP + "]+samoa)|"
+                    + "(az|arizona)|"
+                    + "(ca|california)|"
+                    + "(co|colorado)|"
+                    + "(ct|connecticut)|"
+                    + "(dc|district[" + SP + "]+of[" + SP + "]+columbia)|"
+                    + "(de|delaware)|"
+                    + "(fl|florida)|"
+                    + "(fm|federated[" + SP + "]+states[" + SP + "]+of[" + SP + "]+micronesia)|"
+                    + "(ga|georgia)|"
+                    + "(gu|guam)|"
+                    + "(hi|hawaii)|"
+                    + "(ia|iowa)|"
+                    + "(id|idaho)|"
+                    + "(il|illinois)|"
+                    + "(in|indiana)|"
+                    + "(ks|kansas)|"
+                    + "(ky|kentucky)|"
+                    + "(la|louisiana)|"
+                    + "(ma|massachusetts)|"
+                    + "(md|maryland)|"
+                    + "(me|maine)|"
+                    + "(mh|marshall[" + SP + "]+islands)|"
+                    + "(mi|michigan)|"
+                    + "(mn|minnesota)|"
+                    + "(mo|missouri)|"
+                    + "(mp|northern[" + SP + "]+mariana[" + SP + "]+islands)|"
+                    + "(ms|mississippi)|"
+                    + "(mt|montana)|"
+                    + "(nc|north[" + SP + "]+carolina)|"
+                    + "(nd|north[" + SP + "]+dakota)|"
+                    + "(ne|nebraska)|"
+                    + "(nh|new[" + SP + "]+hampshire)|"
+                    + "(nj|new[" + SP + "]+jersey)|"
+                    + "(nm|new[" + SP + "]+mexico)|"
+                    + "(nv|nevada)|"
+                    + "(ny|new[" + SP + "]+york)|"
+                    + "(oh|ohio)|"
+                    + "(ok|oklahoma)|"
+                    + "(or|oregon)|"
+                    + "(pa|pennsylvania)|"
+                    + "(pr|puerto[" + SP + "]+rico)|"
+                    + "(pw|palau)|"
+                    + "(ri|rhode[" + SP + "]+island)|"
+                    + "(sc|south[" + SP + "]+carolina)|"
+                    + "(sd|south[" + SP + "]+dakota)|"
+                    + "(tn|tennessee)|"
+                    + "(tx|texas)|"
+                    + "(ut|utah)|"
+                    + "(va|virginia)|"
+                    + "(vi|virgin[" + SP + "]+islands)|"
+                    + "(vt|vermont)|"
+                    + "(wa|washington)|"
+                    + "(wi|wisconsin)|"
+                    + "(wv|west[" + SP + "]+virginia)|"
+                    + "(wy|wyoming)"
+                    + ")" + WORD_END,
+            Pattern.CASE_INSENSITIVE);
+
+    private static final Pattern sLocationNameRe = Pattern.compile("(?:"
+                    + "alley|annex|arcade|ave[.]?|avenue|alameda|bayou|"
+                    + "beach|bend|bluffs?|bottom|boulevard|branch|bridge|"
+                    + "brooks?|burgs?|bypass|broadway|camino|camp|canyon|"
+                    + "cape|causeway|centers?|circles?|cliffs?|club|common|"
+                    + "corners?|course|courts?|coves?|creek|crescent|crest|"
+                    + "crossing|crossroad|curve|circulo|dale|dam|divide|"
+                    + "drives?|estates?|expressway|extensions?|falls?|ferry|"
+                    + "fields?|flats?|fords?|forest|forges?|forks?|fort|"
+                    + "freeway|gardens?|gateway|glens?|greens?|groves?|"
+                    + "harbors?|haven|heights|highway|hills?|hollow|inlet|"
+                    + "islands?|isle|junctions?|keys?|knolls?|lakes?|land|"
+                    + "landing|lane|lights?|loaf|locks?|lodge|loop|mall|"
+                    + "manors?|meadows?|mews|mills?|mission|motorway|mount|"
+                    + "mountains?|neck|orchard|oval|overpass|parks?|"
+                    + "parkways?|pass|passage|path|pike|pines?|plains?|"
+                    + "plaza|points?|ports?|prairie|privada|radial|ramp|"
+                    + "ranch|rapids?|rd[.]?|rest|ridges?|river|roads?|route|"
+                    + "row|rue|run|shoals?|shores?|skyway|springs?|spurs?|"
+                    + "squares?|station|stravenue|stream|st[.]?|streets?|"
+                    + "summit|speedway|terrace|throughway|trace|track|"
+                    + "trafficway|trail|tunnel|turnpike|underpass|unions?|"
+                    + "valleys?|viaduct|views?|villages?|ville|vista|walks?|"
+                    + "wall|ways?|wells?|xing|xrd)" + WORD_END,
+            Pattern.CASE_INSENSITIVE);
+
+    private static final Pattern sSuffixedNumberRe =
+            Pattern.compile("(\\d+)(st|nd|rd|th)", Pattern.CASE_INSENSITIVE);
+
+    private static final Pattern sZipCodeRe =
+            Pattern.compile("(?:\\d{5}(?:-\\d{4})?)" + WORD_END, Pattern.CASE_INSENSITIVE);
+
+    private static boolean checkHouseNumber(String houseNumber) {
+        // Make sure that there are at most 5 digits.
+        int digitCount = 0;
+        for (int i = 0; i < houseNumber.length(); ++i) {
+            if (Character.isDigit(houseNumber.charAt(i))) ++digitCount;
+        }
+        if (digitCount > 5) return false;
+
+        // Make sure that any ordinals are valid.
+        Matcher suffixMatcher = sSuffixedNumberRe.matcher(houseNumber);
+        while (suffixMatcher.find()) {
+            int num = Integer.parseInt(suffixMatcher.group(1));
+            if (num == 0) {
+                return false; // 0th is invalid.
+            }
+            String suffix = suffixMatcher.group(2).toLowerCase(Locale.getDefault());
+            switch (num % 10) {
+                case 1:
+                    return suffix.equals(num % 100 == 11 ? "th" : "st");
+                case 2:
+                    return suffix.equals(num % 100 == 12 ? "th" : "nd");
+                case 3:
+                    return suffix.equals(num % 100 == 13 ? "th" : "rd");
+                default:
+                    return suffix.equals("th");
+            }
+        }
+        return true;
+    }
+
+    /**
+     * Attempt to match a house number beginnning at position offset
+     * in content.  The house number must be followed by a word
+     * delimiter or the end of the string, and if offset is non-zero,
+     * then it must also be preceded by a word delimiter.
+     *
+     * @return a MatchResult if a valid house number was found.
+     */
+    private static MatchResult matchHouseNumber(String content, int offset) {
+        if (offset > 0 && HOUSE_PRE_DELIM.indexOf(content.charAt(offset - 1)) == -1) return null;
+        Matcher matcher = sHouseNumberRe.matcher(content).region(offset, content.length());
+        if (matcher.lookingAt()) {
+            MatchResult matchResult = matcher.toMatchResult();
+            if (checkHouseNumber(matchResult.group(0))) return matchResult;
+        }
+        return null;
+    }
+
+    /**
+     * Attempt to match a US state beginnning at position offset in
+     * content.  The matching state must be followed by a word
+     * delimiter or the end of the string, and if offset is non-zero,
+     * then it must also be preceded by a word delimiter.
+     *
+     * @return a MatchResult if a valid US state (or two letter code)
+     * was found.
+     */
+    private static MatchResult matchState(String content, int offset) {
+        if (offset > 0 && WORD_DELIM.indexOf(content.charAt(offset - 1)) == -1) return null;
+        Matcher stateMatcher = sStateRe.matcher(content).region(offset, content.length());
+        return stateMatcher.lookingAt() ? stateMatcher.toMatchResult() : null;
+    }
+
+    /**
+     * Test whether zipCode matches the U.S. zip code format (ddddd or
+     * ddddd-dddd) and is within the expected range, given that
+     * stateMatch is a match of sStateRe.
+     *
+     * @return true if zipCode is a valid zip code, is legal for the
+     * matched state, and is followed by a word delimiter or the end
+     * of the string.
+     */
+    private static boolean isValidZipCode(String zipCode, MatchResult stateMatch) {
+        if (stateMatch == null) return false;
+        // Work out the index of the state, based on which group matched.
+        int stateIndex = stateMatch.groupCount();
+        while (stateIndex > 0) {
+            if (stateMatch.group(stateIndex--) != null) break;
+        }
+        return sZipCodeRe.matcher(zipCode).matches()
+                && sStateZipCodeRanges[stateIndex].matches(zipCode);
+    }
+
+    /**
+     * Test whether location is one of the valid locations.
+     *
+     * @return true if location starts with a valid location name
+     * followed by a word delimiter or the end of the string.
+     */
+    private static boolean isValidLocationName(String location) {
+        return sLocationNameRe.matcher(location).matches();
+    }
+
+    /**
+     * Attempt to match a complete address in content, starting with
+     * houseNumberMatch.
+     *
+     * @param content The string to search.
+     * @param houseNumberMatch A matching house number to start extending.
+     * @return +ve: the end of the match
+     *         +ve: the position to restart searching for house numbers, negated.
+     */
+    private static int attemptMatch(String content, MatchResult houseNumberMatch) {
+        int restartPos = -1;
+        int nonZipMatch = -1;
+        int it = houseNumberMatch.end();
+        int numLines = 1;
+        boolean consecutiveHouseNumbers = true;
+        boolean foundLocationName = false;
+        int wordCount = 1;
+        String lastWord = "";
+
+        Matcher matcher = sWordRe.matcher(content);
+
+        for (; it < content.length(); lastWord = matcher.group(0), it = matcher.end()) {
+            if (!matcher.find(it)) {
+                // No more words in the input sequence.
+                return -content.length();
+            }
+            if (matcher.end() - matcher.start() > kMaxAddressNameWordLength) {
+                // Word is too long to be part of an address. Fail.
+                return -matcher.end();
+            }
+
+            // Count the number of newlines we just consumed.
+            while (it < matcher.start()) {
+                if (NL.indexOf(content.charAt(it++)) != -1) ++numLines;
+            }
+
+            // Consumed too many lines. Fail.
+            if (numLines > MAX_ADDRESS_LINES) break;
+
+            // Consumed too many words. Fail.
+            if (++wordCount > MAX_ADDRESS_WORDS) break;
+
+            if (matchHouseNumber(content, it) != null) {
+                if (consecutiveHouseNumbers && numLines > 1) {
+                    // Last line ended with a number, and this this line starts with one.
+                    // Restart at this number.
+                    return -it;
+                }
+                // Remember the position of this match as the restart position.
+                if (restartPos == -1) restartPos = it;
+                continue;
+            }
+
+            consecutiveHouseNumbers = false;
+
+            if (isValidLocationName(matcher.group(0))) {
+                foundLocationName = true;
+                continue;
+            }
+
+            if (wordCount == MAX_LOCATION_NAME_DISTANCE && !foundLocationName) {
+                // Didn't find a location name in time. Fail.
+                it = matcher.end();
+                break;
+            }
+
+            if (foundLocationName && wordCount > MIN_ADDRESS_WORDS) {
+                // We can now attempt to match a state.
+                MatchResult stateMatch = matchState(content, it);
+                if (stateMatch != null) {
+                    if (lastWord.equals("et") && stateMatch.group(0).equals("al")) {
+                        // Reject "et al" as a false postitive.
+                        it = stateMatch.end();
+                        break;
+                    }
+
+                    // At this point we've matched a state; try to match a zip code after it.
+                    Matcher zipMatcher = sWordRe.matcher(content);
+                    if (zipMatcher.find(stateMatch.end())
+                            && isValidZipCode(zipMatcher.group(0), stateMatch)) {
+                        return zipMatcher.end();
+                    }
+                    // The content ends with a state but no zip
+                    // code. This is a legal match according to the
+                    // documentation. N.B. This differs from the
+                    // original c++ implementation, which only allowed
+                    // the zip code to be optional at the end of the
+                    // string, which presumably is a bug.  Now we
+                    // prefer to find a match with a zip code, but
+                    // remember non-zip matches and return them if
+                    // necessary.
+                    nonZipMatch = stateMatch.end();
+                }
+            }
+        }
+
+        if (nonZipMatch > 0) return nonZipMatch;
+
+        return -(restartPos > 0 ? restartPos : it);
+    }
+
+    /**
+     * Return the first matching address in content.
+     *
+     * @param content The string to search.
+     * @return The first valid address, or null if no address was matched.
+     */
+    static String findAddress(String content) {
+        Matcher houseNumberMatcher = sHouseNumberRe.matcher(content);
+        int start = 0;
+        while (houseNumberMatcher.find(start)) {
+            if (checkHouseNumber(houseNumberMatcher.group(0))) {
+                start = houseNumberMatcher.start();
+                int end = attemptMatch(content, houseNumberMatcher);
+                if (end > 0) {
+                    return content.substring(start, end);
+                }
+                start = -end;
+            } else {
+                start = houseNumberMatcher.end();
+            }
+        }
+        return null;
+    }
+}
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index 5316d0d..deb94e81 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -1831,9 +1831,10 @@
      */
     @Nullable
     public static String findAddress(String addr) {
-        // TODO: Rewrite this in Java so it is not needed to start up chromium
-        // Could also be deprecated
-        return getFactory().getStatics().findAddress(addr);
+        if (addr == null) {
+            throw new NullPointerException("addr is null");
+        }
+        return FindAddress.findAddress(addr);
     }
 
     /**
diff --git a/core/java/android/widget/MediaController2.java b/core/java/android/widget/MediaController2.java
new file mode 100644
index 0000000..9035137
--- /dev/null
+++ b/core/java/android/widget/MediaController2.java
@@ -0,0 +1,236 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.widget;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Context;
+import android.graphics.Canvas;
+import android.media.session.MediaController;
+import android.media.update.ApiLoader;
+import android.media.update.MediaController2Provider;
+import android.media.update.ViewProvider;
+import android.util.AttributeSet;
+import android.view.KeyEvent;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.View.OnClickListener;
+
+/**
+ * TODO PUBLIC API
+ * @hide
+ */
+public class MediaController2 extends FrameLayout {
+    private final MediaController2Provider mProvider;
+
+    public MediaController2(@NonNull Context context) {
+        this(context, null);
+    }
+
+    public MediaController2(@NonNull Context context, @Nullable AttributeSet attrs) {
+        this(context, attrs, 0);
+    }
+
+    public MediaController2(@NonNull Context context, @Nullable AttributeSet attrs,
+                            int defStyleAttr) {
+        this(context, attrs, defStyleAttr, 0);
+    }
+
+    public MediaController2(@NonNull Context context, @Nullable AttributeSet attrs,
+                            int defStyleAttr, int defStyleRes) {
+        super(context, attrs, defStyleAttr, defStyleRes);
+
+        mProvider = ApiLoader.getProvider(context)
+                .createMediaController2(this, new SuperProvider());
+    }
+
+    public void setController(MediaController controller) {
+        mProvider.setController_impl(controller);
+    }
+
+    public void setAnchorView(View view) {
+        mProvider.setAnchorView_impl(view);
+    }
+
+    public void show() {
+        mProvider.show_impl();
+    }
+
+    public void show(int timeout) {
+        mProvider.show_impl(timeout);
+    }
+
+    public boolean isShowing() {
+        return mProvider.isShowing_impl();
+    }
+
+    public void hide() {
+        mProvider.hide_impl();
+    }
+
+    public void setPrevNextListeners(OnClickListener next, OnClickListener prev) {
+        mProvider.setPrevNextListeners_impl(next, prev);
+    }
+
+    public void showCCButton() {
+        mProvider.showCCButton_impl();
+    }
+
+    public boolean isPlaying() {
+        return mProvider.isPlaying_impl();
+    }
+
+    public int getCurrentPosition() {
+        return mProvider.getCurrentPosition_impl();
+    }
+
+    public int getBufferPercentage() {
+        return mProvider.getBufferPercentage_impl();
+    }
+
+    public boolean canPause() {
+        return mProvider.canPause_impl();
+    }
+
+    public boolean canSeekBackward() {
+        return mProvider.canSeekBackward_impl();
+    }
+
+    public boolean canSeekForward() {
+        return mProvider.canSeekForward_impl();
+    }
+
+    public void showSubtitle() {
+        mProvider.showSubtitle_impl();
+    }
+
+    public void hideSubtitle() {
+        mProvider.hideSubtitle_impl();
+    }
+
+    @Override
+    protected void onAttachedToWindow() {
+        mProvider.onAttachedToWindow_impl();
+    }
+
+    @Override
+    protected void onDetachedFromWindow() {
+        mProvider.onDetachedFromWindow_impl();
+    }
+
+    @Override
+    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+        mProvider.onLayout_impl(changed, left, top, right, bottom);
+    }
+
+    @Override
+    public void draw(Canvas canvas) {
+        mProvider.draw_impl(canvas);
+    }
+
+    @Override
+    public CharSequence getAccessibilityClassName() {
+        return mProvider.getAccessibilityClassName_impl();
+    }
+
+    @Override
+    public boolean onTouchEvent(MotionEvent ev) {
+        return mProvider.onTouchEvent_impl(ev);
+    }
+
+    @Override
+    public boolean onTrackballEvent(MotionEvent ev) {
+        return mProvider.onTrackballEvent_impl(ev);
+    }
+
+    @Override
+    public boolean onKeyDown(int keyCode, KeyEvent event) {
+        return mProvider.onKeyDown_impl(keyCode, event);
+    }
+
+    @Override
+    public void onFinishInflate() {
+        mProvider.onFinishInflate_impl();
+    }
+
+    @Override
+    public boolean dispatchKeyEvent(KeyEvent event) {
+        return mProvider.dispatchKeyEvent_impl(event);
+    }
+
+    @Override
+    public void setEnabled(boolean enabled) {
+        mProvider.setEnabled_impl(enabled);
+    }
+
+    private class SuperProvider implements ViewProvider {
+        @Override
+        public void onAttachedToWindow_impl() {
+            MediaController2.super.onAttachedToWindow();
+        }
+
+        @Override
+        public void onDetachedFromWindow_impl() {
+            MediaController2.super.onDetachedFromWindow();
+        }
+
+        @Override
+        public void onLayout_impl(boolean changed, int left, int top, int right, int bottom) {
+            MediaController2.super.onLayout(changed, left, top, right, bottom);
+        }
+
+        @Override
+        public void draw_impl(Canvas canvas) {
+            MediaController2.super.draw(canvas);
+        }
+
+        @Override
+        public CharSequence getAccessibilityClassName_impl() {
+            return MediaController2.super.getAccessibilityClassName();
+        }
+
+        @Override
+        public boolean onTouchEvent_impl(MotionEvent ev) {
+            return MediaController2.super.onTouchEvent(ev);
+        }
+
+        @Override
+        public boolean onTrackballEvent_impl(MotionEvent ev) {
+            return MediaController2.super.onTrackballEvent(ev);
+        }
+
+        @Override
+        public boolean onKeyDown_impl(int keyCode, KeyEvent event) {
+            return MediaController2.super.onKeyDown(keyCode, event);
+        }
+
+        @Override
+        public void onFinishInflate_impl() {
+            MediaController2.super.onFinishInflate();
+        }
+
+        @Override
+        public boolean dispatchKeyEvent_impl(KeyEvent event) {
+            return MediaController2.super.dispatchKeyEvent(event);
+        }
+
+        @Override
+        public void setEnabled_impl(boolean enabled) {
+            MediaController2.super.setEnabled(enabled);
+        }
+    }
+}
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index dc54127..ff374fa 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -788,7 +788,7 @@
 
     // Indicates whether the text was set statically or dynamically, so it can be used to
     // sanitize autofill requests.
-    private boolean mSetFromXmlOrResourceId = false;
+    private boolean mTextSetFromXmlOrResourceId = false;
     // Resource id used to set the text - used for autofill purposes.
     private @StringRes int mTextId = ResourceId.ID_NULL;
 
@@ -930,7 +930,7 @@
         int n = a.getIndexCount();
 
         // Must set id in a temporary variable because it will be reset by setText()
-        boolean setFromXml = false;
+        boolean textIsSetFromXml = false;
         for (int i = 0; i < n; i++) {
             int attr = a.getIndex(i);
 
@@ -1072,7 +1072,7 @@
                     break;
 
                 case com.android.internal.R.styleable.TextView_text:
-                    setFromXml = true;
+                    textIsSetFromXml = true;
                     mTextId = a.getResourceId(attr, ResourceId.ID_NULL);
                     text = a.getText(attr);
                     break;
@@ -1465,8 +1465,8 @@
         }
 
         setText(text, bufferType);
-        if (setFromXml) {
-            mSetFromXmlOrResourceId = true;
+        if (textIsSetFromXml) {
+            mTextSetFromXmlOrResourceId = true;
         }
 
         if (hint != null) setHint(hint);
@@ -5335,7 +5335,7 @@
 
     private void setText(CharSequence text, BufferType type,
                          boolean notifyBefore, int oldlen) {
-        mSetFromXmlOrResourceId = false;
+        mTextSetFromXmlOrResourceId = false;
         if (text == null) {
             text = "";
         }
@@ -5573,7 +5573,7 @@
     @android.view.RemotableViewMethod
     public final void setText(@StringRes int resid) {
         setText(getContext().getResources().getText(resid));
-        mSetFromXmlOrResourceId = true;
+        mTextSetFromXmlOrResourceId = true;
         mTextId = resid;
     }
 
@@ -5601,7 +5601,7 @@
      */
     public final void setText(@StringRes int resid, BufferType type) {
         setText(getContext().getResources().getText(resid), type);
-        mSetFromXmlOrResourceId = true;
+        mTextSetFromXmlOrResourceId = true;
         mTextId = resid;
     }
 
@@ -10293,7 +10293,7 @@
         final boolean isPassword = hasPasswordTransformationMethod()
                 || isPasswordInputType(getInputType());
         if (forAutofill) {
-            structure.setDataIsSensitive(!mSetFromXmlOrResourceId);
+            structure.setDataIsSensitive(!mTextSetFromXmlOrResourceId);
             if (mTextId != ResourceId.ID_NULL) {
                 try {
                     structure.setTextIdEntry(getResources().getResourceEntryName(mTextId));
diff --git a/core/java/com/android/internal/app/ResolverComparator.java b/core/java/com/android/internal/app/ResolverComparator.java
index 77cfc2fc..96d3baf 100644
--- a/core/java/com/android/internal/app/ResolverComparator.java
+++ b/core/java/com/android/internal/app/ResolverComparator.java
@@ -411,6 +411,9 @@
             mContext.unbindService(mConnection);
             mConnection.destroy();
         }
+        if (mAfterCompute != null) {
+            mAfterCompute.afterCompute();
+        }
         if (DEBUG) {
             Log.d(TAG, "Unbinded Resolver Ranker.");
         }
@@ -573,7 +576,6 @@
             if (DEBUG) {
                 Log.d(TAG, "Has not found valid ResolverRankerService; Skip Prediction");
             }
-            return;
         } else {
             try {
                 mConnectSignal.await(CONNECTION_COST_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS);
diff --git a/core/java/com/android/internal/app/procstats/ProcessState.java b/core/java/com/android/internal/app/procstats/ProcessState.java
index efc9c02..79f4c1b 100644
--- a/core/java/com/android/internal/app/procstats/ProcessState.java
+++ b/core/java/com/android/internal/app/procstats/ProcessState.java
@@ -153,7 +153,6 @@
     private int mNumActiveServices;
     private int mNumStartedServices;
 
-    private int mNumExcessiveWake;
     private int mNumExcessiveCpu;
 
     private int mNumCachedKill;
@@ -470,9 +469,23 @@
         }
     }
 
-    public void addPss(long pss, long uss, boolean always,
+    public void addPss(long pss, long uss, boolean always, int type, long duration,
             ArrayMap<String, ProcessStateHolder> pkgList) {
         ensureNotDead();
+        switch (type) {
+            case ProcessStats.ADD_PSS_INTERNAL:
+                mStats.mInternalPssCount++;
+                mStats.mInternalPssTime += duration;
+                break;
+            case ProcessStats.ADD_PSS_EXTERNAL:
+                mStats.mExternalPssCount++;
+                mStats.mExternalPssTime += duration;
+                break;
+            case ProcessStats.ADD_PSS_EXTERNAL_SLOW:
+                mStats.mExternalSlowPssCount++;
+                mStats.mExternalSlowPssTime += duration;
+                break;
+        }
         if (!always) {
             if (mLastPssState == mCurState && SystemClock.uptimeMillis()
                     < (mLastPssTime+(30*1000))) {
diff --git a/core/java/com/android/internal/app/procstats/ProcessStats.java b/core/java/com/android/internal/app/procstats/ProcessStats.java
index 96ba2b0..35b2dd2 100644
--- a/core/java/com/android/internal/app/procstats/ProcessStats.java
+++ b/core/java/com/android/internal/app/procstats/ProcessStats.java
@@ -134,6 +134,10 @@
     public static final int FLAG_SHUTDOWN = 1<<1;
     public static final int FLAG_SYSPROPS = 1<<2;
 
+    public static final int ADD_PSS_INTERNAL = 0;
+    public static final int ADD_PSS_EXTERNAL = 1;
+    public static final int ADD_PSS_EXTERNAL_SLOW = 2;
+
     public static final int[] ALL_MEM_ADJ = new int[] { ADJ_MEM_FACTOR_NORMAL,
             ADJ_MEM_FACTOR_MODERATE, ADJ_MEM_FACTOR_LOW, ADJ_MEM_FACTOR_CRITICAL };
 
@@ -158,7 +162,7 @@
     };
 
     // Current version of the parcel format.
-    private static final int PARCEL_VERSION = 23;
+    private static final int PARCEL_VERSION = 24;
     // In-memory Parcel magic number, used to detect attempts to unmarshall bad data
     private static final int MAGIC = 0x50535454;
 
@@ -183,6 +187,18 @@
 
     boolean mHasSwappedOutPss;
 
+    // Count and total time expended doing "quick" pss computations for internal use.
+    public long mInternalPssCount;
+    public long mInternalPssTime;
+
+    // Count and total time expended doing "quick" pss computations due to external requests.
+    public long mExternalPssCount;
+    public long mExternalPssTime;
+
+    // Count and total time expended doing full/slow pss computations due to external requests.
+    public long mExternalSlowPssCount;
+    public long mExternalSlowPssTime;
+
     public final SparseMappingTable mTableData = new SparseMappingTable();
 
     public final long[] mSysMemUsageArgs = new long[SYS_MEM_USAGE_COUNT];
@@ -302,6 +318,13 @@
         mTimePeriodEndRealtime += other.mTimePeriodEndRealtime - other.mTimePeriodStartRealtime;
         mTimePeriodEndUptime += other.mTimePeriodEndUptime - other.mTimePeriodStartUptime;
 
+        mInternalPssCount += other.mInternalPssCount;
+        mInternalPssTime += other.mInternalPssTime;
+        mExternalPssCount += other.mExternalPssCount;
+        mExternalPssTime += other.mExternalPssTime;
+        mExternalSlowPssCount += other.mExternalSlowPssCount;
+        mExternalSlowPssTime += other.mExternalSlowPssTime;
+
         mHasSwappedOutPss |= other.mHasSwappedOutPss;
     }
 
@@ -500,6 +523,12 @@
         buildTimePeriodStartClockStr();
         mTimePeriodStartRealtime = mTimePeriodEndRealtime = SystemClock.elapsedRealtime();
         mTimePeriodStartUptime = mTimePeriodEndUptime = SystemClock.uptimeMillis();
+        mInternalPssCount = 0;
+        mInternalPssTime = 0;
+        mExternalPssCount = 0;
+        mExternalPssTime = 0;
+        mExternalSlowPssCount = 0;
+        mExternalSlowPssTime = 0;
         mTableData.reset();
         Arrays.fill(mMemFactorDurations, 0);
         mSysMemUsage.resetTable();
@@ -760,6 +789,12 @@
         out.writeLong(mTimePeriodEndRealtime);
         out.writeLong(mTimePeriodStartUptime);
         out.writeLong(mTimePeriodEndUptime);
+        out.writeLong(mInternalPssCount);
+        out.writeLong(mInternalPssTime);
+        out.writeLong(mExternalPssCount);
+        out.writeLong(mExternalPssTime);
+        out.writeLong(mExternalSlowPssCount);
+        out.writeLong(mExternalSlowPssTime);
         out.writeString(mRuntime);
         out.writeInt(mHasSwappedOutPss ? 1 : 0);
         out.writeInt(mFlags);
@@ -928,6 +963,12 @@
         mTimePeriodEndRealtime = in.readLong();
         mTimePeriodStartUptime = in.readLong();
         mTimePeriodEndUptime = in.readLong();
+        mInternalPssCount = in.readLong();
+        mInternalPssTime = in.readLong();
+        mExternalPssCount = in.readLong();
+        mExternalPssTime = in.readLong();
+        mExternalSlowPssCount = in.readLong();
+        mExternalSlowPssTime = in.readLong();
         mRuntime = in.readString();
         mHasSwappedOutPss = in.readInt() != 0;
         mFlags = in.readInt();
@@ -1484,9 +1525,31 @@
                 totalMem.processStateWeight[STATE_SERVICE_RESTARTING], totalMem.totalTime, totalPss,
                 totalMem.processStateSamples[STATE_SERVICE_RESTARTING]);
         pw.println();
+        pw.println("PSS collection stats:");
+        pw.print("  Internal: ");
+        pw.print(mInternalPssCount);
+        pw.print("x over ");
+        TimeUtils.formatDuration(mInternalPssTime, pw);
+        pw.println();
+        pw.print("  External: ");
+        pw.print(mExternalPssCount);
+        pw.print("x over ");
+        TimeUtils.formatDuration(mExternalPssTime, pw);
+        pw.println();
+        pw.print("  External Slow: ");
+        pw.print(mExternalSlowPssCount);
+        pw.print("x over ");
+        TimeUtils.formatDuration(mExternalSlowPssTime, pw);
+        pw.println();
+        pw.println();
         pw.print("          Start time: ");
         pw.print(DateFormat.format("yyyy-MM-dd HH:mm:ss", mTimePeriodStartClock));
         pw.println();
+        pw.print("        Total uptime: ");
+        TimeUtils.formatDuration(
+                (mRunning ? SystemClock.uptimeMillis() : mTimePeriodEndUptime)
+                        - mTimePeriodStartUptime, pw);
+        pw.println();
         pw.print("  Total elapsed time: ");
         TimeUtils.formatDuration(
                 (mRunning ? SystemClock.elapsedRealtime() : mTimePeriodEndRealtime)
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index c420e6d..b8ff9e4 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -21,10 +21,13 @@
 import android.app.ActivityManager;
 import android.bluetooth.BluetoothActivityEnergyInfo;
 import android.bluetooth.UidTraffic;
+import android.content.ContentResolver;
 import android.content.Context;
 import android.content.Intent;
+import android.database.ContentObserver;
 import android.net.ConnectivityManager;
 import android.net.NetworkStats;
+import android.net.Uri;
 import android.net.wifi.WifiActivityEnergyInfo;
 import android.net.wifi.WifiManager;
 import android.os.BatteryManager;
@@ -46,6 +49,7 @@
 import android.os.UserHandle;
 import android.os.WorkSource;
 import android.os.WorkSource.WorkChain;
+import android.provider.Settings;
 import android.telephony.DataConnectionRealTimeInfo;
 import android.telephony.ModemActivityInfo;
 import android.telephony.ServiceState;
@@ -54,6 +58,7 @@
 import android.text.TextUtils;
 import android.util.ArrayMap;
 import android.util.IntArray;
+import android.util.KeyValueListParser;
 import android.util.Log;
 import android.util.LogWriter;
 import android.util.LongSparseArray;
@@ -124,7 +129,7 @@
     private static final int MAGIC = 0xBA757475; // 'BATSTATS'
 
     // Current on-disk Parcel version
-    private static final int VERSION = 172 + (USE_OLD_HISTORY ? 1000 : 0);
+    private static final int VERSION = 173 + (USE_OLD_HISTORY ? 1000 : 0);
 
     // Maximum number of items we will record in the history.
     private static final int MAX_HISTORY_ITEMS;
@@ -289,12 +294,21 @@
     /**
      * Update per-freq cpu times for all the uids in {@link #mPendingUids}.
      */
-    public void updateProcStateCpuTimes() {
+    public void updateProcStateCpuTimes(boolean onBattery, boolean onBatteryScreenOff) {
         final SparseIntArray uidStates;
         synchronized (BatteryStatsImpl.this) {
+            if (!mConstants.TRACK_CPU_TIMES_BY_PROC_STATE) {
+                return;
+            }
             if(!initKernelSingleUidTimeReaderLocked()) {
                 return;
             }
+            // If the KernelSingleUidTimeReader has stale cpu times, then we shouldn't try to
+            // compute deltas since it might result in mis-attributing cpu times to wrong states.
+            if (mKernelSingleUidTimeReader.hasStaleData()) {
+                mPendingUids.clear();
+                return;
+            }
 
             if (mPendingUids.size() == 0) {
                 return;
@@ -307,7 +321,6 @@
             final int procState = uidStates.valueAt(i);
             final int[] isolatedUids;
             final Uid u;
-            final boolean onBattery;
             synchronized (BatteryStatsImpl.this) {
                 // It's possible that uid no longer exists and any internal references have
                 // already been deleted, so using {@link #getAvailableUidStatsLocked} to avoid
@@ -324,7 +337,6 @@
                         isolatedUids[j] = u.mChildUids.get(j);
                     }
                 }
-                onBattery = mOnBatteryInternal;
             }
             long[] cpuTimesMs = mKernelSingleUidTimeReader.readDeltaMs(uid);
             if (isolatedUids != null) {
@@ -335,27 +347,45 @@
             }
             if (onBattery && cpuTimesMs != null) {
                 synchronized (BatteryStatsImpl.this) {
-                    u.addProcStateTimesMs(procState, cpuTimesMs);
-                    u.addProcStateScreenOffTimesMs(procState, cpuTimesMs);
+                    u.addProcStateTimesMs(procState, cpuTimesMs, onBattery);
+                    u.addProcStateScreenOffTimesMs(procState, cpuTimesMs, onBatteryScreenOff);
                 }
             }
         }
     }
 
+    public void copyFromAllUidsCpuTimes() {
+        synchronized (BatteryStatsImpl.this) {
+            copyFromAllUidsCpuTimes(
+                    mOnBatteryTimeBase.isRunning(), mOnBatteryScreenOffTimeBase.isRunning());
+        }
+    }
+
     /**
      * When the battery/screen state changes, we don't attribute the cpu times to any process
      * but we still need to snapshots of all uids to get correct deltas later on. Since we
      * already read this data for updating per-freq cpu times, we can use the same data for
      * per-procstate cpu times.
      */
-    public void copyFromAllUidsCpuTimes() {
+    public void copyFromAllUidsCpuTimes(boolean onBattery, boolean onBatteryScreenOff) {
         synchronized (BatteryStatsImpl.this) {
+            if (!mConstants.TRACK_CPU_TIMES_BY_PROC_STATE) {
+                return;
+            }
             if(!initKernelSingleUidTimeReaderLocked()) {
                 return;
             }
 
             final SparseArray<long[]> allUidCpuFreqTimesMs =
                     mKernelUidCpuFreqTimeReader.getAllUidCpuFreqTimeMs();
+            // If the KernelSingleUidTimeReader has stale cpu times, then we shouldn't try to
+            // compute deltas since it might result in mis-attributing cpu times to wrong states.
+            if (mKernelSingleUidTimeReader.hasStaleData()) {
+                mKernelSingleUidTimeReader.setAllUidsCpuTimesMs(allUidCpuFreqTimesMs);
+                mKernelSingleUidTimeReader.markDataAsStale(false);
+                mPendingUids.clear();
+                return;
+            }
             for (int i = allUidCpuFreqTimesMs.size() - 1; i >= 0; --i) {
                 final int uid = allUidCpuFreqTimesMs.keyAt(i);
                 final Uid u = getAvailableUidStatsLocked(mapUid(uid));
@@ -368,7 +398,7 @@
                 }
                 final long[] deltaTimesMs = mKernelSingleUidTimeReader.computeDelta(
                         uid, cpuTimesMs.clone());
-                if (mOnBatteryInternal && deltaTimesMs != null) {
+                if (onBattery && deltaTimesMs != null) {
                     final int procState;
                     final int idx = mPendingUids.indexOfKey(uid);
                     if (idx >= 0) {
@@ -378,8 +408,8 @@
                         procState = u.mProcessState;
                     }
                     if (procState >= 0 && procState < Uid.NUM_PROCESS_STATE) {
-                        u.addProcStateTimesMs(procState, deltaTimesMs);
-                        u.addProcStateScreenOffTimesMs(procState, deltaTimesMs);
+                        u.addProcStateTimesMs(procState, deltaTimesMs, onBattery);
+                        u.addProcStateScreenOffTimesMs(procState, deltaTimesMs, onBatteryScreenOff);
                     }
                 }
             }
@@ -443,8 +473,9 @@
 
         Future<?> scheduleSync(String reason, int flags);
         Future<?> scheduleCpuSyncDueToRemovedUid(int uid);
-        Future<?> scheduleReadProcStateCpuTimes();
-        Future<?> scheduleCopyFromAllUidsCpuTimes();
+        Future<?> scheduleReadProcStateCpuTimes(boolean onBattery, boolean onBatteryScreenOff);
+        Future<?> scheduleCopyFromAllUidsCpuTimes(boolean onBattery, boolean onBatteryScreenOff);
+        Future<?> scheduleCpuSyncDueToSettingChange();
     }
 
     public Handler mHandler;
@@ -807,6 +838,9 @@
     @VisibleForTesting
     protected PowerProfile mPowerProfile;
 
+    @GuardedBy("this")
+    private final Constants mConstants;
+
     /*
      * Holds a SamplingTimer associated with each Resource Power Manager state and voter,
      * recording their times when on-battery (regardless of screen state).
@@ -895,6 +929,7 @@
         mHandler = null;
         mPlatformIdleStateCallback = null;
         mUserInfoProvider = null;
+        mConstants = new Constants(mHandler);
         clearHistoryLocked();
     }
 
@@ -1231,12 +1266,10 @@
         public long[] mCounts;
         public long[] mLoadedCounts;
         public long[] mUnpluggedCounts;
-        public long[] mPluggedCounts;
 
         private LongSamplingCounterArray(TimeBase timeBase, Parcel in) {
             mTimeBase = timeBase;
-            mPluggedCounts = in.createLongArray();
-            mCounts = copyArray(mPluggedCounts, mCounts);
+            mCounts = in.createLongArray();
             mLoadedCounts = in.createLongArray();
             mUnpluggedCounts = in.createLongArray();
             timeBase.add(this);
@@ -1255,17 +1288,16 @@
 
         @Override
         public void onTimeStarted(long elapsedRealTime, long baseUptime, long baseRealtime) {
-            mUnpluggedCounts = copyArray(mPluggedCounts, mUnpluggedCounts);
+            mUnpluggedCounts = copyArray(mCounts, mUnpluggedCounts);
         }
 
         @Override
         public void onTimeStopped(long elapsedRealtime, long baseUptime, long baseRealtime) {
-            mPluggedCounts = copyArray(mCounts, mPluggedCounts);
         }
 
         @Override
         public long[] getCountsLocked(int which) {
-            long[] val = copyArray(mTimeBase.isRunning() ? mCounts : mPluggedCounts, null);
+            long[] val = copyArray(mCounts, null);
             if (which == STATS_SINCE_UNPLUGGED) {
                 subtract(val, mUnpluggedCounts);
             } else if (which != STATS_SINCE_CHARGED) {
@@ -1278,15 +1310,18 @@
         public void logState(Printer pw, String prefix) {
             pw.println(prefix + "mCounts=" + Arrays.toString(mCounts)
                     + " mLoadedCounts=" + Arrays.toString(mLoadedCounts)
-                    + " mUnpluggedCounts=" + Arrays.toString(mUnpluggedCounts)
-                    + " mPluggedCounts=" + Arrays.toString(mPluggedCounts));
+                    + " mUnpluggedCounts=" + Arrays.toString(mUnpluggedCounts));
         }
 
         public void addCountLocked(long[] counts) {
+            addCountLocked(counts, mTimeBase.isRunning());
+        }
+
+        public void addCountLocked(long[] counts, boolean isRunning) {
             if (counts == null) {
                 return;
             }
-            if (mTimeBase.isRunning()) {
+            if (isRunning) {
                 if (mCounts == null) {
                     mCounts = new long[counts.length];
                 }
@@ -1306,7 +1341,6 @@
         public void reset(boolean detachIfReset) {
             fillArray(mCounts, 0);
             fillArray(mLoadedCounts, 0);
-            fillArray(mPluggedCounts, 0);
             fillArray(mUnpluggedCounts, 0);
             if (detachIfReset) {
                 detach();
@@ -1325,7 +1359,6 @@
             mCounts = in.createLongArray();
             mLoadedCounts = copyArray(mCounts, mLoadedCounts);
             mUnpluggedCounts = copyArray(mCounts, mUnpluggedCounts);
-            mPluggedCounts = copyArray(mCounts, mPluggedCounts);
         }
 
         public static void writeToParcel(Parcel out, LongSamplingCounterArray counterArray) {
@@ -3783,7 +3816,8 @@
                         + " and battery is " + (unplugged ? "on" : "off"));
             }
             updateCpuTimeLocked();
-            mExternalSync.scheduleCopyFromAllUidsCpuTimes();
+            mExternalSync.scheduleCopyFromAllUidsCpuTimes(mOnBatteryTimeBase.isRunning(),
+                    mOnBatteryScreenOffTimeBase.isRunning());
 
             mOnBatteryTimeBase.setRunning(unplugged, uptime, realtime);
             if (updateOnBatteryTimeBase) {
@@ -6648,7 +6682,7 @@
             return null;
         }
 
-        private void addProcStateTimesMs(int procState, long[] cpuTimesMs) {
+        private void addProcStateTimesMs(int procState, long[] cpuTimesMs, boolean onBattery) {
             if (mProcStateTimeMs == null) {
                 mProcStateTimeMs = new LongSamplingCounterArray[NUM_PROCESS_STATE];
             }
@@ -6657,10 +6691,11 @@
                 mProcStateTimeMs[procState] = new LongSamplingCounterArray(
                         mBsi.mOnBatteryTimeBase);
             }
-            mProcStateTimeMs[procState].addCountLocked(cpuTimesMs);
+            mProcStateTimeMs[procState].addCountLocked(cpuTimesMs, onBattery);
         }
 
-        private void addProcStateScreenOffTimesMs(int procState, long[] cpuTimesMs) {
+        private void addProcStateScreenOffTimesMs(int procState, long[] cpuTimesMs,
+                boolean onBatteryScreenOff) {
             if (mProcStateScreenOffTimeMs == null) {
                 mProcStateScreenOffTimeMs = new LongSamplingCounterArray[NUM_PROCESS_STATE];
             }
@@ -6669,7 +6704,7 @@
                 mProcStateScreenOffTimeMs[procState] = new LongSamplingCounterArray(
                         mBsi.mOnBatteryScreenOffTimeBase);
             }
-            mProcStateScreenOffTimeMs[procState].addCountLocked(cpuTimesMs);
+            mProcStateScreenOffTimeMs[procState].addCountLocked(cpuTimesMs, onBatteryScreenOff);
         }
 
         @Override
@@ -9406,9 +9441,11 @@
                 if (mProcessState != ActivityManager.PROCESS_STATE_NONEXISTENT) {
                     mProcessStateTimer[mProcessState].stopRunningLocked(elapsedRealtimeMs);
 
-                    if (mBsi.mPerProcStateCpuTimesAvailable) {
+                    if (mBsi.trackPerProcStateCpuTimes()) {
                         if (mBsi.mPendingUids.size() == 0) {
-                            mBsi.mExternalSync.scheduleReadProcStateCpuTimes();
+                            mBsi.mExternalSync.scheduleReadProcStateCpuTimes(
+                                    mBsi.mOnBatteryTimeBase.isRunning(),
+                                    mBsi.mOnBatteryScreenOffTimeBase.isRunning());
                         }
                         if (mBsi.mPendingUids.indexOfKey(mUid) < 0
                                 || ArrayUtils.contains(CRITICAL_PROC_STATES, mProcessState)) {
@@ -9758,6 +9795,7 @@
         mCheckinFile = new AtomicFile(new File(systemDir, "batterystats-checkin.bin"));
         mDailyFile = new AtomicFile(new File(systemDir, "batterystats-daily.xml"));
         mHandler = new MyHandler(handler.getLooper());
+        mConstants = new Constants(mHandler);
         mStartCount++;
         mScreenOnTimer = new StopwatchTimer(mClocks, null, -1, null, mOnBatteryTimeBase);
         mScreenDozeTimer = new StopwatchTimer(mClocks, null, -1, null, mOnBatteryTimeBase);
@@ -9853,6 +9891,7 @@
         mDailyFile = null;
         mHandler = null;
         mExternalSync = null;
+        mConstants = new Constants(mHandler);
         clearHistoryLocked();
         readFromParcel(p);
         mPlatformIdleStateCallback = null;
@@ -12606,6 +12645,78 @@
         mShuttingDown = true;
     }
 
+    public boolean trackPerProcStateCpuTimes() {
+        return mConstants.TRACK_CPU_TIMES_BY_PROC_STATE && mPerProcStateCpuTimesAvailable;
+    }
+
+    public void systemServicesReady(Context context) {
+        mConstants.startObserving(context.getContentResolver());
+    }
+
+    @VisibleForTesting
+    public final class Constants extends ContentObserver {
+        public static final String KEY_TRACK_CPU_TIMES_BY_PROC_STATE
+                = "track_cpu_times_by_proc_state";
+
+        private static final boolean DEFAULT_TRACK_CPU_TIMES_BY_PROC_STATE = true;
+
+        public boolean TRACK_CPU_TIMES_BY_PROC_STATE = DEFAULT_TRACK_CPU_TIMES_BY_PROC_STATE;
+
+        private ContentResolver mResolver;
+        private final KeyValueListParser mParser = new KeyValueListParser(',');
+
+        public Constants(Handler handler) {
+            super(handler);
+        }
+
+        public void startObserving(ContentResolver resolver) {
+            mResolver = resolver;
+            mResolver.registerContentObserver(
+                    Settings.Global.getUriFor(Settings.Global.BATTERY_STATS_CONSTANTS),
+                    false /* notifyForDescendants */, this);
+            updateConstants();
+        }
+
+        @Override
+        public void onChange(boolean selfChange, Uri uri) {
+            updateConstants();
+        }
+
+        private void updateConstants() {
+            synchronized (BatteryStatsImpl.this) {
+                try {
+                    mParser.setString(Settings.Global.getString(mResolver,
+                            Settings.Global.BATTERY_STATS_CONSTANTS));
+                } catch (IllegalArgumentException e) {
+                    // Failed to parse the settings string, log this and move on
+                    // with defaults.
+                    Slog.e(TAG, "Bad batterystats settings", e);
+                }
+
+                updateTrackCpuTimesByProcStateLocked(TRACK_CPU_TIMES_BY_PROC_STATE,
+                        mParser.getBoolean(KEY_TRACK_CPU_TIMES_BY_PROC_STATE,
+                                DEFAULT_TRACK_CPU_TIMES_BY_PROC_STATE));
+            }
+        }
+
+        private void updateTrackCpuTimesByProcStateLocked(boolean wasEnabled, boolean isEnabled) {
+            TRACK_CPU_TIMES_BY_PROC_STATE = isEnabled;
+            if (isEnabled && !wasEnabled) {
+                mKernelSingleUidTimeReader.markDataAsStale(true);
+                mExternalSync.scheduleCpuSyncDueToSettingChange();
+            }
+        }
+
+        public void dumpLocked(PrintWriter pw) {
+            pw.print(KEY_TRACK_CPU_TIMES_BY_PROC_STATE); pw.print("=");
+            pw.println(TRACK_CPU_TIMES_BY_PROC_STATE);
+        }
+    }
+
+    public void dumpConstantsLocked(PrintWriter pw) {
+        mConstants.dumpLocked(pw);
+    }
+
     Parcel mPendingWrite = null;
     final ReentrantLock mWriteLock = new ReentrantLock();
 
diff --git a/core/java/com/android/internal/os/KernelSingleUidTimeReader.java b/core/java/com/android/internal/os/KernelSingleUidTimeReader.java
index ca635a4..ebeb24c4 100644
--- a/core/java/com/android/internal/os/KernelSingleUidTimeReader.java
+++ b/core/java/com/android/internal/os/KernelSingleUidTimeReader.java
@@ -46,12 +46,14 @@
     private final int mCpuFreqsCount;
 
     @GuardedBy("this")
-    private final SparseArray<long[]> mLastUidCpuTimeMs = new SparseArray<>();
+    private SparseArray<long[]> mLastUidCpuTimeMs = new SparseArray<>();
 
     @GuardedBy("this")
     private int mReadErrorCounter;
     @GuardedBy("this")
     private boolean mSingleUidCpuTimesAvailable = true;
+    @GuardedBy("this")
+    private boolean mHasStaleData;
 
     private final Injector mInjector;
 
@@ -166,6 +168,30 @@
         return deltaTimesMs;
     }
 
+    public void markDataAsStale(boolean hasStaleData) {
+        synchronized (this) {
+            mHasStaleData = hasStaleData;
+        }
+    }
+
+    public boolean hasStaleData() {
+        synchronized (this) {
+            return mHasStaleData;
+        }
+    }
+
+    public void setAllUidsCpuTimesMs(SparseArray<long[]> allUidsCpuTimesMs) {
+        synchronized (this) {
+            mLastUidCpuTimeMs.clear();
+            for (int i = allUidsCpuTimesMs.size() - 1; i >= 0; --i) {
+                final long[] cpuTimesMs = allUidsCpuTimesMs.valueAt(i);
+                if (cpuTimesMs != null) {
+                    mLastUidCpuTimeMs.put(allUidsCpuTimesMs.keyAt(i), cpuTimesMs.clone());
+                }
+            }
+        }
+    }
+
     public void removeUid(int uid) {
         synchronized (this) {
             mLastUidCpuTimeMs.delete(uid);
diff --git a/core/java/com/android/internal/os/Zygote.java b/core/java/com/android/internal/os/Zygote.java
index cbc63cf..3ebe921 100644
--- a/core/java/com/android/internal/os/Zygote.java
+++ b/core/java/com/android/internal/os/Zygote.java
@@ -17,6 +17,7 @@
 package com.android.internal.os;
 
 import android.os.IVold;
+import android.os.Seccomp;
 import android.os.Trace;
 import android.system.ErrnoException;
 import android.system.Os;
@@ -153,6 +154,9 @@
      */
     public static int forkSystemServer(int uid, int gid, int[] gids, int runtimeFlags,
             int[][] rlimits, long permittedCapabilities, long effectiveCapabilities) {
+        // Set system server specific seccomp policy.
+        Seccomp.setSystemServerPolicy();
+
         VM_HOOKS.preFork();
         // Resets nice priority for zygote process.
         resetNicePriority();
diff --git a/core/java/com/android/internal/os/ZygoteConnection.java b/core/java/com/android/internal/os/ZygoteConnection.java
index 6a87b1f..24c4a8d 100644
--- a/core/java/com/android/internal/os/ZygoteConnection.java
+++ b/core/java/com/android/internal/os/ZygoteConnection.java
@@ -30,6 +30,7 @@
 import android.net.LocalSocket;
 import android.os.FactoryTest;
 import android.os.Process;
+import android.os.Seccomp;
 import android.os.SystemProperties;
 import android.os.Trace;
 import android.system.ErrnoException;
@@ -767,6 +768,9 @@
             Process.setArgV0(parsedArgs.niceName);
         }
 
+        // Set app specific seccomp policy.
+        Seccomp.setAppPolicy();
+
         // End of the postFork event.
         Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
         if (parsedArgs.invokeWith != null) {
diff --git a/core/java/com/android/internal/os/ZygoteInit.java b/core/java/com/android/internal/os/ZygoteInit.java
index 2be6212..c906db7 100644
--- a/core/java/com/android/internal/os/ZygoteInit.java
+++ b/core/java/com/android/internal/os/ZygoteInit.java
@@ -650,7 +650,7 @@
         String args[] = {
             "--setuid=1000",
             "--setgid=1000",
-            "--setgroups=1001,1002,1003,1004,1005,1006,1007,1008,1009,1010,1018,1021,1023,1032,3001,3002,3003,3006,3007,3009,3010",
+            "--setgroups=1001,1002,1003,1004,1005,1006,1007,1008,1009,1010,1018,1021,1023,1032,1065,3001,3002,3003,3006,3007,3009,3010",
             "--capabilities=" + capabilities + "," + capabilities,
             "--nice-name=system_server",
             "--runtime-args",
@@ -782,9 +782,6 @@
             // Zygote process unmounts root storage spaces.
             Zygote.nativeUnmountStorageOnInit();
 
-            // Set seccomp policy
-            Seccomp.setPolicy();
-
             ZygoteHooks.stopZygoteNoThreadCreation();
 
             if (startSystemServer) {
diff --git a/core/jni/android_os_seccomp.cpp b/core/jni/android_os_seccomp.cpp
index 06e2a16..b9006e4 100644
--- a/core/jni/android_os_seccomp.cpp
+++ b/core/jni/android_os_seccomp.cpp
@@ -21,20 +21,33 @@
 
 #include "seccomp_policy.h"
 
-static void Seccomp_setPolicy(JNIEnv* /*env*/) {
+static void Seccomp_setSystemServerPolicy(JNIEnv* /*env*/) {
     if (security_getenforce() == 0) {
         ALOGI("seccomp disabled by setenforce 0");
         return;
     }
 
-    if (!set_seccomp_filter()) {
+    if (!set_system_seccomp_filter()) {
+        ALOGE("Failed to set seccomp policy - killing");
+        exit(1);
+    }
+}
+
+static void Seccomp_setAppPolicy(JNIEnv* /*env*/) {
+    if (security_getenforce() == 0) {
+        ALOGI("seccomp disabled by setenforce 0");
+        return;
+    }
+
+    if (!set_app_seccomp_filter()) {
         ALOGE("Failed to set seccomp policy - killing");
         exit(1);
     }
 }
 
 static const JNINativeMethod method_table[] = {
-    NATIVE_METHOD(Seccomp, setPolicy, "()V"),
+    NATIVE_METHOD(Seccomp, setSystemServerPolicy, "()V"),
+    NATIVE_METHOD(Seccomp, setAppPolicy, "()V"),
 };
 
 namespace android {
diff --git a/core/proto/android/app/activitymanager.proto b/core/proto/android/app/activitymanager.proto
index 7385050..3412a32 100644
--- a/core/proto/android/app/activitymanager.proto
+++ b/core/proto/android/app/activitymanager.proto
@@ -38,29 +38,29 @@
   PROCESS_STATE_BOUND_FOREGROUND_SERVICE = 300;
   // Process is hosting a foreground service.
   PROCESS_STATE_FOREGROUND_SERVICE = 400;
-  // Same as PROCESS_STATE_TOP but while device is sleeping.
-  PROCESS_STATE_TOP_SLEEPING = 500;
   // Process is important to the user, and something they are aware of.
-  PROCESS_STATE_IMPORTANT_FOREGROUND = 600;
+  PROCESS_STATE_IMPORTANT_FOREGROUND = 500;
   // Process is important to the user, but not something they are aware of.
-  PROCESS_STATE_IMPORTANT_BACKGROUND = 700;
+  PROCESS_STATE_IMPORTANT_BACKGROUND = 600;
   // Process is in the background transient so we will try to keep running.
-  PROCESS_STATE_TRANSIENT_BACKGROUND = 800;
+  PROCESS_STATE_TRANSIENT_BACKGROUND = 700;
   // Process is in the background running a backup/restore operation.
-  PROCESS_STATE_BACKUP = 900;
-  // Process is in the background, but it can't restore its state so we want
-  // to try to avoid killing it.
-  PROCESS_STATE_HEAVY_WEIGHT = 1000;
+  PROCESS_STATE_BACKUP = 800;
   // Process is in the background running a service. Unlike oom_adj, this
   // level is used for both the normal running in background state and the
   // executing operations state.
-  PROCESS_STATE_SERVICE = 1100;
+  PROCESS_STATE_SERVICE = 900;
   // Process is in the background running a receiver. Note that from the
   // perspective of oom_adj, receivers run at a higher foreground level, but
   // for our prioritization here that is not necessary and putting them
   // below services means many fewer changes in some process states as they
   // receive broadcasts.
-  PROCESS_STATE_RECEIVER = 1200;
+  PROCESS_STATE_RECEIVER = 1000;
+  // Same as PROCESS_STATE_TOP but while device is sleeping.
+  PROCESS_STATE_TOP_SLEEPING = 1100;
+  // Process is in the background, but it can't restore its state so we want
+  // to try to avoid killing it.
+  PROCESS_STATE_HEAVY_WEIGHT = 1200;
   // Process is in the background but hosts the home activity.
   PROCESS_STATE_HOME = 1300;
   // Process is in the background but hosts the last shown activity.
@@ -70,9 +70,12 @@
   // Process is being cached for later use and is a client of another cached
   // process that contains activities.
   PROCESS_STATE_CACHED_ACTIVITY_CLIENT = 1600;
+  // Process is being cached for later use and has an activity that corresponds
+  // to an existing recent task.
+  PROCESS_STATE_CACHED_RECENT = 1700;
   // Process is being cached for later use and is empty.
-  PROCESS_STATE_CACHED_EMPTY = 1700;
+  PROCESS_STATE_CACHED_EMPTY = 1800;
   // Process does not exist.
-  PROCESS_STATE_NONEXISTENT = 1800;
+  PROCESS_STATE_NONEXISTENT = 1900;
 }
 
diff --git a/core/proto/android/content/clipdata.proto b/core/proto/android/content/clipdata.proto
new file mode 100644
index 0000000..6967b69
--- /dev/null
+++ b/core/proto/android/content/clipdata.proto
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+syntax = "proto2";
+package android.content;
+
+option java_multiple_files = true;
+
+import "frameworks/base/core/proto/android/content/clipdescription.proto";
+import "frameworks/base/core/proto/android/content/intent.proto";
+
+// An android.content.ClipData object.
+message ClipDataProto {
+    optional android.content.ClipDescriptionProto description = 1;
+
+    // Custom dump of an android.graphics.Bitmap object.
+    message Icon {
+        optional int32 width = 1;
+        optional int32 height = 2;
+    }
+    optional Icon icon = 2;
+
+    // An android.content.ClipData.Item object.
+    message Item {
+        oneof data {
+            string html_text = 1;
+            string text = 2;
+            string uri = 3;
+            android.content.IntentProto intent = 4;
+            bool nothing = 5;
+        }
+    }
+    repeated Item items = 3;
+}
diff --git a/core/proto/android/content/clipdescription.proto b/core/proto/android/content/clipdescription.proto
new file mode 100644
index 0000000..40f4ad3
--- /dev/null
+++ b/core/proto/android/content/clipdescription.proto
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+syntax = "proto2";
+package android.content;
+
+option java_multiple_files = true;
+
+import "frameworks/base/core/proto/android/os/persistablebundle.proto";
+
+// An android.content.ClipDescription object.
+message ClipDescriptionProto {
+    repeated string mime_types = 1;
+    optional string label = 2;
+    optional android.os.PersistableBundleProto extras = 3;
+    optional int64 timestamp_ms = 4;
+}
diff --git a/core/proto/android/net/network.proto b/core/proto/android/net/network.proto
new file mode 100644
index 0000000..9c7ea5d
--- /dev/null
+++ b/core/proto/android/net/network.proto
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+syntax = "proto2";
+option java_multiple_files = true;
+
+package android.net;
+
+/**
+ * An android.net.Network object.
+ */
+message NetworkProto {
+    optional int32 net_id = 1;
+}
diff --git a/core/proto/android/net/networkcapabilities.proto b/core/proto/android/net/networkcapabilities.proto
new file mode 100644
index 0000000..e1c2af1
--- /dev/null
+++ b/core/proto/android/net/networkcapabilities.proto
@@ -0,0 +1,129 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+syntax = "proto2";
+
+package android.net;
+
+option java_multiple_files = true;
+
+/**
+ * An android.net.NetworkCapabilities object.
+ */
+message NetworkCapabilitiesProto {
+    enum Transport {
+        // Indicates this network uses a Cellular transport.
+        TRANSPORT_CELLULAR = 0;
+        // Indicates this network uses a Wi-Fi transport.
+        TRANSPORT_WIFI = 1;
+        // Indicates this network uses a Bluetooth transport.
+        TRANSPORT_BLUETOOTH = 2;
+        // Indicates this network uses an Ethernet transport.
+        TRANSPORT_ETHERNET = 3;
+        // Indicates this network uses a VPN transport.
+        TRANSPORT_VPN = 4;
+        // Indicates this network uses a Wi-Fi Aware transport.
+        TRANSPORT_WIFI_AWARE = 5;
+        // Indicates this network uses a LoWPAN transport.
+        TRANSPORT_LOWPAN = 6;
+    }
+    repeated Transport transports = 1;
+
+    enum NetCapability {
+        // Indicates this is a network that has the ability to reach the
+        // carrier's MMSC for sending and receiving MMS messages.
+        NET_CAPABILITY_MMS = 0;
+        // Indicates this is a network that has the ability to reach the
+        // carrier's SUPL server, used to retrieve GPS information.
+        NET_CAPABILITY_SUPL = 1;
+        // Indicates this is a network that has the ability to reach the
+        // carrier's DUN or tethering gateway.
+        NET_CAPABILITY_DUN = 2;
+        // Indicates this is a network that has the ability to reach the
+        // carrier's FOTA portal, used for over the air updates.
+        NET_CAPABILITY_FOTA = 3;
+        // Indicates this is a network that has the ability to reach the
+        // carrier's IMS servers, used for network registration and signaling.
+        NET_CAPABILITY_IMS = 4;
+        // Indicates this is a network that has the ability to reach the
+        // carrier's CBS servers, used for carrier specific services.
+        NET_CAPABILITY_CBS = 5;
+        // Indicates this is a network that has the ability to reach a Wi-Fi
+        // direct peer.
+        NET_CAPABILITY_WIFI_P2P = 6;
+        // Indicates this is a network that has the ability to reach a carrier's
+        // Initial Attach servers.
+        NET_CAPABILITY_IA = 7;
+        // Indicates this is a network that has the ability to reach a carrier's
+        // RCS servers, used for Rich Communication Services.
+        NET_CAPABILITY_RCS = 8;
+        // Indicates this is a network that has the ability to reach a carrier's
+        // XCAP servers, used for configuration and control.
+        NET_CAPABILITY_XCAP = 9;
+        // Indicates this is a network that has the ability to reach a carrier's
+        // Emergency IMS servers or other services, used for network signaling
+        // during emergency calls.
+        NET_CAPABILITY_EIMS = 10;
+        // Indicates that this network is unmetered.
+        NET_CAPABILITY_NOT_METERED = 11;
+        // Indicates that this network should be able to reach the internet.
+        NET_CAPABILITY_INTERNET = 12;
+        // Indicates that this network is available for general use. If this is
+        // not set applications should not attempt to communicate on this
+        // network. Note that this is simply informative and not enforcement -
+        // enforcement is handled via other means. Set by default.
+        NET_CAPABILITY_NOT_RESTRICTED = 13;
+        // Indicates that the user has indicated implicit trust of this network.
+        // This generally means it's a sim-selected carrier, a plugged in
+        // ethernet, a paired BT device or a wifi the user asked to connect to.
+        // Untrusted networks are probably limited to unknown wifi AP. Set by
+        // default.
+        NET_CAPABILITY_TRUSTED = 14;
+        // Indicates that this network is not a VPN.  This capability is set by
+        // default and should be explicitly cleared for VPN networks.
+        NET_CAPABILITY_NOT_VPN = 15;
+        // Indicates that connectivity on this network was successfully
+        // validated. For example, for a network with NET_CAPABILITY_INTERNET,
+        // it means that Internet connectivity was successfully detected.
+        NET_CAPABILITY_VALIDATED = 16;
+        // Indicates that this network was found to have a captive portal in
+        // place last time it was probed.
+        NET_CAPABILITY_CAPTIVE_PORTAL = 17;
+        // Indicates that this network is not roaming.
+        NET_CAPABILITY_NOT_ROAMING = 18;
+        // Indicates that this network is available for use by apps, and not a
+        // network that is being kept up in the background to facilitate fast
+        // network switching.
+        NET_CAPABILITY_FOREGROUND = 19;
+    }
+    repeated NetCapability capabilities = 2;
+
+    // Passive link bandwidth. This is a rough guide of the expected peak
+    // bandwidth for the first hop on the given transport.  It is not measured,
+    // but may take into account link parameters (Radio technology, allocated
+    // channels, etc).
+    optional int32 link_up_bandwidth_kbps = 3;
+    optional int32 link_down_bandwidth_kbps = 4;
+
+    optional string network_specifier = 5;
+
+    // True if this object specifies a signal strength.
+    optional bool can_report_signal_strength = 6;
+    // This is a signed integer, and higher values indicate better signal. The
+    // exact units are bearer-dependent. For example, Wi-Fi uses RSSI.
+    // Only valid if can_report_signal_strength is true.
+    optional sint32 signal_strength = 7;
+}
diff --git a/core/proto/android/net/networkrequest.proto b/core/proto/android/net/networkrequest.proto
new file mode 100644
index 0000000..9884464
--- /dev/null
+++ b/core/proto/android/net/networkrequest.proto
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+syntax = "proto2";
+
+package android.net;
+
+option java_multiple_files = true;
+
+import "frameworks/base/core/proto/android/net/networkcapabilities.proto";
+
+/**
+ * An android.net.NetworkRequest object.
+ */
+message NetworkRequestProto {
+    enum Type {
+        TYPE_UNKNOWN = 0;
+        // Only used by applications. When an application creates a
+        // NetworkRequest, it does not have a type; the type is set by the
+        // system depending on the method used to file the request
+        // (requestNetwork, registerNetworkCallback, etc.).
+        TYPE_NONE = 1;
+        // The framework will issue callbacks about any and all networks that
+        // match the specified NetworkCapabilities.
+        TYPE_LISTEN = 2;
+        // A hybrid of the two designed such that the framework will issue
+        // callbacks for the single, highest scoring current network (if any)
+        // that matches the capabilities of the default Internet request
+        // (mDefaultRequest), but which cannot cause the framework to either
+        // create or retain the existence of any specific network. Note that
+        // from the point of view of the request matching code, TRACK_DEFAULT is
+        // identical to REQUEST: its special behaviour is not due to different
+        // semantics, but to the fact that the system will only ever create a
+        // TRACK_DEFAULT with capabilities that are identical to the default
+        // request's capabilities, thus causing it to share fate in every way
+        // with the default request.
+        TYPE_TRACK_DEFAULT = 3;
+        // Capable of causing a specific network to be created first (e.g. a
+        // telephony DUN request), the framework will issue callbacks about the
+        // single, highest scoring current network (if any) that matches the
+        // specified NetworkCapabilities.
+        TYPE_REQUEST = 4;
+        // Like REQUEST but does not cause any networks to retain the
+        // NET_CAPABILITY_FOREGROUND capability. A network with no foreground
+        // requests is in the background. A network that has one or more
+        // background requests and loses its last foreground request to a
+        // higher-scoring network will not go into the background immediately,
+        // but will linger and go into the background after the linger timeout.
+        TYPE_BACKGROUND_REQUEST = 5;
+    }
+    // The type of the request. This is only used by the system and is always
+    // NONE elsewhere.
+    optional Type type = 1;
+    // Identifies the request.
+    optional int32 request_id = 2;
+    // Set for legacy requests and the default.
+    optional int32 legacy_type = 3;
+    optional NetworkCapabilitiesProto network_capabilities = 4;
+}
diff --git a/core/proto/android/os/bundle.proto b/core/proto/android/os/bundle.proto
new file mode 100644
index 0000000..6990281
--- /dev/null
+++ b/core/proto/android/os/bundle.proto
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+syntax = "proto2";
+package android.os;
+
+option java_multiple_files = true;
+
+// An android.os.Bundle object.
+message BundleProto {
+    oneof data {
+        int32 parcelled_data_size = 1;
+        string map_data = 2;
+    }
+}
diff --git a/core/proto/android/os/incident.proto b/core/proto/android/os/incident.proto
index 2f856ab..2752a7e 100644
--- a/core/proto/android/os/incident.proto
+++ b/core/proto/android/os/incident.proto
@@ -31,6 +31,7 @@
 import "frameworks/base/core/proto/android/server/activitymanagerservice.proto";
 import "frameworks/base/core/proto/android/server/alarmmanagerservice.proto";
 import "frameworks/base/core/proto/android/server/fingerprint.proto";
+import "frameworks/base/core/proto/android/server/jobscheduler.proto";
 import "frameworks/base/core/proto/android/server/powermanagerservice.proto";
 import "frameworks/base/core/proto/android/server/windowmanagerservice.proto";
 import "frameworks/base/core/proto/android/service/appwidget.proto";
@@ -247,4 +248,8 @@
         (section).args = "graphicsstats --proto"
     ];
 
+    optional com.android.server.job.JobSchedulerServiceDumpProto jobscheduler = 3020 [
+        (section).type = SECTION_DUMPSYS,
+        (section).args = "jobscheduler --proto"
+    ];
 }
diff --git a/core/proto/android/os/persistablebundle.proto b/core/proto/android/os/persistablebundle.proto
new file mode 100644
index 0000000..75ff787
--- /dev/null
+++ b/core/proto/android/os/persistablebundle.proto
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+syntax = "proto2";
+package android.os;
+
+option java_multiple_files = true;
+
+// An android.os.PersistableBundle object.
+message PersistableBundleProto {
+    oneof data {
+        int32 parcelled_data_size = 1;
+        string map_data = 2;
+    }
+}
diff --git a/core/proto/android/providers/settings.proto b/core/proto/android/providers/settings.proto
index fb0ebed..f5d098c 100644
--- a/core/proto/android/providers/settings.proto
+++ b/core/proto/android/providers/settings.proto
@@ -582,7 +582,7 @@
     optional SettingProto downloads_backup_charging_only = 162;
     optional SettingProto automatic_storage_manager_downloads_days_to_retain = 163;
     optional SettingProto qs_tiles = 164;
-    optional SettingProto demo_user_setup_complete = 165;
+    reserved 165; // Removed demo_user_setup_complete
     optional SettingProto instant_apps_enabled = 166;
     optional SettingProto device_paired = 167;
     optional SettingProto package_verifier_state = 191;
diff --git a/core/proto/android/server/jobscheduler.proto b/core/proto/android/server/jobscheduler.proto
new file mode 100644
index 0000000..f72ca62
--- /dev/null
+++ b/core/proto/android/server/jobscheduler.proto
@@ -0,0 +1,561 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+syntax = "proto2";
+
+package com.android.server.job;
+
+option java_multiple_files = true;
+
+import "frameworks/base/core/proto/android/app/jobparameters.proto";
+import "frameworks/base/core/proto/android/content/clipdata.proto";
+import "frameworks/base/core/proto/android/content/component_name.proto";
+import "frameworks/base/core/proto/android/content/intent.proto";
+import "frameworks/base/core/proto/android/net/network.proto";
+import "frameworks/base/core/proto/android/net/networkrequest.proto";
+import "frameworks/base/core/proto/android/os/bundle.proto";
+import "frameworks/base/core/proto/android/os/persistablebundle.proto";
+import "frameworks/base/core/proto/android/server/forceappstandbytracker.proto";
+
+message JobSchedulerServiceDumpProto {
+    optional ConstantsProto settings = 1;
+
+    repeated int32 started_users = 2;
+
+    message RegisteredJob {
+        optional JobStatusShortInfoProto info = 1;
+        optional JobStatusDumpProto dump = 2;
+
+        // A job is ready to be executed if:
+        // is_job_ready && is_user_started && !is_job_pending &&
+        // !is_job_currently_active && !is_uid_backing_up &&
+        // is_component_present.
+        optional bool is_job_ready = 3;
+        optional bool is_user_started = 4;
+        optional bool is_job_pending = 5;
+        optional bool is_job_currently_active = 6;
+        optional bool is_uid_backing_up = 7;
+        optional bool is_component_present = 8;
+    }
+    repeated RegisteredJob registered_jobs = 3;
+
+    repeated StateControllerProto controllers = 4;
+
+    // Which uids are currently in the foreground.
+    message PriorityOverride {
+        optional int32 uid = 1;
+        // Use sint32 instead of an enum since priorities can technically be
+        // negative.
+        optional sint32 override_value = 2;
+    }
+    repeated PriorityOverride priority_overrides = 5;
+
+    // UIDs that are currently performing backups, so their jobs won't be
+    // allowed to run.
+    repeated int32 backing_up_uids = 6;
+
+    optional JobPackageHistoryProto history = 7;
+    optional JobPackageTrackerDumpProto package_tracker = 8;
+
+    message PendingJob {
+        optional JobStatusShortInfoProto info = 1;
+        optional JobStatusDumpProto dump = 2;
+        optional sint32 evaluated_priority = 3;
+        // How long this job has been pending.
+        optional int64 enqueued_duration_ms = 4;
+    }
+    repeated PendingJob pending_jobs = 9;
+
+    // From a service that has currently active or pending jobs.
+    message ActiveJob {
+        message InactiveJob {
+            optional int64 time_since_stopped_ms = 1;
+            // This is not always provided.
+            optional string stopped_reason = 2;
+        }
+        message RunningJob {
+            optional JobStatusShortInfoProto info = 1;
+            // How long this job has been running for.
+            optional int64 running_duration_ms = 2;
+            optional int64 time_until_timeout_ms = 3;
+
+            optional JobStatusDumpProto dump = 4;
+
+            optional sint32 evaluated_priority = 5;
+
+            optional int64 time_since_made_active_ms = 6;
+            // How long this job has been pending.
+            optional int64 pending_duration_ms = 7;
+        }
+        oneof job {
+            InactiveJob inactive = 1;
+            RunningJob running = 2;
+        }
+    }
+    repeated ActiveJob active_jobs = 10;
+
+    // True when JobScheduler is allowed to run third party apps.
+    optional bool is_ready_to_rock = 11;
+    // What was last reported to DeviceIdleController about whether the device
+    // is active.
+    optional bool reported_active = 12;
+    // The current limit on the number of concurrent JobServiceContext entries
+    // we want to keep actively running a job.
+    optional int32 max_active_jobs = 13;
+}
+
+// A com.android.server.job.JobSchedulerService.Constants object.
+message ConstantsProto {
+    // Minimum # of idle jobs that must be ready in order to force the JMS to
+    // schedule things early.
+    optional int32 min_idle_count = 1;
+    // Minimum # of charging jobs that must be ready in order to force the JMS
+    // to schedule things early.
+    optional int32 min_charging_count = 2;
+    // Minimum # of "battery not low" jobs that must be ready in order to force
+    // the JMS to schedule things early.
+    optional int32 min_battery_not_low_count = 3;
+    // Minimum # of "storage not low" jobs that must be ready in order to force
+    // the JMS to schedule things early.
+    optional int32 min_storage_not_low_count = 4;
+    // Minimum # of connectivity jobs that must be ready in order to force the
+    // JMS to schedule things early. 1 == Run connectivity jobs as soon as
+    // ready.
+    optional int32 min_connectivity_count = 5;
+    // Minimum # of content trigger jobs that must be ready in order to force
+    // the JMS to schedule things early.
+    optional int32 min_content_count = 6;
+    // Minimum # of jobs (with no particular constraints) for which the JMS will
+    // be happy running some work early. This (and thus the other min counts)
+    // is now set to 1, to prevent any batching at this level. Since we now do
+    // batching through doze, that is a much better mechanism.
+    optional int32 min_ready_jobs_count = 7;
+    // This is the job execution factor that is considered to be heavy use of
+    // the system.
+    optional double heavy_use_factor = 8;
+    // This is the job execution factor that is considered to be moderate use of
+    // the system.
+    optional double moderate_use_factor = 9;
+    // The number of MAX_JOB_CONTEXTS_COUNT we reserve for the foreground app.
+    optional int32 fg_job_count = 10;
+    // The maximum number of background jobs we allow when the system is in a
+    // normal memory state.
+    optional int32 bg_normal_job_count = 11;
+    // The maximum number of background jobs we allow when the system is in a
+    // moderate memory state.
+    optional int32 bg_moderate_job_count = 12;
+    // The maximum number of background jobs we allow when the system is in a
+    // low memory state.
+    optional int32 bg_low_job_count = 13;
+    // The maximum number of background jobs we allow when the system is in a
+    // critical memory state.
+    optional int32 bg_critical_job_count = 14;
+    // The maximum number of times we allow a job to have itself rescheduled
+    // before giving up on it, for standard jobs.
+    optional int32 max_standard_reschedule_count = 15;
+    // The maximum number of times we allow a job to have itself rescheduled
+    // before giving up on it, for jobs that are executing work.
+    optional int32 max_work_reschedule_count = 16;
+    // The minimum backoff time to allow for linear backoff.
+    optional int64 min_linear_backoff_time_ms = 17;
+    // The minimum backoff time to allow for exponential backoff.
+    optional int64 min_exp_backoff_time_ms = 18;
+    // How often we recalculate runnability based on apps' standby bucket
+    // assignment. This should be prime relative to common time interval lengths
+    // such as a quarter-hour or day, so that the heartbeat drifts relative to
+    // wall-clock milestones.
+    optional int64 standby_heartbeat_time_ms = 19;
+    // Mapping: standby bucket -> number of heartbeats between each sweep of
+    // that bucket's jobs.
+    // Bucket assignments as recorded in the JobStatus objects are normalized to
+    // be indices into this array, rather than the raw constants used by
+    // AppIdleHistory.
+    repeated int32 standby_beats = 20;
+}
+
+message StateControllerProto {
+    message AppIdleController {
+        optional bool is_parole_on = 1;
+
+        message TrackedJob {
+            optional JobStatusShortInfoProto info = 1;
+            optional int32 source_uid = 2;
+            optional string source_package_name = 3;
+            // If the constraints are satisfied, then the controller will mark
+            // the job as RUNNABLE, otherwise, it will be WAITING.
+            optional bool are_constraints_satisfied = 4;
+        }
+        repeated TrackedJob tracked_jobs = 2;
+    }
+    message BackgroundJobsController {
+        optional com.android.server.ForceAppStandbyTrackerProto force_app_standby_tracker = 1;
+
+        message TrackedJob {
+            optional JobStatusShortInfoProto info = 1;
+            optional int32 source_uid = 2;
+            optional string source_package_name = 3;
+            optional bool is_in_foreground = 4;
+            optional bool is_whitelisted = 5;
+            optional bool can_run_any_in_background = 6;
+            // If the constraints are satisfied, then the controller will mark
+            // the job as RUNNABLE, otherwise, it will be WAITING.
+            optional bool are_constraints_satisfied = 7;
+        }
+        repeated TrackedJob tracked_jobs = 2;
+    }
+    message BatteryController {
+        optional bool is_on_stable_power = 1;
+        optional bool is_battery_not_low = 2;
+
+        // Whether or not the controller is monitoring battery changes.
+        optional bool is_monitoring = 3;
+        // Only valid if is_monitoring is true.
+        optional int32 last_broadcast_sequence_number = 4;
+
+        message TrackedJob {
+            optional JobStatusShortInfoProto info = 1;
+            optional int32 source_uid = 2;
+        }
+        repeated TrackedJob tracked_jobs = 5;
+    }
+    message ConnectivityController {
+        optional bool is_connected = 1;
+
+        message TrackedJob {
+            optional JobStatusShortInfoProto info = 1;
+            optional int32 source_uid = 2;
+            optional .android.net.NetworkRequestProto required_network = 3;
+        }
+        repeated TrackedJob tracked_jobs = 2;
+    }
+    message ContentObserverController {
+        message TrackedJob {
+            optional JobStatusShortInfoProto info = 1;
+            optional int32 source_uid = 2;
+        }
+        repeated TrackedJob tracked_jobs = 1;
+
+        message Observer {
+            optional int32 user_id = 1;
+
+            message TriggerContentData {
+                optional string uri = 1;
+                optional int32 flags = 2;
+
+                // A
+                // com.android.server.job.controllers.ContentObserverController.JobInstance
+                // object.
+                message JobInstance {
+                    optional JobStatusShortInfoProto info = 1;
+                    optional int32 source_uid = 2;
+
+                    optional int64 trigger_content_update_delay_ms = 3;
+                    optional int64 trigger_content_max_delay_ms = 4;
+
+                    repeated string changed_authorities = 5;
+                    repeated string changed_uris = 6;
+                }
+                repeated JobInstance jobs = 3;
+            }
+            repeated TriggerContentData triggers = 2;
+        }
+        repeated Observer observers = 2;
+    }
+    message DeviceIdleJobsController {
+        // True when in device idle mode.
+        optional bool is_device_idle_mode = 1;
+
+        message TrackedJob {
+            optional JobStatusShortInfoProto info = 1;
+            optional int32 source_uid = 2;
+            optional string source_package_name = 3;
+            // If the constraints are satisfied, then the controller will mark
+            // the job as RUNNABLE, otherwise, it will be WAITING.
+            optional bool are_constraints_satisfied = 4;
+            optional bool is_doze_whitelisted = 5;
+            // A job that is exempted from Doze when the app is temp whitelisted
+            // or in the foreground.
+            optional bool is_allowed_in_doze = 6;
+        }
+        repeated TrackedJob tracked_jobs = 2;
+    }
+    message IdleController {
+        optional bool is_idle = 1;
+
+        message TrackedJob {
+            optional JobStatusShortInfoProto info = 1;
+            optional int32 source_uid = 2;
+        }
+        repeated TrackedJob tracked_jobs = 2;
+    }
+    message StorageController {
+        optional bool is_storage_not_low = 1;
+        optional int32 last_broadcast_sequence_number = 2;
+
+        message TrackedJob {
+            optional JobStatusShortInfoProto info = 1;
+            optional int32 source_uid = 2;
+        }
+        repeated TrackedJob tracked_jobs = 3;
+    }
+    message TimeController {
+        optional int64 now_elapsed_realtime = 1;
+        optional int64 time_until_next_delay_alarm_ms = 2;
+        optional int64 time_until_next_deadline_alarm_ms = 3;
+
+        message TrackedJob {
+            optional JobStatusShortInfoProto info = 1;
+            optional int32 source_uid = 2;
+
+            optional bool has_timing_delay_constraint = 3;
+            // Only valid if has_timing_delay_constraint is true. Can be
+            // negative if the delay is in the past.
+            optional int64 delay_time_remaining_ms = 4;
+
+            optional bool has_deadline_constraint = 5;
+            // Only valid if has_timing_delay_constraint is true. Can be
+            // negative in certain situations.
+            optional int64 time_remaining_until_deadline_ms = 6;
+        }
+        repeated TrackedJob tracked_jobs = 4;
+    }
+    oneof controller {
+        AppIdleController app_idle = 1;
+        BackgroundJobsController background = 2;
+        BatteryController battery = 3;
+        ConnectivityController connectivity = 4;
+        ContentObserverController content_observer = 5;
+        DeviceIdleJobsController device_idle = 6;
+        IdleController idle = 7;
+        StorageController storage = 8;
+        TimeController time = 9;
+    }
+}
+
+// A com.android.server.job.JobPackageTracker.DataSet object.
+message DataSetProto {
+    optional int64 start_clock_time_ms = 1;
+    // How much time has elapsed since the DataSet was instantiated.
+    optional int64 elapsed_time_ms = 2;
+    optional int64 period_ms = 3;
+
+    // Represents a com.android.server.job.JobPackageTracker.PackageEntry
+    // object, but with some extra data.
+    message PackageEntryProto {
+        optional int32 uid = 1;
+        optional string package_name = 2;
+
+        message State {
+            optional int64 duration_ms = 1;
+            optional int32 count = 2;
+        }
+        optional State pending_state = 3;
+        optional State active_state = 4;
+        optional State active_top_state = 5;
+
+        // True if the PackageEntry is currently pending or has been pending in
+        // the past.
+        optional bool pending = 6;
+        // True if the PackageEntry is currently active or has been active in
+        // the past.
+        optional bool active = 7;
+        // True if the PackageEntry is currently active top or has been active
+        // top in the past.
+        optional bool active_top = 8;
+
+        message StopReasonCount {
+            optional .android.app.JobParametersProto.CancelReason reason = 1;
+            optional int32 count = 2;
+        }
+        repeated StopReasonCount stop_reasons = 9;
+    }
+    repeated PackageEntryProto package_entries = 4;
+
+    optional int32 max_concurrency = 5;
+    optional int32 max_foreground_concurrency = 6;
+}
+
+// Dump from com.android.server.job.GrantedUriPermissions.
+message GrantedUriPermissionsDumpProto {
+    optional int32 flags = 1;
+    optional int32 source_user_id = 2;
+    optional string tag = 3;
+    optional string permission_owner = 4;
+    repeated string uris = 5;
+}
+
+message JobPackageTrackerDumpProto {
+    repeated DataSetProto historical_stats = 1;
+    optional DataSetProto current_stats = 2;
+}
+
+message JobPackageHistoryProto {
+    enum Event {
+        UNKNOWN = 0;
+        START_JOB = 1;
+        STOP_JOB = 2;
+        START_PERIODIC_JOB = 3;
+        STOP_PERIODIC_JOB = 4;
+    }
+    message HistoryEvent {
+        optional Event event = 1;
+        optional int64 time_since_event_ms = 2;
+        optional int32 uid = 3;
+        // Job IDs can technically be negative.
+        optional int32 job_id = 4;
+        optional string tag = 5;
+        // Only valid for STOP_JOB or STOP_PERIODIC_JOB Events.
+        optional .android.app.JobParametersProto.CancelReason stop_reason = 6;
+    }
+    repeated HistoryEvent history_event = 1;
+}
+
+message JobStatusShortInfoProto {
+    optional int32 calling_uid = 1;
+    // Job IDs can technically be negative.
+    optional int32 job_id = 2;
+    optional string battery_name = 3;
+}
+
+// Dump from a com.android.server.job.controllers.JobStatus object.
+message JobStatusDumpProto {
+    // The UID that scheduled the job.
+    optional int32 calling_uid = 1;
+    optional string tag = 2;
+
+    // The UID for which the job is being run.
+    optional int32 source_uid = 3;
+    optional int32 source_user_id = 4;
+    // The package for which the job is being run.
+    optional string source_package_name = 5;
+
+    // Custom dump of android.app.job.JobInfo object.
+    message JobInfo {
+        optional .android.content.ComponentNameProto service = 1;
+
+        optional bool is_periodic = 2;
+        // Only valid if is_periodic is true.
+        optional int64 period_interval_ms = 3;
+        // Only valid if is_periodic is true.
+        optional int64 period_flex_ms = 4;
+
+        optional bool is_persisted = 5;
+        optional sint32 priority = 6;
+        optional int32 flags = 7;
+
+        optional bool requires_charging = 8;
+        optional bool requires_battery_not_low = 9;
+        optional bool requires_device_idle = 10;
+
+        message TriggerContentUri {
+            optional int32 flags = 1;
+            optional string uri = 2;
+        }
+        repeated TriggerContentUri trigger_content_uris = 11;
+        optional int64 trigger_content_update_delay_ms = 12;
+        optional int64 trigger_content_max_delay_ms = 13;
+
+        optional .android.os.PersistableBundleProto extras = 14;
+        optional .android.os.BundleProto transient_extras = 15;
+        optional .android.content.ClipDataProto clip_data = 16;
+
+        optional GrantedUriPermissionsDumpProto granted_uri_permissions = 17;
+
+        optional .android.net.NetworkRequestProto required_network = 18;
+
+        optional int64 total_network_bytes = 19;
+
+        optional int64 min_latency_ms = 20;
+        optional int64 max_execution_delay_ms = 21;
+
+        message Backoff {
+            enum Policy {
+                BACKOFF_POLICY_LINEAR = 0;
+                BACKOFF_POLICY_EXPONENTIAL = 1;
+            }
+            optional Policy policy = 1;
+            optional int64 initial_backoff_ms = 2;
+        }
+        optional Backoff backoff_policy = 22;
+
+        optional bool has_early_constraint = 23;
+        optional bool has_late_constraint = 24;
+    }
+    optional JobInfo job_info = 6;
+
+    enum Constraint {
+        CONSTRAINT_CHARGING = 1;
+        CONSTRAINT_BATTERY_NOT_LOW = 2;
+        CONSTRAINT_STORAGE_NOT_LOW = 3;
+        CONSTRAINT_TIMING_DELAY = 4;
+        CONSTRAINT_DEADLINE = 5;
+        CONSTRAINT_IDLE = 6;
+        CONSTRAINT_CONNECTIVITY = 7;
+        CONSTRAINT_APP_NOT_IDLE = 8;
+        CONSTRAINT_CONTENT_TRIGGER = 9;
+        CONSTRAINT_DEVICE_NOT_DOZING = 10;
+    }
+    repeated Constraint required_constraints = 7;
+    repeated Constraint satisfied_constraints = 8;
+    repeated Constraint unsatisfied_constraints = 9;
+    optional bool is_doze_whitelisted = 10;
+
+    enum TrackingController {
+        TRACKING_BATTERY = 0;
+        TRACKING_CONNECTIVITY = 1;
+        TRACKING_CONTENT = 2;
+        TRACKING_IDLE = 3;
+        TRACKING_STORAGE = 4;
+        TRACKING_TIME = 5;
+    }
+    // Controllers that are currently tracking the job.
+    repeated TrackingController tracking_controllers = 11;
+
+    repeated string changed_authorities = 12;
+    repeated string changed_uris = 13;
+
+    optional .android.net.NetworkProto network = 14;
+
+    // Only the desired data from an android.app.job.JobWorkItem object.
+    message JobWorkItem {
+        optional int32 work_id = 1;
+        optional int32 delivery_count = 2;
+        optional .android.content.IntentProto intent = 3;
+        optional GrantedUriPermissionsDumpProto uri_grants = 4;
+    }
+    repeated JobWorkItem pending_work = 15;
+    repeated JobWorkItem executing_work = 16;
+
+    enum Bucket {
+        ACTIVE = 0;
+        WORKING_SET = 1;
+        FREQUENT = 2;
+        RARE = 3;
+        NEVER = 4;
+    }
+    optional Bucket standby_bucket = 17;
+
+    optional int64 enqueue_duration_ms = 18;
+    // Can be negative if the earliest runtime deadline has passed.
+    optional sint64 time_until_earliest_runtime_ms = 19;
+    // Can be negative if the latest runtime deadline has passed.
+    optional sint64 time_until_latest_runtime_ms = 20;
+
+    optional int32 num_failures = 21;
+
+    optional int64 last_successful_run_time = 22;
+    optional int64 last_failed_run_time = 23;
+}
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index c40f374..beb5cdc 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -159,6 +159,8 @@
     <protected-broadcast
         android:name="android.bluetooth.headset.action.HF_INDICATORS_VALUE_CHANGED" />
     <protected-broadcast
+        android:name="android.bluetooth.headset.profile.action.ACTIVE_DEVICE_CHANGED" />
+    <protected-broadcast
         android:name="android.bluetooth.headsetclient.profile.action.CONNECTION_STATE_CHANGED" />
     <protected-broadcast
         android:name="android.bluetooth.headsetclient.profile.action.AUDIO_STATE_CHANGED" />
@@ -1799,6 +1801,15 @@
     <permission android:name="android.permission.ALLOCATE_AGGRESSIVE"
         android:protectionLevel="signature|privileged" />
 
+    <!-- @SystemApi @hide
+         Allows an application to use reserved disk space.
+         <p>Not for use by third-party applications.  Should only be requested by
+         apps that provide core system functionality, to ensure system stability
+         when disk is otherwise completely full.
+    -->
+    <permission android:name="android.permission.USE_RESERVED_DISK"
+        android:protectionLevel="signature|privileged" />
+
     <!-- ================================== -->
     <!-- Permissions for screenlock         -->
     <!-- ================================== -->
diff --git a/core/res/res/anim/cross_profile_apps_thumbnail_enter.xml b/core/res/res/anim/cross_profile_apps_thumbnail_enter.xml
new file mode 100644
index 0000000..3254ebb
--- /dev/null
+++ b/core/res/res/anim/cross_profile_apps_thumbnail_enter.xml
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 2018, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+<!-- This should be kept in sync with task_open_enter.xml -->
+<set xmlns:android="http://schemas.android.com/apk/res/android"
+     android:shareInterpolator="false" android:zAdjustment="top">
+
+    <alpha android:fromAlpha="0" android:toAlpha="1.0"
+           android:startOffset="300"
+           android:fillEnabled="true" android:fillBefore="true" android:fillAfter="true"
+           android:interpolator="@interpolator/decelerate_quart"
+           android:duration="167"/>
+
+    <translate android:fromYDelta="110%" android:toYDelta="0"
+               android:startOffset="300"
+               android:fillEnabled="true" android:fillBefore="true" android:fillAfter="true"
+               android:interpolator="@interpolator/decelerate_quint"
+               android:duration="417"/>
+
+    <!-- To keep the thumbnail around longer -->
+    <alpha android:fromAlpha="1.0" android:toAlpha="0"
+           android:fillEnabled="true" android:fillBefore="true" android:fillAfter="true"
+           android:interpolator="@interpolator/decelerate_quint"
+           android:startOffset="717"
+           android:duration="200"/>
+</set>
\ No newline at end of file
diff --git a/core/res/res/anim/task_open_enter.xml b/core/res/res/anim/task_open_enter.xml
index e511cc9..b73e14f 100644
--- a/core/res/res/anim/task_open_enter.xml
+++ b/core/res/res/anim/task_open_enter.xml
@@ -16,7 +16,7 @@
 ** limitations under the License.
 */
 -->
-
+<!-- This should in sync with task_open_enter_cross_profile_apps.xml -->
 <set xmlns:android="http://schemas.android.com/apk/res/android"
         android:shareInterpolator="false" android:zAdjustment="top">
 
diff --git a/core/res/res/anim/task_open_enter_cross_profile_apps.xml b/core/res/res/anim/task_open_enter_cross_profile_apps.xml
new file mode 100644
index 0000000..ad89fde
--- /dev/null
+++ b/core/res/res/anim/task_open_enter_cross_profile_apps.xml
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 2018, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+<!-- This should in sync with task_open_enter.xml -->
+<set xmlns:android="http://schemas.android.com/apk/res/android"
+     android:shareInterpolator="false" android:zAdjustment="top">
+
+    <alpha android:fromAlpha="0" android:toAlpha="1.0"
+           android:fillEnabled="true" android:fillBefore="true" android:fillAfter="true"
+           android:interpolator="@interpolator/decelerate_quart"
+           android:startOffset="300"
+           android:duration="167"/>
+
+    <translate android:fromYDelta="110%" android:toYDelta="0"
+               android:fillEnabled="true" android:fillBefore="true" android:fillAfter="true"
+               android:interpolator="@interpolator/decelerate_quint"
+               android:startOffset="300"
+               android:duration="417"/>
+
+    <!-- To keep the transition around longer for the thumbnail, should be kept in sync with
+         cross_profile_apps_thumbmail.xml -->
+    <alpha android:fromAlpha="1.0" android:toAlpha="1.0"
+           android:fillEnabled="true" android:fillBefore="true" android:fillAfter="true"
+           android:startOffset="717"
+           android:duration="200"/>
+</set>
\ No newline at end of file
diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml
index a659b37..6c1661c 100644
--- a/core/res/res/values/dimens.xml
+++ b/core/res/res/values/dimens.xml
@@ -631,4 +631,8 @@
 
     <!-- Default dialog corner radius -->
     <dimen name="dialog_corner_radius">2dp</dimen>
+
+    <!-- Size of thumbnail used in the cross profile apps animation -->
+    <dimen name="cross_profile_apps_thumbnail_size">72dp</dimen>
+
 </resources>
diff --git a/core/res/res/values/disallowed_apps_managed_device.xml b/core/res/res/values/disallowed_apps_managed_device.xml
index 8940b15..3fb9ba4 100644
--- a/core/res/res/values/disallowed_apps_managed_device.xml
+++ b/core/res/res/values/disallowed_apps_managed_device.xml
@@ -18,6 +18,6 @@
 -->
 <resources>
     <!-- A list of apps to be removed from the managed device. -->
-    <string-array name="disallowed_apps_managed_device">
+    <string-array translatable="false" name="disallowed_apps_managed_device">
     </string-array>
 </resources>
diff --git a/core/res/res/values/disallowed_apps_managed_profile.xml b/core/res/res/values/disallowed_apps_managed_profile.xml
index e3a513f..c3ea8ec 100644
--- a/core/res/res/values/disallowed_apps_managed_profile.xml
+++ b/core/res/res/values/disallowed_apps_managed_profile.xml
@@ -18,6 +18,6 @@
 -->
 <resources>
     <!-- A list of apps to be removed from the managed profile. -->
-    <string-array name="disallowed_apps_managed_profile">
+    <string-array translatable="false" name="disallowed_apps_managed_profile">
     </string-array>
 </resources>
diff --git a/core/res/res/values/disallowed_apps_managed_user.xml b/core/res/res/values/disallowed_apps_managed_user.xml
index b7b645d..e5b29afe 100644
--- a/core/res/res/values/disallowed_apps_managed_user.xml
+++ b/core/res/res/values/disallowed_apps_managed_user.xml
@@ -18,6 +18,6 @@
 -->
 <resources>
     <!-- A list of apps to be removed from the managed user. -->
-    <string-array name="disallowed_apps_managed_user">
+    <string-array translatable="false" name="disallowed_apps_managed_user">
     </string-array>
 </resources>
diff --git a/core/res/res/values/required_apps_managed_device.xml b/core/res/res/values/required_apps_managed_device.xml
index 0ac706f..40db9df 100644
--- a/core/res/res/values/required_apps_managed_device.xml
+++ b/core/res/res/values/required_apps_managed_device.xml
@@ -19,7 +19,7 @@
 <resources>
     <!-- A list of apps to be retained on the managed device.
             Takes precedence over the disallowed apps lists. -->
-    <string-array name="required_apps_managed_device">
+    <string-array translatable="false" name="required_apps_managed_device">
         <item>com.android.settings</item>
         <item>com.android.contacts</item>
         <item>com.android.dialer</item>
diff --git a/core/res/res/values/required_apps_managed_profile.xml b/core/res/res/values/required_apps_managed_profile.xml
index a0b8492..c6b37e8 100644
--- a/core/res/res/values/required_apps_managed_profile.xml
+++ b/core/res/res/values/required_apps_managed_profile.xml
@@ -19,7 +19,7 @@
 <resources>
     <!-- A list of apps to be retained in the managed profile.
             Takes precedence over the disallowed apps lists. -->
-    <string-array name="required_apps_managed_profile">
+    <string-array translatable="false" name="required_apps_managed_profile">
         <item>com.android.contacts</item>
         <item>com.android.settings</item>
         <item>com.android.providers.downloads</item>
diff --git a/core/res/res/values/required_apps_managed_user.xml b/core/res/res/values/required_apps_managed_user.xml
index e8fdb21..8800e535 100644
--- a/core/res/res/values/required_apps_managed_user.xml
+++ b/core/res/res/values/required_apps_managed_user.xml
@@ -19,7 +19,7 @@
 <resources>
     <!-- A list of apps to be retained on the managed user.
             Takes precedence over the disallowed apps lists. -->
-    <string-array name="required_apps_managed_user">
+    <string-array translatable="false" name="required_apps_managed_user">
         <item>com.android.settings</item>
         <item>com.android.contacts</item>
         <item>com.android.dialer</item>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 4343ba0..9f582ad 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -1578,8 +1578,10 @@
   <java-symbol type="anim" name="voice_activity_close_enter" />
   <java-symbol type="anim" name="voice_activity_open_exit" />
   <java-symbol type="anim" name="voice_activity_open_enter" />
-  <java-symbol type="anim" name="activity_open_exit" />
-  <java-symbol type="anim" name="activity_open_enter" />
+  <java-symbol type="anim" name="task_open_exit" />
+  <java-symbol type="anim" name="task_open_enter" />
+  <java-symbol type="anim" name="cross_profile_apps_thumbnail_enter" />
+  <java-symbol type="anim" name="task_open_enter_cross_profile_apps" />
 
   <java-symbol type="array" name="config_autoRotationTiltTolerance" />
   <java-symbol type="array" name="config_keyboardTapVibePattern" />
@@ -1726,6 +1728,7 @@
   <java-symbol type="style" name="Theme.ExpandedMenu" />
   <java-symbol type="string" name="forward_intent_to_owner" />
   <java-symbol type="string" name="forward_intent_to_work" />
+  <java-symbol type="dimen" name="cross_profile_apps_thumbnail_size" />
 
   <!-- From services -->
   <java-symbol type="anim" name="screen_rotate_0_enter" />
diff --git a/core/res/res/values/vendor_disallowed_apps_managed_device.xml b/core/res/res/values/vendor_disallowed_apps_managed_device.xml
index c826d27..493cd60 100644
--- a/core/res/res/values/vendor_disallowed_apps_managed_device.xml
+++ b/core/res/res/values/vendor_disallowed_apps_managed_device.xml
@@ -18,6 +18,6 @@
 -->
 <resources>
     <!-- A list of apps to be removed from the managed device by a particular vendor. -->
-    <string-array name="vendor_disallowed_apps_managed_device">
+    <string-array translatable="false" name="vendor_disallowed_apps_managed_device">
     </string-array>
 </resources>
diff --git a/core/res/res/values/vendor_disallowed_apps_managed_profile.xml b/core/res/res/values/vendor_disallowed_apps_managed_profile.xml
index 5fcb2778..84cab5f 100644
--- a/core/res/res/values/vendor_disallowed_apps_managed_profile.xml
+++ b/core/res/res/values/vendor_disallowed_apps_managed_profile.xml
@@ -18,6 +18,6 @@
 -->
 <resources>
     <!-- A list of apps to be removed from the managed profile by a particular vendor. -->
-    <string-array name="vendor_disallowed_apps_managed_profile">
+    <string-array translatable="false" name="vendor_disallowed_apps_managed_profile">
     </string-array>
 </resources>
diff --git a/core/res/res/values/vendor_disallowed_apps_managed_user.xml b/core/res/res/values/vendor_disallowed_apps_managed_user.xml
index 3355d77..975bb5d 100644
--- a/core/res/res/values/vendor_disallowed_apps_managed_user.xml
+++ b/core/res/res/values/vendor_disallowed_apps_managed_user.xml
@@ -18,6 +18,6 @@
 -->
 <resources>
     <!-- A list of apps to be removed from the managed user by a particular vendor. -->
-    <string-array name="vendor_disallowed_apps_managed_user">
+    <string-array translatable="false" name="vendor_disallowed_apps_managed_user">
     </string-array>
 </resources>
diff --git a/core/res/res/values/vendor_required_apps_managed_device.xml b/core/res/res/values/vendor_required_apps_managed_device.xml
index e684e22..1ef48db 100644
--- a/core/res/res/values/vendor_required_apps_managed_device.xml
+++ b/core/res/res/values/vendor_required_apps_managed_device.xml
@@ -19,6 +19,6 @@
 <resources>
     <!-- A list of apps to be retained on the managed device by a particular vendor.
             Takes precedence over the disallowed apps lists. -->
-    <string-array name="vendor_required_apps_managed_device">
+    <string-array translatable="false" name="vendor_required_apps_managed_device">
     </string-array>
 </resources>
diff --git a/core/res/res/values/vendor_required_apps_managed_profile.xml b/core/res/res/values/vendor_required_apps_managed_profile.xml
index 4a3edf8..49cfa8b 100644
--- a/core/res/res/values/vendor_required_apps_managed_profile.xml
+++ b/core/res/res/values/vendor_required_apps_managed_profile.xml
@@ -19,6 +19,6 @@
 <resources>
     <!-- A list of apps to be retained in the managed profile by a particular vendor.
             Takes precedence over the disallowed apps lists. -->
-    <string-array name="vendor_required_apps_managed_profile">
+    <string-array translatable="false" name="vendor_required_apps_managed_profile">
     </string-array>
 </resources>
diff --git a/core/res/res/values/vendor_required_apps_managed_user.xml b/core/res/res/values/vendor_required_apps_managed_user.xml
index 71dbd62..bad7803 100644
--- a/core/res/res/values/vendor_required_apps_managed_user.xml
+++ b/core/res/res/values/vendor_required_apps_managed_user.xml
@@ -19,6 +19,6 @@
 <resources>
     <!-- A list of apps to be retained on the managed user by a particular vendor.
             Takes precedence over the disallowed apps lists. -->
-    <string-array name="vendor_required_apps_managed_user">
+    <string-array translatable="false" name="vendor_required_apps_managed_user">
     </string-array>
 </resources>
diff --git a/core/tests/coretests/src/android/app/NotificationTest.java b/core/tests/coretests/src/android/app/NotificationTest.java
index 7183934..b51c677 100644
--- a/core/tests/coretests/src/android/app/NotificationTest.java
+++ b/core/tests/coretests/src/android/app/NotificationTest.java
@@ -28,6 +28,7 @@
 import android.graphics.BitmapFactory;
 import android.graphics.drawable.Icon;
 import android.media.session.MediaSession;
+import android.os.Build;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.support.test.InstrumentationRegistry;
@@ -215,9 +216,11 @@
     }
 
     @Test
-    public void testMessagingStyle_isGroupConversation() {
+    public void messagingStyle_isGroupConversation() {
+        mContext.getApplicationInfo().targetSdkVersion = Build.VERSION_CODES.P;
         Notification.MessagingStyle messagingStyle = new Notification.MessagingStyle("self name")
-                .setGroupConversation(true);
+                .setGroupConversation(true)
+                .setConversationTitle("test conversation title");
         Notification notification = new Notification.Builder(mContext, "test id")
                 .setSmallIcon(1)
                 .setContentTitle("test title")
@@ -228,6 +231,56 @@
         assertTrue(notification.extras.getBoolean(Notification.EXTRA_IS_GROUP_CONVERSATION));
     }
 
+    @Test
+    public void messagingStyle_isGroupConversation_noConversationTitle() {
+        mContext.getApplicationInfo().targetSdkVersion = Build.VERSION_CODES.P;
+        Notification.MessagingStyle messagingStyle = new Notification.MessagingStyle("self name")
+                .setGroupConversation(true)
+                .setConversationTitle(null);
+        Notification notification = new Notification.Builder(mContext, "test id")
+                .setSmallIcon(1)
+                .setContentTitle("test title")
+                .setStyle(messagingStyle)
+                .build();
+
+        assertTrue(messagingStyle.isGroupConversation());
+        assertTrue(notification.extras.getBoolean(Notification.EXTRA_IS_GROUP_CONVERSATION));
+    }
+
+    @Test
+    public void messagingStyle_isGroupConversation_withConversationTitle_legacy() {
+        // In legacy (version < P), isGroupConversation is controlled by conversationTitle.
+        mContext.getApplicationInfo().targetSdkVersion = Build.VERSION_CODES.O;
+        Notification.MessagingStyle messagingStyle = new Notification.MessagingStyle("self name")
+                .setGroupConversation(false)
+                .setConversationTitle("test conversation title");
+        Notification notification = new Notification.Builder(mContext, "test id")
+                .setSmallIcon(1)
+                .setContentTitle("test title")
+                .setStyle(messagingStyle)
+                .build();
+
+        assertTrue(messagingStyle.isGroupConversation());
+        assertFalse(notification.extras.getBoolean(Notification.EXTRA_IS_GROUP_CONVERSATION));
+    }
+
+    @Test
+    public void messagingStyle_isGroupConversation_withoutConversationTitle_legacy() {
+        // In legacy (version < P), isGroupConversation is controlled by conversationTitle.
+        mContext.getApplicationInfo().targetSdkVersion = Build.VERSION_CODES.O;
+        Notification.MessagingStyle messagingStyle = new Notification.MessagingStyle("self name")
+                .setGroupConversation(true)
+                .setConversationTitle(null);
+        Notification notification = new Notification.Builder(mContext, "test id")
+                .setSmallIcon(1)
+                .setContentTitle("test title")
+                .setStyle(messagingStyle)
+                .build();
+
+        assertFalse(messagingStyle.isGroupConversation());
+        assertTrue(notification.extras.getBoolean(Notification.EXTRA_IS_GROUP_CONVERSATION));
+    }
+
     private Notification.Builder getMediaNotification() {
         MediaSession session = new MediaSession(mContext, "test");
         return new Notification.Builder(mContext, "color")
diff --git a/core/tests/coretests/src/android/graphics/drawable/IconTest.java b/core/tests/coretests/src/android/graphics/drawable/IconTest.java
index b7a48c7..64fadc0 100644
--- a/core/tests/coretests/src/android/graphics/drawable/IconTest.java
+++ b/core/tests/coretests/src/android/graphics/drawable/IconTest.java
@@ -16,6 +16,8 @@
 
 package android.graphics.drawable;
 
+import static com.google.common.truth.Truth.assertThat;
+
 import android.graphics.Bitmap;
 import android.graphics.Canvas;
 import android.graphics.Region;
@@ -108,6 +110,19 @@
     }
 
     @SmallTest
+    public void testScaleDownIfNecessary() throws Exception {
+        final Bitmap bm = Bitmap.createBitmap(4321, 78, Bitmap.Config.ARGB_8888);
+        final Icon ic = Icon.createWithBitmap(bm);
+        ic.scaleDownIfNecessary(40, 20);
+
+        assertThat(bm.getWidth()).isEqualTo(4321);
+        assertThat(bm.getHeight()).isEqualTo(78);
+
+        assertThat(ic.getBitmap().getWidth()).isLessThan(41);
+        assertThat(ic.getBitmap().getHeight()).isLessThan(21);
+    }
+
+    @SmallTest
     public void testWithAdaptiveBitmap() throws Exception {
         final Bitmap bm1 = Bitmap.createBitmap(150, 150, Bitmap.Config.ARGB_8888);
 
diff --git a/core/tests/coretests/src/android/os/WorkSourceTest.java b/core/tests/coretests/src/android/os/WorkSourceTest.java
index 90b4575..566ac4d 100644
--- a/core/tests/coretests/src/android/os/WorkSourceTest.java
+++ b/core/tests/coretests/src/android/os/WorkSourceTest.java
@@ -331,4 +331,24 @@
         wc.addNode(200, "tag2");
         assertEquals(100, wc.getAttributionUid());
     }
+
+    public void testRemove_fromChainedWorkSource() {
+        WorkSource ws1 = new WorkSource();
+        ws1.createWorkChain().addNode(50, "foo");
+        ws1.createWorkChain().addNode(75, "bar");
+        ws1.add(100);
+
+        WorkSource ws2 = new WorkSource();
+        ws2.add(100);
+
+        assertTrue(ws1.remove(ws2));
+        assertEquals(2, ws1.getWorkChains().size());
+        assertEquals(50, ws1.getWorkChains().get(0).getAttributionUid());
+        assertEquals(75, ws1.getWorkChains().get(1).getAttributionUid());
+
+        ws2.createWorkChain().addNode(50, "foo");
+        assertTrue(ws1.remove(ws2));
+        assertEquals(1, ws1.getWorkChains().size());
+        assertEquals(75, ws1.getWorkChains().get(0).getAttributionUid());
+    }
 }
diff --git a/core/tests/coretests/src/android/provider/SettingsBackupTest.java b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
index fc86500..7cfedc8 100644
--- a/core/tests/coretests/src/android/provider/SettingsBackupTest.java
+++ b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
@@ -112,6 +112,7 @@
                     Settings.Global.BATTERY_DISCHARGE_DURATION_THRESHOLD,
                     Settings.Global.BATTERY_DISCHARGE_THRESHOLD,
                     Settings.Global.BATTERY_SAVER_DEVICE_SPECIFIC_CONSTANTS,
+                    Settings.Global.BATTERY_STATS_CONSTANTS,
                     Settings.Global.BLE_SCAN_ALWAYS_AVAILABLE,
                     Settings.Global.BLUETOOTH_A2DP_SINK_PRIORITY_PREFIX,
                     Settings.Global.BLUETOOTH_A2DP_SRC_PRIORITY_PREFIX,
@@ -281,6 +282,7 @@
                     Settings.Global.NETWORK_SCORING_UI_ENABLED,
                     Settings.Global.NETWORK_SWITCH_NOTIFICATION_DAILY_LIMIT,
                     Settings.Global.NETWORK_SWITCH_NOTIFICATION_RATE_LIMIT_MILLIS,
+                    Settings.Global.NETWORK_WATCHLIST_ENABLED,
                     Settings.Global.NEW_CONTACT_AGGREGATOR,
                     Settings.Global.NITZ_UPDATE_DIFF,
                     Settings.Global.NITZ_UPDATE_SPACING,
@@ -452,7 +454,6 @@
                  Settings.Secure.COMPLETED_CATEGORY_PREFIX,
                  Settings.Secure.CONNECTIVITY_RELEASE_PENDING_INTENT_DELAY_MS,
                  Settings.Secure.DEFAULT_INPUT_METHOD,
-                 Settings.Secure.DEMO_USER_SETUP_COMPLETE,
                  Settings.Secure.DEVICE_PAIRED,
                  Settings.Secure.DIALER_DEFAULT_APPLICATION,
                  Settings.Secure.DISABLED_PRINT_SERVICES,
diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryStatsImplTest.java b/core/tests/coretests/src/com/android/internal/os/BatteryStatsImplTest.java
index 3794b5f..a8094ea 100644
--- a/core/tests/coretests/src/com/android/internal/os/BatteryStatsImplTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/BatteryStatsImplTest.java
@@ -71,7 +71,7 @@
     @Test
     public void testUpdateProcStateCpuTimes() {
         mBatteryStatsImpl.setOnBatteryInternal(true);
-        mBatteryStatsImpl.updateTimeBasesLocked(true, Display.STATE_ON, 0, 0);
+        mBatteryStatsImpl.updateTimeBasesLocked(false, Display.STATE_ON, 0, 0);
 
         final int[] testUids = {10032, 10048, 10145, 10139};
         final int[] testProcStates = {
@@ -98,7 +98,7 @@
             }
         }
 
-        mBatteryStatsImpl.updateProcStateCpuTimes();
+        mBatteryStatsImpl.updateProcStateCpuTimes(true, false);
 
         verifyNoPendingUids();
         for (int i = 0; i < testUids.length; ++i) {
@@ -125,7 +125,7 @@
         }
         addPendingUids(testUids, testProcStates);
 
-        mBatteryStatsImpl.updateProcStateCpuTimes();
+        mBatteryStatsImpl.updateProcStateCpuTimes(true, false);
 
         verifyNoPendingUids();
         for (int i = 0; i < testUids.length; ++i) {
@@ -157,7 +157,7 @@
         }
         addPendingUids(testUids, testProcStates);
 
-        mBatteryStatsImpl.updateProcStateCpuTimes();
+        mBatteryStatsImpl.updateProcStateCpuTimes(true, true);
 
         verifyNoPendingUids();
         for (int i = 0; i < testUids.length; ++i) {
@@ -196,7 +196,7 @@
         final long[] isolatedUidCpuTimes = {495784, 398473, 4895, 4905, 30984093};
         when(mKernelSingleUidTimeReader.readDeltaMs(childUid)).thenReturn(isolatedUidCpuTimes);
 
-        mBatteryStatsImpl.updateProcStateCpuTimes();
+        mBatteryStatsImpl.updateProcStateCpuTimes(true, true);
 
         verifyNoPendingUids();
         for (int i = 0; i < testUids.length; ++i) {
@@ -227,8 +227,8 @@
 
     @Test
     public void testCopyFromAllUidsCpuTimes() {
-        mBatteryStatsImpl.setOnBatteryInternal(true);
-        mBatteryStatsImpl.updateTimeBasesLocked(true, Display.STATE_ON, 0, 0);
+        mBatteryStatsImpl.setOnBatteryInternal(false);
+        mBatteryStatsImpl.updateTimeBasesLocked(false, Display.STATE_ON, 0, 0);
 
         final int[] testUids = {10032, 10048, 10145, 10139};
         final int[] testProcStates = {
@@ -264,7 +264,7 @@
                     .thenReturn(expectedCpuTimes[i]);
         }
 
-        mBatteryStatsImpl.copyFromAllUidsCpuTimes();
+        mBatteryStatsImpl.copyFromAllUidsCpuTimes(true, false);
 
         verifyNoPendingUids();
         for (int i = 0; i < testUids.length; ++i) {
diff --git a/core/tests/coretests/src/com/android/internal/os/BstatsCpuTimesValidationTest.java b/core/tests/coretests/src/com/android/internal/os/BstatsCpuTimesValidationTest.java
index e54fe7d..4d34721 100644
--- a/core/tests/coretests/src/com/android/internal/os/BstatsCpuTimesValidationTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/BstatsCpuTimesValidationTest.java
@@ -25,6 +25,8 @@
 import static android.os.BatteryStats.Uid.PROCESS_STATE_TOP_SLEEPING;
 import static android.os.BatteryStats.Uid.UID_PROCESS_TYPES;
 
+import static com.android.internal.os.BatteryStatsImpl.Constants.KEY_TRACK_CPU_TIMES_BY_PROC_STATE;
+
 import static junit.framework.Assert.assertNotNull;
 import static junit.framework.Assert.assertNull;
 import static junit.framework.Assert.assertTrue;
@@ -48,15 +50,19 @@
 import android.os.PowerManager;
 import android.os.Process;
 import android.os.SystemClock;
+import android.provider.Settings;
 import android.support.test.InstrumentationRegistry;
 import android.support.test.filters.LargeTest;
 import android.support.test.runner.AndroidJUnit4;
 import android.support.test.uiautomator.UiDevice;
+import android.text.TextUtils;
 import android.util.DebugUtils;
 import android.util.Log;
 
 import org.junit.BeforeClass;
+import org.junit.Rule;
 import org.junit.Test;
+import org.junit.rules.TestName;
 import org.junit.runner.RunWith;
 
 import java.util.Arrays;
@@ -86,6 +92,9 @@
     private static final int START_SERVICE_TIMEOUT_MS = 2000;
     private static final int START_ISOLATED_SERVICE_TIMEOUT_MS = 2000;
 
+    private static final int SETTING_UPDATE_TIMEOUT_MS = 2000;
+    private static final int SETTING_UPDATE_CHECK_INTERVAL_MS = 200;
+
     private static final int GENERAL_TIMEOUT_MS = 4000;
     private static final int GENERAL_INTERVAL_MS = 200;
 
@@ -97,6 +106,8 @@
     private static boolean sCpuFreqTimesAvailable;
     private static boolean sPerProcStateTimesAvailable;
 
+    @Rule public TestName testName = new TestName();
+
     @BeforeClass
     public static void setupOnce() throws Exception {
         sContext = InstrumentationRegistry.getContext();
@@ -123,6 +134,9 @@
     @Test
     public void testCpuFreqTimes() throws Exception {
         if (!sCpuFreqTimesAvailable) {
+            Log.w(TAG, "Skipping " + testName.getMethodName()
+                    + "; freqTimesAvailable=" + sCpuFreqTimesAvailable
+                    + ", procStateTimesAvailable=" + sPerProcStateTimesAvailable);
             return;
         }
 
@@ -148,6 +162,9 @@
     @Test
     public void testCpuFreqTimes_screenOff() throws Exception {
         if (!sCpuFreqTimesAvailable) {
+            Log.w(TAG, "Skipping " + testName.getMethodName()
+                    + "; freqTimesAvailable=" + sCpuFreqTimesAvailable
+                    + ", procStateTimesAvailable=" + sPerProcStateTimesAvailable);
             return;
         }
 
@@ -179,6 +196,9 @@
     @Test
     public void testCpuFreqTimes_isolatedProcess() throws Exception {
         if (!sCpuFreqTimesAvailable) {
+            Log.w(TAG, "Skipping " + testName.getMethodName()
+                    + "; freqTimesAvailable=" + sCpuFreqTimesAvailable
+                    + ", procStateTimesAvailable=" + sPerProcStateTimesAvailable);
             return;
         }
 
@@ -204,6 +224,9 @@
     @Test
     public void testCpuFreqTimes_stateTop() throws Exception {
         if (!sCpuFreqTimesAvailable || !sPerProcStateTimesAvailable) {
+            Log.w(TAG, "Skipping " + testName.getMethodName()
+                    + "; freqTimesAvailable=" + sCpuFreqTimesAvailable
+                    + ", procStateTimesAvailable=" + sPerProcStateTimesAvailable);
             return;
         }
 
@@ -234,6 +257,9 @@
     @Test
     public void testIsolatedCpuFreqTimes_stateService() throws Exception {
         if (!sCpuFreqTimesAvailable || !sPerProcStateTimesAvailable) {
+            Log.w(TAG, "Skipping " + testName.getMethodName()
+                    + "; freqTimesAvailable=" + sCpuFreqTimesAvailable
+                    + ", procStateTimesAvailable=" + sPerProcStateTimesAvailable);
             return;
         }
 
@@ -272,6 +298,9 @@
     @Test
     public void testCpuFreqTimes_stateTopSleeping() throws Exception {
         if (!sCpuFreqTimesAvailable || !sPerProcStateTimesAvailable) {
+            Log.w(TAG, "Skipping " + testName.getMethodName()
+                    + "; freqTimesAvailable=" + sCpuFreqTimesAvailable
+                    + ", procStateTimesAvailable=" + sPerProcStateTimesAvailable);
             return;
         }
 
@@ -302,6 +331,9 @@
     @Test
     public void testCpuFreqTimes_stateFgService() throws Exception {
         if (!sCpuFreqTimesAvailable || !sPerProcStateTimesAvailable) {
+            Log.w(TAG, "Skipping " + testName.getMethodName()
+                    + "; freqTimesAvailable=" + sCpuFreqTimesAvailable
+                    + ", procStateTimesAvailable=" + sPerProcStateTimesAvailable);
             return;
         }
 
@@ -332,6 +364,9 @@
     @Test
     public void testCpuFreqTimes_stateFg() throws Exception {
         if (!sCpuFreqTimesAvailable || !sPerProcStateTimesAvailable) {
+            Log.w(TAG, "Skipping " + testName.getMethodName()
+                    + "; freqTimesAvailable=" + sCpuFreqTimesAvailable
+                    + ", procStateTimesAvailable=" + sPerProcStateTimesAvailable);
             return;
         }
 
@@ -362,6 +397,9 @@
     @Test
     public void testCpuFreqTimes_stateBg() throws Exception {
         if (!sCpuFreqTimesAvailable || !sPerProcStateTimesAvailable) {
+            Log.w(TAG, "Skipping " + testName.getMethodName()
+                    + "; freqTimesAvailable=" + sCpuFreqTimesAvailable
+                    + ", procStateTimesAvailable=" + sPerProcStateTimesAvailable);
             return;
         }
 
@@ -392,6 +430,9 @@
     @Test
     public void testCpuFreqTimes_stateCached() throws Exception {
         if (!sCpuFreqTimesAvailable || !sPerProcStateTimesAvailable) {
+            Log.w(TAG, "Skipping " + testName.getMethodName()
+                    + "; freqTimesAvailable=" + sCpuFreqTimesAvailable
+                    + ", procStateTimesAvailable=" + sPerProcStateTimesAvailable);
             return;
         }
 
@@ -419,6 +460,124 @@
         batteryOffScreenOn();
     }
 
+    @Test
+    public void testCpuFreqTimes_trackingDisabled() throws Exception {
+        if (!sCpuFreqTimesAvailable || !sPerProcStateTimesAvailable) {
+            Log.w(TAG, "Skipping " + testName.getMethodName()
+                    + "; freqTimesAvailable=" + sCpuFreqTimesAvailable
+                    + ", procStateTimesAvailable=" + sPerProcStateTimesAvailable);
+            return;
+        }
+
+        final String bstatsConstants = Settings.Global.getString(sContext.getContentResolver(),
+                Settings.Global.BATTERY_STATS_CONSTANTS);
+        try {
+            batteryOnScreenOn();
+            forceStop();
+            resetBatteryStats();
+            final long[] initialSnapshot = getAllCpuFreqTimes(sTestPkgUid);
+            assertNull("Initial snapshot should be null, initial="
+                    + Arrays.toString(initialSnapshot), initialSnapshot);
+            assertNull("Initial top state snapshot should be null",
+                    getAllCpuFreqTimes(sTestPkgUid, PROCESS_STATE_TOP));
+
+            doSomeWork(PROCESS_STATE_TOP);
+            forceStop();
+
+            final long[] cpuTimesMs = getAllCpuFreqTimes(sTestPkgUid, PROCESS_STATE_TOP);
+            final String msgCpuTimes = getAllCpuTimesMsg();
+            assertCpuTimesValid(cpuTimesMs);
+            long actualCpuTimeMs = 0;
+            for (int i = 0; i < cpuTimesMs.length / 2; ++i) {
+                actualCpuTimeMs += cpuTimesMs[i];
+            }
+            assertApproximateValue("Incorrect total cpu time, " + msgCpuTimes,
+                    WORK_DURATION_MS, actualCpuTimeMs);
+
+            updateTrackPerProcStateCpuTimesSetting(bstatsConstants, false);
+
+            doSomeWork(PROCESS_STATE_TOP);
+            forceStop();
+
+            final long[] cpuTimesMs2 = getAllCpuFreqTimes(sTestPkgUid, PROCESS_STATE_TOP);
+            assertCpuTimesValid(cpuTimesMs2);
+            assertCpuTimesEqual(cpuTimesMs2, cpuTimesMs, 20,
+                    "Unexpected cpu times with tracking off");
+
+            updateTrackPerProcStateCpuTimesSetting(bstatsConstants, true);
+
+            final long[] cpuTimesMs3 = getAllCpuFreqTimes(sTestPkgUid, PROCESS_STATE_TOP);
+            assertCpuTimesValid(cpuTimesMs3);
+            assertCpuTimesEqual(cpuTimesMs3, cpuTimesMs, 20,
+                    "Unexpected cpu times after turning on tracking");
+
+            doSomeWork(PROCESS_STATE_TOP);
+            forceStop();
+
+            final long[] cpuTimesMs4 = getAllCpuFreqTimes(sTestPkgUid, PROCESS_STATE_TOP);
+            assertCpuTimesValid(cpuTimesMs4);
+            actualCpuTimeMs = 0;
+            for (int i = 0; i < cpuTimesMs.length / 2; ++i) {
+                actualCpuTimeMs += cpuTimesMs[i];
+            }
+            assertApproximateValue("Incorrect total cpu time, " + msgCpuTimes,
+                    2 * WORK_DURATION_MS, actualCpuTimeMs);
+
+            batteryOffScreenOn();
+        } finally {
+            Settings.Global.putString(sContext.getContentResolver(),
+                    Settings.Global.BATTERY_STATS_CONSTANTS, bstatsConstants);
+        }
+    }
+
+    private void assertCpuTimesEqual(long[] actual, long[] expected, long delta, String errMsg) {
+        for (int i = actual.length - 1; i >= 0; --i) {
+            if (actual[i] > expected[i] + delta || actual[i] < expected[i]) {
+                fail(errMsg + ", actual=" + actual + ", expected=" + expected + ", delta=" + delta);
+            }
+        }
+    }
+
+    private void updateTrackPerProcStateCpuTimesSetting(String originalConstants, boolean enabled)
+            throws Exception {
+        final String newConstants;
+        final String setting = KEY_TRACK_CPU_TIMES_BY_PROC_STATE + "=" + enabled;
+        if (originalConstants == null || "null".equals(originalConstants)) {
+            newConstants = setting;
+        } else if (originalConstants.contains(KEY_TRACK_CPU_TIMES_BY_PROC_STATE)) {
+            newConstants = originalConstants.replaceAll(
+                    KEY_TRACK_CPU_TIMES_BY_PROC_STATE + "=(true|false)", setting);
+        } else {
+            newConstants = originalConstants + "," + setting;
+        }
+        Settings.Global.putString(sContext.getContentResolver(),
+                Settings.Global.BATTERY_STATS_CONSTANTS, newConstants);
+        assertTrackPerProcStateCpuTimesSetting(enabled);
+    }
+
+    private void assertTrackPerProcStateCpuTimesSetting(boolean enabled) throws Exception {
+        final String expectedValue = Boolean.toString(enabled);
+        assertDelayedCondition("Unexpected value for " + KEY_TRACK_CPU_TIMES_BY_PROC_STATE, () -> {
+            final String actualValue = getSettingValueFromDump(KEY_TRACK_CPU_TIMES_BY_PROC_STATE);
+            return expectedValue.equals(actualValue)
+                    ? null : "expected=" + expectedValue + ", actual=" + actualValue;
+        }, SETTING_UPDATE_TIMEOUT_MS, SETTING_UPDATE_CHECK_INTERVAL_MS);
+    }
+
+    private String getSettingValueFromDump(String key) throws Exception {
+        final String settingsDump = executeCmdSilent("dumpsys batterystats --settings");
+        final TextUtils.SimpleStringSplitter splitter = new TextUtils.SimpleStringSplitter('\n');
+        splitter.setString(settingsDump);
+        String next;
+        while (splitter.hasNext()) {
+            next = splitter.next();
+            if (next.startsWith(key)) {
+                return next.split("=")[1];
+            }
+        }
+        return null;
+    }
+
     private void assertCpuTimesValid(long[] cpuTimes) {
         assertNotNull(cpuTimes);
         for (int i = 0; i < cpuTimes.length; ++i) {
@@ -750,13 +909,18 @@
     }
 
     private void assertDelayedCondition(String errMsgPrefix, ExpectedCondition condition)
-            throws Exception {
-        final long endTime = SystemClock.uptimeMillis() + GENERAL_TIMEOUT_MS;
+        throws Exception {
+        assertDelayedCondition(errMsgPrefix, condition, GENERAL_TIMEOUT_MS, GENERAL_INTERVAL_MS);
+    }
+
+    private void assertDelayedCondition(String errMsgPrefix, ExpectedCondition condition,
+            long timeoutMs, long checkIntervalMs) throws Exception {
+        final long endTime = SystemClock.uptimeMillis() + timeoutMs;
         while (SystemClock.uptimeMillis() <= endTime) {
             if (condition.getErrIfNotTrue() == null) {
                 return;
             }
-            SystemClock.sleep(GENERAL_INTERVAL_MS);
+            SystemClock.sleep(checkIntervalMs);
         }
         final String errMsg = condition.getErrIfNotTrue();
         if (errMsg != null) {
diff --git a/core/tests/coretests/src/com/android/internal/os/LongSamplingCounterArrayTest.java b/core/tests/coretests/src/com/android/internal/os/LongSamplingCounterArrayTest.java
index 27aec56..37b4e41a 100644
--- a/core/tests/coretests/src/com/android/internal/os/LongSamplingCounterArrayTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/LongSamplingCounterArrayTest.java
@@ -16,7 +16,9 @@
 
 package com.android.internal.os;
 
+import static android.os.BatteryStats.STATS_CURRENT;
 import static android.os.BatteryStats.STATS_SINCE_CHARGED;
+import static android.os.BatteryStats.STATS_SINCE_UNPLUGGED;
 
 import static com.android.internal.os.BatteryStatsImpl.LongSamplingCounterArray;
 import static com.android.internal.os.BatteryStatsImpl.TimeBase;
@@ -61,7 +63,6 @@
 
     private static final long[] COUNTS = {1111, 2222, 3333, 4444};
     private static final long[] LOADED_COUNTS = {5555, 6666, 7777, 8888};
-    private static final long[] PLUGGED_COUNTS = {9999, 11111, 22222, 33333};
     private static final long[] UNPLUGGED_COUNTS = {44444, 55555, 66666, 77777};
     private static final long[] ZEROES = {0, 0, 0, 0};
 
@@ -83,11 +84,10 @@
         parcel.setDataPosition(0);
 
         // Now clear counterArray and verify values are read from parcel correctly.
-        updateCounts(null, null, null, null);
+        updateCounts(null, null, null);
         mCounterArray = LongSamplingCounterArray.readFromParcel(parcel, mTimeBase);
         assertArrayEquals(COUNTS, mCounterArray.mCounts, "Unexpected counts");
         assertArrayEquals(LOADED_COUNTS, mCounterArray.mLoadedCounts, "Unexpected loadedCounts");
-        assertArrayEquals(COUNTS, mCounterArray.mPluggedCounts, "Unexpected pluggedCounts");
         assertArrayEquals(UNPLUGGED_COUNTS, mCounterArray.mUnpluggedCounts,
                 "Unexpected unpluggedCounts");
         parcel.recycle();
@@ -101,11 +101,10 @@
         parcel.setDataPosition(0);
 
         // Now clear counterArray and verify values are read from parcel correctly.
-        updateCounts(null, null, null, null);
+        updateCounts(null, null, null);
         mCounterArray = LongSamplingCounterArray.readSummaryFromParcelLocked(parcel, mTimeBase);
         assertArrayEquals(COUNTS, mCounterArray.mCounts, "Unexpected counts");
         assertArrayEquals(COUNTS, mCounterArray.mLoadedCounts, "Unexpected loadedCounts");
-        assertArrayEquals(COUNTS, mCounterArray.mPluggedCounts, "Unexpected pluggedCounts");
         assertArrayEquals(COUNTS, mCounterArray.mUnpluggedCounts, "Unexpected unpluggedCounts");
         parcel.recycle();
     }
@@ -116,8 +115,7 @@
         mCounterArray.onTimeStarted(0, 0, 0);
         assertArrayEquals(COUNTS, mCounterArray.mCounts, "Unexpected counts");
         assertArrayEquals(LOADED_COUNTS, mCounterArray.mLoadedCounts, "Unexpected loadedCounts");
-        assertArrayEquals(PLUGGED_COUNTS, mCounterArray.mPluggedCounts, "Unexpected pluggedCounts");
-        assertArrayEquals(PLUGGED_COUNTS, mCounterArray.mUnpluggedCounts,
+        assertArrayEquals(COUNTS, mCounterArray.mUnpluggedCounts,
                 "Unexpected unpluggedCounts");
     }
 
@@ -127,7 +125,6 @@
         mCounterArray.onTimeStopped(0, 0, 0);
         assertArrayEquals(COUNTS, mCounterArray.mCounts, "Unexpected counts");
         assertArrayEquals(LOADED_COUNTS, mCounterArray.mLoadedCounts, "Unexpected loadedCounts");
-        assertArrayEquals(COUNTS, mCounterArray.mPluggedCounts, "Unexpected pluggedCounts");
         assertArrayEquals(UNPLUGGED_COUNTS, mCounterArray.mUnpluggedCounts,
                 "Unexpected unpluggedCounts");
     }
@@ -137,24 +134,50 @@
         initializeCounterArrayWithDefaultValues();
 
         when(mTimeBase.isRunning()).thenReturn(false);
-        long[] actualVal = mCounterArray.getCountsLocked(STATS_SINCE_CHARGED);
-        long[] expectedVal = PLUGGED_COUNTS;
-        assertArrayEquals(expectedVal, actualVal, "Unexpected values");
+        assertArrayEquals(COUNTS, mCounterArray.getCountsLocked(STATS_SINCE_CHARGED),
+                "Unexpected values");
+        assertArrayEquals(subtract(COUNTS, LOADED_COUNTS),
+                mCounterArray.getCountsLocked(STATS_CURRENT), "Unexpected values");
+        assertArrayEquals(subtract(COUNTS, UNPLUGGED_COUNTS),
+                mCounterArray.getCountsLocked(STATS_SINCE_UNPLUGGED), "Unexpected values");
 
         when(mTimeBase.isRunning()).thenReturn(true);
-        actualVal = mCounterArray.getCountsLocked(STATS_SINCE_CHARGED);
-        expectedVal = COUNTS;
-        assertArrayEquals(expectedVal, actualVal, "Unexpected values");
+        assertArrayEquals(COUNTS, mCounterArray.getCountsLocked(STATS_SINCE_CHARGED),
+                "Unexpected values");
+        assertArrayEquals(subtract(COUNTS, LOADED_COUNTS),
+                mCounterArray.getCountsLocked(STATS_CURRENT), "Unexpected values");
+        assertArrayEquals(subtract(COUNTS, UNPLUGGED_COUNTS),
+                mCounterArray.getCountsLocked(STATS_SINCE_UNPLUGGED), "Unexpected values");
+    }
+
+    private long[] subtract(long[] val, long[] toSubtract) {
+        final long[] result = val.clone();
+        if (toSubtract != null) {
+            for (int i = val.length - 1; i >= 0; --i) {
+                result[i] -= toSubtract[i];
+            }
+        }
+        return result;
     }
 
     @Test
     public void testAddCountLocked() {
+        updateCounts(null, null, null);
         final long[] deltas = {123, 234, 345, 456};
         when(mTimeBase.isRunning()).thenReturn(true);
         mCounterArray.addCountLocked(deltas);
         assertArrayEquals(deltas, mCounterArray.mCounts, "Unexpected counts");
         assertArrayEquals(null, mCounterArray.mLoadedCounts, "Unexpected loadedCounts");
-        assertArrayEquals(null, mCounterArray.mPluggedCounts, "Unexpected pluggedCounts");
+        assertArrayEquals(null, mCounterArray.mUnpluggedCounts, "Unexpected unpluggedCounts");
+
+        updateCounts(null, null, null);
+        mCounterArray.addCountLocked(deltas, false);
+        assertArrayEquals(null, mCounterArray.mCounts, "Unexpected counts");
+        assertArrayEquals(null, mCounterArray.mLoadedCounts, "Unexpected loadedCounts");
+        assertArrayEquals(null, mCounterArray.mUnpluggedCounts, "Unexpected unpluggedCounts");
+        mCounterArray.addCountLocked(deltas, true);
+        assertArrayEquals(deltas, mCounterArray.mCounts, "Unexpected counts");
+        assertArrayEquals(null, mCounterArray.mLoadedCounts, "Unexpected loadedCounts");
         assertArrayEquals(null, mCounterArray.mUnpluggedCounts, "Unexpected unpluggedCounts");
 
         initializeCounterArrayWithDefaultValues();
@@ -165,7 +188,18 @@
         mCounterArray.addCountLocked(deltas);
         assertArrayEquals(newCounts, mCounterArray.mCounts, "Unexpected counts");
         assertArrayEquals(LOADED_COUNTS, mCounterArray.mLoadedCounts, "Unexpected loadedCounts");
-        assertArrayEquals(PLUGGED_COUNTS, mCounterArray.mPluggedCounts, "Unexpected pluggedCounts");
+        assertArrayEquals(UNPLUGGED_COUNTS, mCounterArray.mUnpluggedCounts,
+                "Unexpected unpluggedCounts");
+
+        initializeCounterArrayWithDefaultValues();
+        mCounterArray.addCountLocked(deltas, false);
+        assertArrayEquals(COUNTS, mCounterArray.mCounts, "Unexpected counts");
+        assertArrayEquals(LOADED_COUNTS, mCounterArray.mLoadedCounts, "Unexpected loadedCounts");
+        assertArrayEquals(UNPLUGGED_COUNTS, mCounterArray.mUnpluggedCounts,
+                "Unexpected unpluggedCounts");
+        mCounterArray.addCountLocked(deltas, true);
+        assertArrayEquals(newCounts, mCounterArray.mCounts, "Unexpected counts");
+        assertArrayEquals(LOADED_COUNTS, mCounterArray.mLoadedCounts, "Unexpected loadedCounts");
         assertArrayEquals(UNPLUGGED_COUNTS, mCounterArray.mUnpluggedCounts,
                 "Unexpected unpluggedCounts");
     }
@@ -177,7 +211,6 @@
         mCounterArray.reset(false /* detachIfReset */);
         assertArrayEquals(ZEROES, mCounterArray.mCounts, "Unexpected counts");
         assertArrayEquals(ZEROES, mCounterArray.mLoadedCounts, "Unexpected loadedCounts");
-        assertArrayEquals(ZEROES, mCounterArray.mPluggedCounts, "Unexpected pluggedCounts");
         assertArrayEquals(ZEROES, mCounterArray.mUnpluggedCounts, "Unexpected unpluggedCounts");
         verifyZeroInteractions(mTimeBase);
 
@@ -186,7 +219,6 @@
         mCounterArray.reset(true /* detachIfReset */);
         assertArrayEquals(ZEROES, mCounterArray.mCounts, "Unexpected counts");
         assertArrayEquals(ZEROES, mCounterArray.mLoadedCounts, "Unexpected loadedCounts");
-        assertArrayEquals(ZEROES, mCounterArray.mPluggedCounts, "Unexpected pluggedCounts");
         assertArrayEquals(ZEROES, mCounterArray.mUnpluggedCounts, "Unexpected unpluggedCounts");
         verify(mTimeBase).remove(mCounterArray);
         verifyNoMoreInteractions(mTimeBase);
@@ -200,7 +232,7 @@
     }
 
     private void initializeCounterArrayWithDefaultValues() {
-        updateCounts(COUNTS, LOADED_COUNTS, PLUGGED_COUNTS, UNPLUGGED_COUNTS);
+        updateCounts(COUNTS, LOADED_COUNTS, UNPLUGGED_COUNTS);
     }
 
     private void assertArrayEquals(long[] expected, long[] actual, String msg) {
@@ -208,11 +240,9 @@
                 + ", actual: " + Arrays.toString(actual), Arrays.equals(expected, actual));
     }
 
-    private void updateCounts(long[] counts, long[] loadedCounts,
-            long[] pluggedCounts, long[] unpluggedCounts) {
-        mCounterArray.mCounts = counts;
-        mCounterArray.mLoadedCounts = loadedCounts;
-        mCounterArray.mPluggedCounts = pluggedCounts;
-        mCounterArray.mUnpluggedCounts = unpluggedCounts;
+    private void updateCounts(long[] counts, long[] loadedCounts, long[] unpluggedCounts) {
+        mCounterArray.mCounts = counts == null ? null : counts.clone();
+        mCounterArray.mLoadedCounts = loadedCounts == null ? null : loadedCounts.clone();
+        mCounterArray.mUnpluggedCounts = unpluggedCounts == null ? null : unpluggedCounts.clone();
     }
 }
diff --git a/core/tests/coretests/src/com/android/internal/os/MockBatteryStatsImpl.java b/core/tests/coretests/src/com/android/internal/os/MockBatteryStatsImpl.java
index f19ff67..6c5a2aa 100644
--- a/core/tests/coretests/src/com/android/internal/os/MockBatteryStatsImpl.java
+++ b/core/tests/coretests/src/com/android/internal/os/MockBatteryStatsImpl.java
@@ -139,12 +139,19 @@
         }
 
         @Override
-        public Future<?> scheduleReadProcStateCpuTimes() {
+        public Future<?> scheduleCpuSyncDueToSettingChange() {
             return null;
         }
 
         @Override
-        public Future<?> scheduleCopyFromAllUidsCpuTimes() {
+        public Future<?> scheduleReadProcStateCpuTimes(
+                boolean onBattery, boolean onBatteryScreenOff) {
+            return null;
+        }
+
+        @Override
+        public Future<?> scheduleCopyFromAllUidsCpuTimes(
+                boolean onBattery, boolean onBatteryScreenOff) {
             return null;
         }
 
diff --git a/data/etc/platform.xml b/data/etc/platform.xml
index 1affba0..d2c855b 100644
--- a/data/etc/platform.xml
+++ b/data/etc/platform.xml
@@ -112,6 +112,10 @@
         <group gid="media" />
     </permission>
 
+    <permission name="android.permission.USE_RESERVED_DISK">
+        <group gid="reserved_disk" />
+    </permission>
+
     <!-- These are permissions that were mapped to gids but we need
          to keep them here until an upgrade from L to the current
          version is to be supported. These permissions are built-in
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index 7bb2859..4732bec 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -136,6 +136,7 @@
         <permission name="android.permission.MANAGE_USERS"/>
         <permission name="android.permission.OBSERVE_GRANT_REVOKE_PERMISSIONS"/>
         <permission name="android.permission.UPDATE_APP_OPS_STATS"/>
+        <permission name="android.permission.USE_RESERVED_DISK"/>
     </privapp-permissions>
 
     <privapp-permissions package="com.android.phone">
@@ -181,6 +182,7 @@
     <privapp-permissions package="com.android.providers.calendar">
         <permission name="android.permission.GET_ACCOUNTS_PRIVILEGED"/>
         <permission name="android.permission.UPDATE_APP_OPS_STATS"/>
+        <permission name="android.permission.USE_RESERVED_DISK"/>
     </privapp-permissions>
 
     <privapp-permissions package="com.android.providers.contacts">
@@ -189,6 +191,7 @@
         <permission name="android.permission.INTERACT_ACROSS_USERS"/>
         <permission name="android.permission.MANAGE_USERS"/>
         <permission name="android.permission.UPDATE_APP_OPS_STATS"/>
+        <permission name="android.permission.USE_RESERVED_DISK"/>
     </privapp-permissions>
 
     <privapp-permissions package="com.android.providers.downloads">
@@ -203,12 +206,14 @@
         <permission name="android.permission.ACCESS_MTP"/>
         <permission name="android.permission.INTERACT_ACROSS_USERS"/>
         <permission name="android.permission.MANAGE_USERS"/>
+        <permission name="android.permission.USE_RESERVED_DISK"/>
         <permission name="android.permission.WRITE_MEDIA_STORAGE"/>
     </privapp-permissions>
 
     <privapp-permissions package="com.android.providers.telephony">
         <permission name="android.permission.INTERACT_ACROSS_USERS"/>
         <permission name="android.permission.MODIFY_PHONE_STATE"/>
+        <permission name="android.permission.USE_RESERVED_DISK"/>
     </privapp-permissions>
 
     <privapp-permissions package="com.android.provision">
@@ -253,6 +258,7 @@
         <permission name="android.permission.SET_TIME"/>
         <permission name="android.permission.STATUS_BAR"/>
         <permission name="android.permission.TETHER_PRIVILEGED"/>
+        <permission name="android.permission.USE_RESERVED_DISK"/>
         <permission name="android.permission.USER_ACTIVITY"/>
         <permission name="android.permission.WRITE_APN_SETTINGS"/>
         <permission name="android.permission.WRITE_MEDIA_STORAGE"/>
@@ -316,6 +322,7 @@
         <permission name="android.permission.STOP_APP_SWITCHES"/>
         <permission name="android.permission.SUBSTITUTE_NOTIFICATION_APP_NAME"/>
         <permission name="android.permission.UPDATE_APP_OPS_STATS"/>
+        <permission name="android.permission.USE_RESERVED_DISK"/>
         <permission name="android.permission.WRITE_MEDIA_STORAGE"/>
         <permission name="android.permission.WRITE_SECURE_SETTINGS"/>
     </privapp-permissions>
@@ -329,6 +336,7 @@
         <permission name="android.permission.INTERACT_ACROSS_USERS"/>
         <permission name="android.permission.MANAGE_USERS"/>
         <permission name="android.permission.PACKAGE_USAGE_STATS"/>
+        <permission name="android.permission.USE_RESERVED_DISK"/>
         <permission name="android.permission.WRITE_SECURE_SETTINGS"/>
     </privapp-permissions>
 
@@ -365,6 +373,7 @@
         <permission name="android.permission.SUBSTITUTE_NOTIFICATION_APP_NAME"/>
         <permission name="android.permission.TETHER_PRIVILEGED"/>
         <permission name="android.permission.UPDATE_APP_OPS_STATS"/>
+        <permission name="android.permission.USE_RESERVED_DISK"/>
         <permission name="android.permission.WRITE_DREAM_STATE"/>
         <permission name="android.permission.WRITE_MEDIA_STORAGE"/>
         <permission name="android.permission.WRITE_SECURE_SETTINGS"/>
diff --git a/docs/html/_shared/_reference-head-tags.html b/docs/html/_shared/_reference-head-tags.html
new file mode 100644
index 0000000..f66a7b7
--- /dev/null
+++ b/docs/html/_shared/_reference-head-tags.html
@@ -0,0 +1,8 @@
+  <!-- Added to Gerrit so we can stage reference docs directly from
+       Android builds; this file should *not* be migrated to Piper -->
+
+  <meta name="top_category" value="develop" />
+  <meta name="subcategory" value="reference" />
+  <meta name="book_path" value="{% if book_path %}{{ book_path }}{% else %}/reference/_book.yaml{% endif %}" />
+  <meta name="project_path" value="/reference/_project.yaml" />
+  <meta name="page_type" value="reference" />
diff --git a/graphics/java/android/graphics/drawable/Icon.java b/graphics/java/android/graphics/drawable/Icon.java
index c329918..749b7594 100644
--- a/graphics/java/android/graphics/drawable/Icon.java
+++ b/graphics/java/android/graphics/drawable/Icon.java
@@ -819,8 +819,10 @@
         if (bitmapWidth > maxWidth || bitmapHeight > maxHeight) {
             float scale = Math.min((float) maxWidth / bitmapWidth,
                     (float) maxHeight / bitmapHeight);
-            bitmap = Bitmap.createScaledBitmap(bitmap, (int) (scale * bitmapWidth),
-                    (int) (scale * bitmapHeight), true /* filter */);
+            bitmap = Bitmap.createScaledBitmap(bitmap,
+                    Math.max(1, (int) (scale * bitmapWidth)),
+                    Math.max(1, (int) (scale * bitmapHeight)),
+                    true /* filter */);
         }
         return bitmap;
     }
diff --git a/libs/hwui/pipeline/skia/ShaderCache.cpp b/libs/hwui/pipeline/skia/ShaderCache.cpp
index 87edd69..c7a3014 100644
--- a/libs/hwui/pipeline/skia/ShaderCache.cpp
+++ b/libs/hwui/pipeline/skia/ShaderCache.cpp
@@ -64,7 +64,6 @@
     size_t keySize = key.size();
     std::lock_guard<std::mutex> lock(mMutex);
     if (!mInitialized) {
-        ALOGE("ShaderCache::load not initialized");
         return nullptr;
     }
 
@@ -103,7 +102,6 @@
     std::lock_guard<std::mutex> lock(mMutex);
 
     if (!mInitialized) {
-        ALOGE("ShaderCache::store not initialized");
         return;
     }
 
diff --git a/media/java/android/media/update/ApiLoader.java b/media/java/android/media/update/ApiLoader.java
new file mode 100644
index 0000000..b57e02d
--- /dev/null
+++ b/media/java/android/media/update/ApiLoader.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.update;
+
+import android.content.Context;
+import android.content.pm.PackageManager.NameNotFoundException;
+
+/**
+ * @hide
+ */
+public final class ApiLoader {
+    private static Object sMediaLibrary;
+
+    private static final String UPDATE_PACKAGE = "com.android.media.update";
+    private static final String UPDATE_CLASS = "com.android.media.update.ApiFactory";
+    private static final String UPDATE_METHOD = "initialize";
+
+    private ApiLoader() { }
+
+    public static StaticProvider getProvider(Context context) {
+        try {
+            return (StaticProvider) getMediaLibraryImpl(context);
+        } catch (NameNotFoundException | ReflectiveOperationException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    // TODO This method may do I/O; Ensure it does not violate (emit warnings in) strict mode.
+    private static synchronized Object getMediaLibraryImpl(Context appContext)
+            throws NameNotFoundException, ReflectiveOperationException {
+        if (sMediaLibrary != null) return sMediaLibrary;
+
+        // TODO Dynamically find the package name
+        Context libContext = appContext.createPackageContext(UPDATE_PACKAGE,
+                Context.CONTEXT_INCLUDE_CODE | Context.CONTEXT_IGNORE_SECURITY);
+        sMediaLibrary = libContext.getClassLoader()
+                .loadClass(UPDATE_CLASS)
+                .getMethod(UPDATE_METHOD, Context.class)
+                .invoke(null, appContext);
+        return sMediaLibrary;
+    }
+}
diff --git a/media/java/android/media/update/MediaController2Provider.java b/media/java/android/media/update/MediaController2Provider.java
new file mode 100644
index 0000000..71fbd08
--- /dev/null
+++ b/media/java/android/media/update/MediaController2Provider.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.update;
+
+import android.annotation.SystemApi;
+import android.media.session.MediaController;
+import android.view.View;
+import android.view.View.OnClickListener;
+
+/**
+ * Interface for connecting the public API to an updatable implementation.
+ *
+ * Each instance object is connected to one corresponding updatable object which implements the
+ * runtime behavior of that class. There should a corresponding provider method for all public
+ * methods.
+ *
+ * All methods behave as per their namesake in the public API.
+ *
+ * @see android.widget.MediaController2
+ *
+ * @hide
+ */
+// TODO @SystemApi
+public interface MediaController2Provider extends ViewProvider {
+    void setController_impl(MediaController controller);
+    void setAnchorView_impl(View view);
+    void show_impl();
+    void show_impl(int timeout);
+    boolean isShowing_impl();
+    void hide_impl();
+    void setPrevNextListeners_impl(OnClickListener next, OnClickListener prev);
+    void showCCButton_impl();
+    boolean isPlaying_impl();
+    int getCurrentPosition_impl();
+    int getBufferPercentage_impl();
+    boolean canPause_impl();
+    boolean canSeekBackward_impl();
+    boolean canSeekForward_impl();
+    void showSubtitle_impl();
+    void hideSubtitle_impl();
+}
diff --git a/media/java/android/media/update/StaticProvider.java b/media/java/android/media/update/StaticProvider.java
new file mode 100644
index 0000000..19f01c2
--- /dev/null
+++ b/media/java/android/media/update/StaticProvider.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.update;
+
+import android.annotation.SystemApi;
+import android.widget.MediaController2;
+
+/**
+ * Interface for connecting the public API to an updatable implementation.
+ *
+ * This interface provides access to constructors and static methods that are otherwise not directly
+ * accessible via an implementation object.
+ *
+ * @hide
+ */
+// TODO @SystemApi
+public interface StaticProvider {
+    MediaController2Provider createMediaController2(
+            MediaController2 instance, ViewProvider superProvider);
+}
diff --git a/media/java/android/media/update/ViewProvider.java b/media/java/android/media/update/ViewProvider.java
new file mode 100644
index 0000000..bc8f203
--- /dev/null
+++ b/media/java/android/media/update/ViewProvider.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.update;
+
+import android.annotation.SystemApi;
+import android.graphics.Canvas;
+import android.view.KeyEvent;
+import android.view.MotionEvent;
+
+/**
+ * Interface for connecting the public API to an updatable implementation.
+ *
+ * Each instance object is connected to one corresponding updatable object which implements the
+ * runtime behavior of that class. There should a corresponding provider method for all public
+ * methods.
+ *
+ * All methods behave as per their namesake in the public API.
+ *
+ * @see android.view.View
+ *
+ * @hide
+ */
+// TODO @SystemApi
+public interface ViewProvider {
+    // TODO Add more (all?) methods from View
+    void onAttachedToWindow_impl();
+    void onDetachedFromWindow_impl();
+    void onLayout_impl(boolean changed, int left, int top, int right, int bottom);
+    void draw_impl(Canvas canvas);
+    CharSequence getAccessibilityClassName_impl();
+    boolean onTouchEvent_impl(MotionEvent ev);
+    boolean onTrackballEvent_impl(MotionEvent ev);
+    boolean onKeyDown_impl(int keyCode, KeyEvent event);
+    void onFinishInflate_impl();
+    boolean dispatchKeyEvent_impl(KeyEvent event);
+    void setEnabled_impl(boolean enabled);
+}
diff --git a/packages/PrintSpooler/Android.mk b/packages/PrintSpooler/Android.mk
index 19e44e3..6feb8a6 100644
--- a/packages/PrintSpooler/Android.mk
+++ b/packages/PrintSpooler/Android.mk
@@ -18,17 +18,26 @@
 
 LOCAL_MODULE_TAGS := optional
 
-LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res frameworks/support/v7/recyclerview/res
-LOCAL_AAPT_FLAGS := --auto-add-overlay --extra-packages android.support.v7.recyclerview
+LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
+LOCAL_USE_AAPT2 := true
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 LOCAL_SRC_FILES += \
-        src/com/android/printspooler/renderer/IPdfRenderer.aidl \
-        src/com/android/printspooler/renderer/IPdfEditor.aidl
+    src/com/android/printspooler/renderer/IPdfRenderer.aidl \
+    src/com/android/printspooler/renderer/IPdfEditor.aidl
 
 LOCAL_PACKAGE_NAME := PrintSpooler
 
 LOCAL_JNI_SHARED_LIBRARIES := libprintspooler_jni
-LOCAL_STATIC_JAVA_LIBRARIES := android-support-v4 android-support-v7-recyclerview
+LOCAL_STATIC_ANDROID_LIBRARIES := \
+    android-support-v7-recyclerview \
+    android-support-compat \
+    android-support-media-compat \
+    android-support-core-utils \
+    android-support-core-ui \
+    android-support-fragment
+
+LOCAL_STATIC_JAVA_LIBRARIES := \
+    android-support-annotations
 
 include $(BUILD_PACKAGE)
 
diff --git a/packages/SettingsLib/res/values/styles_support_preference.xml b/packages/SettingsLib/res/values/styles_support_preference.xml
index cf9f3c6..59de6c9 100644
--- a/packages/SettingsLib/res/values/styles_support_preference.xml
+++ b/packages/SettingsLib/res/values/styles_support_preference.xml
@@ -66,6 +66,14 @@
         <item name="singleLineTitle">false</item>
     </style>
 
+    <!-- CheckBox Preferences -->
+    <style name="Preference.CheckBoxPreference.SettingsBase" parent="@style/Preference.CheckBoxPreference.Material">
+        <item name="allowDividerAbove">false</item>
+        <item name="allowDividerBelow">true</item>
+        <item name="iconSpaceReserved">true</item>
+        <item name="singleLineTitle">false</item>
+    </style>
+
     <!-- EditText Preferences -->
     <style name="Preference.EditTextPreference.SettingsBase"
            parent="@style/Preference.DialogPreference.EditTextPreference.Material">
@@ -86,6 +94,7 @@
         <item name="editTextPreferenceStyle">@style/Preference.EditTextPreference.SettingsBase</item>
         <item name="footerPreferenceStyle">@style/Preference.FooterPreference.SettingsBase</item>
         <item name="switchPreferenceStyle">@style/Preference.SwitchPreference.SettingsBase</item>
+        <item name="checkBoxPreferenceStyle">@style/Preference.CheckBoxPreference.SettingsBase</item>
         <item name="dropdownPreferenceStyle">@style/Preference.DropdownPreference.SettingsBase</item>
     </style>
 
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java
index f1fb208..ae88227 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java
@@ -288,65 +288,9 @@
 
     @Override
     public void onFullBackup(FullBackupDataOutput data)  throws IOException {
-        byte[] systemSettingsData = getSystemSettings();
-        byte[] secureSettingsData = getSecureSettings();
-        byte[] globalSettingsData = getGlobalSettings();
-        byte[] lockSettingsData   = getLockSettings(UserHandle.myUserId());
-        byte[] locale = mSettingsHelper.getLocaleData();
-        byte[] softApConfigData = getSoftAPConfiguration();
-        byte[] netPoliciesData = getNetworkPolicies();
-        byte[] wifiFullConfigData = getNewWifiConfigData();
-
-        // Write the data to the staging file, then emit that as our tarfile
-        // representation of the backed-up settings.
-        String root = getFilesDir().getAbsolutePath();
-        File stage = new File(root, STAGE_FILE);
-        try {
-            FileOutputStream filestream = new FileOutputStream(stage);
-            BufferedOutputStream bufstream = new BufferedOutputStream(filestream);
-            DataOutputStream out = new DataOutputStream(bufstream);
-
-            if (DEBUG_BACKUP) Log.d(TAG, "Writing flattened data version " + FULL_BACKUP_VERSION);
-            out.writeInt(FULL_BACKUP_VERSION);
-
-            if (DEBUG_BACKUP) Log.d(TAG, systemSettingsData.length + " bytes of settings data");
-            out.writeInt(systemSettingsData.length);
-            out.write(systemSettingsData);
-            if (DEBUG_BACKUP) {
-                Log.d(TAG, secureSettingsData.length + " bytes of secure settings data");
-            }
-            out.writeInt(secureSettingsData.length);
-            out.write(secureSettingsData);
-            if (DEBUG_BACKUP) {
-                Log.d(TAG, globalSettingsData.length + " bytes of global settings data");
-            }
-            out.writeInt(globalSettingsData.length);
-            out.write(globalSettingsData);
-            if (DEBUG_BACKUP) Log.d(TAG, locale.length + " bytes of locale data");
-            out.writeInt(locale.length);
-            out.write(locale);
-            if (DEBUG_BACKUP) Log.d(TAG, lockSettingsData.length + " bytes of lock settings data");
-            out.writeInt(lockSettingsData.length);
-            out.write(lockSettingsData);
-            if (DEBUG_BACKUP) Log.d(TAG, softApConfigData.length + " bytes of softap config data");
-            out.writeInt(softApConfigData.length);
-            out.write(softApConfigData);
-            if (DEBUG_BACKUP) Log.d(TAG, netPoliciesData.length + " bytes of net policies data");
-            out.writeInt(netPoliciesData.length);
-            out.write(netPoliciesData);
-            if (DEBUG_BACKUP) {
-                Log.d(TAG, wifiFullConfigData.length + " bytes of wifi config data");
-            }
-            out.writeInt(wifiFullConfigData.length);
-            out.write(wifiFullConfigData);
-
-            out.flush();    // also flushes downstream
-
-            // now we're set to emit the tar stream
-            fullBackupFile(stage, data);
-        } finally {
-            stage.delete();
-        }
+        // Full backup of SettingsBackupAgent support was removed in Android P. If you want to adb
+        // backup com.android.providers.settings package use \"-keyvalue\" flag.
+        // Full restore of SettingsBackupAgent is still available for backwards compatibility.
     }
 
     @Override
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
index 1d3f26ee..48de1c9 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
@@ -1720,9 +1720,6 @@
                 Settings.Secure.QS_TILES,
                 SecureSettingsProto.QS_TILES);
         dumpSetting(s, p,
-                Settings.Secure.DEMO_USER_SETUP_COMPLETE,
-                SecureSettingsProto.DEMO_USER_SETUP_COMPLETE);
-        dumpSetting(s, p,
                 Settings.Secure.INSTANT_APPS_ENABLED,
                 SecureSettingsProto.INSTANT_APPS_ENABLED);
         dumpSetting(s, p,
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index d675a7a..b3d6357 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -43,6 +43,7 @@
     <uses-permission android:name="android.permission.DISABLE_KEYGUARD" />
     <uses-permission android:name="android.permission.MANAGE_NETWORK_POLICY" />
     <uses-permission android:name="android.permission.MANAGE_USB" />
+    <uses-permission android:name="android.permission.USE_RESERVED_DISK" />
     <!-- System tool permissions granted to the shell. -->
     <uses-permission android:name="android.permission.REAL_GET_TASKS" />
     <uses-permission android:name="android.permission.CHANGE_CONFIGURATION" />
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index 29ecac0..aa2cdbb 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -49,6 +49,7 @@
     <uses-permission android:name="android.permission.CONFIGURE_WIFI_DISPLAY" />
     <uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" />
     <uses-permission android:name="android.permission.GET_APP_OPS_STATS" />
+    <uses-permission android:name="android.permission.USE_RESERVED_DISK" />
 
     <!-- Networking and telephony -->
     <uses-permission android:name="android.permission.BLUETOOTH" />
diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_status_area.xml b/packages/SystemUI/res-keyguard/layout/keyguard_status_area.xml
index b154d46..5e09e75 100644
--- a/packages/SystemUI/res-keyguard/layout/keyguard_status_area.xml
+++ b/packages/SystemUI/res-keyguard/layout/keyguard_status_area.xml
@@ -32,6 +32,8 @@
               android:layout_width="match_parent"
               android:layout_height="wrap_content"
               android:layout_marginBottom="@dimen/widget_vertical_padding"
+              android:paddingStart="64dp"
+              android:paddingEnd="64dp"
               android:theme="@style/TextAppearance.Keyguard"
     />
     <LinearLayout android:id="@+id/row"
diff --git a/packages/SystemUI/res-keyguard/values/dimens.xml b/packages/SystemUI/res-keyguard/values/dimens.xml
index 463af61..04cf6b0 100644
--- a/packages/SystemUI/res-keyguard/values/dimens.xml
+++ b/packages/SystemUI/res-keyguard/values/dimens.xml
@@ -57,7 +57,7 @@
     <dimen name="widget_separator_thickness">2dp</dimen>
     <dimen name="widget_horizontal_padding">8dp</dimen>
     <dimen name="widget_icon_size">16dp</dimen>
-    <dimen name="widget_icon_padding">4dp</dimen>
+    <dimen name="widget_icon_padding">8dp</dimen>
 
     <!-- The y translation to apply at the start in appear animations. -->
     <dimen name="appear_y_translation_start">32dp</dimen>
diff --git a/packages/SystemUI/res-keyguard/values/styles.xml b/packages/SystemUI/res-keyguard/values/styles.xml
index d50bab5..5f52e2a 100644
--- a/packages/SystemUI/res-keyguard/values/styles.xml
+++ b/packages/SystemUI/res-keyguard/values/styles.xml
@@ -83,13 +83,13 @@
         <item name="android:gravity">center</item>
         <item name="android:ellipsize">end</item>
         <item name="android:maxLines">2</item>
+        <item name="android:fontFamily">@*android:string/config_headlineFontFamilyLight</item>
     </style>
 
     <style name="TextAppearance.Keyguard.Secondary">
         <item name="android:layout_width">wrap_content</item>
         <item name="android:layout_height">wrap_content</item>
         <item name="android:textSize">@dimen/widget_label_font_size</item>
-        <item name="android:singleLine">true</item>
     </style>
 
 </resources>
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java
index b8adb6a..8135c61 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java
@@ -26,6 +26,9 @@
 import android.graphics.drawable.Drawable;
 import android.net.Uri;
 import android.provider.Settings;
+import android.text.Layout;
+import android.text.TextUtils;
+import android.text.TextUtils.TruncateAt;
 import android.util.AttributeSet;
 import android.util.Log;
 import android.view.View;
@@ -40,6 +43,7 @@
 import com.android.systemui.keyguard.KeyguardSliceProvider;
 import com.android.systemui.tuner.TunerService;
 
+import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.List;
 import java.util.function.Consumer;
@@ -129,7 +133,20 @@
                     android.app.slice.SliceItem.FORMAT_TEXT,
                     new String[]{android.app.slice.Slice.HINT_TITLE},
                     null /* nonHints */);
-            mTitle.setText(mainTitle.getText());
+            CharSequence title = mainTitle.getText();
+            mTitle.setText(title);
+
+            // Check if we're already ellipsizing the text.
+            // We're going to figure out the best possible line break if not.
+            Layout layout = mTitle.getLayout();
+            if (layout != null){
+                final int lineCount = layout.getLineCount();
+                if (lineCount > 0) {
+                    if (layout.getEllipsisCount(lineCount - 1) == 0) {
+                        mTitle.setText(findBestLineBreak(title));
+                    }
+                }
+            }
         }
 
         mClickActions.clear();
@@ -195,6 +212,46 @@
         mListener.accept(mHasHeader);
     }
 
+    /**
+     * Breaks a string in 2 lines where both have similar character count
+     * but first line is always longer.
+     *
+     * @param charSequence Original text.
+     * @return Optimal string.
+     */
+    private CharSequence findBestLineBreak(CharSequence charSequence) {
+        if (TextUtils.isEmpty(charSequence)) {
+            return charSequence;
+        }
+
+        String source = charSequence.toString();
+        // Ignore if there is only 1 word,
+        // or if line breaks were manually set.
+        if (source.contains("\n") || !source.contains(" ")) {
+            return source;
+        }
+
+        final String[] words = source.split(" ");
+        final StringBuilder optimalString = new StringBuilder(source.length());
+        int current = 0;
+        while (optimalString.length() < source.length() - optimalString.length()) {
+            optimalString.append(words[current]);
+            if (current < words.length - 1) {
+                optimalString.append(" ");
+            }
+            current++;
+        }
+        optimalString.append("\n");
+        for (int i = current; i < words.length; i++) {
+            optimalString.append(words[i]);
+            if (current < words.length - 1) {
+                optimalString.append(" ");
+            }
+        }
+
+        return optimalString.toString();
+    }
+
     public void setDark(float darkAmount) {
         mDarkAmount = darkAmount;
         updateTextColors();
@@ -287,6 +344,9 @@
             setPadding(horizontalPadding, 0, horizontalPadding, 0);
             setCompoundDrawablePadding((int) context.getResources()
                     .getDimension(R.dimen.widget_icon_padding));
+            setMaxWidth(KeyguardSliceView.this.getWidth() / 2);
+            setMaxLines(1);
+            setEllipsize(TruncateAt.END);
         }
 
         public void setHasDivider(boolean hasDivider) {
diff --git a/packages/SystemUI/src/com/android/systemui/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/OverviewProxyService.java
index 22922e7..a7d1f0d 100644
--- a/packages/SystemUI/src/com/android/systemui/OverviewProxyService.java
+++ b/packages/SystemUI/src/com/android/systemui/OverviewProxyService.java
@@ -146,6 +146,7 @@
         filter.addDataScheme("package");
         filter.addDataSchemeSpecificPart(mLauncherComponentName.getPackageName(),
                 PatternMatcher.PATTERN_LITERAL);
+        filter.addAction(Intent.ACTION_PACKAGE_CHANGED);
         mContext.registerReceiver(mLauncherAddedReceiver, filter);
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeUi.java b/packages/SystemUI/src/com/android/systemui/doze/DozeUi.java
index b352ec9..75f1b50 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeUi.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeUi.java
@@ -16,6 +16,8 @@
 
 package com.android.systemui.doze;
 
+import static com.android.systemui.doze.DozeMachine.State.DOZE_AOD_PAUSED;
+
 import android.app.AlarmManager;
 import android.content.Context;
 import android.os.Handler;
@@ -79,6 +81,11 @@
     public void transitionTo(DozeMachine.State oldState, DozeMachine.State newState) {
         switch (newState) {
             case DOZE_AOD:
+                if (oldState == DOZE_AOD_PAUSED) {
+                    mHost.dozeTimeTick();
+                }
+                scheduleTimeTick();
+                break;
             case DOZE_AOD_PAUSING:
                 scheduleTimeTick();
                 break;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/AirplaneModeTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/AirplaneModeTile.java
index bef1aff..9883da6 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/AirplaneModeTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/AirplaneModeTile.java
@@ -21,6 +21,7 @@
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.net.ConnectivityManager;
+import android.os.UserManager;
 import android.provider.Settings;
 import android.provider.Settings.Global;
 import android.service.quicksettings.Tile;
@@ -82,6 +83,7 @@
 
     @Override
     protected void handleUpdateState(BooleanState state, Object arg) {
+        checkIfRestrictionEnforcedByAdminOnly(state, UserManager.DISALLOW_AIRPLANE_MODE);
         final int value = arg instanceof Integer ? (Integer)arg : mSetting.getValue();
         final boolean airplaneMode = value != 0;
         state.value = airplaneMode;
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 c35f591..8bdbf28 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/LocationTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/LocationTile.java
@@ -101,6 +101,9 @@
         // state.visible = !(mKeyguard.isSecure() && mKeyguard.isShowing());
         state.value = locationEnabled;
         checkIfRestrictionEnforcedByAdminOnly(state, UserManager.DISALLOW_SHARE_LOCATION);
+        if (state.disabledByPolicy == false) {
+            checkIfRestrictionEnforcedByAdminOnly(state, UserManager.DISALLOW_CONFIG_LOCATION_MODE);
+        }
         state.icon = mIcon;
         state.slash.isSlashed = !state.value;
         if (locationEnabled) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarGestureHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarGestureHelper.java
index bed6d82..6f636aa 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarGestureHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarGestureHelper.java
@@ -47,8 +47,7 @@
 /**
  * Class to detect gestures on the navigation bar.
  */
-public class NavigationBarGestureHelper extends GestureDetector.SimpleOnGestureListener
-        implements TunerService.Tunable, GestureHelper {
+public class NavigationBarGestureHelper implements TunerService.Tunable, GestureHelper {
 
     private static final String TAG = "NavBarGestureHelper";
     private static final String KEY_DOCK_WINDOW_GESTURE = "overview_nav_bar_gesture";
@@ -72,11 +71,8 @@
     private Context mContext;
     private NavigationBarView mNavigationBarView;
     private boolean mIsVertical;
-    private boolean mIsRTL;
 
-    private final GestureDetector mTaskSwitcherDetector;
     private final int mScrollTouchSlop;
-    private final int mMinFlingVelocity;
     private final Matrix mTransformGlobalMatrix = new Matrix();
     private final Matrix mTransformLocalMatrix = new Matrix();
     private int mTouchDownX;
@@ -91,11 +87,8 @@
 
     public NavigationBarGestureHelper(Context context) {
         mContext = context;
-        ViewConfiguration configuration = ViewConfiguration.get(context);
         Resources r = context.getResources();
         mScrollTouchSlop = r.getDimensionPixelSize(R.dimen.navigation_bar_min_swipe_distance);
-        mMinFlingVelocity = configuration.getScaledMinimumFlingVelocity();
-        mTaskSwitcherDetector = new GestureDetector(context, this);
         Dependency.get(TunerService.class).addTunable(this, KEY_DOCK_WINDOW_GESTURE);
     }
 
@@ -112,7 +105,6 @@
 
     public void setBarState(boolean isVertical, boolean isRTL) {
         mIsVertical = isVertical;
-        mIsRTL = isRTL;
     }
 
     private boolean proxyMotionEvents(MotionEvent event) {
@@ -161,11 +153,7 @@
             case MotionEvent.ACTION_UP:
                 break;
         }
-        if (!proxyMotionEvents(event)) {
-            // If we move more than a fixed amount, then start capturing for the
-            // task switcher detector, disabled when proxying motion events to launcher service
-            mTaskSwitcherDetector.onTouchEvent(event);
-        }
+        proxyMotionEvents(event);
         return result || (mDockWindowEnabled && interceptDockWindowEvent(event));
     }
 
@@ -306,7 +294,7 @@
     }
 
     public boolean onTouchEvent(MotionEvent event) {
-        boolean result = proxyMotionEvents(event) || mTaskSwitcherDetector.onTouchEvent(event);
+        boolean result = proxyMotionEvents(event);
         if (mDockWindowEnabled) {
             result |= handleDockWindowEvent(event);
         }
@@ -314,29 +302,6 @@
     }
 
     @Override
-    public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
-        float absVelX = Math.abs(velocityX);
-        float absVelY = Math.abs(velocityY);
-        boolean isValidFling = absVelX > mMinFlingVelocity &&
-                mIsVertical ? (absVelY > absVelX) : (absVelX > absVelY);
-        if (isValidFling && mRecentsComponent != null) {
-            boolean showNext;
-            if (!mIsRTL) {
-                showNext = mIsVertical ? (velocityY < 0) : (velocityX < 0);
-            } else {
-                // In RTL, vertical is still the same, but horizontal is flipped
-                showNext = mIsVertical ? (velocityY < 0) : (velocityX > 0);
-            }
-            if (showNext) {
-                mRecentsComponent.showNextAffiliatedTask();
-            } else {
-                mRecentsComponent.showPrevAffiliatedTask();
-            }
-        }
-        return true;
-    }
-
-    @Override
     public void onTuningChanged(String key, String newValue) {
         switch (key) {
             case KEY_DOCK_WINDOW_GESTURE:
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
index 2796f0f..392581d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
@@ -199,8 +199,10 @@
         }
     }
 
-    private final OverviewProxyListener mOverviewProxyListener =
-            isConnected -> setSlippery(!isConnected);
+    private final OverviewProxyListener mOverviewProxyListener = isConnected -> {
+        setSlippery(!isConnected);
+        setDisabledFlags(mDisabledFlags, true);
+    };
 
     public NavigationBarView(Context context, AttributeSet attrs) {
         super(context, attrs);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeHostFake.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeHostFake.java
index 3e6bd7e..2398fd3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeHostFake.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeHostFake.java
@@ -61,7 +61,7 @@
 
     @Override
     public void dozeTimeTick() {
-        throw new RuntimeException("not implemented");
+        // Nothing to do in here. Real host would just update the UI.
     }
 
     @Override
diff --git a/proto/src/metrics_constants.proto b/proto/src/metrics_constants.proto
index 1aaa538..4f04d36 100644
--- a/proto/src/metrics_constants.proto
+++ b/proto/src/metrics_constants.proto
@@ -5124,6 +5124,11 @@
     // OS: P
     ACTION_USB_CONFIG_ACCESSORY = 1280;
 
+    // OPEN: Settings > Battery > Smart Battery
+    // CATEGORY: SETTINGS
+    // OS: P
+    FUELGAUGE_SMART_BATTERY = 1281;
+
     // ---- End P Constants, all P constants go above this line ----
     // Add new aosp constants above this line.
     // END OF AOSP CONSTANTS
diff --git a/services/backup/java/com/android/server/backup/RefactoredBackupManagerService.java b/services/backup/java/com/android/server/backup/RefactoredBackupManagerService.java
index b99d341..03591a8 100644
--- a/services/backup/java/com/android/server/backup/RefactoredBackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/RefactoredBackupManagerService.java
@@ -3001,63 +3001,63 @@
         }
     }
 
-    // Select which transport to use for the next backup operation.
+    /** Selects transport {@code transportName} and returns previous selected transport. */
     @Override
-    public String selectBackupTransport(String transport) {
-        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
-                "selectBackupTransport");
+    public String selectBackupTransport(String transportName) {
+        mContext.enforceCallingOrSelfPermission(
+                android.Manifest.permission.BACKUP, "selectBackupTransport");
 
         final long oldId = Binder.clearCallingIdentity();
         try {
-            String prevTransport = mTransportManager.selectTransport(transport);
-            updateStateForTransport(transport);
-            Slog.v(TAG, "selectBackupTransport() set " + mTransportManager.getCurrentTransportName()
-                    + " returning " + prevTransport);
-            return prevTransport;
+            String previousTransportName = mTransportManager.selectTransport(transportName);
+            updateStateForTransport(transportName);
+            Slog.v(TAG, "selectBackupTransport(transport = " + transportName
+                    + "): previous transport = " + previousTransportName);
+            return previousTransportName;
         } finally {
             Binder.restoreCallingIdentity(oldId);
         }
     }
 
     @Override
-    public void selectBackupTransportAsync(final ComponentName transport,
-            final ISelectBackupTransportCallback listener) {
-        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
-                "selectBackupTransportAsync");
+    public void selectBackupTransportAsync(
+            ComponentName transportComponent, ISelectBackupTransportCallback listener) {
+        mContext.enforceCallingOrSelfPermission(
+                android.Manifest.permission.BACKUP, "selectBackupTransportAsync");
 
         final long oldId = Binder.clearCallingIdentity();
-
-        Slog.v(TAG, "selectBackupTransportAsync() called with transport " +
-                transport.flattenToShortString());
-
-        mTransportManager.ensureTransportReady(transport,
-                new TransportManager.TransportReadyCallback() {
-                    @Override
-                    public void onSuccess(String transportName) {
-                        mTransportManager.selectTransport(transportName);
-                        updateStateForTransport(mTransportManager.getCurrentTransportName());
-                        Slog.v(TAG, "Transport successfully selected: "
-                                + transport.flattenToShortString());
-                        try {
-                            listener.onSuccess(transportName);
-                        } catch (RemoteException e) {
-                            // Nothing to do here.
+        try {
+            String transportString = transportComponent.flattenToShortString();
+            Slog.v(TAG, "selectBackupTransportAsync(transport = " + transportString + ")");
+            mBackupHandler.post(
+                    () -> {
+                        String transportName = null;
+                        int result =
+                                mTransportManager.registerAndSelectTransport(transportComponent);
+                        if (result == BackupManager.SUCCESS) {
+                            try {
+                                transportName =
+                                        mTransportManager.getTransportName(transportComponent);
+                                updateStateForTransport(transportName);
+                            } catch (TransportNotRegisteredException e) {
+                                Slog.e(TAG, "Transport got unregistered");
+                                result = BackupManager.ERROR_TRANSPORT_UNAVAILABLE;
+                            }
                         }
-                    }
 
-                    @Override
-                    public void onFailure(int reason) {
-                        Slog.v(TAG,
-                                "Failed to select transport: " + transport.flattenToShortString());
                         try {
-                            listener.onFailure(reason);
+                            if (transportName != null) {
+                                listener.onSuccess(transportName);
+                            } else {
+                                listener.onFailure(result);
+                            }
                         } catch (RemoteException e) {
-                            // Nothing to do here.
+                            Slog.e(TAG, "ISelectBackupTransportCallback listener not available");
                         }
-                    }
-                });
-
-        Binder.restoreCallingIdentity(oldId);
+                    });
+        } finally {
+            Binder.restoreCallingIdentity(oldId);
+        }
     }
 
     private void updateStateForTransport(String newTransportName) {
@@ -3066,18 +3066,23 @@
                 Settings.Secure.BACKUP_TRANSPORT, newTransportName);
 
         // And update our current-dataset bookkeeping
-        IBackupTransport transport = mTransportManager.getTransportBinder(newTransportName);
-        if (transport != null) {
+        String callerLogString = "BMS.updateStateForTransport()";
+        TransportClient transportClient =
+                mTransportManager.getTransportClient(newTransportName, callerLogString);
+        if (transportClient != null) {
             try {
+                IBackupTransport transport = transportClient.connectOrThrow(callerLogString);
                 mCurrentToken = transport.getCurrentRestoreSet();
             } catch (Exception e) {
                 // Oops.  We can't know the current dataset token, so reset and figure it out
                 // when we do the next k/v backup operation on this transport.
                 mCurrentToken = 0;
+                Slog.w(TAG, "Transport " + newTransportName + " not available: current token = 0");
             }
         } else {
-            // The named transport isn't bound at this particular moment, so we can't
-            // know yet what its current dataset token is.  Reset as above.
+            Slog.w(TAG, "Transport " + newTransportName + " not registered: current token = 0");
+            // The named transport isn't registered, so we can't know what its current dataset token
+            // is. Reset as above.
             mCurrentToken = 0;
         }
     }
diff --git a/services/backup/java/com/android/server/backup/TransportManager.java b/services/backup/java/com/android/server/backup/TransportManager.java
index 72a3a32..34b8935 100644
--- a/services/backup/java/com/android/server/backup/TransportManager.java
+++ b/services/backup/java/com/android/server/backup/TransportManager.java
@@ -112,23 +112,6 @@
     @GuardedBy("mTransportLock")
     private volatile String mCurrentTransportName;
 
-
-    /**
-     * Callback interface for {@link #ensureTransportReady(ComponentName, TransportReadyCallback)}.
-     */
-    public interface TransportReadyCallback {
-
-        /**
-         * Will be called when the transport is ready.
-         */
-        void onSuccess(String transportName);
-
-        /**
-         * Will be called when it's not possible to make transport ready.
-         */
-        void onFailure(int reason);
-    }
-
     TransportManager(
             Context context,
             Set<ComponentName> whitelist,
@@ -240,6 +223,17 @@
     }
 
     /**
+     * Returns the transport name associated with {@code transportComponent}.
+     * @throws TransportNotRegisteredException if the transport is not registered.
+     */
+    public String getTransportName(ComponentName transportComponent)
+            throws TransportNotRegisteredException {
+        synchronized (mTransportLock) {
+            return getRegisteredTransportDescriptionOrThrowLocked(transportComponent).name;
+        }
+    }
+
+    /**
      * Retrieve the configuration intent of {@code transportName}.
      * @throws TransportNotRegisteredException if the transport is not registered.
      */
@@ -340,23 +334,6 @@
         return null;
     }
 
-    /**
-     * Returns the transport name associated with {@param transportComponent} or {@code null} if not
-     * found.
-     */
-    @Nullable
-    public String getTransportName(ComponentName transportComponent) {
-        synchronized (mTransportLock) {
-            TransportDescription description =
-                    mRegisteredTransportsDescriptionMap.get(transportComponent);
-            if (description == null) {
-                Slog.e(TAG, "Trying to find name of unregistered transport " + transportComponent);
-                return null;
-            }
-            return description.name;
-        }
-    }
-
     @GuardedBy("mTransportLock")
     @Nullable
     private ComponentName getRegisteredTransportComponentLocked(String transportName) {
@@ -552,29 +529,6 @@
         return mCurrentTransportName;
     }
 
-    String selectTransport(String transport) {
-        synchronized (mTransportLock) {
-            String prevTransport = mCurrentTransportName;
-            mCurrentTransportName = transport;
-            return prevTransport;
-        }
-    }
-
-    void ensureTransportReady(ComponentName transportComponent,
-            TransportReadyCallback listener) {
-        synchronized (mTransportLock) {
-            TransportConnection conn = mValidTransports.get(transportComponent);
-            if (conn == null) {
-                listener.onFailure(BackupManager.ERROR_TRANSPORT_UNAVAILABLE);
-                return;
-            }
-            // Transport can be unbound if the process hosting it crashed.
-            conn.bindIfUnbound();
-            conn.addListener(listener);
-        }
-    }
-
-
     // This is for mocking, Mockito can't mock if package-protected and in the same package but
     // different class loaders. Checked with the debugger and class loaders are different
     // See https://github.com/mockito/mockito/issues/796
@@ -674,6 +628,90 @@
                 createSystemUserHandle());
     }
 
+    String selectTransport(String transportName) {
+        synchronized (mTransportLock) {
+            String prevTransport = mCurrentTransportName;
+            mCurrentTransportName = transportName;
+            return prevTransport;
+        }
+    }
+
+    /**
+     * Tries to register the transport if not registered. If successful also selects the transport.
+     *
+     * @param transportComponent Host of the transport.
+     * @return One of {@link BackupManager#SUCCESS}, {@link BackupManager#ERROR_TRANSPORT_INVALID}
+     *     or {@link BackupManager#ERROR_TRANSPORT_UNAVAILABLE}.
+     */
+    public int registerAndSelectTransport(ComponentName transportComponent) {
+        synchronized (mTransportLock) {
+            if (!mRegisteredTransportsDescriptionMap.containsKey(transportComponent)) {
+                int result = registerTransport(transportComponent);
+                if (result != BackupManager.SUCCESS) {
+                    return result;
+                }
+            }
+
+            try {
+                selectTransport(getTransportName(transportComponent));
+                return BackupManager.SUCCESS;
+            } catch (TransportNotRegisteredException e) {
+                // Shouldn't happen because we are holding the lock
+                Slog.wtf(TAG, "Transport unexpectedly not registered");
+                return BackupManager.ERROR_TRANSPORT_UNAVAILABLE;
+            }
+        }
+    }
+
+    /**
+     * Tries to register transport represented by {@code transportComponent}.
+     *
+     * @param transportComponent Host of the transport that we want to register.
+     * @return One of {@link BackupManager#SUCCESS}, {@link BackupManager#ERROR_TRANSPORT_INVALID}
+     *     or {@link BackupManager#ERROR_TRANSPORT_UNAVAILABLE}.
+     */
+    private int registerTransport(ComponentName transportComponent) {
+        String transportString = transportComponent.flattenToShortString();
+
+        String callerLogString = "TransportManager.registerTransport()";
+        TransportClient transportClient =
+                mTransportClientManager.getTransportClient(transportComponent, callerLogString);
+
+        final IBackupTransport transport;
+        try {
+            transport = transportClient.connectOrThrow(callerLogString);
+        } catch (TransportNotAvailableException e) {
+            Slog.e(TAG, "Couldn't connect to transport " + transportString + " for registration");
+            mTransportClientManager.disposeOfTransportClient(transportClient, callerLogString);
+            return BackupManager.ERROR_TRANSPORT_UNAVAILABLE;
+        }
+
+        EventLog.writeEvent(EventLogTags.BACKUP_TRANSPORT_LIFECYCLE, transportString, 1);
+
+        int result;
+        if (isTransportValid(transport)) {
+            try {
+                registerTransport(transportComponent, transport);
+                // If registerTransport() hasn't thrown...
+                result = BackupManager.SUCCESS;
+            } catch (RemoteException e) {
+                Slog.e(TAG, "Transport " + transportString + " died while registering");
+                result = BackupManager.ERROR_TRANSPORT_UNAVAILABLE;
+            }
+        } else {
+            Slog.w(TAG, "Can't register invalid transport " + transportString);
+            result = BackupManager.ERROR_TRANSPORT_INVALID;
+        }
+
+        mTransportClientManager.disposeOfTransportClient(transportClient, callerLogString);
+        if (result == BackupManager.SUCCESS) {
+            Slog.d(TAG, "Transport " + transportString + " registered");
+        } else {
+            EventLog.writeEvent(EventLogTags.BACKUP_TRANSPORT_LIFECYCLE, transportString, 0);
+        }
+        return result;
+    }
+
     /** If {@link RemoteException} is thrown the transport is guaranteed to not be registered. */
     private void registerTransport(ComponentName transportComponent, IBackupTransport transport)
             throws RemoteException {
@@ -690,11 +728,18 @@
         }
     }
 
+    private boolean isTransportValid(IBackupTransport transport) {
+        if (mTransportBoundListener == null) {
+            Slog.w(TAG, "setTransportBoundListener() not called, assuming transport invalid");
+            return false;
+        }
+        return mTransportBoundListener.onTransportBound(transport);
+    }
+
     private class TransportConnection implements ServiceConnection {
 
         // Hold mTransportLock to access these fields so as to provide a consistent view of them.
         private volatile IBackupTransport mBinder;
-        private final List<TransportReadyCallback> mListeners = new ArrayList<>();
         private volatile String mTransportName;
 
         private final ComponentName mTransportComponent;
@@ -716,15 +761,7 @@
                     mTransportName = mBinder.name();
                     // BackupManager requests some fields from the transport. If they are
                     // invalid, throw away this transport.
-                    final boolean valid;
-                    if (mTransportBoundListener != null) {
-                        valid = mTransportBoundListener.onTransportBound(mBinder);
-                    } else {
-                        Slog.w(TAG, "setTransportBoundListener() not called, assuming transport "
-                                + component + " valid");
-                        valid = true;
-                    }
-                    if (valid) {
+                    if (isTransportValid(mBinder)) {
                         // We're now using the always-bound connection to do the registration but
                         // when we remove the always-bound code this will be in the first binding
                         // TODO: Move registration to first binding
@@ -742,9 +779,6 @@
                     if (success) {
                         Slog.d(TAG, "Bound to transport: " + componentShortString);
                         mBoundTransports.put(mTransportName, component);
-                        for (TransportReadyCallback listener : mListeners) {
-                            listener.onSuccess(mTransportName);
-                        }
                         // cancel rebinding on timeout for this component as we've already connected
                         mHandler.removeMessages(REBINDING_TIMEOUT_MSG, componentShortString);
                     } else {
@@ -756,11 +790,7 @@
                         mValidTransports.remove(component);
                         mEligibleTransports.remove(component);
                         mBinder = null;
-                        for (TransportReadyCallback listener : mListeners) {
-                            listener.onFailure(BackupManager.ERROR_TRANSPORT_INVALID);
-                        }
                     }
-                    mListeners.clear();
                 }
             }
         }
@@ -815,19 +845,6 @@
             }
         }
 
-        private void addListener(TransportReadyCallback listener) {
-            synchronized (mTransportLock) {
-                if (mBinder == null) {
-                    // We are waiting for bind to complete. If mBinder is set to null after the bind
-                    // is complete due to transport being invalid, we won't find 'this' connection
-                    // object in mValidTransports list and this function can't be called.
-                    mListeners.add(listener);
-                } else {
-                    listener.onSuccess(mTransportName);
-                }
-            }
-        }
-
         private long getRebindTimeout() {
             final boolean isDeviceProvisioned = Settings.Global.getInt(
                     mContext.getContentResolver(),
diff --git a/services/backup/java/com/android/server/backup/internal/PerformBackupTask.java b/services/backup/java/com/android/server/backup/internal/PerformBackupTask.java
index a002334..c232241 100644
--- a/services/backup/java/com/android/server/backup/internal/PerformBackupTask.java
+++ b/services/backup/java/com/android/server/backup/internal/PerformBackupTask.java
@@ -560,16 +560,9 @@
                 }
                 backupManagerService.addBackupTrace("init required; rerunning");
                 try {
-                    final String name = backupManagerService.getTransportManager()
+                    String name = backupManagerService.getTransportManager()
                             .getTransportName(mTransportClient.getTransportComponent());
-                    if (name != null) {
-                        backupManagerService.getPendingInits().add(name);
-                    } else {
-                        if (DEBUG) {
-                            Slog.w(TAG, "Couldn't find name of transport "
-                                    + mTransportClient.getTransportComponent() + " for init");
-                        }
-                    }
+                    backupManagerService.getPendingInits().add(name);
                 } catch (Exception e) {
                     Slog.w(TAG, "Failed to query transport name for init: " + e.getMessage());
                     // swallow it and proceed; we don't rely on this
diff --git a/services/core/java/com/android/server/DeviceIdleController.java b/services/core/java/com/android/server/DeviceIdleController.java
index 6f697c4..985f16d 100644
--- a/services/core/java/com/android/server/DeviceIdleController.java
+++ b/services/core/java/com/android/server/DeviceIdleController.java
@@ -795,64 +795,65 @@
                     Slog.e(TAG, "Bad device idle settings", e);
                 }
 
-                LIGHT_IDLE_AFTER_INACTIVE_TIMEOUT = mParser.getLong(
+                LIGHT_IDLE_AFTER_INACTIVE_TIMEOUT = mParser.getDurationMillis(
                         KEY_LIGHT_IDLE_AFTER_INACTIVE_TIMEOUT,
                         !COMPRESS_TIME ? 5 * 60 * 1000L : 15 * 1000L);
-                LIGHT_PRE_IDLE_TIMEOUT = mParser.getLong(KEY_LIGHT_PRE_IDLE_TIMEOUT,
+                LIGHT_PRE_IDLE_TIMEOUT = mParser.getDurationMillis(KEY_LIGHT_PRE_IDLE_TIMEOUT,
                         !COMPRESS_TIME ? 10 * 60 * 1000L : 30 * 1000L);
-                LIGHT_IDLE_TIMEOUT = mParser.getLong(KEY_LIGHT_IDLE_TIMEOUT,
+                LIGHT_IDLE_TIMEOUT = mParser.getDurationMillis(KEY_LIGHT_IDLE_TIMEOUT,
                         !COMPRESS_TIME ? 5 * 60 * 1000L : 15 * 1000L);
                 LIGHT_IDLE_FACTOR = mParser.getFloat(KEY_LIGHT_IDLE_FACTOR,
                         2f);
-                LIGHT_MAX_IDLE_TIMEOUT = mParser.getLong(KEY_LIGHT_MAX_IDLE_TIMEOUT,
+                LIGHT_MAX_IDLE_TIMEOUT = mParser.getDurationMillis(KEY_LIGHT_MAX_IDLE_TIMEOUT,
                         !COMPRESS_TIME ? 15 * 60 * 1000L : 60 * 1000L);
-                LIGHT_IDLE_MAINTENANCE_MIN_BUDGET = mParser.getLong(
+                LIGHT_IDLE_MAINTENANCE_MIN_BUDGET = mParser.getDurationMillis(
                         KEY_LIGHT_IDLE_MAINTENANCE_MIN_BUDGET,
                         !COMPRESS_TIME ? 1 * 60 * 1000L : 15 * 1000L);
-                LIGHT_IDLE_MAINTENANCE_MAX_BUDGET = mParser.getLong(
+                LIGHT_IDLE_MAINTENANCE_MAX_BUDGET = mParser.getDurationMillis(
                         KEY_LIGHT_IDLE_MAINTENANCE_MAX_BUDGET,
                         !COMPRESS_TIME ? 5 * 60 * 1000L : 30 * 1000L);
-                MIN_LIGHT_MAINTENANCE_TIME = mParser.getLong(
+                MIN_LIGHT_MAINTENANCE_TIME = mParser.getDurationMillis(
                         KEY_MIN_LIGHT_MAINTENANCE_TIME,
                         !COMPRESS_TIME ? 5 * 1000L : 1 * 1000L);
-                MIN_DEEP_MAINTENANCE_TIME = mParser.getLong(
+                MIN_DEEP_MAINTENANCE_TIME = mParser.getDurationMillis(
                         KEY_MIN_DEEP_MAINTENANCE_TIME,
                         !COMPRESS_TIME ? 30 * 1000L : 5 * 1000L);
                 long inactiveTimeoutDefault = (mSmallBatteryDevice ? 15 : 30) * 60 * 1000L;
-                INACTIVE_TIMEOUT = mParser.getLong(KEY_INACTIVE_TIMEOUT,
+                INACTIVE_TIMEOUT = mParser.getDurationMillis(KEY_INACTIVE_TIMEOUT,
                         !COMPRESS_TIME ? inactiveTimeoutDefault : (inactiveTimeoutDefault / 10));
-                SENSING_TIMEOUT = mParser.getLong(KEY_SENSING_TIMEOUT,
+                SENSING_TIMEOUT = mParser.getDurationMillis(KEY_SENSING_TIMEOUT,
                         !DEBUG ? 4 * 60 * 1000L : 60 * 1000L);
-                LOCATING_TIMEOUT = mParser.getLong(KEY_LOCATING_TIMEOUT,
+                LOCATING_TIMEOUT = mParser.getDurationMillis(KEY_LOCATING_TIMEOUT,
                         !DEBUG ? 30 * 1000L : 15 * 1000L);
                 LOCATION_ACCURACY = mParser.getFloat(KEY_LOCATION_ACCURACY, 20);
-                MOTION_INACTIVE_TIMEOUT = mParser.getLong(KEY_MOTION_INACTIVE_TIMEOUT,
+                MOTION_INACTIVE_TIMEOUT = mParser.getDurationMillis(KEY_MOTION_INACTIVE_TIMEOUT,
                         !COMPRESS_TIME ? 10 * 60 * 1000L : 60 * 1000L);
                 long idleAfterInactiveTimeout = (mSmallBatteryDevice ? 15 : 30) * 60 * 1000L;
-                IDLE_AFTER_INACTIVE_TIMEOUT = mParser.getLong(KEY_IDLE_AFTER_INACTIVE_TIMEOUT,
+                IDLE_AFTER_INACTIVE_TIMEOUT = mParser.getDurationMillis(
+                        KEY_IDLE_AFTER_INACTIVE_TIMEOUT,
                         !COMPRESS_TIME ? idleAfterInactiveTimeout
                                        : (idleAfterInactiveTimeout / 10));
-                IDLE_PENDING_TIMEOUT = mParser.getLong(KEY_IDLE_PENDING_TIMEOUT,
+                IDLE_PENDING_TIMEOUT = mParser.getDurationMillis(KEY_IDLE_PENDING_TIMEOUT,
                         !COMPRESS_TIME ? 5 * 60 * 1000L : 30 * 1000L);
-                MAX_IDLE_PENDING_TIMEOUT = mParser.getLong(KEY_MAX_IDLE_PENDING_TIMEOUT,
+                MAX_IDLE_PENDING_TIMEOUT = mParser.getDurationMillis(KEY_MAX_IDLE_PENDING_TIMEOUT,
                         !COMPRESS_TIME ? 10 * 60 * 1000L : 60 * 1000L);
                 IDLE_PENDING_FACTOR = mParser.getFloat(KEY_IDLE_PENDING_FACTOR,
                         2f);
-                IDLE_TIMEOUT = mParser.getLong(KEY_IDLE_TIMEOUT,
+                IDLE_TIMEOUT = mParser.getDurationMillis(KEY_IDLE_TIMEOUT,
                         !COMPRESS_TIME ? 60 * 60 * 1000L : 6 * 60 * 1000L);
-                MAX_IDLE_TIMEOUT = mParser.getLong(KEY_MAX_IDLE_TIMEOUT,
+                MAX_IDLE_TIMEOUT = mParser.getDurationMillis(KEY_MAX_IDLE_TIMEOUT,
                         !COMPRESS_TIME ? 6 * 60 * 60 * 1000L : 30 * 60 * 1000L);
                 IDLE_FACTOR = mParser.getFloat(KEY_IDLE_FACTOR,
                         2f);
-                MIN_TIME_TO_ALARM = mParser.getLong(KEY_MIN_TIME_TO_ALARM,
+                MIN_TIME_TO_ALARM = mParser.getDurationMillis(KEY_MIN_TIME_TO_ALARM,
                         !COMPRESS_TIME ? 60 * 60 * 1000L : 6 * 60 * 1000L);
-                MAX_TEMP_APP_WHITELIST_DURATION = mParser.getLong(
+                MAX_TEMP_APP_WHITELIST_DURATION = mParser.getDurationMillis(
                         KEY_MAX_TEMP_APP_WHITELIST_DURATION, 5 * 60 * 1000L);
-                MMS_TEMP_APP_WHITELIST_DURATION = mParser.getLong(
+                MMS_TEMP_APP_WHITELIST_DURATION = mParser.getDurationMillis(
                         KEY_MMS_TEMP_APP_WHITELIST_DURATION, 60 * 1000L);
-                SMS_TEMP_APP_WHITELIST_DURATION = mParser.getLong(
+                SMS_TEMP_APP_WHITELIST_DURATION = mParser.getDurationMillis(
                         KEY_SMS_TEMP_APP_WHITELIST_DURATION, 20 * 1000L);
-                NOTIFICATION_WHITELIST_DURATION = mParser.getLong(
+                NOTIFICATION_WHITELIST_DURATION = mParser.getDurationMillis(
                         KEY_NOTIFICATION_WHITELIST_DURATION, 30 * 1000L);
             }
         }
diff --git a/services/core/java/com/android/server/IpSecService.java b/services/core/java/com/android/server/IpSecService.java
index 5c098e3..02cfe3d 100644
--- a/services/core/java/com/android/server/IpSecService.java
+++ b/services/core/java/com/android/server/IpSecService.java
@@ -52,6 +52,7 @@
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.Preconditions;
 
 import java.io.FileDescriptor;
 import java.io.IOException;
@@ -1030,6 +1031,30 @@
         releaseResource(userRecord.mEncapSocketRecords, resourceId);
     }
 
+    @VisibleForTesting
+    void validateAlgorithms(IpSecConfig config, int direction) throws IllegalArgumentException {
+            IpSecAlgorithm auth = config.getAuthentication(direction);
+            IpSecAlgorithm crypt = config.getEncryption(direction);
+            IpSecAlgorithm aead = config.getAuthenticatedEncryption(direction);
+
+            // Validate the algorithm set
+            Preconditions.checkArgument(
+                    aead != null || crypt != null || auth != null,
+                    "No Encryption or Authentication algorithms specified");
+            Preconditions.checkArgument(
+                    auth == null || auth.isAuthentication(),
+                    "Unsupported algorithm for Authentication");
+            Preconditions.checkArgument(
+                crypt == null || crypt.isEncryption(), "Unsupported algorithm for Encryption");
+            Preconditions.checkArgument(
+                    aead == null || aead.isAead(),
+                    "Unsupported algorithm for Authenticated Encryption");
+            Preconditions.checkArgument(
+                    aead == null || (auth == null && crypt == null),
+                    "Authenticated Encryption is mutually exclusive with other Authentication "
+                                    + "or Encryption algorithms");
+    }
+
     /**
      * Checks an IpSecConfig parcel to ensure that the contents are sane and throws an
      * IllegalArgumentException if they are not.
@@ -1079,17 +1104,7 @@
         }
 
         for (int direction : DIRECTIONS) {
-            IpSecAlgorithm crypt = config.getEncryption(direction);
-            IpSecAlgorithm auth = config.getAuthentication(direction);
-            IpSecAlgorithm authenticatedEncryption = config.getAuthenticatedEncryption(direction);
-            if (authenticatedEncryption == null && crypt == null && auth == null) {
-                throw new IllegalArgumentException(
-                        "No Encryption or Authentication algorithms specified");
-            } else if (authenticatedEncryption != null && (auth != null || crypt != null)) {
-                throw new IllegalArgumentException(
-                        "Authenticated Encryption is mutually"
-                                + " exclusive with other Authentication or Encryption algorithms");
-            }
+            validateAlgorithms(config, direction);
 
             // Retrieve SPI record; will throw IllegalArgumentException if not found
             userRecord.mSpiRecords.getResourceOrThrow(config.getSpiResourceId(direction));
diff --git a/services/core/java/com/android/server/am/ActivityManagerConstants.java b/services/core/java/com/android/server/am/ActivityManagerConstants.java
index b3a596c..0d6d2bd 100644
--- a/services/core/java/com/android/server/am/ActivityManagerConstants.java
+++ b/services/core/java/com/android/server/am/ActivityManagerConstants.java
@@ -77,8 +77,8 @@
     private static final long DEFAULT_CONTENT_PROVIDER_RETAIN_TIME = 20*1000;
     private static final long DEFAULT_GC_TIMEOUT = 5*1000;
     private static final long DEFAULT_GC_MIN_INTERVAL = 60*1000;
-    private static final long DEFAULT_FULL_PSS_MIN_INTERVAL = 10*60*1000;
-    private static final long DEFAULT_FULL_PSS_LOWERED_INTERVAL = 2*60*1000;
+    private static final long DEFAULT_FULL_PSS_MIN_INTERVAL = 20*60*1000;
+    private static final long DEFAULT_FULL_PSS_LOWERED_INTERVAL = 5*60*1000;
     private static final long DEFAULT_POWER_CHECK_INTERVAL = (DEBUG_POWER_QUICK ? 1 : 5) * 60*1000;
     private static final int DEFAULT_POWER_CHECK_MAX_CPU_1 = 25;
     private static final int DEFAULT_POWER_CHECK_MAX_CPU_2 = 25;
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 81e34df..624035d 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -2521,13 +2521,15 @@
                         }
                     }
                     if (proc != null) {
+                        long startTime = SystemClock.currentThreadTimeMillis();
                         long pss = Debug.getPss(pid, tmp, null);
+                        long endTime = SystemClock.currentThreadTimeMillis();
                         synchronized (ActivityManagerService.this) {
                             if (pss != 0 && proc.thread != null && proc.setProcState == procState
                                     && proc.pid == pid && proc.lastPssTime == lastPssTime) {
                                 num++;
                                 recordPssSampleLocked(proc, procState, pss, tmp[0], tmp[1],
-                                        SystemClock.uptimeMillis());
+                                        endTime-startTime, SystemClock.uptimeMillis());
                             }
                         }
                     }
@@ -2695,6 +2697,13 @@
         }
 
         @Override
+        public void onBootPhase(int phase) {
+            if (phase == PHASE_SYSTEM_SERVICES_READY) {
+                mService.mBatteryStatsService.systemServicesReady();
+            }
+        }
+
+        @Override
         public void onCleanupUser(int userId) {
             mService.mBatteryStatsService.onCleanupUser(userId);
         }
@@ -6539,13 +6548,17 @@
                 }
             }
             infos[i] = new Debug.MemoryInfo();
+            long startTime = SystemClock.currentThreadTimeMillis();
             Debug.getMemoryInfo(pids[i], infos[i]);
+            long endTime = SystemClock.currentThreadTimeMillis();
             if (proc != null) {
                 synchronized (this) {
                     if (proc.thread != null && proc.setAdj == oomAdj) {
                         // Record this for posterity if the process has been stable.
                         proc.baseProcessTracker.addPss(infos[i].getTotalPss(),
-                                infos[i].getTotalUss(), false, proc.pkgList);
+                                infos[i].getTotalUss(), false,
+                                ProcessStats.ADD_PSS_EXTERNAL_SLOW, endTime-startTime,
+                                proc.pkgList);
                     }
                 }
             }
@@ -6567,12 +6580,15 @@
                 }
             }
             long[] tmpUss = new long[1];
+            long startTime = SystemClock.currentThreadTimeMillis();
             pss[i] = Debug.getPss(pids[i], tmpUss, null);
+            long endTime = SystemClock.currentThreadTimeMillis();
             if (proc != null) {
                 synchronized (this) {
                     if (proc.thread != null && proc.setAdj == oomAdj) {
                         // Record this for posterity if the process has been stable.
-                        proc.baseProcessTracker.addPss(pss[i], tmpUss[0], false, proc.pkgList);
+                        proc.baseProcessTracker.addPss(pss[i], tmpUss[0], false,
+                                ProcessStats.ADD_PSS_EXTERNAL, endTime-startTime, proc.pkgList);
                     }
                 }
             }
@@ -17661,11 +17677,20 @@
                 if (mi == null) {
                     mi = new Debug.MemoryInfo();
                 }
+                final int reportType;
+                final long startTime;
+                final long endTime;
                 if (opts.dumpDetails || (!brief && !opts.oomOnly)) {
+                    reportType = ProcessStats.ADD_PSS_EXTERNAL_SLOW;
+                    startTime = SystemClock.currentThreadTimeMillis();
                     Debug.getMemoryInfo(pid, mi);
+                    endTime = SystemClock.currentThreadTimeMillis();
                     hasSwapPss = mi.hasSwappedOutPss;
                 } else {
+                    reportType = ProcessStats.ADD_PSS_EXTERNAL;
+                    startTime = SystemClock.currentThreadTimeMillis();
                     mi.dalvikPss = (int)Debug.getPss(pid, tmpLong, null);
+                    endTime = SystemClock.currentThreadTimeMillis();
                     mi.dalvikPrivateDirty = (int)tmpLong[0];
                 }
                 if (opts.dumpDetails) {
@@ -17708,7 +17733,8 @@
                 synchronized (this) {
                     if (r.thread != null && oomAdj == r.getSetAdjWithServices()) {
                         // Record this for posterity if the process has been stable.
-                        r.baseProcessTracker.addPss(myTotalPss, myTotalUss, true, r.pkgList);
+                        r.baseProcessTracker.addPss(myTotalPss, myTotalUss, true,
+                                reportType, endTime-startTime, r.pkgList);
                     }
                 }
 
@@ -18151,11 +18177,20 @@
             if (mi == null) {
                 mi = new Debug.MemoryInfo();
             }
+            final int reportType;
+            final long startTime;
+            final long endTime;
             if (opts.dumpDetails || (!brief && !opts.oomOnly)) {
+                reportType = ProcessStats.ADD_PSS_EXTERNAL_SLOW;
+                startTime = SystemClock.currentThreadTimeMillis();
                 Debug.getMemoryInfo(pid, mi);
+                endTime = SystemClock.currentThreadTimeMillis();
                 hasSwapPss = mi.hasSwappedOutPss;
             } else {
+                reportType = ProcessStats.ADD_PSS_EXTERNAL;
+                startTime = SystemClock.currentThreadTimeMillis();
                 mi.dalvikPss = (int) Debug.getPss(pid, tmpLong, null);
+                endTime = SystemClock.currentThreadTimeMillis();
                 mi.dalvikPrivateDirty = (int) tmpLong[0];
             }
             if (opts.dumpDetails) {
@@ -18194,7 +18229,8 @@
             synchronized (this) {
                 if (r.thread != null && oomAdj == r.getSetAdjWithServices()) {
                     // Record this for posterity if the process has been stable.
-                    r.baseProcessTracker.addPss(myTotalPss, myTotalUss, true, r.pkgList);
+                    r.baseProcessTracker.addPss(myTotalPss, myTotalUss, true,
+                            reportType, endTime-startTime, r.pkgList);
                 }
             }
 
@@ -22456,11 +22492,12 @@
      * Record new PSS sample for a process.
      */
     void recordPssSampleLocked(ProcessRecord proc, int procState, long pss, long uss, long swapPss,
-            long now) {
+            long pssDuration, long now) {
         EventLogTags.writeAmPss(proc.pid, proc.uid, proc.processName, pss * 1024, uss * 1024,
                 swapPss * 1024);
         proc.lastPssTime = now;
-        proc.baseProcessTracker.addPss(pss, uss, true, proc.pkgList);
+        proc.baseProcessTracker.addPss(pss, uss, true, ProcessStats.ADD_PSS_INTERNAL,
+                pssDuration, proc.pkgList);
         if (DEBUG_PSS) Slog.d(TAG_PSS,
                 "PSS of " + proc.toShortString() + ": " + pss + " lastPss=" + proc.lastPss
                 + " state=" + ProcessList.makeProcStateString(procState));
@@ -22955,8 +22992,11 @@
                 // the data right when a process is transitioning between process
                 // states, which well tend to give noisy data.
                 long start = SystemClock.uptimeMillis();
+                long startTime = SystemClock.currentThreadTimeMillis();
                 long pss = Debug.getPss(app.pid, mTmpLong, null);
-                recordPssSampleLocked(app, app.curProcState, pss, mTmpLong[0], mTmpLong[1], now);
+                long endTime = SystemClock.currentThreadTimeMillis();
+                recordPssSampleLocked(app, app.curProcState, pss, endTime-startTime,
+                        mTmpLong[0], mTmpLong[1], now);
                 mPendingPssProcesses.remove(app);
                 Slog.i(TAG, "Recorded pss for " + app + " state " + app.setProcState
                         + " to " + app.curProcState + ": "
diff --git a/services/core/java/com/android/server/am/ActivityMetricsLogger.java b/services/core/java/com/android/server/am/ActivityMetricsLogger.java
index eb022b7..66f0592 100644
--- a/services/core/java/com/android/server/am/ActivityMetricsLogger.java
+++ b/services/core/java/com/android/server/am/ActivityMetricsLogger.java
@@ -42,6 +42,7 @@
 import android.util.Slog;
 import android.util.SparseArray;
 import android.util.SparseIntArray;
+import android.util.StatsLog;
 
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.os.SomeArgs;
@@ -431,6 +432,12 @@
         builder.setType(type);
         builder.addTaggedData(FIELD_CLASS_NAME, info.launchedActivity.info.name);
         mMetricsLogger.write(builder);
+        StatsLog.write(
+                StatsLog.APP_START_CANCEL_CHANGED,
+                info.launchedActivity.appInfo.uid,
+                info.launchedActivity.packageName,
+                convertAppStartTransitionType(type),
+                info.launchedActivity.info.name);
     }
 
     private void logAppTransitionMultiEvents() {
@@ -450,9 +457,9 @@
                 builder.addTaggedData(APP_TRANSITION_CALLING_PACKAGE_NAME,
                         info.launchedActivity.launchedFromPackage);
             }
-            if (info.launchedActivity.info.launchToken != null) {
-                builder.addTaggedData(FIELD_INSTANT_APP_LAUNCH_TOKEN,
-                        info.launchedActivity.info.launchToken);
+            String launchToken = info.launchedActivity.info.launchToken;
+            if (launchToken != null) {
+                builder.addTaggedData(FIELD_INSTANT_APP_LAUNCH_TOKEN, launchToken);
                 info.launchedActivity.info.launchToken = null;
             }
             builder.addTaggedData(APP_TRANSITION_IS_EPHEMERAL, isInstantApp ? 1 : 0);
@@ -470,9 +477,37 @@
             }
             builder.addTaggedData(APP_TRANSITION_WINDOWS_DRAWN_DELAY_MS, info.windowsDrawnDelayMs);
             mMetricsLogger.write(builder);
+            StatsLog.write(
+                    StatsLog.APP_START_CHANGED,
+                    info.launchedActivity.appInfo.uid,
+                    info.launchedActivity.packageName,
+                    convertAppStartTransitionType(type),
+                    info.launchedActivity.info.name,
+                    info.launchedActivity.launchedFromPackage,
+                    isInstantApp,
+                    mCurrentTransitionDeviceUptime * 1000,
+                    info.reason,
+                    mCurrentTransitionDelayMs,
+                    info.startingWindowDelayMs,
+                    info.bindApplicationDelayMs,
+                    info.windowsDrawnDelayMs,
+                    launchToken);
         }
     }
 
+    private int convertAppStartTransitionType(int tronType) {
+        if (tronType == TYPE_TRANSITION_COLD_LAUNCH) {
+            return StatsLog.APP_START_CHANGED__TYPE__COLD;
+        }
+        if (tronType == TYPE_TRANSITION_WARM_LAUNCH) {
+            return StatsLog.APP_START_CHANGED__TYPE__WARM;
+        }
+        if (tronType == TYPE_TRANSITION_HOT_LAUNCH) {
+            return StatsLog.APP_START_CHANGED__TYPE__HOT;
+        }
+        return StatsLog.APP_START_CHANGED__TYPE__APP_START_TRANSITION_TYPE_UNKNOWN;
+     }
+
     void logAppTransitionReportedDrawn(ActivityRecord r, boolean restoredFromBundle) {
         final StackTransitionInfo info = mLastStackTransitionInfo.get(r.getStackId());
         if (info == null) {
@@ -481,14 +516,24 @@
         final LogMaker builder = new LogMaker(APP_TRANSITION_REPORTED_DRAWN);
         builder.setPackageName(r.packageName);
         builder.addTaggedData(FIELD_CLASS_NAME, r.info.name);
-        builder.addTaggedData(APP_TRANSITION_REPORTED_DRAWN_MS,
-                SystemClock.uptimeMillis() - mLastTransitionStartTime);
+        long startupTimeMs = SystemClock.uptimeMillis() - mLastTransitionStartTime;
+        builder.addTaggedData(APP_TRANSITION_REPORTED_DRAWN_MS, startupTimeMs);
         builder.setType(restoredFromBundle
                 ? TYPE_TRANSITION_REPORTED_DRAWN_WITH_BUNDLE
                 : TYPE_TRANSITION_REPORTED_DRAWN_NO_BUNDLE);
         builder.addTaggedData(APP_TRANSITION_PROCESS_RUNNING,
                 info.currentTransitionProcessRunning ? 1 : 0);
         mMetricsLogger.write(builder);
+        StatsLog.write(
+                StatsLog.APP_START_FULLY_DRAWN_CHANGED,
+                info.launchedActivity.appInfo.uid,
+                info.launchedActivity.packageName,
+                restoredFromBundle
+                        ? StatsLog.APP_START_FULLY_DRAWN_CHANGED__TYPE__WITH_BUNDLE
+                        : StatsLog.APP_START_FULLY_DRAWN_CHANGED__TYPE__WITHOUT_BUNDLE,
+                info.launchedActivity.info.name,
+                info.currentTransitionProcessRunning,
+                startupTimeMs);
     }
 
     private int getTransitionType(StackTransitionInfo info) {
diff --git a/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java b/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java
index 4582430..1fcaeef 100644
--- a/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java
+++ b/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java
@@ -35,6 +35,7 @@
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.os.BatteryStatsImpl;
+import com.android.internal.util.function.pooled.PooledLambda;
 
 import libcore.util.EmptyArray;
 
@@ -117,49 +118,45 @@
     }
 
     @Override
-    public Future<?> scheduleReadProcStateCpuTimes() {
+    public synchronized Future<?> scheduleCpuSyncDueToSettingChange() {
+        return scheduleSyncLocked("setting-change", UPDATE_CPU);
+    }
+
+    @Override
+    public Future<?> scheduleReadProcStateCpuTimes(boolean onBattery, boolean onBatteryScreenOff) {
         synchronized (mStats) {
-            if (!mStats.mPerProcStateCpuTimesAvailable) {
+            if (!mStats.trackPerProcStateCpuTimes()) {
                 return null;
             }
         }
         synchronized (BatteryExternalStatsWorker.this) {
             if (!mExecutorService.isShutdown()) {
-                return mExecutorService.submit(mReadProcStateCpuTimesTask);
+                return mExecutorService.submit(PooledLambda.obtainRunnable(
+                        BatteryStatsImpl::updateProcStateCpuTimes,
+                        mStats, onBattery, onBatteryScreenOff).recycleOnUse());
             }
         }
         return null;
     }
 
     @Override
-    public Future<?> scheduleCopyFromAllUidsCpuTimes() {
+    public Future<?> scheduleCopyFromAllUidsCpuTimes(
+            boolean onBattery, boolean onBatteryScreenOff) {
         synchronized (mStats) {
-            if (!mStats.mPerProcStateCpuTimesAvailable) {
+            if (!mStats.trackPerProcStateCpuTimes()) {
                 return null;
             }
         }
         synchronized (BatteryExternalStatsWorker.this) {
             if (!mExecutorService.isShutdown()) {
-                return mExecutorService.submit(mCopyFromAllUidsCpuTimesTask);
+                return mExecutorService.submit(PooledLambda.obtainRunnable(
+                        BatteryStatsImpl::copyFromAllUidsCpuTimes,
+                        mStats, onBattery, onBatteryScreenOff).recycleOnUse());
             }
         }
         return null;
     }
 
-    private final Runnable mReadProcStateCpuTimesTask = new Runnable() {
-        @Override
-        public void run() {
-            mStats.updateProcStateCpuTimes();
-        }
-    };
-
-    private final Runnable mCopyFromAllUidsCpuTimesTask = new Runnable() {
-        @Override
-        public void run() {
-            mStats.copyFromAllUidsCpuTimes();
-        }
-    };
-
     public synchronized Future<?> scheduleWrite() {
         if (mExecutorService.isShutdown()) {
             return CompletableFuture.failedFuture(new IllegalStateException("worker shutdown"));
diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java
index 813e617..c9aa9a2 100644
--- a/services/core/java/com/android/server/am/BatteryStatsService.java
+++ b/services/core/java/com/android/server/am/BatteryStatsService.java
@@ -185,6 +185,10 @@
         ServiceManager.addService(BatteryStats.SERVICE_NAME, asBinder());
     }
 
+    public void systemServicesReady() {
+        mStats.systemServicesReady(mContext);
+    }
+
     private final class LocalService extends BatteryStatsInternal {
         @Override
         public String[] getWifiIfaces() {
@@ -1185,6 +1189,7 @@
         pw.println("  --write: force write current collected stats to disk.");
         pw.println("  --new-daily: immediately create and write new daily stats record.");
         pw.println("  --read-daily: read-load last written daily stats.");
+        pw.println("  --settings: dump the settings key/values related to batterystats");
         pw.println("  <package.name>: optional name of package to filter output by.");
         pw.println("  -h: print this help text.");
         pw.println("Battery stats (batterystats) commands:");
@@ -1197,6 +1202,12 @@
         pw.println("      pretend-screen-off: pretend the screen is off, even if screen state changes");
     }
 
+    private void dumpSettings(PrintWriter pw) {
+        synchronized (mStats) {
+            mStats.dumpConstantsLocked(pw);
+        }
+    }
+
     private int doEnableOrDisable(PrintWriter pw, int i, String[] args, boolean enable) {
         i++;
         if (i >= args.length) {
@@ -1307,6 +1318,9 @@
                 } else if ("-h".equals(arg)) {
                     dumpHelp(pw);
                     return;
+                } else if ("--settings".equals(arg)) {
+                    dumpSettings(pw);
+                    return;
                 } else if ("-a".equals(arg)) {
                     flags |= BatteryStats.DUMP_VERBOSE;
                 } else if (arg.length() > 0 && arg.charAt(0) == '-'){
diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java
index ab5d64c..b39e96d 100644
--- a/services/core/java/com/android/server/am/ProcessList.java
+++ b/services/core/java/com/android/server/am/ProcessList.java
@@ -432,13 +432,13 @@
     public static final int PSS_MIN_TIME_FROM_STATE_CHANGE = 15*1000;
 
     // The maximum amount of time we want to go between PSS collections.
-    public static final int PSS_MAX_INTERVAL = 30*60*1000;
+    public static final int PSS_MAX_INTERVAL = 40*60*1000;
 
     // The minimum amount of time between successive PSS requests for *all* processes.
-    public static final int PSS_ALL_INTERVAL = 10*60*1000;
+    public static final int PSS_ALL_INTERVAL = 20*60*1000;
 
-    // The minimum amount of time between successive PSS requests for a process.
-    private static final int PSS_SHORT_INTERVAL = 2*60*1000;
+    // The amount of time until PSS when a persistent process first appears.
+    private static final int PSS_FIRST_PERSISTENT_INTERVAL = 30*1000;
 
     // The amount of time until PSS when a process first becomes top.
     private static final int PSS_FIRST_TOP_INTERVAL = 10*1000;
@@ -449,6 +449,9 @@
     // The amount of time until PSS when a process first becomes cached.
     private static final int PSS_FIRST_CACHED_INTERVAL = 30*1000;
 
+    // The amount of time until PSS when the top process stays in the same state.
+    private static final int PSS_SAME_TOP_INTERVAL = 5*60*1000;
+
     // The amount of time until PSS when an important process stays in the same state.
     private static final int PSS_SAME_IMPORTANT_INTERVAL = 15*60*1000;
 
@@ -458,6 +461,18 @@
     // The amount of time until PSS when a cached process stays in the same state.
     private static final int PSS_SAME_CACHED_INTERVAL = 30*60*1000;
 
+    // The amount of time until PSS when a persistent process first appears.
+    private static final int PSS_FIRST_ASLEEP_PERSISTENT_INTERVAL = 1*60*1000;
+
+    // The amount of time until PSS when a process first becomes top.
+    private static final int PSS_FIRST_ASLEEP_TOP_INTERVAL = 20*1000;
+
+    // The amount of time until PSS when a process first goes into the background.
+    private static final int PSS_FIRST_ASLEEP_BACKGROUND_INTERVAL = 30*1000;
+
+    // The amount of time until PSS when a process first becomes cached.
+    private static final int PSS_FIRST_ASLEEP_CACHED_INTERVAL = 1*60*1000;
+
     // The minimum time interval after a state change it is safe to collect PSS.
     public static final int PSS_TEST_MIN_TIME_FROM_STATE_CHANGE = 10*1000;
 
@@ -502,8 +517,8 @@
     };
 
     private static final long[] sFirstAwakePssTimes = new long[] {
-        PSS_SHORT_INTERVAL,             // ActivityManager.PROCESS_STATE_PERSISTENT
-        PSS_SHORT_INTERVAL,             // ActivityManager.PROCESS_STATE_PERSISTENT_UI
+        PSS_FIRST_PERSISTENT_INTERVAL,  // ActivityManager.PROCESS_STATE_PERSISTENT
+        PSS_FIRST_PERSISTENT_INTERVAL,  // ActivityManager.PROCESS_STATE_PERSISTENT_UI
         PSS_FIRST_TOP_INTERVAL,         // ActivityManager.PROCESS_STATE_TOP
         PSS_FIRST_BACKGROUND_INTERVAL,  // ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE
         PSS_FIRST_BACKGROUND_INTERVAL,  // ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE
@@ -526,7 +541,51 @@
     private static final long[] sSameAwakePssTimes = new long[] {
         PSS_SAME_IMPORTANT_INTERVAL,    // ActivityManager.PROCESS_STATE_PERSISTENT
         PSS_SAME_IMPORTANT_INTERVAL,    // ActivityManager.PROCESS_STATE_PERSISTENT_UI
-        PSS_SHORT_INTERVAL,             // ActivityManager.PROCESS_STATE_TOP
+        PSS_SAME_TOP_INTERVAL,          // ActivityManager.PROCESS_STATE_TOP
+        PSS_SAME_IMPORTANT_INTERVAL,    // ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE
+        PSS_SAME_IMPORTANT_INTERVAL,    // ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE
+        PSS_SAME_IMPORTANT_INTERVAL,    // ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND
+        PSS_SAME_IMPORTANT_INTERVAL,    // ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND
+        PSS_SAME_IMPORTANT_INTERVAL,    // ActivityManager.PROCESS_STATE_TRANSIENT_BACKGROUND
+        PSS_SAME_IMPORTANT_INTERVAL,    // ActivityManager.PROCESS_STATE_BACKUP
+        PSS_SAME_SERVICE_INTERVAL,      // ActivityManager.PROCESS_STATE_SERVICE
+        PSS_SAME_SERVICE_INTERVAL,      // ActivityManager.PROCESS_STATE_RECEIVER
+        PSS_SAME_CACHED_INTERVAL,       // ActivityManager.PROCESS_STATE_TOP_SLEEPING
+        PSS_SAME_CACHED_INTERVAL,       // ActivityManager.PROCESS_STATE_HEAVY_WEIGHT
+        PSS_SAME_CACHED_INTERVAL,       // ActivityManager.PROCESS_STATE_HOME
+        PSS_SAME_CACHED_INTERVAL,       // ActivityManager.PROCESS_STATE_LAST_ACTIVITY
+        PSS_SAME_CACHED_INTERVAL,       // ActivityManager.PROCESS_STATE_CACHED_ACTIVITY
+        PSS_SAME_CACHED_INTERVAL,       // ActivityManager.PROCESS_STATE_CACHED_ACTIVITY_CLIENT
+        PSS_SAME_CACHED_INTERVAL,       // ActivityManager.PROCESS_STATE_CACHED_RECENT
+        PSS_SAME_CACHED_INTERVAL,       // ActivityManager.PROCESS_STATE_CACHED_EMPTY
+    };
+
+    private static final long[] sFirstAsleepPssTimes = new long[] {
+        PSS_FIRST_ASLEEP_PERSISTENT_INTERVAL,   // ActivityManager.PROCESS_STATE_PERSISTENT
+        PSS_FIRST_ASLEEP_PERSISTENT_INTERVAL,   // ActivityManager.PROCESS_STATE_PERSISTENT_UI
+        PSS_FIRST_ASLEEP_TOP_INTERVAL,          // ActivityManager.PROCESS_STATE_TOP
+        PSS_FIRST_ASLEEP_BACKGROUND_INTERVAL,   // ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE
+        PSS_FIRST_ASLEEP_BACKGROUND_INTERVAL,   // ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE
+        PSS_FIRST_ASLEEP_BACKGROUND_INTERVAL,   // ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND
+        PSS_FIRST_ASLEEP_BACKGROUND_INTERVAL,   // ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND
+        PSS_FIRST_ASLEEP_BACKGROUND_INTERVAL,   // ActivityManager.PROCESS_STATE_TRANSIENT_BACKGROUND
+        PSS_FIRST_ASLEEP_BACKGROUND_INTERVAL,   // ActivityManager.PROCESS_STATE_BACKUP
+        PSS_FIRST_ASLEEP_BACKGROUND_INTERVAL,   // ActivityManager.PROCESS_STATE_SERVICE
+        PSS_FIRST_ASLEEP_CACHED_INTERVAL,       // ActivityManager.PROCESS_STATE_RECEIVER
+        PSS_FIRST_ASLEEP_CACHED_INTERVAL,       // ActivityManager.PROCESS_STATE_TOP_SLEEPING
+        PSS_FIRST_ASLEEP_CACHED_INTERVAL,       // ActivityManager.PROCESS_STATE_HEAVY_WEIGHT
+        PSS_FIRST_ASLEEP_CACHED_INTERVAL,       // ActivityManager.PROCESS_STATE_HOME
+        PSS_FIRST_ASLEEP_CACHED_INTERVAL,       // ActivityManager.PROCESS_STATE_LAST_ACTIVITY
+        PSS_FIRST_ASLEEP_CACHED_INTERVAL,       // ActivityManager.PROCESS_STATE_CACHED_ACTIVITY
+        PSS_FIRST_ASLEEP_CACHED_INTERVAL,       // ActivityManager.PROCESS_STATE_CACHED_ACTIVITY_CLIENT
+        PSS_FIRST_ASLEEP_CACHED_INTERVAL,       // ActivityManager.PROCESS_STATE_CACHED_RECENT
+        PSS_FIRST_ASLEEP_CACHED_INTERVAL,       // ActivityManager.PROCESS_STATE_CACHED_EMPTY
+    };
+
+    private static final long[] sSameAsleepPssTimes = new long[] {
+        PSS_SAME_IMPORTANT_INTERVAL,    // ActivityManager.PROCESS_STATE_PERSISTENT
+        PSS_SAME_IMPORTANT_INTERVAL,    // ActivityManager.PROCESS_STATE_PERSISTENT_UI
+        PSS_SAME_TOP_INTERVAL,          // ActivityManager.PROCESS_STATE_TOP
         PSS_SAME_IMPORTANT_INTERVAL,    // ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE
         PSS_SAME_IMPORTANT_INTERVAL,    // ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE
         PSS_SAME_IMPORTANT_INTERVAL,    // ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND
@@ -604,8 +663,8 @@
                         ? sTestFirstPssTimes
                         : sTestSamePssTimes)
                 : (first
-                        ? sFirstAwakePssTimes
-                        : sSameAwakePssTimes);
+                        ? (sleeping ? sFirstAsleepPssTimes : sFirstAwakePssTimes)
+                        : (sleeping ? sSameAsleepPssTimes : sSameAwakePssTimes));
         return now + table[procState];
     }
 
diff --git a/services/core/java/com/android/server/broadcastradio/hal1/Tuner.java b/services/core/java/com/android/server/broadcastradio/hal1/Tuner.java
index cce534d..e5090ed 100644
--- a/services/core/java/com/android/server/broadcastradio/hal1/Tuner.java
+++ b/services/core/java/com/android/server/broadcastradio/hal1/Tuner.java
@@ -263,19 +263,31 @@
     }
 
     @Override
-    public boolean isAnalogForced() {
-        synchronized (mLock) {
-            checkNotClosedLocked();
-            return nativeIsAnalogForced(mNativeContext);
-        }
+    public boolean isConfigFlagSupported(int flag) {
+        return flag == RadioManager.CONFIG_FORCE_ANALOG;
     }
 
     @Override
-    public void setAnalogForced(boolean isForced) {
-        synchronized (mLock) {
-            checkNotClosedLocked();
-            nativeSetAnalogForced(mNativeContext, isForced);
+    public boolean isConfigFlagSet(int flag) {
+        if (flag == RadioManager.CONFIG_FORCE_ANALOG) {
+            synchronized (mLock) {
+                checkNotClosedLocked();
+                return nativeIsAnalogForced(mNativeContext);
+            }
         }
+        throw new UnsupportedOperationException("Not supported by HAL 1.x");
+    }
+
+    @Override
+    public void setConfigFlag(int flag, boolean value) {
+        if (flag == RadioManager.CONFIG_FORCE_ANALOG) {
+            synchronized (mLock) {
+                checkNotClosedLocked();
+                nativeSetAnalogForced(mNativeContext, value);
+                return;
+            }
+        }
+        throw new UnsupportedOperationException("Not supported by HAL 1.x");
     }
 
     @Override
diff --git a/services/core/java/com/android/server/broadcastradio/hal2/TunerSession.java b/services/core/java/com/android/server/broadcastradio/hal2/TunerSession.java
index c4ec94f..8ed646a 100644
--- a/services/core/java/com/android/server/broadcastradio/hal2/TunerSession.java
+++ b/services/core/java/com/android/server/broadcastradio/hal2/TunerSession.java
@@ -191,8 +191,21 @@
         }
     }
 
-    private boolean getConfigFlag(int flag) {
-        Slog.v(TAG, "getConfigFlag " + ConfigFlag.toString(flag));
+    @Override
+    public boolean isConfigFlagSupported(int flag) {
+        try {
+            isConfigFlagSet(flag);
+            return true;
+        } catch (IllegalStateException ex) {
+            return true;
+        } catch (UnsupportedOperationException ex) {
+            return false;
+        }
+    }
+
+    @Override
+    public boolean isConfigFlagSet(int flag) {
+        Slog.v(TAG, "isConfigFlagSet " + ConfigFlag.toString(flag));
         synchronized (mLock) {
             checkNotClosedLocked();
 
@@ -204,15 +217,16 @@
                     flagState.value = value;
                 });
             } catch (RemoteException ex) {
-                throw new RuntimeException("Failed to get flag " + ConfigFlag.toString(flag), ex);
+                throw new RuntimeException("Failed to check flag " + ConfigFlag.toString(flag), ex);
             }
-            Convert.throwOnError("getConfigFlag", halResult.value);
+            Convert.throwOnError("isConfigFlagSet", halResult.value);
 
             return flagState.value;
         }
     }
 
-    private void setConfigFlag(int flag, boolean value) {
+    @Override
+    public void setConfigFlag(int flag, boolean value) {
         Slog.v(TAG, "setConfigFlag " + ConfigFlag.toString(flag) + " = " + value);
         synchronized (mLock) {
             checkNotClosedLocked();
@@ -228,24 +242,6 @@
     }
 
     @Override
-    public boolean isAnalogForced() {
-        try {
-            return getConfigFlag(ConfigFlag.FORCE_ANALOG);
-        } catch (UnsupportedOperationException ex) {
-            throw new IllegalStateException(ex);
-        }
-    }
-
-    @Override
-    public void setAnalogForced(boolean isForced) {
-        try {
-            setConfigFlag(ConfigFlag.FORCE_ANALOG, isForced);
-        } catch (UnsupportedOperationException ex) {
-            throw new IllegalStateException(ex);
-        }
-    }
-
-    @Override
     public Map setParameters(Map parameters) {
         synchronized (mLock) {
             checkNotClosedLocked();
diff --git a/services/core/java/com/android/server/content/SyncJobService.java b/services/core/java/com/android/server/content/SyncJobService.java
index 29b322e..d957ca0 100644
--- a/services/core/java/com/android/server/content/SyncJobService.java
+++ b/services/core/java/com/android/server/content/SyncJobService.java
@@ -25,6 +25,9 @@
 import android.util.Log;
 import android.util.Slog;
 import android.util.SparseArray;
+import android.util.SparseBooleanArray;
+
+import com.android.internal.annotations.GuardedBy;
 
 public class SyncJobService extends JobService {
     private static final String TAG = "SyncManager";
@@ -32,7 +35,14 @@
     public static final String EXTRA_MESSENGER = "messenger";
 
     private Messenger mMessenger;
-    private SparseArray<JobParameters> jobParamsMap = new SparseArray<JobParameters>();
+
+    private final Object mLock = new Object();
+
+    @GuardedBy("mLock")
+    private final SparseArray<JobParameters> mJobParamsMap = new SparseArray<>();
+
+    @GuardedBy("mLock")
+    private final SparseBooleanArray mStartedSyncs = new SparseBooleanArray();
 
     private final SyncLogger mLogger = SyncLogger.getInstance();
 
@@ -69,8 +79,10 @@
         mLogger.purgeOldLogs();
 
         boolean isLoggable = Log.isLoggable(TAG, Log.VERBOSE);
-        synchronized (jobParamsMap) {
-            jobParamsMap.put(params.getJobId(), params);
+        synchronized (mLock) {
+            final int jobId = params.getJobId();
+            mJobParamsMap.put(jobId, params);
+            mStartedSyncs.delete(jobId);
         }
         Message m = Message.obtain();
         m.what = SyncManager.SyncHandler.MESSAGE_START_SYNC;
@@ -97,8 +109,18 @@
                     + params.getStopReason());
         }
         mLogger.log("onStopJob() ", mLogger.jobParametersToString(params));
-        synchronized (jobParamsMap) {
-            jobParamsMap.remove(params.getJobId());
+        synchronized (mLock) {
+            final int jobId = params.getJobId();
+            mJobParamsMap.remove(jobId);
+
+            if (!mStartedSyncs.get(jobId)) {
+                final String message = "Job " + jobId + " didn't start: params=" +
+                        jobParametersToString(params);
+                mLogger.log(message);
+                Slog.wtf(TAG, message);
+            }
+
+            mStartedSyncs.delete(jobId);
         }
         Message m = Message.obtain();
         m.what = SyncManager.SyncHandler.MESSAGE_STOP_SYNC;
@@ -117,8 +139,8 @@
     }
 
     public void callJobFinished(int jobId, boolean needsReschedule, String why) {
-        synchronized (jobParamsMap) {
-            JobParameters params = jobParamsMap.get(jobId);
+        synchronized (mLock) {
+            JobParameters params = mJobParamsMap.get(jobId);
             mLogger.log("callJobFinished()",
                     " jobid=", jobId,
                     " needsReschedule=", needsReschedule,
@@ -126,10 +148,25 @@
                     " why=", why);
             if (params != null) {
                 jobFinished(params, needsReschedule);
-                jobParamsMap.remove(jobId);
+                mJobParamsMap.remove(jobId);
             } else {
                 Slog.e(TAG, "Job params not found for " + String.valueOf(jobId));
             }
         }
     }
+
+    public void markSyncStarted(int jobId) {
+        synchronized (mLock) {
+            mStartedSyncs.put(jobId, true);
+        }
+    }
+
+    public static String jobParametersToString(JobParameters params) {
+        if (params == null) {
+            return "job:null";
+        } else {
+            return "job:#" + params.getJobId() + ":"
+                    + SyncOperation.maybeCreateFromJobExtras(params.getExtras());
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/content/SyncLogger.java b/services/core/java/com/android/server/content/SyncLogger.java
index 8503768..75c0181 100644
--- a/services/core/java/com/android/server/content/SyncLogger.java
+++ b/services/core/java/com/android/server/content/SyncLogger.java
@@ -233,12 +233,7 @@
 
         @Override
         public String jobParametersToString(JobParameters params) {
-            if (params == null) {
-                return "job:null";
-            } else {
-                return "job:#" + params.getJobId() + ":"
-                        + SyncOperation.maybeCreateFromJobExtras(params.getExtras());
-            }
+            return SyncJobService.jobParametersToString(params);
         }
 
         @Override
diff --git a/services/core/java/com/android/server/content/SyncManager.java b/services/core/java/com/android/server/content/SyncManager.java
index 965159b..ad2cf6c 100644
--- a/services/core/java/com/android/server/content/SyncManager.java
+++ b/services/core/java/com/android/server/content/SyncManager.java
@@ -2898,6 +2898,8 @@
             final boolean isLoggable = Log.isLoggable(TAG, Log.VERBOSE);
             if (isLoggable) Slog.v(TAG, op.toString());
 
+            mSyncJobService.markSyncStarted(op.jobId);
+
             if (mStorageIsLow) {
                 deferSyncH(op, SYNC_DELAY_ON_LOW_STORAGE, "storage low");
                 return;
diff --git a/services/core/java/com/android/server/job/GrantedUriPermissions.java b/services/core/java/com/android/server/job/GrantedUriPermissions.java
index c23b109..8fecb8f 100644
--- a/services/core/java/com/android/server/job/GrantedUriPermissions.java
+++ b/services/core/java/com/android/server/job/GrantedUriPermissions.java
@@ -25,6 +25,7 @@
 import android.os.RemoteException;
 import android.os.UserHandle;
 import android.util.Slog;
+import android.util.proto.ProtoOutputStream;
 
 import java.io.PrintWriter;
 import java.util.ArrayList;
@@ -153,4 +154,21 @@
             pw.println(mUris.get(i));
         }
     }
+
+    public void dump(ProtoOutputStream proto, long fieldId) {
+        final long token = proto.start(fieldId);
+
+        proto.write(GrantedUriPermissionsDumpProto.FLAGS, mGrantFlags);
+        proto.write(GrantedUriPermissionsDumpProto.SOURCE_USER_ID, mSourceUserId);
+        proto.write(GrantedUriPermissionsDumpProto.TAG, mTag);
+        proto.write(GrantedUriPermissionsDumpProto.PERMISSION_OWNER, mPermissionOwner.toString());
+        for (int i = 0; i < mUris.size(); i++) {
+            Uri u = mUris.get(i);
+            if (u != null) {
+                proto.write(GrantedUriPermissionsDumpProto.URIS, u.toString());
+            }
+        }
+
+        proto.end(token);
+    }
 }
diff --git a/services/core/java/com/android/server/job/JobPackageTracker.java b/services/core/java/com/android/server/job/JobPackageTracker.java
index 296743b..8b8faa3 100644
--- a/services/core/java/com/android/server/job/JobPackageTracker.java
+++ b/services/core/java/com/android/server/job/JobPackageTracker.java
@@ -28,6 +28,7 @@
 import android.util.SparseArray;
 import android.util.SparseIntArray;
 import android.util.TimeUtils;
+import android.util.proto.ProtoOutputStream;
 
 import com.android.internal.util.RingBufferIndices;
 import com.android.server.job.controllers.JobStatus;
@@ -308,13 +309,13 @@
             }
         }
 
-        void dump(PrintWriter pw, String header, String prefix, long now, long nowEllapsed,
+        void dump(PrintWriter pw, String header, String prefix, long now, long nowElapsed,
                 int filterUid) {
             final long period = getTotalTime(now);
             pw.print(prefix); pw.print(header); pw.print(" at ");
             pw.print(DateFormat.format("yyyy-MM-dd-HH-mm-ss", mStartClockTime).toString());
             pw.print(" (");
-            TimeUtils.formatDuration(mStartElapsedTime, nowEllapsed, pw);
+            TimeUtils.formatDuration(mStartElapsedTime, nowElapsed, pw);
             pw.print(") over ");
             TimeUtils.formatDuration(period, pw);
             pw.println(":");
@@ -365,6 +366,73 @@
             pw.print(mMaxTotalActive); pw.print(" total, ");
             pw.print(mMaxFgActive); pw.println(" foreground");
         }
+
+        private void printPackageEntryState(ProtoOutputStream proto, long fieldId,
+                long duration, int count) {
+            final long token = proto.start(fieldId);
+            proto.write(DataSetProto.PackageEntryProto.State.DURATION_MS, duration);
+            proto.write(DataSetProto.PackageEntryProto.State.COUNT, count);
+            proto.end(token);
+        }
+
+        void dump(ProtoOutputStream proto, long fieldId, long now, long nowElapsed, int filterUid) {
+            final long token = proto.start(fieldId);
+            final long period = getTotalTime(now);
+
+            proto.write(DataSetProto.START_CLOCK_TIME_MS, mStartClockTime);
+            proto.write(DataSetProto.ELAPSED_TIME_MS, nowElapsed - mStartElapsedTime);
+            proto.write(DataSetProto.PERIOD_MS, period);
+
+            final int NE = mEntries.size();
+            for (int i = 0; i < NE; i++) {
+                int uid = mEntries.keyAt(i);
+                if (filterUid != -1 && filterUid != UserHandle.getAppId(uid)) {
+                    continue;
+                }
+                ArrayMap<String, PackageEntry> uidMap = mEntries.valueAt(i);
+                final int NP = uidMap.size();
+                for (int j = 0; j < NP; j++) {
+                    final long peToken = proto.start(DataSetProto.PACKAGE_ENTRIES);
+                    PackageEntry pe = uidMap.valueAt(j);
+
+                    proto.write(DataSetProto.PackageEntryProto.UID, uid);
+                    proto.write(DataSetProto.PackageEntryProto.PACKAGE_NAME, uidMap.keyAt(j));
+
+                    printPackageEntryState(proto, DataSetProto.PackageEntryProto.PENDING_STATE,
+                            pe.getPendingTime(now), pe.pendingCount);
+                    printPackageEntryState(proto, DataSetProto.PackageEntryProto.ACTIVE_STATE,
+                            pe.getActiveTime(now), pe.activeCount);
+                    printPackageEntryState(proto, DataSetProto.PackageEntryProto.ACTIVE_TOP_STATE,
+                            pe.getActiveTopTime(now), pe.activeTopCount);
+
+                    proto.write(DataSetProto.PackageEntryProto.PENDING,
+                          pe.pendingNesting > 0 || pe.hadPending);
+                    proto.write(DataSetProto.PackageEntryProto.ACTIVE,
+                          pe.activeNesting > 0 || pe.hadActive);
+                    proto.write(DataSetProto.PackageEntryProto.ACTIVE_TOP,
+                          pe.activeTopNesting > 0 || pe.hadActiveTop);
+
+                    for (int k = 0; k < pe.stopReasons.size(); k++) {
+                        final long srcToken =
+                                proto.start(DataSetProto.PackageEntryProto.STOP_REASONS);
+
+                        proto.write(DataSetProto.PackageEntryProto.StopReasonCount.REASON,
+                                pe.stopReasons.keyAt(k));
+                        proto.write(DataSetProto.PackageEntryProto.StopReasonCount.COUNT,
+                                pe.stopReasons.valueAt(k));
+
+                        proto.end(srcToken);
+                    }
+
+                    proto.end(peToken);
+                }
+            }
+
+            proto.write(DataSetProto.MAX_CONCURRENCY, mMaxTotalActive);
+            proto.write(DataSetProto.MAX_FOREGROUND_CONCURRENCY, mMaxFgActive);
+
+            proto.end(token);
+        }
     }
 
     void rebatchIfNeeded(long now) {
@@ -450,7 +518,7 @@
 
     public void dump(PrintWriter pw, String prefix, int filterUid) {
         final long now = sUptimeMillisClock.millis();
-        final long nowEllapsed = sElapsedRealtimeClock.millis();
+        final long nowElapsed = sElapsedRealtimeClock.millis();
         final DataSet total;
         if (mLastDataSets[0] != null) {
             total = new DataSet(mLastDataSets[0]);
@@ -461,11 +529,37 @@
         mCurDataSet.addTo(total, now);
         for (int i = 1; i < mLastDataSets.length; i++) {
             if (mLastDataSets[i] != null) {
-                mLastDataSets[i].dump(pw, "Historical stats", prefix, now, nowEllapsed, filterUid);
+                mLastDataSets[i].dump(pw, "Historical stats", prefix, now, nowElapsed, filterUid);
                 pw.println();
             }
         }
-        total.dump(pw, "Current stats", prefix, now, nowEllapsed, filterUid);
+        total.dump(pw, "Current stats", prefix, now, nowElapsed, filterUid);
+    }
+
+    public void dump(ProtoOutputStream proto, long fieldId, int filterUid) {
+        final long token = proto.start(fieldId);
+        final long now = sUptimeMillisClock.millis();
+        final long nowElapsed = sElapsedRealtimeClock.millis();
+
+        final DataSet total;
+        if (mLastDataSets[0] != null) {
+            total = new DataSet(mLastDataSets[0]);
+            mLastDataSets[0].addTo(total, now);
+        } else {
+            total = new DataSet(mCurDataSet);
+        }
+        mCurDataSet.addTo(total, now);
+
+        for (int i = 1; i < mLastDataSets.length; i++) {
+            if (mLastDataSets[i] != null) {
+                mLastDataSets[i].dump(proto, JobPackageTrackerDumpProto.HISTORICAL_STATS,
+                        now, nowElapsed, filterUid);
+            }
+        }
+        total.dump(proto, JobPackageTrackerDumpProto.CURRENT_STATS,
+                now, nowElapsed, filterUid);
+
+        proto.end(token);
     }
 
     public boolean dumpHistory(PrintWriter pw, String prefix, int filterUid) {
@@ -512,4 +606,40 @@
         }
         return true;
     }
+
+    public void dumpHistory(ProtoOutputStream proto, long fieldId, int filterUid) {
+        final int size = mEventIndices.size();
+        if (size == 0) {
+            return;
+        }
+        final long token = proto.start(fieldId);
+
+        final long now = sElapsedRealtimeClock.millis();
+        for (int i = 0; i < size; i++) {
+            final int index = mEventIndices.indexOf(i);
+            final int uid = mEventUids[index];
+            if (filterUid != -1 && filterUid != UserHandle.getAppId(uid)) {
+                continue;
+            }
+            final int cmd = mEventCmds[index] & EVENT_CMD_MASK;
+            if (cmd == EVENT_NULL) {
+                continue;
+            }
+            final long heToken = proto.start(JobPackageHistoryProto.HISTORY_EVENT);
+
+            proto.write(JobPackageHistoryProto.HistoryEvent.EVENT, cmd);
+            proto.write(JobPackageHistoryProto.HistoryEvent.TIME_SINCE_EVENT_MS, now - mEventTimes[index]);
+            proto.write(JobPackageHistoryProto.HistoryEvent.UID, uid);
+            proto.write(JobPackageHistoryProto.HistoryEvent.JOB_ID, mEventJobIds[index]);
+            proto.write(JobPackageHistoryProto.HistoryEvent.TAG, mEventTags[index]);
+            if (cmd == EVENT_STOP_JOB || cmd == EVENT_STOP_PERIODIC_JOB) {
+                proto.write(JobPackageHistoryProto.HistoryEvent.STOP_REASON,
+                    (mEventCmds[index] & EVENT_STOP_REASON_MASK) >> EVENT_STOP_REASON_SHIFT);
+            }
+
+            proto.end(heToken);
+        }
+
+        proto.end(token);
+    }
 }
diff --git a/services/core/java/com/android/server/job/JobSchedulerService.java b/services/core/java/com/android/server/job/JobSchedulerService.java
index d895636..987baf9 100644
--- a/services/core/java/com/android/server/job/JobSchedulerService.java
+++ b/services/core/java/com/android/server/job/JobSchedulerService.java
@@ -64,6 +64,7 @@
 import android.util.SparseArray;
 import android.util.SparseIntArray;
 import android.util.TimeUtils;
+import android.util.proto.ProtoOutputStream;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.app.IBatteryStats;
@@ -74,6 +75,8 @@
 import com.android.server.DeviceIdleController;
 import com.android.server.FgThread;
 import com.android.server.LocalServices;
+import com.android.server.job.JobSchedulerServiceDumpProto.ActiveJob;
+import com.android.server.job.JobSchedulerServiceDumpProto.PendingJob;
 import com.android.server.job.JobStore.JobStatusFunctor;
 import com.android.server.job.controllers.AppIdleController;
 import com.android.server.job.controllers.BackgroundJobsController;
@@ -196,7 +199,7 @@
     int mMaxActiveJobs = 1;
 
     /**
-     * Which uids are currently in the foreground.
+     * A mapping of which uids are currently in the foreground to their effective priority.
      */
     final SparseIntArray mUidPriorityOverride = new SparseIntArray();
 
@@ -471,11 +474,11 @@
                         DEFAULT_MAX_STANDARD_RESCHEDULE_COUNT);
                 MAX_WORK_RESCHEDULE_COUNT = mParser.getInt(KEY_MAX_WORK_RESCHEDULE_COUNT,
                         DEFAULT_MAX_WORK_RESCHEDULE_COUNT);
-                MIN_LINEAR_BACKOFF_TIME = mParser.getLong(KEY_MIN_LINEAR_BACKOFF_TIME,
+                MIN_LINEAR_BACKOFF_TIME = mParser.getDurationMillis(KEY_MIN_LINEAR_BACKOFF_TIME,
                         DEFAULT_MIN_LINEAR_BACKOFF_TIME);
-                MIN_EXP_BACKOFF_TIME = mParser.getLong(KEY_MIN_EXP_BACKOFF_TIME,
+                MIN_EXP_BACKOFF_TIME = mParser.getDurationMillis(KEY_MIN_EXP_BACKOFF_TIME,
                         DEFAULT_MIN_EXP_BACKOFF_TIME);
-                STANDBY_HEARTBEAT_TIME = mParser.getLong(KEY_STANDBY_HEARTBEAT_TIME,
+                STANDBY_HEARTBEAT_TIME = mParser.getDurationMillis(KEY_STANDBY_HEARTBEAT_TIME,
                         DEFAULT_STANDBY_HEARTBEAT_TIME);
                 STANDBY_BEATS[1] = mParser.getInt(KEY_STANDBY_WORKING_BEATS,
                         DEFAULT_STANDBY_WORKING_BEATS);
@@ -554,6 +557,36 @@
             }
             pw.println('}');
         }
+
+        void dump(ProtoOutputStream proto, long fieldId) {
+            final long token = proto.start(fieldId);
+
+            proto.write(ConstantsProto.MIN_IDLE_COUNT, MIN_IDLE_COUNT);
+            proto.write(ConstantsProto.MIN_CHARGING_COUNT, MIN_CHARGING_COUNT);
+            proto.write(ConstantsProto.MIN_BATTERY_NOT_LOW_COUNT, MIN_BATTERY_NOT_LOW_COUNT);
+            proto.write(ConstantsProto.MIN_STORAGE_NOT_LOW_COUNT, MIN_STORAGE_NOT_LOW_COUNT);
+            proto.write(ConstantsProto.MIN_CONNECTIVITY_COUNT, MIN_CONNECTIVITY_COUNT);
+            proto.write(ConstantsProto.MIN_CONTENT_COUNT, MIN_CONTENT_COUNT);
+            proto.write(ConstantsProto.MIN_READY_JOBS_COUNT, MIN_READY_JOBS_COUNT);
+            proto.write(ConstantsProto.HEAVY_USE_FACTOR, HEAVY_USE_FACTOR);
+            proto.write(ConstantsProto.MODERATE_USE_FACTOR, MODERATE_USE_FACTOR);
+            proto.write(ConstantsProto.FG_JOB_COUNT, FG_JOB_COUNT);
+            proto.write(ConstantsProto.BG_NORMAL_JOB_COUNT, BG_NORMAL_JOB_COUNT);
+            proto.write(ConstantsProto.BG_MODERATE_JOB_COUNT, BG_MODERATE_JOB_COUNT);
+            proto.write(ConstantsProto.BG_LOW_JOB_COUNT, BG_LOW_JOB_COUNT);
+            proto.write(ConstantsProto.BG_CRITICAL_JOB_COUNT, BG_CRITICAL_JOB_COUNT);
+            proto.write(ConstantsProto.MAX_STANDARD_RESCHEDULE_COUNT, MAX_STANDARD_RESCHEDULE_COUNT);
+            proto.write(ConstantsProto.MAX_WORK_RESCHEDULE_COUNT, MAX_WORK_RESCHEDULE_COUNT);
+            proto.write(ConstantsProto.MIN_LINEAR_BACKOFF_TIME_MS, MIN_LINEAR_BACKOFF_TIME);
+            proto.write(ConstantsProto.MIN_EXP_BACKOFF_TIME_MS, MIN_EXP_BACKOFF_TIME);
+            proto.write(ConstantsProto.STANDBY_HEARTBEAT_TIME_MS, STANDBY_HEARTBEAT_TIME);
+
+            for (int period : STANDBY_BEATS) {
+                proto.write(ConstantsProto.STANDBY_BEATS, period);
+            }
+
+            proto.end(token);
+        }
     }
 
     final Constants mConstants;
@@ -2333,9 +2366,46 @@
         public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
             if (!DumpUtils.checkDumpAndUsageStatsPermission(getContext(), TAG, pw)) return;
 
+            int filterUid = -1;
+            boolean proto = false;
+            if (!ArrayUtils.isEmpty(args)) {
+                int opti = 0;
+                while (opti < args.length) {
+                    String arg = args[opti];
+                    if ("-h".equals(arg)) {
+                        dumpHelp(pw);
+                        return;
+                    } else if ("-a".equals(arg)) {
+                        // Ignore, we always dump all.
+                    } else if ("--proto".equals(arg)) {
+                        proto = true;
+                    } else if (arg.length() > 0 && arg.charAt(0) == '-') {
+                        pw.println("Unknown option: " + arg);
+                        return;
+                    } else {
+                        break;
+                    }
+                    opti++;
+                }
+                if (opti < args.length) {
+                    String pkg = args[opti];
+                    try {
+                        filterUid = getContext().getPackageManager().getPackageUid(pkg,
+                                PackageManager.MATCH_ANY_USER);
+                    } catch (NameNotFoundException ignored) {
+                        pw.println("Invalid package: " + pkg);
+                        return;
+                    }
+                }
+            }
+
             long identityToken = Binder.clearCallingIdentity();
             try {
-                JobSchedulerService.this.dumpInternal(pw, args);
+                if (proto) {
+                    JobSchedulerService.this.dumpInternalProto(fd, filterUid);
+                } else {
+                    JobSchedulerService.this.dumpInternal(pw, filterUid);
+                }
             } finally {
                 Binder.restoreCallingIdentity(identityToken);
             }
@@ -2600,37 +2670,24 @@
         pw.println("  [package] is an optional package name to limit the output to.");
     }
 
-    void dumpInternal(final PrintWriter pw, String[] args) {
-        int filterUid = -1;
-        if (!ArrayUtils.isEmpty(args)) {
-            int opti = 0;
-            while (opti < args.length) {
-                String arg = args[opti];
-                if ("-h".equals(arg)) {
-                    dumpHelp(pw);
-                    return;
-                } else if ("-a".equals(arg)) {
-                    // Ignore, we always dump all.
-                } else if (arg.length() > 0 && arg.charAt(0) == '-') {
-                    pw.println("Unknown option: " + arg);
-                    return;
-                } else {
-                    break;
+    /** Sort jobs by caller UID, then by Job ID. */
+    private static void sortJobs(List<JobStatus> jobs) {
+        Collections.sort(jobs, new Comparator<JobStatus>() {
+            @Override
+            public int compare(JobStatus o1, JobStatus o2) {
+                int uid1 = o1.getUid();
+                int uid2 = o2.getUid();
+                int id1 = o1.getJobId();
+                int id2 = o2.getJobId();
+                if (uid1 != uid2) {
+                    return uid1 < uid2 ? -1 : 1;
                 }
-                opti++;
+                return id1 < id2 ? -1 : (id1 > id2 ? 1 : 0);
             }
-            if (opti < args.length) {
-                String pkg = args[opti];
-                try {
-                    filterUid = getContext().getPackageManager().getPackageUid(pkg,
-                            PackageManager.MATCH_ANY_USER);
-                } catch (NameNotFoundException ignored) {
-                    pw.println("Invalid package: " + pkg);
-                    return;
-                }
-            }
-        }
+        });
+    }
 
+    void dumpInternal(final PrintWriter pw, int filterUid) {
         final int filterUidFinal = UserHandle.getAppId(filterUid);
         final long nowElapsed = sElapsedRealtimeClock.millis();
         final long nowUptime = sUptimeMillisClock.millis();
@@ -2643,19 +2700,7 @@
             pw.println(" jobs:");
             if (mJobs.size() > 0) {
                 final List<JobStatus> jobs = mJobs.mJobSet.getAllJobs();
-                Collections.sort(jobs, new Comparator<JobStatus>() {
-                    @Override
-                    public int compare(JobStatus o1, JobStatus o2) {
-                        int uid1 = o1.getUid();
-                        int uid2 = o2.getUid();
-                        int id1 = o1.getJobId();
-                        int id2 = o2.getJobId();
-                        if (uid1 != uid2) {
-                            return uid1 < uid2 ? -1 : 1;
-                        }
-                        return id1 < id2 ? -1 : (id1 > id2 ? 1 : 0);
-                    }
-                });
+                sortJobs(jobs);
                 for (JobStatus job : jobs) {
                     pw.print("  JOB #"); job.printUniqueId(pw); pw.print(": ");
                     pw.println(job.toShortStringExceptUniqueId());
@@ -2792,4 +2837,144 @@
         }
         pw.println();
     }
+
+    void dumpInternalProto(final FileDescriptor fd, int filterUid) {
+        ProtoOutputStream proto = new ProtoOutputStream(fd);
+        final int filterUidFinal = UserHandle.getAppId(filterUid);
+        final long nowElapsed = sElapsedRealtimeClock.millis();
+        final long nowUptime = sUptimeMillisClock.millis();
+
+        synchronized (mLock) {
+            mConstants.dump(proto, JobSchedulerServiceDumpProto.SETTINGS);
+            for (int u : mStartedUsers) {
+                proto.write(JobSchedulerServiceDumpProto.STARTED_USERS, u);
+            }
+            if (mJobs.size() > 0) {
+                final List<JobStatus> jobs = mJobs.mJobSet.getAllJobs();
+                sortJobs(jobs);
+                for (JobStatus job : jobs) {
+                    final long rjToken = proto.start(JobSchedulerServiceDumpProto.REGISTERED_JOBS);
+                    job.writeToShortProto(proto, JobSchedulerServiceDumpProto.RegisteredJob.INFO);
+
+                    // Skip printing details if the caller requested a filter
+                    if (!job.shouldDump(filterUidFinal)) {
+                        continue;
+                    }
+
+                    job.dump(proto, JobSchedulerServiceDumpProto.RegisteredJob.DUMP, true, nowElapsed);
+
+                    // isReadyToBeExecuted
+                    proto.write(JobSchedulerServiceDumpProto.RegisteredJob.IS_JOB_READY,
+                            job.isReady());
+                    proto.write(JobSchedulerServiceDumpProto.RegisteredJob.IS_USER_STARTED,
+                            ArrayUtils.contains(mStartedUsers, job.getUserId()));
+                    proto.write(JobSchedulerServiceDumpProto.RegisteredJob.IS_JOB_PENDING,
+                            mPendingJobs.contains(job));
+                    proto.write(JobSchedulerServiceDumpProto.RegisteredJob.IS_JOB_CURRENTLY_ACTIVE,
+                            isCurrentlyActiveLocked(job));
+                    proto.write(JobSchedulerServiceDumpProto.RegisteredJob.IS_UID_BACKING_UP,
+                            mBackingUpUids.indexOfKey(job.getSourceUid()) >= 0);
+                    boolean componentPresent = false;
+                    try {
+                        componentPresent = (AppGlobals.getPackageManager().getServiceInfo(
+                                job.getServiceComponent(),
+                                PackageManager.MATCH_DEBUG_TRIAGED_MISSING,
+                                job.getUserId()) != null);
+                    } catch (RemoteException e) {
+                    }
+                    proto.write(JobSchedulerServiceDumpProto.RegisteredJob.IS_COMPONENT_PRESENT,
+                            componentPresent);
+
+                    proto.end(rjToken);
+                }
+            }
+            for (StateController controller : mControllers) {
+                controller.dumpControllerStateLocked(
+                        proto, JobSchedulerServiceDumpProto.CONTROLLERS, filterUidFinal);
+            }
+            for (int i=0; i< mUidPriorityOverride.size(); i++) {
+                int uid = mUidPriorityOverride.keyAt(i);
+                if (filterUidFinal == -1 || filterUidFinal == UserHandle.getAppId(uid)) {
+                    long pToken = proto.start(JobSchedulerServiceDumpProto.PRIORITY_OVERRIDES);
+                    proto.write(JobSchedulerServiceDumpProto.PriorityOverride.UID, uid);
+                    proto.write(JobSchedulerServiceDumpProto.PriorityOverride.OVERRIDE_VALUE,
+                            mUidPriorityOverride.valueAt(i));
+                    proto.end(pToken);
+                }
+            }
+            for (int i = 0; i < mBackingUpUids.size(); i++) {
+                int uid = mBackingUpUids.keyAt(i);
+                if (filterUidFinal == -1 || filterUidFinal == UserHandle.getAppId(uid)) {
+                    proto.write(JobSchedulerServiceDumpProto.BACKING_UP_UIDS, uid);
+                }
+            }
+
+            mJobPackageTracker.dump(proto, JobSchedulerServiceDumpProto.PACKAGE_TRACKER,
+                    filterUidFinal);
+            mJobPackageTracker.dumpHistory(proto, JobSchedulerServiceDumpProto.HISTORY,
+                    filterUidFinal);
+
+            for (JobStatus job : mPendingJobs) {
+                final long pjToken = proto.start(JobSchedulerServiceDumpProto.PENDING_JOBS);
+
+                job.writeToShortProto(proto, PendingJob.INFO);
+                job.dump(proto, PendingJob.DUMP, false, nowElapsed);
+                int priority = evaluateJobPriorityLocked(job);
+                if (priority != JobInfo.PRIORITY_DEFAULT) {
+                    proto.write(PendingJob.EVALUATED_PRIORITY, priority);
+                }
+                proto.write(PendingJob.ENQUEUED_DURATION_MS, nowUptime - job.madePending);
+
+                proto.end(pjToken);
+            }
+            for (JobServiceContext jsc : mActiveServices) {
+                final long ajToken = proto.start(JobSchedulerServiceDumpProto.ACTIVE_JOBS);
+                final JobStatus job = jsc.getRunningJobLocked();
+
+                if (job == null) {
+                    final long ijToken = proto.start(ActiveJob.INACTIVE);
+
+                        proto.write(ActiveJob.InactiveJob.TIME_SINCE_STOPPED_MS,
+                                nowElapsed - jsc.mStoppedTime);
+                    if (jsc.mStoppedReason != null) {
+                        proto.write(ActiveJob.InactiveJob.STOPPED_REASON,
+                                jsc.mStoppedReason);
+                    }
+
+                    proto.end(ijToken);
+                } else {
+                    final long rjToken = proto.start(ActiveJob.RUNNING);
+
+                    job.writeToShortProto(proto, ActiveJob.RunningJob.INFO);
+
+                    proto.write(ActiveJob.RunningJob.RUNNING_DURATION_MS,
+                            nowElapsed - jsc.getExecutionStartTimeElapsed());
+                    proto.write(ActiveJob.RunningJob.TIME_UNTIL_TIMEOUT_MS,
+                            jsc.getTimeoutElapsed() - nowElapsed);
+
+                    job.dump(proto, ActiveJob.RunningJob.DUMP, false, nowElapsed);
+
+                    int priority = evaluateJobPriorityLocked(jsc.getRunningJobLocked());
+                    if (priority != JobInfo.PRIORITY_DEFAULT) {
+                        proto.write(ActiveJob.RunningJob.EVALUATED_PRIORITY, priority);
+                    }
+
+                    proto.write(ActiveJob.RunningJob.TIME_SINCE_MADE_ACTIVE_MS,
+                            nowUptime - job.madeActive);
+                    proto.write(ActiveJob.RunningJob.PENDING_DURATION_MS,
+                            job.madeActive - job.madePending);
+
+                    proto.end(rjToken);
+                }
+                proto.end(ajToken);
+            }
+            if (filterUid == -1) {
+                proto.write(JobSchedulerServiceDumpProto.IS_READY_TO_ROCK, mReadyToRock);
+                proto.write(JobSchedulerServiceDumpProto.REPORTED_ACTIVE, mReportedActive);
+                proto.write(JobSchedulerServiceDumpProto.MAX_ACTIVE_JOBS, mMaxActiveJobs);
+            }
+        }
+
+        proto.flush();
+    }
 }
diff --git a/services/core/java/com/android/server/job/controllers/AppIdleController.java b/services/core/java/com/android/server/job/controllers/AppIdleController.java
index a7ed2f5..8d11d1e 100644
--- a/services/core/java/com/android/server/job/controllers/AppIdleController.java
+++ b/services/core/java/com/android/server/job/controllers/AppIdleController.java
@@ -20,10 +20,12 @@
 import android.content.Context;
 import android.os.UserHandle;
 import android.util.Slog;
+import android.util.proto.ProtoOutputStream;
 
 import com.android.server.LocalServices;
 import com.android.server.job.JobSchedulerService;
 import com.android.server.job.JobStore;
+import com.android.server.job.StateControllerProto;
 
 import java.io.PrintWriter;
 
@@ -152,6 +154,38 @@
         });
     }
 
+    @Override
+    public void dumpControllerStateLocked(ProtoOutputStream proto, long fieldId, int filterUid) {
+        final long token = proto.start(fieldId);
+        final long mToken = proto.start(StateControllerProto.APP_IDLE);
+
+        proto.write(StateControllerProto.AppIdleController.IS_PAROLE_ON, mAppIdleParoleOn);
+
+        mJobSchedulerService.getJobStore().forEachJob(new JobStore.JobStatusFunctor() {
+            @Override public void process(JobStatus js) {
+                // Skip printing details if the caller requested a filter
+                if (!js.shouldDump(filterUid)) {
+                    return;
+                }
+
+                final long jsToken =
+                        proto.start(StateControllerProto.AppIdleController.TRACKED_JOBS);
+                js.writeToShortProto(proto, StateControllerProto.AppIdleController.TrackedJob.INFO);
+                proto.write(StateControllerProto.AppIdleController.TrackedJob.SOURCE_UID,
+                        js.getSourceUid());
+                proto.write(StateControllerProto.AppIdleController.TrackedJob.SOURCE_PACKAGE_NAME,
+                        js.getSourcePackageName());
+                proto.write(
+                        StateControllerProto.AppIdleController.TrackedJob.ARE_CONSTRAINTS_SATISFIED,
+                        (js.satisfiedConstraints & JobStatus.CONSTRAINT_APP_NOT_IDLE) != 0);
+                proto.end(jsToken);
+            }
+        });
+
+        proto.end(mToken);
+        proto.end(token);
+    }
+
     void setAppIdleParoleOn(boolean isAppIdleParoleOn) {
         // Flag if any app's idle state has changed
         boolean changed = false;
diff --git a/services/core/java/com/android/server/job/controllers/BackgroundJobsController.java b/services/core/java/com/android/server/job/controllers/BackgroundJobsController.java
index fc4015d..5f95f1a 100644
--- a/services/core/java/com/android/server/job/controllers/BackgroundJobsController.java
+++ b/services/core/java/com/android/server/job/controllers/BackgroundJobsController.java
@@ -22,12 +22,15 @@
 import android.os.SystemClock;
 import android.os.UserHandle;
 import android.util.Slog;
+import android.util.proto.ProtoOutputStream;
 
 import com.android.internal.util.ArrayUtils;
 import com.android.server.ForceAppStandbyTracker;
 import com.android.server.ForceAppStandbyTracker.Listener;
 import com.android.server.job.JobSchedulerService;
 import com.android.server.job.JobStore;
+import com.android.server.job.StateControllerProto;
+import com.android.server.job.StateControllerProto.BackgroundJobsController.TrackedJob;
 
 import java.io.PrintWriter;
 
@@ -90,6 +93,7 @@
                 return;
             }
             final int uid = jobStatus.getSourceUid();
+            final String sourcePkg = jobStatus.getSourcePackageName();
             pw.print("  #");
             jobStatus.printUniqueId(pw);
             pw.print(" from ");
@@ -100,11 +104,10 @@
                 pw.print(", whitelisted");
             }
             pw.print(": ");
-            pw.print(jobStatus.getSourcePackageName());
+            pw.print(sourcePkg);
 
             pw.print(" [RUN_ANY_IN_BACKGROUND ");
-            pw.print(mForceAppStandbyTracker.isRunAnyInBackgroundAppOpsAllowed(
-                    jobStatus.getSourceUid(), jobStatus.getSourcePackageName())
+            pw.print(mForceAppStandbyTracker.isRunAnyInBackgroundAppOpsAllowed(uid, sourcePkg)
                     ? "allowed]" : "disallowed]");
 
             if ((jobStatus.satisfiedConstraints
@@ -116,6 +119,51 @@
         });
     }
 
+    @Override
+    public void dumpControllerStateLocked(ProtoOutputStream proto, long fieldId, int filterUid) {
+        final long token = proto.start(fieldId);
+        final long mToken = proto.start(StateControllerProto.BACKGROUND);
+
+        mForceAppStandbyTracker.dumpProto(proto,
+                StateControllerProto.BackgroundJobsController.FORCE_APP_STANDBY_TRACKER);
+
+        mJobSchedulerService.getJobStore().forEachJob((jobStatus) -> {
+            if (!jobStatus.shouldDump(filterUid)) {
+                return;
+            }
+            final long jsToken =
+                    proto.start(StateControllerProto.BackgroundJobsController.TRACKED_JOBS);
+
+            jobStatus.writeToShortProto(proto,
+                    TrackedJob.INFO);
+            final int sourceUid = jobStatus.getSourceUid();
+            proto.write(TrackedJob.SOURCE_UID, sourceUid);
+            final String sourcePkg = jobStatus.getSourcePackageName();
+            proto.write(TrackedJob.SOURCE_PACKAGE_NAME, sourcePkg);
+
+            proto.write(TrackedJob.IS_IN_FOREGROUND,
+                    mForceAppStandbyTracker.isInForeground(sourceUid));
+            proto.write(TrackedJob.IS_WHITELISTED,
+                    mForceAppStandbyTracker.isUidPowerSaveWhitelisted(sourceUid) ||
+                    mForceAppStandbyTracker.isUidTempPowerSaveWhitelisted(sourceUid));
+
+            proto.write(
+                    TrackedJob.CAN_RUN_ANY_IN_BACKGROUND,
+                    mForceAppStandbyTracker.isRunAnyInBackgroundAppOpsAllowed(
+                            sourceUid, sourcePkg));
+
+            proto.write(
+                    TrackedJob.ARE_CONSTRAINTS_SATISFIED,
+                    (jobStatus.satisfiedConstraints &
+                            JobStatus.CONSTRAINT_BACKGROUND_NOT_RESTRICTED) != 0);
+
+            proto.end(jsToken);
+        });
+
+        proto.end(mToken);
+        proto.end(token);
+    }
+
     private void updateAllJobRestrictionsLocked() {
         updateJobRestrictionsLocked(/*filterUid=*/ -1);
     }
diff --git a/services/core/java/com/android/server/job/controllers/BatteryController.java b/services/core/java/com/android/server/job/controllers/BatteryController.java
index 76ff834..8d3d116 100644
--- a/services/core/java/com/android/server/job/controllers/BatteryController.java
+++ b/services/core/java/com/android/server/job/controllers/BatteryController.java
@@ -27,11 +27,13 @@
 import android.os.UserHandle;
 import android.util.ArraySet;
 import android.util.Slog;
+import android.util.proto.ProtoOutputStream;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.server.LocalServices;
 import com.android.server.job.JobSchedulerService;
 import com.android.server.job.StateChangedListener;
+import com.android.server.job.StateControllerProto;
 
 import java.io.PrintWriter;
 
@@ -263,4 +265,35 @@
             pw.println();
         }
     }
+
+    @Override
+    public void dumpControllerStateLocked(ProtoOutputStream proto, long fieldId, int filterUid) {
+        final long token = proto.start(fieldId);
+        final long mToken = proto.start(StateControllerProto.BATTERY);
+
+        proto.write(StateControllerProto.BatteryController.IS_ON_STABLE_POWER,
+                mChargeTracker.isOnStablePower());
+        proto.write(StateControllerProto.BatteryController.IS_BATTERY_NOT_LOW,
+                mChargeTracker.isBatteryNotLow());
+
+        proto.write(StateControllerProto.BatteryController.IS_MONITORING,
+                mChargeTracker.isMonitoring());
+        proto.write(StateControllerProto.BatteryController.LAST_BROADCAST_SEQUENCE_NUMBER,
+                mChargeTracker.getSeq());
+
+        for (int i = 0; i < mTrackedTasks.size(); i++) {
+            final JobStatus js = mTrackedTasks.valueAt(i);
+            if (!js.shouldDump(filterUid)) {
+                continue;
+            }
+            final long jsToken = proto.start(StateControllerProto.BatteryController.TRACKED_JOBS);
+            js.writeToShortProto(proto, StateControllerProto.BatteryController.TrackedJob.INFO);
+            proto.write(StateControllerProto.BatteryController.TrackedJob.SOURCE_UID,
+                    js.getSourceUid());
+            proto.end(jsToken);
+        }
+
+        proto.end(mToken);
+        proto.end(token);
+    }
 }
diff --git a/services/core/java/com/android/server/job/controllers/ConnectivityController.java b/services/core/java/com/android/server/job/controllers/ConnectivityController.java
index da28769..03fd7b3 100644
--- a/services/core/java/com/android/server/job/controllers/ConnectivityController.java
+++ b/services/core/java/com/android/server/job/controllers/ConnectivityController.java
@@ -25,17 +25,20 @@
 import android.net.NetworkCapabilities;
 import android.net.NetworkInfo;
 import android.net.NetworkPolicyManager;
+import android.net.NetworkRequest;
 import android.net.TrafficStats;
 import android.os.Process;
 import android.os.UserHandle;
 import android.text.format.DateUtils;
 import android.util.ArraySet;
 import android.util.Slog;
+import android.util.proto.ProtoOutputStream;
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.server.job.JobSchedulerService;
 import com.android.server.job.JobServiceContext;
 import com.android.server.job.StateChangedListener;
+import com.android.server.job.StateControllerProto;
 
 import java.io.PrintWriter;
 
@@ -290,4 +293,32 @@
             }
         }
     }
+
+    @Override
+    public void dumpControllerStateLocked(ProtoOutputStream proto, long fieldId, int filterUid) {
+        final long token = proto.start(fieldId);
+        final long mToken = proto.start(StateControllerProto.CONNECTIVITY);
+
+        proto.write(StateControllerProto.ConnectivityController.IS_CONNECTED, mConnected);
+
+        for (int i = 0; i < mTrackedJobs.size(); i++) {
+            final JobStatus js = mTrackedJobs.valueAt(i);
+            if (!js.shouldDump(filterUid)) {
+                continue;
+            }
+            final long jsToken = proto.start(StateControllerProto.ConnectivityController.TRACKED_JOBS);
+            js.writeToShortProto(proto, StateControllerProto.ConnectivityController.TrackedJob.INFO);
+            proto.write(StateControllerProto.ConnectivityController.TrackedJob.SOURCE_UID,
+                    js.getSourceUid());
+            NetworkRequest rn = js.getJob().getRequiredNetwork();
+            if (rn != null) {
+                rn.writeToProto(proto,
+                        StateControllerProto.ConnectivityController.TrackedJob.REQUIRED_NETWORK);
+            }
+            proto.end(jsToken);
+        }
+
+        proto.end(mToken);
+        proto.end(token);
+    }
 }
diff --git a/services/core/java/com/android/server/job/controllers/ContentObserverController.java b/services/core/java/com/android/server/job/controllers/ContentObserverController.java
index ff807ec..7394e23f 100644
--- a/services/core/java/com/android/server/job/controllers/ContentObserverController.java
+++ b/services/core/java/com/android/server/job/controllers/ContentObserverController.java
@@ -28,10 +28,13 @@
 import android.util.TimeUtils;
 import android.util.ArrayMap;
 import android.util.ArraySet;
+import android.util.proto.ProtoOutputStream;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.server.job.JobSchedulerService;
 import com.android.server.job.StateChangedListener;
+import com.android.server.job.StateControllerProto;
+import com.android.server.job.StateControllerProto.ContentObserverController.Observer.TriggerContentData;
 
 import java.io.PrintWriter;
 import java.util.ArrayList;
@@ -451,4 +454,103 @@
             }
         }
     }
+
+    @Override
+    public void dumpControllerStateLocked(ProtoOutputStream proto, long fieldId, int filterUid) {
+        final long token = proto.start(fieldId);
+        final long mToken = proto.start(StateControllerProto.CONTENT_OBSERVER);
+
+        for (int i = 0; i < mTrackedTasks.size(); i++) {
+            JobStatus js = mTrackedTasks.valueAt(i);
+            if (!js.shouldDump(filterUid)) {
+                continue;
+            }
+            final long jsToken =
+                    proto.start(StateControllerProto.ContentObserverController.TRACKED_JOBS);
+            js.writeToShortProto(proto,
+                    StateControllerProto.ContentObserverController.TrackedJob.INFO);
+            proto.write(StateControllerProto.ContentObserverController.TrackedJob.SOURCE_UID,
+                    js.getSourceUid());
+            proto.end(jsToken);
+        }
+
+        final int n = mObservers.size();
+        for (int userIdx = 0; userIdx < n; userIdx++) {
+            final long oToken =
+                    proto.start(StateControllerProto.ContentObserverController.OBSERVERS);
+            final int userId = mObservers.keyAt(userIdx);
+
+            proto.write(StateControllerProto.ContentObserverController.Observer.USER_ID, userId);
+
+            ArrayMap<JobInfo.TriggerContentUri, ObserverInstance> observersOfUser =
+                    mObservers.get(userId);
+            int numbOfObserversPerUser = observersOfUser.size();
+            for (int observerIdx = 0 ; observerIdx < numbOfObserversPerUser; observerIdx++) {
+                ObserverInstance obs = observersOfUser.valueAt(observerIdx);
+                int m = obs.mJobs.size();
+                boolean shouldDump = false;
+                for (int j = 0; j < m; j++) {
+                    JobInstance inst = obs.mJobs.valueAt(j);
+                    if (inst.mJobStatus.shouldDump(filterUid)) {
+                        shouldDump = true;
+                        break;
+                    }
+                }
+                if (!shouldDump) {
+                    continue;
+                }
+                final long tToken = proto.start(
+                        StateControllerProto.ContentObserverController.Observer.TRIGGERS);
+
+                JobInfo.TriggerContentUri trigger = observersOfUser.keyAt(observerIdx);
+                Uri u = trigger.getUri();
+                if (u != null) {
+                    proto.write(TriggerContentData.URI, u.toString());
+                }
+                proto.write(TriggerContentData.FLAGS, trigger.getFlags());
+
+                for (int j = 0; j < m; j++) {
+                    final long jToken = proto.start(TriggerContentData.JOBS);
+                    JobInstance inst = obs.mJobs.valueAt(j);
+
+                    inst.mJobStatus.writeToShortProto(proto, TriggerContentData.JobInstance.INFO);
+                    proto.write(TriggerContentData.JobInstance.SOURCE_UID,
+                            inst.mJobStatus.getSourceUid());
+
+                    if (inst.mChangedAuthorities == null) {
+                        proto.end(jToken);
+                        continue;
+                    }
+                    if (inst.mTriggerPending) {
+                        proto.write(TriggerContentData.JobInstance.TRIGGER_CONTENT_UPDATE_DELAY_MS,
+                                inst.mJobStatus.getTriggerContentUpdateDelay());
+                        proto.write(TriggerContentData.JobInstance.TRIGGER_CONTENT_MAX_DELAY_MS,
+                                inst.mJobStatus.getTriggerContentMaxDelay());
+                    }
+                    for (int k = 0; k < inst.mChangedAuthorities.size(); k++) {
+                        proto.write(TriggerContentData.JobInstance.CHANGED_AUTHORITIES,
+                                inst.mChangedAuthorities.valueAt(k));
+                    }
+                    if (inst.mChangedUris != null) {
+                        for (int k = 0; k < inst.mChangedUris.size(); k++) {
+                            u = inst.mChangedUris.valueAt(k);
+                            if (u != null) {
+                                proto.write(TriggerContentData.JobInstance.CHANGED_URIS,
+                                        u.toString());
+                            }
+                        }
+                    }
+
+                    proto.end(jToken);
+                }
+
+                proto.end(tToken);
+            }
+
+            proto.end(oToken);
+        }
+
+        proto.end(mToken);
+        proto.end(token);
+    }
 }
diff --git a/services/core/java/com/android/server/job/controllers/DeviceIdleJobsController.java b/services/core/java/com/android/server/job/controllers/DeviceIdleJobsController.java
index b7eb9e0..0dbcbee 100644
--- a/services/core/java/com/android/server/job/controllers/DeviceIdleJobsController.java
+++ b/services/core/java/com/android/server/job/controllers/DeviceIdleJobsController.java
@@ -29,12 +29,15 @@
 import android.util.ArraySet;
 import android.util.Slog;
 import android.util.SparseBooleanArray;
+import android.util.proto.ProtoOutputStream;
 
 import com.android.internal.util.ArrayUtils;
 import com.android.server.DeviceIdleController;
 import com.android.server.LocalServices;
 import com.android.server.job.JobSchedulerService;
 import com.android.server.job.JobStore;
+import com.android.server.job.StateControllerProto;
+import com.android.server.job.StateControllerProto.DeviceIdleJobsController.TrackedJob;
 
 import java.io.PrintWriter;
 import java.util.Arrays;
@@ -270,6 +273,38 @@
         });
     }
 
+    @Override
+    public void dumpControllerStateLocked(ProtoOutputStream proto, long fieldId, int filterUid) {
+        final long token = proto.start(fieldId);
+        final long mToken = proto.start(StateControllerProto.DEVICE_IDLE);
+
+        proto.write(StateControllerProto.DeviceIdleJobsController.IS_DEVICE_IDLE_MODE,
+                mDeviceIdleMode);
+        mJobSchedulerService.getJobStore().forEachJob(new JobStore.JobStatusFunctor() {
+            @Override public void process(JobStatus jobStatus) {
+                if (!jobStatus.shouldDump(filterUid)) {
+                    return;
+                }
+                final long jsToken =
+                        proto.start(StateControllerProto.DeviceIdleJobsController.TRACKED_JOBS);
+
+                jobStatus.writeToShortProto(proto, TrackedJob.INFO);
+                proto.write(TrackedJob.SOURCE_UID, jobStatus.getSourceUid());
+                proto.write(TrackedJob.SOURCE_PACKAGE_NAME, jobStatus.getSourcePackageName());
+                proto.write(TrackedJob.ARE_CONSTRAINTS_SATISFIED,
+                        (jobStatus.satisfiedConstraints &
+                            JobStatus.CONSTRAINT_DEVICE_NOT_DOZING) != 0);
+                proto.write(TrackedJob.IS_DOZE_WHITELISTED, jobStatus.dozeWhitelisted);
+                proto.write(TrackedJob.IS_ALLOWED_IN_DOZE, mAllowInIdleJobs.contains(jobStatus));
+
+                proto.end(jsToken);
+            }
+        });
+
+        proto.end(mToken);
+        proto.end(token);
+    }
+
     final class DeviceIdleUpdateFunctor implements JobStore.JobStatusFunctor {
         boolean mChanged;
 
@@ -300,4 +335,4 @@
             }
         }
     }
-}
\ No newline at end of file
+}
diff --git a/services/core/java/com/android/server/job/controllers/IdleController.java b/services/core/java/com/android/server/job/controllers/IdleController.java
index 7bde174..a9bc7e0 100644
--- a/services/core/java/com/android/server/job/controllers/IdleController.java
+++ b/services/core/java/com/android/server/job/controllers/IdleController.java
@@ -27,10 +27,12 @@
 import android.os.UserHandle;
 import android.util.ArraySet;
 import android.util.Slog;
+import android.util.proto.ProtoOutputStream;
 
 import com.android.server.am.ActivityManagerService;
 import com.android.server.job.JobSchedulerService;
 import com.android.server.job.StateChangedListener;
+import com.android.server.job.StateControllerProto;
 
 import java.io.PrintWriter;
 
@@ -216,4 +218,27 @@
             pw.println();
         }
     }
+
+    @Override
+    public void dumpControllerStateLocked(ProtoOutputStream proto, long fieldId, int filterUid) {
+        final long token = proto.start(fieldId);
+        final long mToken = proto.start(StateControllerProto.IDLE);
+
+        proto.write(StateControllerProto.IdleController.IS_IDLE, mIdleTracker.isIdle());
+
+        for (int i = 0; i < mTrackedTasks.size(); i++) {
+            final JobStatus js = mTrackedTasks.valueAt(i);
+            if (!js.shouldDump(filterUid)) {
+                continue;
+            }
+            final long jsToken = proto.start(StateControllerProto.IdleController.TRACKED_JOBS);
+            js.writeToShortProto(proto, StateControllerProto.IdleController.TrackedJob.INFO);
+            proto.write(StateControllerProto.IdleController.TrackedJob.SOURCE_UID,
+                    js.getSourceUid());
+            proto.end(jsToken);
+        }
+
+        proto.end(mToken);
+        proto.end(token);
+    }
 }
diff --git a/services/core/java/com/android/server/job/controllers/JobStatus.java b/services/core/java/com/android/server/job/controllers/JobStatus.java
index e71b8ec..8f68713 100644
--- a/services/core/java/com/android/server/job/controllers/JobStatus.java
+++ b/services/core/java/com/android/server/job/controllers/JobStatus.java
@@ -33,11 +33,14 @@
 import android.util.Pair;
 import android.util.Slog;
 import android.util.TimeUtils;
+import android.util.proto.ProtoOutputStream;
 
 import com.android.server.LocalServices;
 import com.android.server.job.GrantedUriPermissions;
 import com.android.server.job.JobSchedulerInternal;
 import com.android.server.job.JobSchedulerService;
+import com.android.server.job.JobStatusDumpProto;
+import com.android.server.job.JobStatusShortInfoProto;
 
 import java.io.PrintWriter;
 import java.util.ArrayList;
@@ -968,6 +971,20 @@
         return sb.toString();
     }
 
+    /**
+     * Convenience function to dump data that identifies a job uniquely to proto. This is intended
+     * to mimic {@link #toShortString}.
+     */
+    public void writeToShortProto(ProtoOutputStream proto, long fieldId) {
+        final long token = proto.start(fieldId);
+
+        proto.write(JobStatusShortInfoProto.CALLING_UID, callingUid);
+        proto.write(JobStatusShortInfoProto.JOB_ID, job.getId());
+        proto.write(JobStatusShortInfoProto.BATTERY_NAME, batteryName);
+
+        proto.end(token);
+    }
+
     void dumpConstraints(PrintWriter pw, int constraints) {
         if ((constraints&CONSTRAINT_CHARGING) != 0) {
             pw.print(" CHARGING");
@@ -1001,6 +1018,40 @@
         }
     }
 
+    /** Writes constraints to the given repeating proto field. */
+    void dumpConstraints(ProtoOutputStream proto, long fieldId, int constraints) {
+        if ((constraints & CONSTRAINT_CHARGING) != 0) {
+            proto.write(fieldId, JobStatusDumpProto.CONSTRAINT_CHARGING);
+        }
+        if ((constraints & CONSTRAINT_BATTERY_NOT_LOW) != 0) {
+            proto.write(fieldId, JobStatusDumpProto.CONSTRAINT_BATTERY_NOT_LOW);
+        }
+        if ((constraints & CONSTRAINT_STORAGE_NOT_LOW) != 0) {
+            proto.write(fieldId, JobStatusDumpProto.CONSTRAINT_STORAGE_NOT_LOW);
+        }
+        if ((constraints & CONSTRAINT_TIMING_DELAY) != 0) {
+            proto.write(fieldId, JobStatusDumpProto.CONSTRAINT_TIMING_DELAY);
+        }
+        if ((constraints & CONSTRAINT_DEADLINE) != 0) {
+            proto.write(fieldId, JobStatusDumpProto.CONSTRAINT_DEADLINE);
+        }
+        if ((constraints & CONSTRAINT_IDLE) != 0) {
+            proto.write(fieldId, JobStatusDumpProto.CONSTRAINT_IDLE);
+        }
+        if ((constraints & CONSTRAINT_CONNECTIVITY) != 0) {
+            proto.write(fieldId, JobStatusDumpProto.CONSTRAINT_CONNECTIVITY);
+        }
+        if ((constraints & CONSTRAINT_APP_NOT_IDLE) != 0) {
+            proto.write(fieldId, JobStatusDumpProto.CONSTRAINT_APP_NOT_IDLE);
+        }
+        if ((constraints & CONSTRAINT_CONTENT_TRIGGER) != 0) {
+            proto.write(fieldId, JobStatusDumpProto.CONSTRAINT_CONTENT_TRIGGER);
+        }
+        if ((constraints & CONSTRAINT_DEVICE_NOT_DOZING) != 0) {
+            proto.write(fieldId, JobStatusDumpProto.CONSTRAINT_DEVICE_NOT_DOZING);
+        }
+    }
+
     private void dumpJobWorkItem(PrintWriter pw, String prefix, JobWorkItem work, int index) {
         pw.print(prefix); pw.print("  #"); pw.print(index); pw.print(": #");
         pw.print(work.getWorkId()); pw.print(" "); pw.print(work.getDeliveryCount());
@@ -1011,6 +1062,22 @@
         }
     }
 
+    private void dumpJobWorkItem(ProtoOutputStream proto, long fieldId, JobWorkItem work) {
+        final long token = proto.start(fieldId);
+
+        proto.write(JobStatusDumpProto.JobWorkItem.WORK_ID, work.getWorkId());
+        proto.write(JobStatusDumpProto.JobWorkItem.DELIVERY_COUNT, work.getDeliveryCount());
+        if (work.getIntent() != null) {
+            work.getIntent().writeToProto(proto, JobStatusDumpProto.JobWorkItem.INTENT);
+        }
+        Object grants = work.getGrants();
+        if (grants != null) {
+            ((GrantedUriPermissions) grants).dump(proto, JobStatusDumpProto.JobWorkItem.URI_GRANTS);
+        }
+
+        proto.end(token);
+    }
+
     // normalized bucket indices, not the AppStandby constants
     private String bucketName(int bucket) {
         switch (bucket) {
@@ -1198,4 +1265,168 @@
             pw.println(t.format(format));
         }
     }
+
+    public void dump(ProtoOutputStream proto, long fieldId, boolean full, long elapsedRealtimeMillis) {
+        final long token = proto.start(fieldId);
+
+        proto.write(JobStatusDumpProto.CALLING_UID, callingUid);
+        proto.write(JobStatusDumpProto.TAG, tag);
+        proto.write(JobStatusDumpProto.SOURCE_UID, getSourceUid());
+        proto.write(JobStatusDumpProto.SOURCE_USER_ID, getSourceUserId());
+        proto.write(JobStatusDumpProto.SOURCE_PACKAGE_NAME, getSourcePackageName());
+
+        if (full) {
+            final long jiToken = proto.start(JobStatusDumpProto.JOB_INFO);
+
+            job.getService().writeToProto(proto, JobStatusDumpProto.JobInfo.SERVICE);
+
+            proto.write(JobStatusDumpProto.JobInfo.IS_PERIODIC, job.isPeriodic());
+            proto.write(JobStatusDumpProto.JobInfo.PERIOD_INTERVAL_MS, job.getIntervalMillis());
+            proto.write(JobStatusDumpProto.JobInfo.PERIOD_FLEX_MS, job.getFlexMillis());
+
+            proto.write(JobStatusDumpProto.JobInfo.IS_PERSISTED, job.isPersisted());
+            proto.write(JobStatusDumpProto.JobInfo.PRIORITY, job.getPriority());
+            proto.write(JobStatusDumpProto.JobInfo.FLAGS, job.getFlags());
+
+            proto.write(JobStatusDumpProto.JobInfo.REQUIRES_CHARGING, job.isRequireCharging());
+            proto.write(JobStatusDumpProto.JobInfo.REQUIRES_BATTERY_NOT_LOW, job.isRequireBatteryNotLow());
+            proto.write(JobStatusDumpProto.JobInfo.REQUIRES_DEVICE_IDLE, job.isRequireDeviceIdle());
+
+            if (job.getTriggerContentUris() != null) {
+                for (int i = 0; i < job.getTriggerContentUris().length; i++) {
+                    final long tcuToken = proto.start(JobStatusDumpProto.JobInfo.TRIGGER_CONTENT_URIS);
+                    JobInfo.TriggerContentUri trig = job.getTriggerContentUris()[i];
+
+                    proto.write(JobStatusDumpProto.JobInfo.TriggerContentUri.FLAGS, trig.getFlags());
+                    Uri u = trig.getUri();
+                    if (u != null) {
+                        proto.write(JobStatusDumpProto.JobInfo.TriggerContentUri.URI, u.toString());
+                    }
+
+                    proto.end(tcuToken);
+                }
+                if (job.getTriggerContentUpdateDelay() >= 0) {
+                    proto.write(JobStatusDumpProto.JobInfo.TRIGGER_CONTENT_UPDATE_DELAY_MS,
+                            job.getTriggerContentUpdateDelay());
+                }
+                if (job.getTriggerContentMaxDelay() >= 0) {
+                    proto.write(JobStatusDumpProto.JobInfo.TRIGGER_CONTENT_MAX_DELAY_MS,
+                            job.getTriggerContentMaxDelay());
+                }
+            }
+            if (job.getExtras() != null && !job.getExtras().maybeIsEmpty()) {
+                job.getExtras().writeToProto(proto, JobStatusDumpProto.JobInfo.EXTRAS);
+            }
+            if (job.getTransientExtras() != null && !job.getTransientExtras().maybeIsEmpty()) {
+                job.getTransientExtras().writeToProto(proto, JobStatusDumpProto.JobInfo.TRANSIENT_EXTRAS);
+            }
+            if (job.getClipData() != null) {
+                job.getClipData().writeToProto(proto, JobStatusDumpProto.JobInfo.CLIP_DATA);
+            }
+            if (uriPerms != null) {
+                uriPerms.dump(proto, JobStatusDumpProto.JobInfo.GRANTED_URI_PERMISSIONS);
+            }
+            if (job.getRequiredNetwork() != null) {
+                job.getRequiredNetwork().writeToProto(proto, JobStatusDumpProto.JobInfo.REQUIRED_NETWORK);
+            }
+            if (totalNetworkBytes != JobInfo.NETWORK_BYTES_UNKNOWN) {
+                proto.write(JobStatusDumpProto.JobInfo.TOTAL_NETWORK_BYTES, totalNetworkBytes);
+            }
+            proto.write(JobStatusDumpProto.JobInfo.MIN_LATENCY_MS, job.getMinLatencyMillis());
+            proto.write(JobStatusDumpProto.JobInfo.MAX_EXECUTION_DELAY_MS, job.getMaxExecutionDelayMillis());
+
+            final long bpToken = proto.start(JobStatusDumpProto.JobInfo.BACKOFF_POLICY);
+            proto.write(JobStatusDumpProto.JobInfo.Backoff.POLICY, job.getBackoffPolicy());
+            proto.write(JobStatusDumpProto.JobInfo.Backoff.INITIAL_BACKOFF_MS,
+                    job.getInitialBackoffMillis());
+            proto.end(bpToken);
+
+            proto.write(JobStatusDumpProto.JobInfo.HAS_EARLY_CONSTRAINT, job.hasEarlyConstraint());
+            proto.write(JobStatusDumpProto.JobInfo.HAS_LATE_CONSTRAINT, job.hasLateConstraint());
+
+            proto.end(jiToken);
+        }
+
+        dumpConstraints(proto, JobStatusDumpProto.REQUIRED_CONSTRAINTS, requiredConstraints);
+        if (full) {
+            dumpConstraints(proto, JobStatusDumpProto.SATISFIED_CONSTRAINTS, satisfiedConstraints);
+            dumpConstraints(proto, JobStatusDumpProto.UNSATISFIED_CONSTRAINTS,
+                    (requiredConstraints & ~satisfiedConstraints));
+            proto.write(JobStatusDumpProto.IS_DOZE_WHITELISTED, dozeWhitelisted);
+        }
+
+        // Tracking controllers
+        if ((trackingControllers&TRACKING_BATTERY) != 0) {
+            proto.write(JobStatusDumpProto.TRACKING_CONTROLLERS,
+                    JobStatusDumpProto.TRACKING_BATTERY);
+        }
+        if ((trackingControllers&TRACKING_CONNECTIVITY) != 0) {
+            proto.write(JobStatusDumpProto.TRACKING_CONTROLLERS,
+                    JobStatusDumpProto.TRACKING_CONNECTIVITY);
+        }
+        if ((trackingControllers&TRACKING_CONTENT) != 0) {
+            proto.write(JobStatusDumpProto.TRACKING_CONTROLLERS,
+                    JobStatusDumpProto.TRACKING_CONTENT);
+        }
+        if ((trackingControllers&TRACKING_IDLE) != 0) {
+            proto.write(JobStatusDumpProto.TRACKING_CONTROLLERS,
+                    JobStatusDumpProto.TRACKING_IDLE);
+        }
+        if ((trackingControllers&TRACKING_STORAGE) != 0) {
+            proto.write(JobStatusDumpProto.TRACKING_CONTROLLERS,
+                    JobStatusDumpProto.TRACKING_STORAGE);
+        }
+        if ((trackingControllers&TRACKING_TIME) != 0) {
+            proto.write(JobStatusDumpProto.TRACKING_CONTROLLERS,
+                    JobStatusDumpProto.TRACKING_TIME);
+        }
+
+        if (changedAuthorities != null) {
+            for (int k = 0; k < changedAuthorities.size(); k++) {
+                proto.write(JobStatusDumpProto.CHANGED_AUTHORITIES, changedAuthorities.valueAt(k));
+            }
+        }
+        if (changedUris != null) {
+            for (int i = 0; i < changedUris.size(); i++) {
+                Uri u = changedUris.valueAt(i);
+                proto.write(JobStatusDumpProto.CHANGED_URIS, u.toString());
+            }
+        }
+
+        if (network != null) {
+            network.writeToProto(proto, JobStatusDumpProto.NETWORK);
+        }
+
+        if (pendingWork != null && pendingWork.size() > 0) {
+            for (int i = 0; i < pendingWork.size(); i++) {
+                dumpJobWorkItem(proto, JobStatusDumpProto.PENDING_WORK, pendingWork.get(i));
+            }
+        }
+        if (executingWork != null && executingWork.size() > 0) {
+            for (int i = 0; i < executingWork.size(); i++) {
+                dumpJobWorkItem(proto, JobStatusDumpProto.EXECUTING_WORK, executingWork.get(i));
+            }
+        }
+
+        proto.write(JobStatusDumpProto.STANDBY_BUCKET, standbyBucket);
+        proto.write(JobStatusDumpProto.ENQUEUE_DURATION_MS, elapsedRealtimeMillis - enqueueTime);
+        if (earliestRunTimeElapsedMillis == NO_EARLIEST_RUNTIME) {
+            proto.write(JobStatusDumpProto.TIME_UNTIL_EARLIEST_RUNTIME_MS, 0);
+        } else {
+            proto.write(JobStatusDumpProto.TIME_UNTIL_EARLIEST_RUNTIME_MS,
+                    earliestRunTimeElapsedMillis - elapsedRealtimeMillis);
+        }
+        if (latestRunTimeElapsedMillis == NO_LATEST_RUNTIME) {
+            proto.write(JobStatusDumpProto.TIME_UNTIL_LATEST_RUNTIME_MS, 0);
+        } else {
+            proto.write(JobStatusDumpProto.TIME_UNTIL_LATEST_RUNTIME_MS,
+                    latestRunTimeElapsedMillis - elapsedRealtimeMillis);
+        }
+
+        proto.write(JobStatusDumpProto.NUM_FAILURES, numFailures);
+        proto.write(JobStatusDumpProto.LAST_SUCCESSFUL_RUN_TIME, mLastSuccessfulRunTime);
+        proto.write(JobStatusDumpProto.LAST_FAILED_RUN_TIME, mLastFailedRunTime);
+
+        proto.end(token);
+    }
 }
diff --git a/services/core/java/com/android/server/job/controllers/StateController.java b/services/core/java/com/android/server/job/controllers/StateController.java
index 497faab..d3055e6 100644
--- a/services/core/java/com/android/server/job/controllers/StateController.java
+++ b/services/core/java/com/android/server/job/controllers/StateController.java
@@ -17,6 +17,7 @@
 package com.android.server.job.controllers;
 
 import android.content.Context;
+import android.util.proto.ProtoOutputStream;
 
 import com.android.server.job.JobSchedulerService;
 import com.android.server.job.StateChangedListener;
@@ -65,4 +66,6 @@
     }
 
     public abstract void dumpControllerStateLocked(PrintWriter pw, int filterUid);
+    public abstract void dumpControllerStateLocked(ProtoOutputStream proto, long fieldId,
+            int filterUid);
 }
diff --git a/services/core/java/com/android/server/job/controllers/StorageController.java b/services/core/java/com/android/server/job/controllers/StorageController.java
index 84782f5..0519b63 100644
--- a/services/core/java/com/android/server/job/controllers/StorageController.java
+++ b/services/core/java/com/android/server/job/controllers/StorageController.java
@@ -25,10 +25,12 @@
 import android.os.UserHandle;
 import android.util.ArraySet;
 import android.util.Slog;
+import android.util.proto.ProtoOutputStream;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.server.job.JobSchedulerService;
 import com.android.server.job.StateChangedListener;
+import com.android.server.job.StateControllerProto;
 import com.android.server.storage.DeviceStorageMonitorService;
 
 import java.io.PrintWriter;
@@ -119,7 +121,7 @@
          */
         private boolean mStorageLow;
         /** Sequence number of last broadcast. */
-        private int mLastBatterySeq = -1;
+        private int mLastStorageSeq = -1;
 
         public StorageTracker() {
         }
@@ -139,7 +141,7 @@
         }
 
         public int getSeq() {
-            return mLastBatterySeq;
+            return mLastStorageSeq;
         }
 
         @Override
@@ -150,8 +152,8 @@
         @VisibleForTesting
         public void onReceiveInternal(Intent intent) {
             final String action = intent.getAction();
-            mLastBatterySeq = intent.getIntExtra(DeviceStorageMonitorService.EXTRA_SEQUENCE,
-                    mLastBatterySeq);
+            mLastStorageSeq = intent.getIntExtra(DeviceStorageMonitorService.EXTRA_SEQUENCE,
+                    mLastStorageSeq);
             if (Intent.ACTION_DEVICE_STORAGE_LOW.equals(action)) {
                 if (DEBUG) {
                     Slog.d(TAG, "Available storage too low to do work. @ "
@@ -190,4 +192,30 @@
             pw.println();
         }
     }
+
+    @Override
+    public void dumpControllerStateLocked(ProtoOutputStream proto, long fieldId, int filterUid) {
+        final long token = proto.start(fieldId);
+        final long mToken = proto.start(StateControllerProto.STORAGE);
+
+        proto.write(StateControllerProto.StorageController.IS_STORAGE_NOT_LOW,
+                mStorageTracker.isStorageNotLow());
+        proto.write(StateControllerProto.StorageController.LAST_BROADCAST_SEQUENCE_NUMBER,
+                mStorageTracker.getSeq());
+
+        for (int i = 0; i < mTrackedTasks.size(); i++) {
+            final JobStatus js = mTrackedTasks.valueAt(i);
+            if (!js.shouldDump(filterUid)) {
+                continue;
+            }
+            final long jsToken = proto.start(StateControllerProto.StorageController.TRACKED_JOBS);
+            js.writeToShortProto(proto, StateControllerProto.StorageController.TrackedJob.INFO);
+            proto.write(StateControllerProto.StorageController.TrackedJob.SOURCE_UID,
+                    js.getSourceUid());
+            proto.end(jsToken);
+        }
+
+        proto.end(mToken);
+        proto.end(token);
+    }
 }
diff --git a/services/core/java/com/android/server/job/controllers/TimeController.java b/services/core/java/com/android/server/job/controllers/TimeController.java
index cb9e43a..bbee0eb 100644
--- a/services/core/java/com/android/server/job/controllers/TimeController.java
+++ b/services/core/java/com/android/server/job/controllers/TimeController.java
@@ -25,9 +25,11 @@
 import android.os.WorkSource;
 import android.util.Slog;
 import android.util.TimeUtils;
+import android.util.proto.ProtoOutputStream;
 
 import com.android.server.job.JobSchedulerService;
 import com.android.server.job.StateChangedListener;
+import com.android.server.job.StateControllerProto;
 
 import java.io.PrintWriter;
 import java.util.Iterator;
@@ -331,7 +333,7 @@
     public void dumpControllerStateLocked(PrintWriter pw, int filterUid) {
         final long nowElapsed = sElapsedRealtimeClock.millis();
         pw.print("Alarms: now=");
-        pw.print(sElapsedRealtimeClock.millis());
+        pw.print(nowElapsed);
         pw.println();
         pw.print("Next delay alarm in ");
         TimeUtils.formatDuration(mNextDelayExpiredElapsedMillis, nowElapsed, pw);
@@ -365,4 +367,40 @@
             pw.println();
         }
     }
-}
\ No newline at end of file
+
+    @Override
+    public void dumpControllerStateLocked(ProtoOutputStream proto, long fieldId, int filterUid) {
+        final long token = proto.start(fieldId);
+        final long mToken = proto.start(StateControllerProto.TIME);
+
+        final long nowElapsed = sElapsedRealtimeClock.millis();
+        proto.write(StateControllerProto.TimeController.NOW_ELAPSED_REALTIME, nowElapsed);
+        proto.write(StateControllerProto.TimeController.TIME_UNTIL_NEXT_DELAY_ALARM_MS,
+                mNextDelayExpiredElapsedMillis - nowElapsed);
+        proto.write(StateControllerProto.TimeController.TIME_UNTIL_NEXT_DEADLINE_ALARM_MS,
+                mNextJobExpiredElapsedMillis - nowElapsed);
+
+        for (JobStatus ts : mTrackedJobs) {
+            if (!ts.shouldDump(filterUid)) {
+                continue;
+            }
+            final long tsToken = proto.start(StateControllerProto.TimeController.TRACKED_JOBS);
+            ts.writeToShortProto(proto, StateControllerProto.TimeController.TrackedJob.INFO);
+
+            proto.write(StateControllerProto.TimeController.TrackedJob.HAS_TIMING_DELAY_CONSTRAINT,
+                    ts.hasTimingDelayConstraint());
+            proto.write(StateControllerProto.TimeController.TrackedJob.DELAY_TIME_REMAINING_MS,
+                    ts.getEarliestRunTime() - nowElapsed);
+
+            proto.write(StateControllerProto.TimeController.TrackedJob.HAS_DEADLINE_CONSTRAINT,
+                    ts.hasDeadlineConstraint());
+            proto.write(StateControllerProto.TimeController.TrackedJob.TIME_REMAINING_UNTIL_DEADLINE_MS,
+                    ts.getLatestRunTimeElapsed() - nowElapsed);
+
+            proto.end(tsToken);
+        }
+
+        proto.end(mToken);
+        proto.end(token);
+    }
+}
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/KeySyncTask.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/KeySyncTask.java
index 6dafd0b..ded8a79 100644
--- a/services/core/java/com/android/server/locksettings/recoverablekeystore/KeySyncTask.java
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/KeySyncTask.java
@@ -342,7 +342,7 @@
         for (String alias : encryptedApplicationKeys.keySet()) {
             keyEntries.add(
                     new KeyEntryRecoveryData(
-                            alias.getBytes(StandardCharsets.UTF_8),
+                            alias,
                             encryptedApplicationKeys.get(alias)));
         }
         return keyEntries;
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java
index 54acb87..6b8e9ed 100644
--- a/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java
@@ -466,7 +466,7 @@
             @NonNull List<KeyEntryRecoveryData> applicationKeys) throws RemoteException {
         HashMap<String, byte[]> keyMaterialByAlias = new HashMap<>();
         for (KeyEntryRecoveryData applicationKey : applicationKeys) {
-            String alias = new String(applicationKey.getAlias(), StandardCharsets.UTF_8);
+            String alias = applicationKey.getAlias();
             byte[] encryptedKeyMaterial = applicationKey.getEncryptedKeyMaterial();
 
             try {
diff --git a/services/core/java/com/android/server/net/watchlist/NetworkWatchlistService.java b/services/core/java/com/android/server/net/watchlist/NetworkWatchlistService.java
index f35e6ec..3bcc36f 100644
--- a/services/core/java/com/android/server/net/watchlist/NetworkWatchlistService.java
+++ b/services/core/java/com/android/server/net/watchlist/NetworkWatchlistService.java
@@ -29,6 +29,7 @@
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.os.SystemProperties;
+import android.provider.Settings;
 import android.text.TextUtils;
 import android.util.Slog;
 
@@ -53,9 +54,6 @@
     private static final String TAG = NetworkWatchlistService.class.getSimpleName();
     static final boolean DEBUG = false;
 
-    private static final String PROPERTY_NETWORK_WATCHLIST_ENABLED =
-            "ro.network_watchlist_enabled";
-
     private static final int MAX_NUM_OF_WATCHLIST_DIGESTS = 10000;
 
     public static class Lifecycle extends SystemService {
@@ -67,8 +65,10 @@
 
         @Override
         public void onStart() {
-            if (!SystemProperties.getBoolean(PROPERTY_NETWORK_WATCHLIST_ENABLED, false)) {
+            if (Settings.Global.getInt(getContext().getContentResolver(),
+                    Settings.Global.NETWORK_WATCHLIST_ENABLED, 0) == 0) {
                 // Watchlist service is disabled
+                Slog.i(TAG, "Network Watchlist service is disabled");
                 return;
             }
             mService = new NetworkWatchlistService(getContext());
@@ -77,11 +77,13 @@
 
         @Override
         public void onBootPhase(int phase) {
-            if (!SystemProperties.getBoolean(PROPERTY_NETWORK_WATCHLIST_ENABLED, false)) {
-                // Watchlist service is disabled
-                return;
-            }
             if (phase == SystemService.PHASE_ACTIVITY_MANAGER_READY) {
+                if (Settings.Global.getInt(getContext().getContentResolver(),
+                        Settings.Global.NETWORK_WATCHLIST_ENABLED, 0) == 0) {
+                    // Watchlist service is disabled
+                    Slog.i(TAG, "Network Watchlist service is disabled");
+                    return;
+                }
                 try {
                     mService.initIpConnectivityMetrics();
                     mService.startWatchlistLogging();
diff --git a/services/core/java/com/android/server/notification/ManagedServices.java b/services/core/java/com/android/server/notification/ManagedServices.java
index 7d64aed4..37e6ae9 100644
--- a/services/core/java/com/android/server/notification/ManagedServices.java
+++ b/services/core/java/com/android/server/notification/ManagedServices.java
@@ -251,23 +251,17 @@
         for (ComponentName cmpt : mEnabledServicesForCurrentProfiles) {
             if (filter != null && !filter.matches(cmpt)) continue;
 
-            final long cToken = proto.start(ManagedServicesProto.ENABLED);
-            cmpt.toProto(proto);
-            proto.end(cToken);
+            cmpt.writeToProto(proto, ManagedServicesProto.ENABLED);
         }
 
         for (ManagedServiceInfo info : mServices) {
             if (filter != null && !filter.matches(info.component)) continue;
 
-            final long lToken = proto.start(ManagedServicesProto.LIVE_SERVICES);
-            info.toProto(proto, this);
-            proto.end(lToken);
+            info.writeToProto(proto, ManagedServicesProto.LIVE_SERVICES, this);
         }
 
         for (ComponentName name : mSnoozingForCurrentProfiles) {
-            final long cToken = proto.start(ManagedServicesProto.SNOOZED);
-            name.toProto(proto);
-            proto.end(cToken);
+            name.writeToProto(proto, ManagedServicesProto.SNOOZED);
         }
     }
 
@@ -1132,14 +1126,16 @@
                     .append(']').toString();
         }
 
-        public void toProto(ProtoOutputStream proto, ManagedServices host) {
-            final long cToken = proto.start(ManagedServiceInfoProto.COMPONENT);
-            component.toProto(proto);
-            proto.end(cToken);
+        public void writeToProto(ProtoOutputStream proto, long fieldId, ManagedServices host) {
+            final long token = proto.start(fieldId);
+
+            component.writeToProto(proto, ManagedServiceInfoProto.COMPONENT);
             proto.write(ManagedServiceInfoProto.USER_ID, userid);
             proto.write(ManagedServiceInfoProto.SERVICE, service.getClass().getName());
             proto.write(ManagedServiceInfoProto.IS_SYSTEM, isSystem);
             proto.write(ManagedServiceInfoProto.IS_GUEST, isGuest(host));
+
+            proto.end(token);
         }
 
         public boolean enabledAndUserMatches(int nid) {
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index cf01400..575c44d 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -3376,9 +3376,7 @@
                     mListenersDisablingEffects.valueAt(i);
                 for (int j = 0; j < listeners.size(); j++) {
                     final ManagedServiceInfo listener = listeners.valueAt(i);
-                    listenersToken = proto.start(ListenersDisablingEffectsProto.LISTENERS);
-                    listener.toProto(proto, null);
-                    proto.end(listenersToken);
+                    listener.writeToProto(proto, ListenersDisablingEffectsProto.LISTENERS, null);
                 }
 
                 proto.end(effectsToken);
diff --git a/services/core/java/com/android/server/pm/InstantAppRegistry.java b/services/core/java/com/android/server/pm/InstantAppRegistry.java
index af20cd7..c964f91 100644
--- a/services/core/java/com/android/server/pm/InstantAppRegistry.java
+++ b/services/core/java/com/android/server/pm/InstantAppRegistry.java
@@ -49,6 +49,7 @@
 import com.android.internal.os.SomeArgs;
 import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.XmlUtils;
+import com.android.server.pm.permission.BasePermission;
 
 import libcore.io.IoUtils;
 import org.xmlpull.v1.XmlPullParser;
@@ -301,7 +302,7 @@
             // into account but also allow the value from the old computation to avoid
             // data loss.
             final String[] signaturesSha256Digests = PackageUtils.computeSignaturesSha256Digests(
-                    pkg.mSigningDetails.signatures);
+                    pkg.mSignatures);
             final String signaturesSha256Digest = PackageUtils.computeSignaturesSha256Digest(
                     signaturesSha256Digests);
 
@@ -312,7 +313,7 @@
             }
 
             // For backwards compatibility we accept match based on first signature
-            if (pkg.mSigningDetails.signatures.length > 1 && currentCookieFile.equals(computeInstantCookieFile(
+            if (pkg.mSignatures.length > 1 && currentCookieFile.equals(computeInstantCookieFile(
                     pkg.packageName, signaturesSha256Digests[0], userId))) {
                 return;
             }
@@ -1175,13 +1176,12 @@
             // We prefer the modern computation procedure where all certs are taken
             // into account and delete the file derived via the legacy hash computation.
             File newCookieFile = computeInstantCookieFile(pkg.packageName,
-                    PackageUtils.computeSignaturesSha256Digest(pkg.mSigningDetails.signatures), userId);
-            if (!pkg.mSigningDetails.hasSignatures()) {
-                Slog.wtf(LOG_TAG, "Parsed Instant App contains no valid signatures!");
-            }
-            File oldCookieFile = peekInstantCookieFile(pkg.packageName, userId);
-            if (oldCookieFile != null && !newCookieFile.equals(oldCookieFile)) {
-                oldCookieFile.delete();
+                    PackageUtils.computeSignaturesSha256Digest(pkg.mSignatures), userId);
+            if (pkg.mSignatures.length > 0) {
+                File oldCookieFile = peekInstantCookieFile(pkg.packageName, userId);
+                if (oldCookieFile != null && !newCookieFile.equals(oldCookieFile)) {
+                    oldCookieFile.delete();
+                }
             }
             cancelPendingPersistLPw(pkg, userId);
             addPendingPersistCookieLPw(userId, pkg, cookie, newCookieFile);
diff --git a/services/core/java/com/android/server/pm/KeySetManagerService.java b/services/core/java/com/android/server/pm/KeySetManagerService.java
index 93d3b77..fca9585 100644
--- a/services/core/java/com/android/server/pm/KeySetManagerService.java
+++ b/services/core/java/com/android/server/pm/KeySetManagerService.java
@@ -188,7 +188,7 @@
             throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
                     "Passed invalid package to keyset validation.");
         }
-        ArraySet<PublicKey> signingKeys = pkg.mSigningDetails.publicKeys;
+        ArraySet<PublicKey> signingKeys = pkg.mSigningKeys;
         if (signingKeys == null || !(signingKeys.size() > 0) || signingKeys.contains(null)) {
             throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
                     "Package has invalid signing-key-set.");
@@ -223,7 +223,7 @@
         PackageSetting ps = mPackages.get(pkg.packageName);
         Preconditions.checkNotNull(ps, "pkg: " + pkg.packageName
                     + "does not have a corresponding entry in mPackages.");
-        addSigningKeySetToPackageLPw(ps, pkg.mSigningDetails.publicKeys);
+        addSigningKeySetToPackageLPw(ps, pkg.mSigningKeys);
         if (pkg.mKeySetMapping != null) {
             addDefinedKeySetsToPackageLPw(ps, pkg.mKeySetMapping);
             if (pkg.mUpgradeKeySets != null) {
@@ -371,7 +371,7 @@
         long[] upgradeKeySets = oldPS.keySetData.getUpgradeKeySets();
         for (int i = 0; i < upgradeKeySets.length; i++) {
             Set<PublicKey> upgradeSet = getPublicKeysFromKeySetLPr(upgradeKeySets[i]);
-            if (upgradeSet != null && newPkg.mSigningDetails.publicKeys.containsAll(upgradeSet)) {
+            if (upgradeSet != null && newPkg.mSigningKeys.containsAll(upgradeSet)) {
                 return true;
             }
         }
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index 4e91898..5577de8 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -58,6 +58,7 @@
 import android.content.pm.PackageParser.ApkLite;
 import android.content.pm.PackageParser.PackageLite;
 import android.content.pm.PackageParser.PackageParserException;
+import android.content.pm.Signature;
 import android.graphics.Bitmap;
 import android.graphics.BitmapFactory;
 import android.os.Binder;
@@ -83,7 +84,6 @@
 import android.util.ExceptionUtils;
 import android.util.MathUtils;
 import android.util.Slog;
-import android.util.apk.ApkSignatureVerifier;
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.content.NativeLibraryHelper;
@@ -107,7 +107,7 @@
 import java.io.FileFilter;
 import java.io.FileOutputStream;
 import java.io.IOException;
-import java.security.cert.CertificateException;
+import java.security.cert.Certificate;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
@@ -227,7 +227,9 @@
     @GuardedBy("mLock")
     private long mVersionCode;
     @GuardedBy("mLock")
-    private PackageParser.SigningDetails mSigningDetails;
+    private Signature[] mSignatures;
+    @GuardedBy("mLock")
+    private Certificate[][] mCertificates;
 
     /**
      * Path to the validated base APK for this session, which may point at an
@@ -855,7 +857,7 @@
         }
 
         Preconditions.checkNotNull(mPackageName);
-        Preconditions.checkNotNull(mSigningDetails);
+        Preconditions.checkNotNull(mSignatures);
         Preconditions.checkNotNull(mResolvedBaseFile);
 
         if (needToAskForPermissionsLocked()) {
@@ -936,7 +938,7 @@
 
         mRelinquished = true;
         mPm.installStage(mPackageName, stageDir, localObserver, params,
-                mInstallerPackageName, mInstallerUid, user, mSigningDetails);
+                mInstallerPackageName, mInstallerUid, user, mCertificates);
     }
 
     /**
@@ -955,7 +957,7 @@
             throws PackageManagerException {
         mPackageName = null;
         mVersionCode = -1;
-        mSigningDetails = PackageParser.SigningDetails.UNKNOWN;
+        mSignatures = null;
 
         mResolvedBaseFile = null;
         mResolvedStagedFiles.clear();
@@ -1007,8 +1009,9 @@
                 mPackageName = apk.packageName;
                 mVersionCode = apk.getLongVersionCode();
             }
-            if (mSigningDetails == PackageParser.SigningDetails.UNKNOWN) {
-                mSigningDetails = apk.signingDetails;
+            if (mSignatures == null) {
+                mSignatures = apk.signatures;
+                mCertificates = apk.certificates;
             }
 
             assertApkConsistentLocked(String.valueOf(addedFile), apk);
@@ -1057,15 +1060,8 @@
                 mPackageName = pkgInfo.packageName;
                 mVersionCode = pkgInfo.getLongVersionCode();
             }
-            if (mSigningDetails == PackageParser.SigningDetails.UNKNOWN) {
-                try {
-                    mSigningDetails = ApkSignatureVerifier.plsCertsNoVerifyOnlyCerts(
-                            pkgInfo.applicationInfo.sourceDir,
-                            PackageParser.SigningDetails.SignatureSchemeVersion.JAR);
-                } catch (PackageParserException e) {
-                    throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
-                            "Couldn't obtain signatures from base APK");
-                }
+            if (mSignatures == null) {
+                mSignatures = pkgInfo.signatures;
             }
         }
 
@@ -1159,7 +1155,7 @@
                     + " version code " + apk.versionCode + " inconsistent with "
                     + mVersionCode);
         }
-        if (!mSigningDetails.signaturesMatchExactly(apk.signingDetails)) {
+        if (!Signature.areExactMatch(mSignatures, apk.signatures)) {
             throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
                     tag + " signatures are inconsistent");
         }
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 3df7c47..44aad44 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -337,6 +337,7 @@
 import java.security.NoSuchAlgorithmException;
 import java.security.PublicKey;
 import java.security.SecureRandom;
+import java.security.cert.Certificate;
 import java.security.cert.CertificateException;
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -5359,7 +5360,7 @@
                     || filterAppAccessLPr(ps2, callingUid, callingUserId)) {
                 return PackageManager.SIGNATURE_UNKNOWN_PACKAGE;
             }
-            return compareSignatures(p1.mSigningDetails.signatures, p2.mSigningDetails.signatures);
+            return compareSignatures(p1.mSignatures, p2.mSignatures);
         }
     }
 
@@ -8193,11 +8194,19 @@
                 && ps.timeStamp == lastModifiedTime
                 && !isCompatSignatureUpdateNeeded(pkg)
                 && !isRecoverSignatureUpdateNeeded(pkg)) {
-            if ((ps.pkg != null) &&
-                    PackageParser.SigningDetails.UNKNOWN != ps.pkg.mSigningDetails) {
-                // Optimization: reuse the existing cached signing data
+            long mSigningKeySetId = ps.keySetData.getProperSigningKeySet();
+            final KeySetManagerService ksms = mSettings.mKeySetManagerService;
+            ArraySet<PublicKey> signingKs;
+            synchronized (mPackages) {
+                signingKs = ksms.getPublicKeysFromKeySetLPr(mSigningKeySetId);
+            }
+            if (ps.signatures.mSignatures != null
+                    && ps.signatures.mSignatures.length != 0
+                    && signingKs != null) {
+                // Optimization: reuse the existing cached certificates
                 // if the package appears to be unchanged.
-                pkg.mSigningDetails = ps.pkg.mSigningDetails;
+                pkg.mSignatures = ps.signatures.mSignatures;
+                pkg.mSigningKeys = signingKs;
                 return;
             }
 
@@ -8537,7 +8546,7 @@
              * Check to make sure the signatures match first. If they don't,
              * wipe the installed application and its data.
              */
-            if (compareSignatures(ps.signatures.mSignatures, pkg.mSigningDetails.signatures)
+            if (compareSignatures(ps.signatures.mSignatures, pkg.mSignatures)
                     != PackageManager.SIGNATURE_MATCH) {
                 logCriticalInfo(Log.WARN, "Package " + ps.name + " appeared on system, but"
                         + " signatures don't match existing userdata copy; removing");
@@ -9509,10 +9518,9 @@
                     final String[] expectedCertDigests = requiredCertDigests[i];
                     // For apps targeting O MR1 we require explicit enumeration of all certs.
                     final String[] libCertDigests = (targetSdk > Build.VERSION_CODES.O)
-                            ? PackageUtils.computeSignaturesSha256Digests(
-                            libPkg.mSigningDetails.signatures)
+                            ? PackageUtils.computeSignaturesSha256Digests(libPkg.mSignatures)
                             : PackageUtils.computeSignaturesSha256Digests(
-                                    new Signature[]{libPkg.mSigningDetails.signatures[0]});
+                                    new Signature[]{libPkg.mSignatures[0]});
 
                     // Take a shortcut if sizes don't match. Note that if an app doesn't
                     // target O we don't parse the "additional-certificate" tags similarly
@@ -9848,14 +9856,14 @@
             if (ksms.checkUpgradeKeySetLocked(signatureCheckPs, pkg)) {
                 // We just determined the app is signed correctly, so bring
                 // over the latest parsed certs.
-                pkgSetting.signatures.mSignatures = pkg.mSigningDetails.signatures;
+                pkgSetting.signatures.mSignatures = pkg.mSignatures;
             } else {
                 if ((parseFlags & PackageParser.PARSE_IS_SYSTEM_DIR) == 0) {
                     throw new PackageManagerException(INSTALL_FAILED_UPDATE_INCOMPATIBLE,
                             "Package " + pkg.packageName + " upgrade keys do not match the "
                                     + "previously installed version");
                 } else {
-                    pkgSetting.signatures.mSignatures = pkg.mSigningDetails.signatures;
+                    pkgSetting.signatures.mSignatures = pkg.mSignatures;
                     String msg = "System package " + pkg.packageName
                             + " signature changed; retaining data.";
                     reportSettingsProblem(Log.WARN, msg);
@@ -9865,8 +9873,7 @@
             try {
                 final boolean compareCompat = isCompatSignatureUpdateNeeded(pkg);
                 final boolean compareRecover = isRecoverSignatureUpdateNeeded(pkg);
-                final boolean compatMatch = verifySignatures(signatureCheckPs,
-                        pkg.mSigningDetails.signatures,
+                final boolean compatMatch = verifySignatures(signatureCheckPs, pkg.mSignatures,
                         compareCompat, compareRecover);
                 // The new KeySets will be re-added later in the scanning process.
                 if (compatMatch) {
@@ -9876,14 +9883,14 @@
                 }
                 // We just determined the app is signed correctly, so bring
                 // over the latest parsed certs.
-                pkgSetting.signatures.mSignatures = pkg.mSigningDetails.signatures;
+                pkgSetting.signatures.mSignatures = pkg.mSignatures;
             } catch (PackageManagerException e) {
                 if ((parseFlags & PackageParser.PARSE_IS_SYSTEM_DIR) == 0) {
                     throw e;
                 }
                 // The signature has changed, but this package is in the system
                 // image...  let's recover!
-                pkgSetting.signatures.mSignatures = pkg.mSigningDetails.signatures;
+                pkgSetting.signatures.mSignatures = pkg.mSignatures;
                 // However...  if this package is part of a shared user, but it
                 // doesn't match the signature of the shared user, let's fail.
                 // What this means is that you can't change the signatures
@@ -9891,7 +9898,7 @@
                 // that unreasonable.
                 if (signatureCheckPs.sharedUser != null) {
                     if (compareSignatures(signatureCheckPs.sharedUser.signatures.mSignatures,
-                            pkg.mSigningDetails.signatures) != PackageManager.SIGNATURE_MATCH) {
+                            pkg.mSignatures) != PackageManager.SIGNATURE_MATCH) {
                         throw new PackageManagerException(
                                 INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES,
                                 "Signature mismatch for shared user: "
@@ -13197,7 +13204,7 @@
     void installStage(String packageName, File stagedDir,
             IPackageInstallObserver2 observer, PackageInstaller.SessionParams sessionParams,
             String installerPackageName, int installerUid, UserHandle user,
-            PackageParser.SigningDetails signingDetails) {
+            Certificate[][] certificates) {
         if (DEBUG_EPHEMERAL) {
             if ((sessionParams.installFlags & PackageManager.INSTALL_INSTANT_APP) != 0) {
                 Slog.d(TAG, "Ephemeral install of " + packageName);
@@ -13215,7 +13222,7 @@
         final InstallParams params = new InstallParams(origin, null, observer,
                 sessionParams.installFlags, installerPackageName, sessionParams.volumeUuid,
                 verificationInfo, user, sessionParams.abiOverride,
-                sessionParams.grantedRuntimePermissions, signingDetails, installReason);
+                sessionParams.grantedRuntimePermissions, certificates, installReason);
         params.setTraceMethod("installStage").setTraceCookie(System.identityHashCode(params));
         msg.obj = params;
 
@@ -13819,7 +13826,7 @@
             final PackageParser.Package pkg = mPackages.get(verifierInfo.packageName);
             if (pkg == null) {
                 return -1;
-            } else if (pkg.mSigningDetails.signatures.length != 1) {
+            } else if (pkg.mSignatures.length != 1) {
                 Slog.i(TAG, "Verifier package " + verifierInfo.packageName
                         + " has more than one signature; ignoring");
                 return -1;
@@ -13833,7 +13840,7 @@
 
             final byte[] expectedPublicKey;
             try {
-                final Signature verifierSig = pkg.mSigningDetails.signatures[0];
+                final Signature verifierSig = pkg.mSignatures[0];
                 final PublicKey publicKey = verifierSig.getPublicKey();
                 expectedPublicKey = publicKey.getEncoded();
             } catch (CertificateException e) {
@@ -14525,13 +14532,13 @@
         final String packageAbiOverride;
         final String[] grantedRuntimePermissions;
         final VerificationInfo verificationInfo;
-        final PackageParser.SigningDetails signingDetails;
+        final Certificate[][] certificates;
         final int installReason;
 
         InstallParams(OriginInfo origin, MoveInfo move, IPackageInstallObserver2 observer,
                 int installFlags, String installerPackageName, String volumeUuid,
                 VerificationInfo verificationInfo, UserHandle user, String packageAbiOverride,
-                String[] grantedPermissions, PackageParser.SigningDetails signingDetails, int installReason) {
+                String[] grantedPermissions, Certificate[][] certificates, int installReason) {
             super(user);
             this.origin = origin;
             this.move = move;
@@ -14542,7 +14549,7 @@
             this.verificationInfo = verificationInfo;
             this.packageAbiOverride = packageAbiOverride;
             this.grantedRuntimePermissions = grantedPermissions;
-            this.signingDetails = signingDetails;
+            this.certificates = certificates;
             this.installReason = installReason;
         }
 
@@ -14973,7 +14980,7 @@
         /** If non-null, drop an async trace when the install completes */
         final String traceMethod;
         final int traceCookie;
-        final PackageParser.SigningDetails signingDetails;
+        final Certificate[][] certificates;
         final int installReason;
 
         // The list of instruction sets supported by this app. This is currently
@@ -14985,7 +14992,7 @@
                 int installFlags, String installerPackageName, String volumeUuid,
                 UserHandle user, String[] instructionSets,
                 String abiOverride, String[] installGrantPermissions,
-                String traceMethod, int traceCookie, PackageParser.SigningDetails signingDetails,
+                String traceMethod, int traceCookie, Certificate[][] certificates,
                 int installReason) {
             this.origin = origin;
             this.move = move;
@@ -14999,7 +15006,7 @@
             this.installGrantPermissions = installGrantPermissions;
             this.traceMethod = traceMethod;
             this.traceCookie = traceCookie;
-            this.signingDetails = signingDetails;
+            this.certificates = certificates;
             this.installReason = installReason;
         }
 
@@ -15095,7 +15102,7 @@
                     params.installerPackageName, params.volumeUuid,
                     params.getUser(), null /*instructionSets*/, params.packageAbiOverride,
                     params.grantedRuntimePermissions,
-                    params.traceMethod, params.traceCookie, params.signingDetails,
+                    params.traceMethod, params.traceCookie, params.certificates,
                     params.installReason);
             if (isFwdLocked()) {
                 throw new IllegalArgumentException("Forward locking only supported in ASEC");
@@ -15105,7 +15112,7 @@
         /** Existing install */
         FileInstallArgs(String codePath, String resourcePath, String[] instructionSets) {
             super(OriginInfo.fromNothing(), null, null, 0, null, null, null, instructionSets,
-                    null, null, null, 0, PackageParser.SigningDetails.UNKNOWN,
+                    null, null, null, 0, null /*certificates*/,
                     PackageManager.INSTALL_REASON_UNKNOWN);
             this.codeFile = (codePath != null) ? new File(codePath) : null;
             this.resourceFile = (resourcePath != null) ? new File(resourcePath) : null;
@@ -15326,7 +15333,7 @@
                     params.installerPackageName, params.volumeUuid,
                     params.getUser(), null /* instruction sets */, params.packageAbiOverride,
                     params.grantedRuntimePermissions,
-                    params.traceMethod, params.traceCookie, params.signingDetails,
+                    params.traceMethod, params.traceCookie, params.certificates,
                     params.installReason);
         }
 
@@ -15665,8 +15672,7 @@
                 }
             } else {
                 // default to original signature matching
-                if (compareSignatures(oldPackage.mSigningDetails.signatures,
-                        pkg.mSigningDetails.signatures)
+                if (compareSignatures(oldPackage.mSignatures, pkg.mSignatures)
                         != PackageManager.SIGNATURE_MATCH) {
                     res.setError(INSTALL_FAILED_UPDATE_INCOMPATIBLE,
                             "New package has a different signature: " + pkgName);
@@ -16478,8 +16484,14 @@
 
         try {
             // either use what we've been given or parse directly from the APK
-            if (args.signingDetails != PackageParser.SigningDetails.UNKNOWN) {
-                pkg.setSigningDetails(args.signingDetails);
+            if (args.certificates != null) {
+                try {
+                    PackageParser.populateCertificates(pkg, args.certificates);
+                } catch (PackageParserException e) {
+                    // there was something wrong with the certificates we were given;
+                    // try to pull them from the APK
+                    PackageParser.collectCertificates(pkg, parseFlags);
+                }
             } else {
                 PackageParser.collectCertificates(pkg, parseFlags);
             }
@@ -16598,8 +16610,7 @@
                         final boolean compareCompat = isCompatSignatureUpdateNeeded(pkg);
                         final boolean compareRecover = isRecoverSignatureUpdateNeeded(pkg);
                         final boolean compatMatch = verifySignatures(
-                                signatureCheckPs, pkg.mSigningDetails.signatures, compareCompat,
-                                compareRecover);
+                                signatureCheckPs, pkg.mSignatures, compareCompat, compareRecover);
                         // The new KeySets will be re-added later in the scanning process.
                         if (compatMatch) {
                             synchronized (mPackages) {
@@ -16650,7 +16661,7 @@
                         sigsOk = ksms.checkUpgradeKeySetLocked(sourcePackageSetting, pkg);
                     } else {
                         sigsOk = compareSignatures(sourcePackageSetting.signatures.mSignatures,
-                                pkg.mSigningDetails.signatures) == PackageManager.SIGNATURE_MATCH;
+                                pkg.mSignatures) == PackageManager.SIGNATURE_MATCH;
                     }
                     if (!sigsOk) {
                         // If the owning package is the system itself, we log but allow
@@ -16926,8 +16937,7 @@
                 for (ActivityIntentInfo filter : a.intents) {
                     if (filter.needsVerification() && needsNetworkVerificationLPr(filter)) {
                         if (DEBUG_DOMAIN_VERIFICATION) {
-                            Slog.d(TAG,
-                                    "Intent filter needs verification, so processing all filters");
+                            Slog.d(TAG, "Intent filter needs verification, so processing all filters");
                         }
                         needToVerify = true;
                         break;
@@ -22235,8 +22245,8 @@
         final OriginInfo origin = OriginInfo.fromExistingFile(codeFile);
         final InstallParams params = new InstallParams(origin, move, installObserver, installFlags,
                 installerPackageName, volumeUuid, null /*verificationInfo*/, user,
-                packageAbiOverride, null /*grantedPermissions*/,
-                PackageParser.SigningDetails.UNKNOWN, PackageManager.INSTALL_REASON_UNKNOWN);
+                packageAbiOverride, null /*grantedPermissions*/, null /*certificates*/,
+                PackageManager.INSTALL_REASON_UNKNOWN);
         params.setTraceMethod("movePackage").setTraceCookie(System.identityHashCode(params));
         msg.obj = params;
 
diff --git a/services/core/java/com/android/server/pm/SELinuxMMAC.java b/services/core/java/com/android/server/pm/SELinuxMMAC.java
index 37f9a74..fbf3d82 100644
--- a/services/core/java/com/android/server/pm/SELinuxMMAC.java
+++ b/services/core/java/com/android/server/pm/SELinuxMMAC.java
@@ -17,6 +17,8 @@
 package com.android.server.pm;
 
 import android.content.pm.PackageParser;
+import android.content.pm.PackageUserState;
+import android.content.pm.SELinuxUtil;
 import android.content.pm.Signature;
 import android.os.Environment;
 import android.util.Slog;
@@ -451,7 +453,7 @@
     public String getMatchedSeInfo(PackageParser.Package pkg) {
         // Check for exact signature matches across all certs.
         Signature[] certs = mCerts.toArray(new Signature[0]);
-        if (!Signature.areExactMatch(certs, pkg.mSigningDetails.signatures)) {
+        if (!Signature.areExactMatch(certs, pkg.mSignatures)) {
             return null;
         }
 
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index b5d3af1..4cf1814 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -955,7 +955,7 @@
         }
         // Update signatures if needed.
         if (p.signatures.mSignatures == null) {
-            p.signatures.assignSignatures(pkg.mSigningDetails.signatures);
+            p.signatures.assignSignatures(pkg.mSignatures);
         }
         // Update flags if needed.
         if (pkg.applicationInfo.flags != p.pkgFlags) {
@@ -964,7 +964,7 @@
         // If this app defines a shared user id initialize
         // the shared user signatures as well.
         if (p.sharedUser != null && p.sharedUser.signatures.mSignatures == null) {
-            p.sharedUser.signatures.assignSignatures(pkg.mSigningDetails.signatures);
+            p.sharedUser.signatures.assignSignatures(pkg.mSignatures);
         }
         // Update static shared library dependencies if needed
         if (pkg.usesStaticLibraries != null && pkg.usesStaticLibrariesVersions != null
@@ -4565,8 +4565,10 @@
             }
             pw.print(prefix); pw.print("  versionName="); pw.println(ps.pkg.mVersionName);
             pw.print(prefix); pw.print("  splits="); dumpSplitNames(pw, ps.pkg); pw.println();
-            final int apkSigningVersion = ps.pkg.mSigningDetails.signatureSchemeVersion;
-            pw.print(prefix); pw.print("  apkSigningVersion="); pw.println(apkSigningVersion);
+            final int apkSigningVersion = PackageParser.getApkSigningVersion(ps.pkg);
+            if (apkSigningVersion != PackageParser.APK_SIGNING_UNKNOWN) {
+                pw.print(prefix); pw.print("  apkSigningVersion="); pw.println(apkSigningVersion);
+            }
             pw.print(prefix); pw.print("  applicationInfo=");
                 pw.println(ps.pkg.applicationInfo.toString());
             pw.print(prefix); pw.print("  flags="); printFlags(pw, ps.pkg.applicationInfo.flags,
diff --git a/services/core/java/com/android/server/pm/ShortcutPackage.java b/services/core/java/com/android/server/pm/ShortcutPackage.java
index 7bab318..ebf6672 100644
--- a/services/core/java/com/android/server/pm/ShortcutPackage.java
+++ b/services/core/java/com/android/server/pm/ShortcutPackage.java
@@ -459,8 +459,7 @@
         }
 
         // Then, for the pinned set for each launcher, set the pin flag one by one.
-        mShortcutUser.mService.getUserShortcutsLocked(getPackageUserId())
-                .forAllLaunchers(launcherShortcuts -> {
+        mShortcutUser.forAllLaunchers(launcherShortcuts -> {
             final ArraySet<String> pinned = launcherShortcuts.getPinnedShortcutIds(
                     getPackageName(), getPackageUserId());
 
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index c3dce31..7d57566 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -63,6 +63,7 @@
 import android.os.ServiceManager;
 import android.os.ShellCallback;
 import android.os.ShellCommand;
+import android.os.SystemClock;
 import android.os.SystemProperties;
 import android.os.UserHandle;
 import android.os.UserManager;
@@ -79,6 +80,7 @@
 import android.util.SparseArray;
 import android.util.SparseBooleanArray;
 import android.util.SparseIntArray;
+import android.util.SparseLongArray;
 import android.util.TimeUtils;
 import android.util.Xml;
 
@@ -241,8 +243,7 @@
     private static final IBinder mUserRestriconToken = new Binder();
 
     /**
-     * User-related information that is used for persisting to flash. Only UserInfo is
-     * directly exposed to other system apps.
+     * Internal non-parcelable wrapper for UserInfo that is not exposed to other system apps.
      */
     @VisibleForTesting
     static class UserData {
@@ -260,6 +261,12 @@
         // Whether to perist the seed account information to be available after a boot
         boolean persistSeedData;
 
+        /** Elapsed realtime since boot when the user started. */
+        long startRealtime;
+
+        /** Elapsed realtime since boot when the user was unlocked. */
+        long unlockRealtime;
+
         void clearSeedAccountData() {
             seedAccountName = null;
             seedAccountType = null;
@@ -453,6 +460,37 @@
                 mUms.cleanupPartialUsers();
             }
         }
+
+        @Override
+        public void onStartUser(int userHandle) {
+            synchronized (mUms.mUsersLock) {
+                final UserData user = mUms.getUserDataLU(userHandle);
+                if (user != null) {
+                    user.startRealtime = SystemClock.elapsedRealtime();
+                }
+            }
+        }
+
+        @Override
+        public void onUnlockUser(int userHandle) {
+            synchronized (mUms.mUsersLock) {
+                final UserData user = mUms.getUserDataLU(userHandle);
+                if (user != null) {
+                    user.unlockRealtime = SystemClock.elapsedRealtime();
+                }
+            }
+        }
+
+        @Override
+        public void onStopUser(int userHandle) {
+            synchronized (mUms.mUsersLock) {
+                final UserData user = mUms.getUserDataLU(userHandle);
+                if (user != null) {
+                    user.startRealtime = 0;
+                    user.unlockRealtime = 0;
+                }
+            }
+        }
     }
 
     // TODO b/28848102 Add support for test dependencies injection
@@ -1057,6 +1095,29 @@
         return mLocalService.isUserRunning(userId);
     }
 
+    @Override
+    public long getUserStartRealtime() {
+        final int userId = UserHandle.getUserId(Binder.getCallingUid());
+        synchronized (mUsersLock) {
+            final UserData user = getUserDataLU(userId);
+            if (user != null) {
+                return user.startRealtime;
+            }
+            return 0;
+        }
+    }
+
+    @Override
+    public long getUserUnlockRealtime() {
+        synchronized (mUsersLock) {
+            final UserData user = getUserDataLU(UserHandle.getUserId(Binder.getCallingUid()));
+            if (user != null) {
+                return user.unlockRealtime;
+            }
+            return 0;
+        }
+    }
+
     private void checkManageOrInteractPermIfCallerInOtherProfileGroup(int userId, String name) {
         int callingUserId = UserHandle.getCallingUserId();
         if (callingUserId == userId || isSameProfileGroupNoChecks(callingUserId, userId) ||
@@ -3484,6 +3545,7 @@
         if (!DumpUtils.checkDumpPermission(mContext, LOG_TAG, pw)) return;
 
         long now = System.currentTimeMillis();
+        final long nowRealtime = SystemClock.elapsedRealtime();
         StringBuilder sb = new StringBuilder();
         synchronized (mPackagesLock) {
             synchronized (mUsersLock) {
@@ -3511,25 +3573,20 @@
                     }
                     pw.println(UserState.stateToString(state));
                     pw.print("    Created: ");
-                    if (userInfo.creationTime == 0) {
-                        pw.println("<unknown>");
-                    } else {
-                        sb.setLength(0);
-                        TimeUtils.formatDuration(now - userInfo.creationTime, sb);
-                        sb.append(" ago");
-                        pw.println(sb);
-                    }
+                    dumpTimeAgo(pw, sb, now, userInfo.creationTime);
+
                     pw.print("    Last logged in: ");
-                    if (userInfo.lastLoggedInTime == 0) {
-                        pw.println("<unknown>");
-                    } else {
-                        sb.setLength(0);
-                        TimeUtils.formatDuration(now - userInfo.lastLoggedInTime, sb);
-                        sb.append(" ago");
-                        pw.println(sb);
-                    }
+                    dumpTimeAgo(pw, sb, now, userInfo.lastLoggedInTime);
+
                     pw.print("    Last logged in fingerprint: ");
                     pw.println(userInfo.lastLoggedInFingerprint);
+
+                    pw.print("    Start time: ");
+                    dumpTimeAgo(pw, sb, nowRealtime, userData.startRealtime);
+
+                    pw.print("    Unlock time: ");
+                    dumpTimeAgo(pw, sb, nowRealtime, userData.unlockRealtime);
+
                     pw.print("    Has profile owner: ");
                     pw.println(mIsUserManaged.get(userId));
                     pw.println("    Restrictions:");
@@ -3593,6 +3650,17 @@
         }
     }
 
+    private static void dumpTimeAgo(PrintWriter pw, StringBuilder sb, long nowTime, long time) {
+        if (time == 0) {
+            pw.println("<unknown>");
+        } else {
+            sb.setLength(0);
+            TimeUtils.formatDuration(nowTime - time, sb);
+            sb.append(" ago");
+            pw.println(sb);
+        }
+    }
+
     final class MainHandler extends Handler {
 
         @Override
diff --git a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
index 6e07eaa..34c3ce3 100644
--- a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
+++ b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
@@ -27,6 +27,7 @@
 import android.companion.CompanionDeviceManager;
 import android.content.Context;
 import android.content.Intent;
+import android.content.pm.ActivityInfo;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.IPackageManager;
 import android.content.pm.PackageList;
@@ -61,6 +62,8 @@
 import android.util.Xml;
 import com.android.internal.util.XmlUtils;
 import com.android.server.LocalServices;
+import com.android.server.pm.PackageManagerService;
+import com.android.server.pm.PackageSetting;
 
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
@@ -1166,8 +1169,7 @@
         final String systemPackageName = mServiceInternal.getKnownPackageName(
                 PackageManagerInternal.PACKAGE_SYSTEM, UserHandle.USER_SYSTEM);
         final PackageParser.Package systemPackage = getPackage(systemPackageName);
-        return compareSignatures(systemPackage.mSigningDetails.signatures,
-                pkg.mSigningDetails.signatures)
+        return compareSignatures(systemPackage.mSignatures, pkg.mSignatures)
                 == PackageManager.SIGNATURE_MATCH;
     }
 
diff --git a/services/core/java/com/android/server/pm/permission/OWNERS b/services/core/java/com/android/server/pm/permission/OWNERS
index 6c8c9b2..ffc4731 100644
--- a/services/core/java/com/android/server/pm/permission/OWNERS
+++ b/services/core/java/com/android/server/pm/permission/OWNERS
@@ -5,3 +5,4 @@
 per-file DefaultPermissionGrantPolicy.java = svetoslavganov@google.com
 per-file DefaultPermissionGrantPolicy.java = toddke@google.com
 per-file DefaultPermissionGrantPolicy.java = yamasani@google.com
+per-file DefaultPermissionGrantPolicy.java = patb@google.com
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
index 786b998..90ac4ab 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
@@ -29,6 +29,7 @@
 import android.Manifest;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.app.AppOpsManager;
 import android.content.Context;
 import android.content.pm.PackageManager;
 import android.content.pm.PackageManagerInternal;
@@ -55,17 +56,21 @@
 import android.util.Slog;
 import android.util.SparseArray;
 
+import com.android.internal.R;
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 import com.android.internal.os.RoSystemProperties;
 import com.android.internal.util.ArrayUtils;
+import com.android.server.FgThread;
 import com.android.server.LocalServices;
 import com.android.server.ServiceThread;
 import com.android.server.SystemConfig;
 import com.android.server.Watchdog;
+import com.android.server.pm.PackageManagerService;
 import com.android.server.pm.PackageManagerServiceUtils;
 import com.android.server.pm.PackageSetting;
+import com.android.server.pm.ProcessLoggingHandler;
 import com.android.server.pm.SharedUserSetting;
 import com.android.server.pm.UserManagerService;
 import com.android.server.pm.permission.DefaultPermissionGrantPolicy.DefaultPermissionGrantedCallback;
@@ -1010,10 +1015,10 @@
         final PackageParser.Package systemPackage =
                 mPackageManagerInt.getPackage(systemPackageName);
         boolean allowed = (PackageManagerServiceUtils.compareSignatures(
-                                bp.getSourceSignatures(), pkg.mSigningDetails.signatures)
+                                bp.getSourceSignatures(), pkg.mSignatures)
                         == PackageManager.SIGNATURE_MATCH)
                 || (PackageManagerServiceUtils.compareSignatures(
-                systemPackage.mSigningDetails.signatures, pkg.mSigningDetails.signatures)
+                                systemPackage.mSignatures, pkg.mSignatures)
                         == PackageManager.SIGNATURE_MATCH);
         if (!allowed && (privilegedPermission || oemPermission)) {
             if (pkg.isSystem()) {
diff --git a/services/core/java/com/android/server/wm/AppTransition.java b/services/core/java/com/android/server/wm/AppTransition.java
index 2ac7583..d4b437a 100644
--- a/services/core/java/com/android/server/wm/AppTransition.java
+++ b/services/core/java/com/android/server/wm/AppTransition.java
@@ -49,14 +49,17 @@
 import static com.android.server.wm.proto.AppTransitionProto.APP_TRANSITION_STATE;
 import static com.android.server.wm.proto.AppTransitionProto.LAST_USED_APP_TRANSITION;
 
+import android.annotation.DrawableRes;
 import android.annotation.Nullable;
 import android.app.ActivityManager;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.res.Configuration;
+import android.graphics.Color;
 import android.graphics.GraphicBuffer;
 import android.graphics.Path;
 import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
 import android.os.Binder;
 import android.os.Debug;
 import android.os.IBinder;
@@ -70,7 +73,10 @@
 import android.util.SparseArray;
 import android.util.proto.ProtoOutputStream;
 import android.view.AppTransitionAnimationSpec;
+import android.view.DisplayListCanvas;
 import android.view.IAppTransitionAnimationSpecsFuture;
+import android.view.RenderNode;
+import android.view.ThreadedRenderer;
 import android.view.WindowManager;
 import android.view.animation.AlphaAnimation;
 import android.view.animation.Animation;
@@ -391,6 +397,11 @@
                 mNextAppTransitionType == NEXT_TRANSIT_TYPE_THUMBNAIL_ASPECT_SCALE_DOWN;
     }
 
+
+    boolean isNextAppTransitionOpenCrossProfileApps() {
+        return mNextAppTransitionType == NEXT_TRANSIT_TYPE_OPEN_CROSS_PROFILE_APPS;
+    }
+
     /**
      * @return true if and only if we are currently fetching app transition specs from the future
      *         passed into {@link #overridePendingAppTransitionMultiThumbFuture}
@@ -978,6 +989,43 @@
     }
 
     /**
+     * Creates an overlay with a background color and a thumbnail for the cross profile apps
+     * animation.
+     */
+    GraphicBuffer createCrossProfileAppsThumbnail(
+            @DrawableRes int thumbnailDrawableRes, Rect frame) {
+        final int width = frame.width();
+        final int height = frame.height();
+
+        final RenderNode node = RenderNode.create("CrossProfileAppsThumbnail", null);
+        node.setLeftTopRightBottom(0, 0, width, height);
+        node.setClipToBounds(false);
+
+        final DisplayListCanvas canvas = node.start(width, height);
+        canvas.drawColor(Color.argb(0.6f, 0, 0, 0));
+        final int thumbnailSize = mService.mContext.getResources().getDimensionPixelSize(
+                com.android.internal.R.dimen.cross_profile_apps_thumbnail_size);
+        final Drawable drawable = mService.mContext.getDrawable(thumbnailDrawableRes);
+        drawable.setBounds(
+                (width - thumbnailSize) / 2,
+                (height - thumbnailSize) / 2,
+                (width + thumbnailSize) / 2,
+                (height + thumbnailSize) / 2);
+        drawable.draw(canvas);
+        node.end(canvas);
+
+        return ThreadedRenderer.createHardwareBitmap(node, width, height)
+                .createGraphicBufferHandle();
+    }
+
+    Animation createCrossProfileAppsThumbnailAnimationLocked(Rect appRect) {
+        final Animation animation = loadAnimationRes(
+                "android", com.android.internal.R.anim.cross_profile_apps_thumbnail_enter);
+        return prepareThumbnailAnimationWithDuration(animation, appRect.width(),
+                appRect.height(), 0, null);
+    }
+
+    /**
      * This animation runs for the thumbnail that gets cross faded with the enter/exit activity
      * when a thumbnail is specified with the pending animation override.
      */
@@ -1624,9 +1672,10 @@
                 && (transit == TRANSIT_ACTIVITY_OPEN
                         || transit == TRANSIT_TASK_OPEN
                         || transit == TRANSIT_TASK_TO_FRONT)) {
+
             a = loadAnimationRes("android", enter
-                    ? com.android.internal.R.anim.activity_open_enter
-                    : com.android.internal.R.anim.activity_open_exit);
+                    ? com.android.internal.R.anim.task_open_enter_cross_profile_apps
+                    : com.android.internal.R.anim.task_open_exit);
             Slog.v(TAG,
                     "applyAnimation NEXT_TRANSIT_TYPE_OPEN_CROSS_PROFILE_APPS:"
                             + " anim=" + a + " transit=" + appTransitionToString(transit)
@@ -2007,6 +2056,8 @@
                 return "NEXT_TRANSIT_TYPE_THUMBNAIL_ASPECT_SCALE_UP";
             case NEXT_TRANSIT_TYPE_THUMBNAIL_ASPECT_SCALE_DOWN:
                 return "NEXT_TRANSIT_TYPE_THUMBNAIL_ASPECT_SCALE_DOWN";
+            case NEXT_TRANSIT_TYPE_OPEN_CROSS_PROFILE_APPS:
+                return "NEXT_TRANSIT_TYPE_OPEN_CROSS_PROFILE_APPS";
             default:
                 return "unknown type=" + mNextAppTransitionType;
         }
diff --git a/services/core/java/com/android/server/wm/AppWindowThumbnail.java b/services/core/java/com/android/server/wm/AppWindowThumbnail.java
index c16a531..487b52c 100644
--- a/services/core/java/com/android/server/wm/AppWindowThumbnail.java
+++ b/services/core/java/com/android/server/wm/AppWindowThumbnail.java
@@ -49,7 +49,8 @@
 
     AppWindowThumbnail(Transaction t, AppWindowToken appToken, GraphicBuffer thumbnailHeader) {
         mAppToken = appToken;
-        mSurfaceAnimator = new SurfaceAnimator(this, this::onAnimationFinished, appToken.mService);
+        mSurfaceAnimator = new SurfaceAnimator(this, this::onAnimationFinished,
+                appToken.mService.mAnimator::addAfterPrepareSurfacesRunnable, appToken.mService);
         mWidth = thumbnailHeader.getWidth();
         mHeight = thumbnailHeader.getHeight();
 
@@ -84,10 +85,14 @@
     }
 
     void startAnimation(Transaction t, Animation anim) {
+        startAnimation(t, anim, null /* position */);
+    }
+
+    void startAnimation(Transaction t, Animation anim, Point position) {
         anim.restrictDuration(MAX_ANIMATION_DURATION);
         anim.scaleCurrentDuration(mAppToken.mService.getTransitionAnimationScaleLocked());
         mSurfaceAnimator.startAnimation(t, new LocalAnimationAdapter(
-                new WindowAnimationSpec(anim, null /* position */,
+                new WindowAnimationSpec(anim, position,
                         mAppToken.mService.mAppTransition.canSkipFirstFrame()),
                 mAppToken.mService.mSurfaceAnimationRunner), false /* hidden */);
     }
diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java
index 01e925e..fc0564d 100644
--- a/services/core/java/com/android/server/wm/AppWindowToken.java
+++ b/services/core/java/com/android/server/wm/AppWindowToken.java
@@ -16,7 +16,6 @@
 
 package com.android.server.wm;
 
-import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
 import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
 import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
 import static android.content.pm.ActivityInfo.CONFIG_ORIENTATION;
@@ -31,6 +30,7 @@
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
 import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
+
 import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_ANIM;
 import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
 import static com.android.server.wm.AppTransition.TRANSIT_UNSET;
@@ -56,7 +56,6 @@
 
 import android.annotation.CallSuper;
 import android.app.Activity;
-import android.app.WindowConfiguration.WindowingMode;
 import android.content.res.Configuration;
 import android.graphics.GraphicBuffer;
 import android.graphics.Point;
@@ -69,13 +68,14 @@
 import android.util.Slog;
 import android.util.proto.ProtoOutputStream;
 import android.view.DisplayInfo;
-import android.view.SurfaceControl.Transaction;
-import android.view.animation.Animation;
 import android.view.IApplicationToken;
 import android.view.SurfaceControl;
+import android.view.SurfaceControl.Transaction;
 import android.view.WindowManager;
 import android.view.WindowManager.LayoutParams;
+import android.view.animation.Animation;
 
+import com.android.internal.R;
 import com.android.internal.util.ToBooleanFunction;
 import com.android.server.input.InputApplicationHandle;
 import com.android.server.policy.WindowManagerPolicy.StartingSurface;
@@ -1775,6 +1775,37 @@
         mThumbnail.startAnimation(getPendingTransaction(), loadThumbnailAnimation(thumbnailHeader));
     }
 
+    /**
+     * Attaches a surface with a thumbnail for the
+     * {@link android.app.ActivityOptions#ANIM_OPEN_CROSS_PROFILE_APPS} animation.
+     */
+    void attachCrossProfileAppsThumbnailAnimation() {
+        if (!isReallyAnimating()) {
+            return;
+        }
+        clearThumbnail();
+
+        final WindowState win = findMainWindow();
+        if (win == null) {
+            return;
+        }
+        final Rect frame = win.mFrame;
+        final int thumbnailDrawableRes = getTask().mUserId == mService.mCurrentUserId
+                ? R.drawable.ic_account_circle
+                : R.drawable.ic_corp_badge_no_background;
+        final GraphicBuffer thumbnail =
+                mService.mAppTransition
+                        .createCrossProfileAppsThumbnail(thumbnailDrawableRes, frame);
+        if (thumbnail == null) {
+            return;
+        }
+        mThumbnail = new AppWindowThumbnail(getPendingTransaction(), this, thumbnail);
+        final Animation animation =
+                mService.mAppTransition.createCrossProfileAppsThumbnailAnimationLocked(win.mFrame);
+        mThumbnail.startAnimation(getPendingTransaction(), animation, new Point(frame.left,
+                frame.top));
+    }
+
     private Animation loadThumbnailAnimation(GraphicBuffer thumbnailHeader) {
         final DisplayInfo displayInfo = mDisplayContent.getDisplayInfo();
 
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 252eff5..2cc2a0e 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -335,9 +335,6 @@
             new TaskForResizePointSearchResult();
     private final ApplySurfaceChangesTransactionState mTmpApplySurfaceChangesTransactionState =
             new ApplySurfaceChangesTransactionState();
-    private final ScreenshotApplicationState mScreenshotApplicationState =
-            new ScreenshotApplicationState();
-    private final Transaction mTmpTransaction = new Transaction();
 
     // True if this display is in the process of being removed. Used to determine if the removal of
     // the display's direct children should be allowed.
@@ -654,10 +651,7 @@
             mWallpaperController.updateWallpaperVisibility();
         }
 
-        // Use mTmpTransaction instead of mPendingTransaction because we don't want to commit
-        // other changes in mPendingTransaction at this point.
-        w.handleWindowMovedIfNeeded(mTmpTransaction);
-        SurfaceControl.mergeToGlobalTransaction(mTmpTransaction);
+        w.handleWindowMovedIfNeeded(mPendingTransaction);
 
         final WindowStateAnimator winAnimator = w.mWinAnimator;
 
@@ -692,33 +686,6 @@
                     }
                 }
             }
-            final TaskStack stack = w.getStack();
-            if (!winAnimator.isWaitingForOpening()
-                    || (stack != null && stack.isAnimatingBounds())) {
-                // Updates the shown frame before we set up the surface. This is needed
-                // because the resizing could change the top-left position (in addition to
-                // size) of the window. setSurfaceBoundariesLocked uses mShownPosition to
-                // position the surface.
-                //
-                // If an animation is being started, we can't call this method because the
-                // animation hasn't processed its initial transformation yet, but in general
-                // we do want to update the position if the window is animating. We make an exception
-                // for the bounds animating state, where an application may have been waiting
-                // for an exit animation to start, but instead enters PiP. We need to ensure
-                // we always recompute the top-left in this case.
-                winAnimator.computeShownFrameLocked();
-            }
-            winAnimator.setSurfaceBoundariesLocked(mTmpRecoveringMemory /* recoveringMemory */);
-
-            // Since setSurfaceBoundariesLocked applies the clipping, we need to apply the position
-            // to the surface of the window container and also the position of the stack window
-            // container as well. Use mTmpTransaction instead of mPendingTransaction to avoid
-            // committing any existing changes in there.
-            w.updateSurfacePosition(mTmpTransaction);
-            if (stack != null) {
-                stack.updateSurfaceBounds(mTmpTransaction);
-            }
-            SurfaceControl.mergeToGlobalTransaction(mTmpTransaction);
         }
 
         final AppWindowToken atoken = w.mAppToken;
@@ -2821,6 +2788,7 @@
 
         mTmpRecoveringMemory = recoveringMemory;
         forAllWindows(mApplySurfaceChangesTransaction, true /* traverseTopToBottom */);
+        prepareSurfaces();
 
         mService.mDisplayManagerInternal.setDisplayProperties(mDisplayId,
                 mTmpApplySurfaceChangesTransactionState.displayHasContent,
diff --git a/services/core/java/com/android/server/wm/SurfaceAnimator.java b/services/core/java/com/android/server/wm/SurfaceAnimator.java
index a32e711..e67cdba 100644
--- a/services/core/java/com/android/server/wm/SurfaceAnimator.java
+++ b/services/core/java/com/android/server/wm/SurfaceAnimator.java
@@ -30,6 +30,7 @@
 import com.android.internal.annotations.VisibleForTesting;
 
 import java.io.PrintWriter;
+import java.util.function.Consumer;
 
 /**
  * A class that can run animations on objects that have a set of child surfaces. We do this by
@@ -55,16 +56,20 @@
     /**
      * @param animatable The object to animate.
      * @param animationFinishedCallback Callback to invoke when an animation has finished running.
+     * @param addAfterPrepareSurfaces Consumer that takes a runnable and executes it after preparing
+     *                                surfaces in WM. Can be implemented differently during testing.
      */
     SurfaceAnimator(Animatable animatable, Runnable animationFinishedCallback,
-            WindowManagerService service) {
+            Consumer<Runnable> addAfterPrepareSurfaces, WindowManagerService service) {
         mAnimatable = animatable;
         mService = service;
         mAnimationFinishedCallback = animationFinishedCallback;
-        mInnerAnimationFinishedCallback = getFinishedCallback(animationFinishedCallback);
+        mInnerAnimationFinishedCallback = getFinishedCallback(animationFinishedCallback,
+                addAfterPrepareSurfaces);
     }
 
-    private OnAnimationFinishedCallback getFinishedCallback(Runnable animationFinishedCallback) {
+    private OnAnimationFinishedCallback getFinishedCallback(Runnable animationFinishedCallback,
+            Consumer<Runnable> addAfterPrepareSurfaces) {
         return anim -> {
             synchronized (mService.mWindowMap) {
                 final SurfaceAnimator target = mService.mAnimationTransferMap.remove(anim);
@@ -80,7 +85,7 @@
                 // reparents the surface onto the leash is executed already. Otherwise this may be
                 // executed first, leading to surface loss, as the reparent operations wouldn't
                 // be in order.
-                mService.mAnimator.addAfterPrepareSurfacesRunnable(() -> {
+                addAfterPrepareSurfaces.accept(() -> {
                     if (anim != mAnimation) {
                         // Callback was from another animation - ignore.
                         return;
diff --git a/services/core/java/com/android/server/wm/TaskStack.java b/services/core/java/com/android/server/wm/TaskStack.java
index bdda944..7b4281c 100644
--- a/services/core/java/com/android/server/wm/TaskStack.java
+++ b/services/core/java/com/android/server/wm/TaskStack.java
@@ -44,6 +44,7 @@
 
 import android.annotation.CallSuper;
 import android.content.res.Configuration;
+import android.graphics.Point;
 import android.graphics.Rect;
 import android.graphics.Region;
 import android.os.RemoteException;
@@ -145,6 +146,7 @@
      * For {@link #prepareSurfaces}.
      */
     final Rect mTmpDimBoundsRect = new Rect();
+    private final Point mLastSurfaceSize = new Point();
 
     TaskStack(WindowManagerService service, int stackId, StackWindowController controller) {
         super(service);
@@ -744,7 +746,13 @@
         }
 
         final Rect stackBounds = getBounds();
-        transaction.setSize(mSurfaceControl, stackBounds.width(), stackBounds.height());
+        final int width = stackBounds.width();
+        final int height = stackBounds.height();
+        if (width == mLastSurfaceSize.x && height == mLastSurfaceSize.y) {
+            return;
+        }
+        transaction.setSize(mSurfaceControl, width, height);
+        mLastSurfaceSize.set(width, height);
     }
 
     @Override
diff --git a/services/core/java/com/android/server/wm/WindowAnimationSpec.java b/services/core/java/com/android/server/wm/WindowAnimationSpec.java
index 031b57b..0863ee9 100644
--- a/services/core/java/com/android/server/wm/WindowAnimationSpec.java
+++ b/services/core/java/com/android/server/wm/WindowAnimationSpec.java
@@ -89,7 +89,10 @@
         if (mStackClipMode == STACK_CLIP_NONE) {
             t.setWindowCrop(leash, tmp.transformation.getClipRect());
         } else if (mStackClipMode == STACK_CLIP_AFTER_ANIM) {
-            t.setFinalCrop(leash, mStackBounds);
+            mTmpRect.set(mStackBounds);
+            // Offset stack bounds to stack position so the final crop is in screen space.
+            mTmpRect.offsetTo(mPosition.x, mPosition.y);
+            t.setFinalCrop(leash, mTmpRect);
             t.setWindowCrop(leash, tmp.transformation.getClipRect());
         } else {
             mTmpRect.set(mStackBounds);
diff --git a/services/core/java/com/android/server/wm/WindowAnimator.java b/services/core/java/com/android/server/wm/WindowAnimator.java
index 7295873..3efd6ac 100644
--- a/services/core/java/com/android/server/wm/WindowAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowAnimator.java
@@ -47,7 +47,6 @@
     final WindowManagerService mService;
     final Context mContext;
     final WindowManagerPolicy mPolicy;
-    private final WindowSurfacePlacer mWindowPlacerLocked;
 
     /** Is any window animating? */
     private boolean mAnimating;
@@ -74,7 +73,7 @@
 
     SparseArray<DisplayContentsAnimator> mDisplayContentsAnimators = new SparseArray<>(2);
 
-    boolean mInitialized = false;
+    private boolean mInitialized = false;
 
     // When set to true the animator will go over all windows after an animation frame is posted and
     // check if some got replaced and can be removed.
@@ -98,7 +97,6 @@
         mService = service;
         mContext = service.mContext;
         mPolicy = service.mPolicy;
-        mWindowPlacerLocked = service.mWindowPlacerLocked;
         AnimationThread.getHandler().runWithScissors(
                 () -> mChoreographer = Choreographer.getSfInstance(), 0 /* timeout */);
 
@@ -241,7 +239,7 @@
             }
 
             if (hasPendingLayoutChanges || doRequest) {
-                mWindowPlacerLocked.requestTraversal();
+                mService.mWindowPlacerLocked.requestTraversal();
             }
 
             final boolean rootAnimating = mService.mRoot.isSelfOrChildAnimating();
@@ -254,7 +252,7 @@
                 Trace.asyncTraceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "animating", 0);
             }
             if (!rootAnimating && mLastRootAnimating) {
-                mWindowPlacerLocked.requestTraversal();
+                mService.mWindowPlacerLocked.requestTraversal();
                 mService.mTaskSnapshotController.setPersisterPaused(false);
                 Trace.asyncTraceEnd(Trace.TRACE_TAG_WINDOW_MANAGER, "animating", 0);
             }
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index 5d445ef..36e6418 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -95,6 +95,7 @@
     protected final WindowManagerService mService;
 
     private final Point mTmpPos = new Point();
+    protected final Point mLastSurfacePosition = new Point();
 
     /** Total number of elements in this subtree, including our own hierarchy element. */
     private int mTreeWeight = 1;
@@ -102,7 +103,8 @@
     WindowContainer(WindowManagerService service) {
         mService = service;
         mPendingTransaction = service.mTransactionFactory.make();
-        mSurfaceAnimator = new SurfaceAnimator(this, this::onAnimationFinished, service);
+        mSurfaceAnimator = new SurfaceAnimator(this, this::onAnimationFinished,
+                service.mAnimator::addAfterPrepareSurfacesRunnable, service);
     }
 
     @Override
@@ -1177,7 +1179,12 @@
         }
 
         getRelativePosition(mTmpPos);
+        if (mTmpPos.equals(mLastSurfacePosition)) {
+            return;
+        }
+
         transaction.setPosition(mSurfaceControl, mTmpPos.x, mTmpPos.y);
+        mLastSurfacePosition.set(mTmpPos.x, mTmpPos.y);
 
         for (int i = mChildren.size() - 1; i >= 0; i--) {
             mChildren.get(i).updateSurfacePosition(transaction);
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 0a2ffbc..e91b16d 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -928,7 +928,6 @@
             boolean haveInputMethods, boolean showBootMsgs, boolean onlyCore,
             WindowManagerPolicy policy) {
         installLock(this, INDEX_WINDOW);
-        mRoot = new RootWindowContainer(this);
         mContext = context;
         mHaveInputMethods = haveInputMethods;
         mAllowBootMessages = showBootMsgs;
@@ -952,8 +951,11 @@
         mDisplaySettings = new DisplaySettings();
         mDisplaySettings.readSettingsLocked();
 
-        mWindowPlacerLocked = new WindowSurfacePlacer(this);
         mPolicy = policy;
+        mAnimator = new WindowAnimator(this);
+        mRoot = new RootWindowContainer(this);
+
+        mWindowPlacerLocked = new WindowSurfacePlacer(this);
         mTaskSnapshotController = new TaskSnapshotController(this);
 
         mWindowTracing = WindowTracing.createDefaultAndStartLooper(context);
@@ -1051,7 +1053,6 @@
                 PowerManager.SCREEN_BRIGHT_WAKE_LOCK | PowerManager.ON_AFTER_RELEASE, TAG_WM);
         mHoldingScreenWakeLock.setReferenceCounted(false);
 
-        mAnimator = new WindowAnimator(this);
         mSurfaceAnimationRunner = new SurfaceAnimationRunner();
 
         mAllowTheaterModeWakeFromLayout = context.getResources().getBoolean(
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index b3809dd..0ad60c9 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -4487,6 +4487,7 @@
 
         // Leash is now responsible for position, so set our position to 0.
         t.setPosition(mSurfaceControl, 0, 0);
+        mLastSurfacePosition.set(0, 0);
     }
 
     @Override
@@ -4502,8 +4503,9 @@
         }
 
         transformFrameToSurfacePosition(mFrame.left, mFrame.top, mSurfacePosition);
-        if (!mSurfaceAnimator.hasLeash()) {
+        if (!mSurfaceAnimator.hasLeash() && !mLastSurfacePosition.equals(mSurfacePosition)) {
             t.setPosition(mSurfaceControl, mSurfacePosition.x, mSurfacePosition.y);
+            mLastSurfacePosition.set(mSurfacePosition.x, mSurfacePosition.y);
         }
     }
 
diff --git a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
index 08f49f6..a512fdf 100644
--- a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
+++ b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
@@ -20,6 +20,7 @@
 import static android.app.ActivityManagerInternal.APP_TRANSITION_SNAPSHOT;
 import static android.app.ActivityManagerInternal.APP_TRANSITION_SPLASH_SCREEN;
 import static android.app.ActivityManagerInternal.APP_TRANSITION_WINDOWS_DRAWN;
+
 import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_CONFIG;
 import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_LAYOUT;
 import static com.android.server.wm.AppTransition.TRANSIT_ACTIVITY_CLOSE;
@@ -43,28 +44,19 @@
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_APP_TRANSITIONS;
 import static com.android.server.wm.WindowManagerDebugConfig.SHOW_LIGHT_TRANSACTIONS;
-import static com.android.server.wm.WindowManagerDebugConfig.SHOW_TRANSACTIONS;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
 import static com.android.server.wm.WindowManagerService.H.NOTIFY_APP_TRANSITION_STARTING;
 import static com.android.server.wm.WindowManagerService.H.REPORT_WINDOWS_CHANGE;
 import static com.android.server.wm.WindowManagerService.LAYOUT_REPEAT_THRESHOLD;
-import static com.android.server.wm.WindowManagerService.MAX_ANIMATION_DURATION;
 import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_PLACING_SURFACES;
 
-import android.content.res.Configuration;
-import android.graphics.GraphicBuffer;
-import android.graphics.PixelFormat;
-import android.graphics.Rect;
-import android.os.Binder;
 import android.os.Debug;
 import android.os.Trace;
 import android.util.ArraySet;
 import android.util.Slog;
 import android.util.SparseIntArray;
 import android.view.Display;
-import android.view.DisplayInfo;
-import android.view.Surface;
 import android.view.SurfaceControl;
 import android.view.WindowManager.LayoutParams;
 import android.view.animation.Animation;
@@ -414,7 +406,6 @@
             }
             wtoken.updateReportedVisibilityLocked();
             wtoken.waitingToShow = false;
-
             if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG,
                     ">>> OPEN TRANSACTION handleAppTransitionReadyLocked()");
             mService.openSurfaceTransaction();
@@ -435,6 +426,8 @@
             }
             if (mService.mAppTransition.isNextAppTransitionThumbnailUp()) {
                 wtoken.attachThumbnailAnimation();
+            } else if (mService.mAppTransition.isNextAppTransitionOpenCrossProfileApps()) {
+                wtoken.attachCrossProfileAppsThumbnailAnimation();
             }
         }
         return topOpeningApp;
diff --git a/services/core/jni/com_android_server_UsbDescriptorParser.cpp b/services/core/jni/com_android_server_UsbDescriptorParser.cpp
index 79f482c6..62bab07 100644
--- a/services/core/jni/com_android_server_UsbDescriptorParser.cpp
+++ b/services/core/jni/com_android_server_UsbDescriptorParser.cpp
@@ -94,6 +94,9 @@
         j_str = env->NewString((jchar*)byteBuffer, numUSC2Bytes/2);
         free(byteBuffer);
     }
+
+    usb_device_close(device);
+
     return j_str;
 }
 
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 11fce4d..f0681e9 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -7190,13 +7190,8 @@
             return;
         }
 
-        final int userId = mInjector.userHandleGetCallingUserId();
         synchronized (this) {
-            getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
-            if (!isUserAffiliatedWithDeviceLocked(userId)) {
-                throw new SecurityException("Admin " + who +
-                        " is neither the device owner or affiliated user's profile owner.");
-            }
+            getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER);
             long token = mInjector.binderClearCallingIdentity();
             try {
                 mLockPatternUtils.setDeviceOwnerInfo(info != null ? info.toString() : null);
@@ -9663,6 +9658,9 @@
                         " is neither the device owner or affiliated user's profile owner.");
             }
         }
+        if (isManagedProfile(userId)) {
+            throw new SecurityException("Managed profile cannot disable keyguard");
+        }
 
         long ident = mInjector.binderClearCallingIdentity();
         try {
@@ -9689,6 +9687,9 @@
                 throw new SecurityException("Admin " + who +
                         " is neither the device owner or affiliated user's profile owner.");
             }
+            if (isManagedProfile(userId)) {
+                throw new SecurityException("Managed profile cannot disable status bar");
+            }
             DevicePolicyData policy = getUserData(userId);
             if (policy.mStatusBarDisabled != disabled) {
                 boolean isLockTaskMode = false;
diff --git a/services/robotests/src/com/android/server/backup/BackupManagerServiceRoboTest.java b/services/robotests/src/com/android/server/backup/BackupManagerServiceRoboTest.java
index d9e8f0f..f9ebd28 100644
--- a/services/robotests/src/com/android/server/backup/BackupManagerServiceRoboTest.java
+++ b/services/robotests/src/com/android/server/backup/BackupManagerServiceRoboTest.java
@@ -16,17 +16,27 @@
 
 package com.android.server.backup;
 
+import static com.android.server.backup.testing.TransportTestUtils.TRANSPORT_NAMES;
+
 import static com.google.common.truth.Truth.assertThat;
 
 import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
+import static org.robolectric.Shadows.shadowOf;
 import static org.testng.Assert.expectThrows;
 
+import android.app.backup.BackupManager;
+import android.app.backup.ISelectBackupTransportCallback;
+import android.content.ComponentName;
+import android.content.Context;
 import android.content.ContextWrapper;
 import android.os.HandlerThread;
 import android.platform.test.annotations.Presubmit;
+import android.provider.Settings;
 
 import com.android.server.backup.testing.ShadowAppBackupUtils;
 import com.android.server.backup.testing.TransportTestUtils;
@@ -42,13 +52,15 @@
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 import org.robolectric.RuntimeEnvironment;
-import org.robolectric.Shadows;
 import org.robolectric.annotation.Config;
 import org.robolectric.shadows.ShadowContextWrapper;
 import org.robolectric.shadows.ShadowLog;
+import org.robolectric.shadows.ShadowLooper;
+import org.robolectric.shadows.ShadowSettings;
 
 import java.io.File;
 import java.util.HashMap;
+import java.util.List;
 import java.util.Map;
 
 @RunWith(FrameworkRobolectricTestRunner.class)
@@ -66,10 +78,12 @@
 
     @Mock private TransportManager mTransportManager;
     private HandlerThread mBackupThread;
+    private ShadowLooper mShadowBackupLooper;
     private File mBaseStateDir;
     private File mDataDir;
     private RefactoredBackupManagerService mBackupManagerService;
     private ShadowContextWrapper mShadowContext;
+    private Context mContext;
 
     @Before
     public void setUp() throws Exception {
@@ -79,18 +93,20 @@
         mBackupThread.setUncaughtExceptionHandler(
                 (t, e) -> ShadowLog.e(TAG, "Uncaught exception in test thread " + t.getName(), e));
         mBackupThread.start();
+        mShadowBackupLooper = shadowOf(mBackupThread.getLooper());
 
         ContextWrapper context = RuntimeEnvironment.application;
-        mShadowContext = Shadows.shadowOf(context);
+        mContext = context;
+        mShadowContext = shadowOf(context);
 
-        File cacheDir = context.getCacheDir();
+        File cacheDir = mContext.getCacheDir();
         mBaseStateDir = new File(cacheDir, "base_state_dir");
         mDataDir = new File(cacheDir, "data_dir");
 
         mBackupManagerService =
                 new RefactoredBackupManagerService(
-                        context,
-                        new Trampoline(context),
+                        mContext,
+                        new Trampoline(mContext),
                         mBackupThread,
                         mBaseStateDir,
                         mDataDir,
@@ -103,6 +119,8 @@
         ShadowAppBackupUtils.reset();
     }
 
+    /* Tests for destination string */
+
     @Test
     public void testDestinationString() throws Exception {
         mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
@@ -136,6 +154,8 @@
                 () -> mBackupManagerService.getDestinationString(TRANSPORT_NAME));
     }
 
+    /* Tests for app eligibility */
+
     @Test
     public void testIsAppEligibleForBackup_whenAppEligible() throws Exception {
         mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
@@ -212,4 +232,110 @@
                         mBackupManagerService.filterAppsEligibleForBackup(
                                 new String[] {"package.a", "package.b"}));
     }
+
+    /* Tests for select transport */
+
+    private TransportData mNewTransport;
+    private TransportData mOldTransport;
+    private ComponentName mNewTransportComponent;
+    private ISelectBackupTransportCallback mCallback;
+
+    private void setUpForSelectTransport() throws Exception {
+        List<TransportData> transports =
+                TransportTestUtils.setUpTransports(mTransportManager, TRANSPORT_NAMES);
+        mNewTransport = transports.get(0);
+        mNewTransportComponent = mNewTransport.transportClientMock.getTransportComponent();
+        mOldTransport = transports.get(1);
+        when(mTransportManager.selectTransport(eq(mNewTransport.transportName)))
+                .thenReturn(mOldTransport.transportName);
+    }
+
+    @Test
+    public void testSelectBackupTransport() throws Exception {
+        setUpForSelectTransport();
+        mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
+
+        String oldTransport =
+                mBackupManagerService.selectBackupTransport(mNewTransport.transportName);
+
+        assertThat(getSettingsTransport()).isEqualTo(mNewTransport.transportName);
+        assertThat(oldTransport).isEqualTo(mOldTransport.transportName);
+    }
+
+    @Test
+    public void testSelectBackupTransport_withoutPermission() throws Exception {
+        setUpForSelectTransport();
+        mShadowContext.denyPermissions(android.Manifest.permission.BACKUP);
+
+        expectThrows(
+                SecurityException.class,
+                () -> mBackupManagerService.selectBackupTransport(mNewTransport.transportName));
+    }
+
+    @Test
+    public void testSelectBackupTransportAsync() throws Exception {
+        setUpForSelectTransport();
+        mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
+        when(mTransportManager.registerAndSelectTransport(eq(mNewTransportComponent)))
+                .thenReturn(BackupManager.SUCCESS);
+        ISelectBackupTransportCallback callback = mock(ISelectBackupTransportCallback.class);
+
+        mBackupManagerService.selectBackupTransportAsync(mNewTransportComponent, callback);
+
+        mShadowBackupLooper.runToEndOfTasks();
+        assertThat(getSettingsTransport()).isEqualTo(mNewTransport.transportName);
+        verify(callback).onSuccess(eq(mNewTransport.transportName));
+    }
+
+    @Test
+    public void testSelectBackupTransportAsync_whenRegistrationFails() throws Exception {
+        setUpForSelectTransport();
+        mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
+        when(mTransportManager.registerAndSelectTransport(eq(mNewTransportComponent)))
+                .thenReturn(BackupManager.ERROR_TRANSPORT_UNAVAILABLE);
+        ISelectBackupTransportCallback callback = mock(ISelectBackupTransportCallback.class);
+
+        mBackupManagerService.selectBackupTransportAsync(mNewTransportComponent, callback);
+
+        mShadowBackupLooper.runToEndOfTasks();
+        assertThat(getSettingsTransport()).isNotEqualTo(mNewTransport.transportName);
+        verify(callback).onFailure(anyInt());
+    }
+
+    @Test
+    public void testSelectBackupTransportAsync_whenTransportGetsUnregistered() throws Exception {
+        TransportTestUtils.setUpTransports(
+                mTransportManager, new TransportData(TRANSPORT_NAME, null, null));
+        ComponentName newTransportComponent =
+                TransportTestUtils.transportComponentName(TRANSPORT_NAME);
+        mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
+        when(mTransportManager.registerAndSelectTransport(eq(newTransportComponent)))
+                .thenReturn(BackupManager.SUCCESS);
+        ISelectBackupTransportCallback callback = mock(ISelectBackupTransportCallback.class);
+
+        mBackupManagerService.selectBackupTransportAsync(newTransportComponent, callback);
+
+        mShadowBackupLooper.runToEndOfTasks();
+        assertThat(getSettingsTransport()).isNotEqualTo(TRANSPORT_NAME);
+        verify(callback).onFailure(anyInt());
+    }
+
+    @Test
+    public void testSelectBackupTransportAsync_withoutPermission() throws Exception {
+        setUpForSelectTransport();
+        mShadowContext.denyPermissions(android.Manifest.permission.BACKUP);
+        ComponentName newTransportComponent =
+                mNewTransport.transportClientMock.getTransportComponent();
+
+        expectThrows(
+                SecurityException.class,
+                () ->
+                        mBackupManagerService.selectBackupTransportAsync(
+                                newTransportComponent, mock(ISelectBackupTransportCallback.class)));
+    }
+
+    private String getSettingsTransport() {
+        return ShadowSettings.ShadowSecure.getString(
+                mContext.getContentResolver(), Settings.Secure.BACKUP_TRANSPORT);
+    }
 }
diff --git a/services/robotests/src/com/android/server/backup/TransportManagerTest.java b/services/robotests/src/com/android/server/backup/TransportManagerTest.java
index 82830fe..acd670f 100644
--- a/services/robotests/src/com/android/server/backup/TransportManagerTest.java
+++ b/services/robotests/src/com/android/server/backup/TransportManagerTest.java
@@ -24,7 +24,6 @@
 import static org.testng.Assert.expectThrows;
 
 import android.annotation.Nullable;
-import android.app.backup.BackupManager;
 import android.content.ComponentName;
 import android.content.Intent;
 import android.content.pm.ApplicationInfo;
@@ -40,7 +39,6 @@
 import com.android.server.backup.testing.ShadowContextImplForBackup;
 import com.android.server.backup.testing.ShadowPackageManagerForBackup;
 import com.android.server.backup.testing.TransportBoundListenerStub;
-import com.android.server.backup.testing.TransportReadyCallbackStub;
 import com.android.server.backup.transport.TransportClient;
 import com.android.server.backup.transport.TransportNotRegisteredException;
 import com.android.server.testing.FrameworkRobolectricTestRunner;
@@ -87,9 +85,6 @@
     private final TransportBoundListenerStub mTransportBoundListenerStub =
             new TransportBoundListenerStub(true);
 
-    private final TransportReadyCallbackStub mTransportReadyCallbackStub =
-            new TransportReadyCallbackStub();
-
     @Before
     public void setUp() throws Exception {
         MockitoAnnotations.initMocks(this);
@@ -466,59 +461,6 @@
     }
 
     @Test
-    public void ensureTransportReady_transportNotYetBound_callsListenerOnFailure()
-            throws Exception {
-        setUpPackageWithTransports(PACKAGE_NAME, Arrays.asList(mTransport1, mTransport2),
-                ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
-
-        TransportManager transportManager = new TransportManager(
-                RuntimeEnvironment.application.getApplicationContext(),
-                new HashSet<>(Arrays.asList(mTransport1.componentName, mTransport2.componentName)),
-                mTransport1.name,
-                mTransportBoundListenerStub,
-                ShadowLooper.getMainLooper());
-
-        transportManager.ensureTransportReady(mTransport1.componentName,
-                mTransportReadyCallbackStub);
-
-        assertThat(mTransportReadyCallbackStub.getSuccessCalls()).isEmpty();
-        assertThat(mTransportReadyCallbackStub.getFailureCalls()).containsExactlyElementsIn(
-                Collections.singleton(
-                        BackupManager.ERROR_TRANSPORT_UNAVAILABLE));
-    }
-
-    @Test
-    public void ensureTransportReady_transportCannotBeBound_callsListenerOnFailure()
-            throws Exception {
-        TransportManager transportManager =
-                createTransportManagerAndSetUpTransports(Collections.singletonList(mTransport2),
-                        Collections.singletonList(mTransport1), mTransport1.name);
-
-        transportManager.ensureTransportReady(mTransport1.componentName,
-                mTransportReadyCallbackStub);
-
-        assertThat(mTransportReadyCallbackStub.getSuccessCalls()).isEmpty();
-        assertThat(mTransportReadyCallbackStub.getFailureCalls()).containsExactlyElementsIn(
-                Collections.singleton(
-                        BackupManager.ERROR_TRANSPORT_UNAVAILABLE));
-    }
-
-    @Test
-    public void ensureTransportReady_transportsAlreadyBound_callsListenerOnSuccess()
-            throws Exception {
-        TransportManager transportManager =
-                createTransportManagerAndSetUpTransports(Collections.singletonList(mTransport2),
-                        Collections.singletonList(mTransport1), mTransport1.name);
-
-        transportManager.ensureTransportReady(mTransport2.componentName,
-                mTransportReadyCallbackStub);
-
-        assertThat(mTransportReadyCallbackStub.getSuccessCalls()).containsExactlyElementsIn(
-                Collections.singleton(mTransport2.name));
-        assertThat(mTransportReadyCallbackStub.getFailureCalls()).isEmpty();
-    }
-
-    @Test
     public void getTransportClient_forRegisteredTransport_returnCorrectly() throws Exception {
         TransportManager transportManager =
                 createTransportManagerAndSetUpTransports(
diff --git a/services/robotests/src/com/android/server/backup/testing/TransportReadyCallbackStub.java b/services/robotests/src/com/android/server/backup/testing/TransportReadyCallbackStub.java
deleted file mode 100644
index bbe7eba..0000000
--- a/services/robotests/src/com/android/server/backup/testing/TransportReadyCallbackStub.java
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT 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.backup.testing;
-
-import com.android.server.backup.TransportManager;
-
-import java.util.HashSet;
-import java.util.Set;
-
-/**
- * Stub implementation of TransportReadyCallback, which can tell which calls were made.
- */
-public class TransportReadyCallbackStub implements
-        TransportManager.TransportReadyCallback {
-    private final Set<String> mSuccessCalls = new HashSet<>();
-    private final Set<Integer> mFailureCalls = new HashSet<>();
-
-    @Override
-    public void onSuccess(String transportName) {
-        mSuccessCalls.add(transportName);
-    }
-
-    @Override
-    public void onFailure(int reason) {
-        mFailureCalls.add(reason);
-    }
-
-    /**
-     * Returns set of transport names for which {@link #onSuccess(String)} was called.
-     */
-    public Set<String> getSuccessCalls() {
-        return mSuccessCalls;
-    }
-
-    /**
-     * Returns set of reasons for which {@link #onFailure(int)} } was called.
-     */
-    public Set<Integer> getFailureCalls() {
-        return mFailureCalls;
-    }
-}
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/KeySyncTaskTest.java b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/KeySyncTaskTest.java
index a0dcfdc..763ee07 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/KeySyncTaskTest.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/KeySyncTaskTest.java
@@ -296,7 +296,7 @@
         List<KeyEntryRecoveryData> applicationKeys = recoveryData.getApplicationKeyBlobs();
         assertEquals(1, applicationKeys.size());
         KeyEntryRecoveryData keyData = applicationKeys.get(0);
-        assertArrayEquals(TEST_APP_KEY_ALIAS.getBytes(StandardCharsets.UTF_8), keyData.getAlias());
+        assertEquals(TEST_APP_KEY_ALIAS, keyData.getAlias());
         byte[] appKey = KeySyncUtils.decryptApplicationKey(
                 recoveryKey, keyData.getEncryptedKeyMaterial());
         assertArrayEquals(applicationKey.getEncoded(), appKey);
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManagerTest.java b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManagerTest.java
index 967c3b8..0105066 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManagerTest.java
@@ -306,7 +306,7 @@
                     TEST_SESSION_ID,
                     /*recoveryKeyBlob=*/ randomBytes(32),
                     /*applicationKeys=*/ ImmutableList.of(
-                            new KeyEntryRecoveryData(getUtf8Bytes("alias"), randomBytes(32))
+                            new KeyEntryRecoveryData("alias", randomBytes(32))
                     ));
             fail("should have thrown");
         } catch (ServiceSpecificException e) {
@@ -356,7 +356,7 @@
         byte[] encryptedClaimResponse = encryptClaimResponse(
                 keyClaimant, TEST_SECRET, TEST_VAULT_PARAMS, recoveryKey);
         KeyEntryRecoveryData badApplicationKey = new KeyEntryRecoveryData(
-                TEST_ALIAS.getBytes(StandardCharsets.UTF_8),
+                TEST_ALIAS,
                 randomBytes(32));
 
         try {
@@ -389,7 +389,7 @@
                 keyClaimant, TEST_SECRET, TEST_VAULT_PARAMS, recoveryKey);
         byte[] applicationKeyBytes = randomBytes(32);
         KeyEntryRecoveryData applicationKey = new KeyEntryRecoveryData(
-                TEST_ALIAS.getBytes(StandardCharsets.UTF_8),
+                TEST_ALIAS,
                 encryptedApplicationKey(recoveryKey, applicationKeyBytes));
 
         Map<String, byte[]> recoveredKeys = mRecoverableKeyStoreManager.recoverKeys(
diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java b/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java
index 49601c3..32b0b26 100644
--- a/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java
@@ -34,6 +34,7 @@
 import java.lang.reflect.Array;
 import java.lang.reflect.Field;
 import java.nio.charset.StandardCharsets;
+import java.security.cert.Certificate;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.HashSet;
@@ -261,13 +262,14 @@
         assertBundleApproximateEquals(a.mAppMetaData, b.mAppMetaData);
         assertEquals(a.mVersionName, b.mVersionName);
         assertEquals(a.mSharedUserId, b.mSharedUserId);
-        assertTrue(Arrays.equals(a.mSigningDetails.signatures, b.mSigningDetails.signatures));
+        assertTrue(Arrays.equals(a.mSignatures, b.mSignatures));
+        assertTrue(Arrays.equals(a.mCertificates, b.mCertificates));
         assertTrue(Arrays.equals(a.mLastPackageUsageTimeInMills, b.mLastPackageUsageTimeInMills));
         assertEquals(a.mExtras, b.mExtras);
         assertEquals(a.mRestrictedAccountType, b.mRestrictedAccountType);
         assertEquals(a.mRequiredAccountType, b.mRequiredAccountType);
         assertEquals(a.mOverlayTarget, b.mOverlayTarget);
-        assertEquals(a.mSigningDetails.publicKeys, b.mSigningDetails.publicKeys);
+        assertEquals(a.mSigningKeys, b.mSigningKeys);
         assertEquals(a.mUpgradeKeySets, b.mUpgradeKeySets);
         assertEquals(a.mKeySetMapping, b.mKeySetMapping);
         assertEquals(a.cpuAbiOverride, b.cpuAbiOverride);
@@ -493,16 +495,14 @@
         pkg.mAppMetaData = new Bundle();
         pkg.mVersionName = "foo17";
         pkg.mSharedUserId = "foo18";
-        pkg.mSigningDetails =
-                new PackageParser.SigningDetails(
-                        new Signature[] { new Signature(new byte[16]) },
-                        2,
-                        new ArraySet<>());
+        pkg.mSignatures = new Signature[] { new Signature(new byte[16]) };
+        pkg.mCertificates = new Certificate[][] { new Certificate[] { null }};
         pkg.mExtras = new Bundle();
         pkg.mRestrictedAccountType = "foo19";
         pkg.mRequiredAccountType = "foo20";
         pkg.mOverlayTarget = "foo21";
         pkg.mOverlayPriority = 100;
+        pkg.mSigningKeys = new ArraySet<>();
         pkg.mUpgradeKeySets = new ArraySet<>();
         pkg.mKeySetMapping = new ArrayMap<>();
         pkg.cpuAbiOverride = "foo22";
diff --git a/services/tests/servicestests/src/com/android/server/usage/AppIdleHistoryTests.java b/services/tests/servicestests/src/com/android/server/usage/AppIdleHistoryTests.java
index b62d724..7b06648 100644
--- a/services/tests/servicestests/src/com/android/server/usage/AppIdleHistoryTests.java
+++ b/services/tests/servicestests/src/com/android/server/usage/AppIdleHistoryTests.java
@@ -21,6 +21,10 @@
 import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_FREQUENT;
 import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_RARE;
 
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
 import android.app.usage.UsageStatsManager;
 import android.os.FileUtils;
 import android.test.AndroidTestCase;
@@ -117,4 +121,16 @@
         assertFalse(aih.shouldInformListeners(PACKAGE_1, USER_ID, 5000, STANDBY_BUCKET_RARE));
         assertTrue(aih.shouldInformListeners(PACKAGE_1, USER_ID, 5000, STANDBY_BUCKET_FREQUENT));
     }
+
+    public void testJobRunTime() throws Exception {
+        AppIdleHistory aih = new AppIdleHistory(mStorageDir, 1000);
+
+        aih.setLastJobRunTime(PACKAGE_1, USER_ID, 2000);
+        assertEquals(Long.MAX_VALUE, aih.getTimeSinceLastJobRun(PACKAGE_2, USER_ID, 0));
+        assertEquals(4000, aih.getTimeSinceLastJobRun(PACKAGE_1, USER_ID, 6000));
+
+        aih.setLastJobRunTime(PACKAGE_2, USER_ID, 6000);
+        assertEquals(1000, aih.getTimeSinceLastJobRun(PACKAGE_2, USER_ID, 7000));
+        assertEquals(5000, aih.getTimeSinceLastJobRun(PACKAGE_1, USER_ID, 7000));
+    }
 }
\ No newline at end of file
diff --git a/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java b/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java
index b792d82..725fb21 100644
--- a/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java
+++ b/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java
@@ -51,6 +51,8 @@
 import android.os.Handler;
 import android.os.Looper;
 import android.os.RemoteException;
+import android.platform.test.annotations.Presubmit;
+import android.support.test.filters.SmallTest;
 import android.support.test.InstrumentationRegistry;
 import android.support.test.runner.AndroidJUnit4;
 import android.view.Display;
@@ -69,6 +71,8 @@
  * Unit test for AppStandbyController.
  */
 @RunWith(AndroidJUnit4.class)
+@Presubmit
+@SmallTest
 public class AppStandbyControllerTests {
 
     private static final String PACKAGE_1 = "com.example.foo";
@@ -285,6 +289,10 @@
                 true);
     }
 
+    private void assertBucket(int bucket) {
+        assertEquals(bucket, getStandbyBucket(mController));
+    }
+
     @Test
     public void testBuckets() throws Exception {
         assertTimeout(mController, 0, STANDBY_BUCKET_NEVER);
@@ -425,4 +433,27 @@
                 REASON_PREDICTED, 2 * HOUR_MS);
         assertEquals(STANDBY_BUCKET_ACTIVE, getStandbyBucket(mController));
     }
+
+    @Test
+    public void testTimeout() throws Exception {
+        setChargingState(mController, false);
+
+        reportEvent(mController, USER_INTERACTION, 0);
+        assertBucket(STANDBY_BUCKET_ACTIVE);
+
+        mInjector.mElapsedRealtime = 2000;
+        mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_FREQUENT,
+                REASON_PREDICTED, mInjector.mElapsedRealtime);
+        assertBucket(STANDBY_BUCKET_ACTIVE);
+
+        // bucketing works after timeout
+        mInjector.mElapsedRealtime = FREQUENT_THRESHOLD - 100;
+        mController.checkIdleStates(USER_ID);
+        assertBucket(STANDBY_BUCKET_WORKING_SET);
+
+        mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_FREQUENT,
+                REASON_PREDICTED, mInjector.mElapsedRealtime);
+        assertBucket(STANDBY_BUCKET_FREQUENT);
+
+    }
 }
diff --git a/services/tests/servicestests/src/com/android/server/wm/SurfaceAnimationRunnerTest.java b/services/tests/servicestests/src/com/android/server/wm/SurfaceAnimationRunnerTest.java
index 4831fcd..b36c7d9 100644
--- a/services/tests/servicestests/src/com/android/server/wm/SurfaceAnimationRunnerTest.java
+++ b/services/tests/servicestests/src/com/android/server/wm/SurfaceAnimationRunnerTest.java
@@ -34,8 +34,10 @@
 import android.graphics.Matrix;
 import android.graphics.Point;
 import android.platform.test.annotations.Presubmit;
+import android.support.test.filters.FlakyTest;
 import android.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
+import android.util.Log;
 import android.view.Choreographer;
 import android.view.Choreographer.FrameCallback;
 import android.view.SurfaceControl;
@@ -135,6 +137,7 @@
         assertFinishCallbackNotCalled();
     }
 
+    @FlakyTest(bugId = 71719744)
     @Test
     public void testCancel_sneakyCancelBeforeUpdate() throws Exception {
         mSurfaceAnimationRunner = new SurfaceAnimationRunner(null, () -> new ValueAnimator() {
@@ -157,8 +160,12 @@
         when(mMockAnimationSpec.getDuration()).thenReturn(200L);
         mSurfaceAnimationRunner.startAnimation(mMockAnimationSpec, mMockSurface, mMockTransaction,
                 this::finishedCallback);
+
+        // We need to wait for two frames: The first frame starts the animation, the second frame
+        // actually cancels the animation.
         waitUntilNextFrame();
-        assertFalse(mSurfaceAnimationRunner.mRunningAnimations.isEmpty());
+        waitUntilNextFrame();
+        assertTrue(mSurfaceAnimationRunner.mRunningAnimations.isEmpty());
         verify(mMockAnimationSpec, atLeastOnce()).apply(any(), any(), eq(0L));
     }
 
diff --git a/services/tests/servicestests/src/com/android/server/wm/SurfaceAnimatorTest.java b/services/tests/servicestests/src/com/android/server/wm/SurfaceAnimatorTest.java
index 463ceeb..64c3037 100644
--- a/services/tests/servicestests/src/com/android/server/wm/SurfaceAnimatorTest.java
+++ b/services/tests/servicestests/src/com/android/server/wm/SurfaceAnimatorTest.java
@@ -75,10 +75,7 @@
         mAnimatable2 = new MyAnimatable();
     }
 
-    // TODO: Tests are flaky, and timeout after 5 minutes. Instead of wasting everybody's time we
-    // mark them as ignore.
     @Test
-    @Ignore
     public void testRunAnimation() throws Exception {
         mAnimatable.mSurfaceAnimator.startAnimation(mTransaction, mSpec, true /* hidden */);
         final ArgumentCaptor<OnAnimationFinishedCallback> callbackCaptor = ArgumentCaptor.forClass(
@@ -88,17 +85,13 @@
         verify(mSpec).startAnimation(any(), any(), callbackCaptor.capture());
 
         callbackCaptor.getValue().onAnimationFinished(mSpec);
-        waitUntilPrepareSurfaces();
         assertNotAnimating(mAnimatable);
         assertTrue(mAnimatable.mFinishedCallbackCalled);
         assertTrue(mAnimatable.mPendingDestroySurfaces.contains(mAnimatable.mLeash));
         // TODO: Verify reparenting once we use mPendingTransaction to reparent it back
     }
 
-    // TODO: Tests are flaky, and timeout after 5 minutes. Instead of wasting everybody's time we
-    // mark them as ignore.
     @Test
-    @Ignore
     public void testOverrideAnimation() throws Exception {
         mAnimatable.mSurfaceAnimator.startAnimation(mTransaction, mSpec, true /* hidden */);
         final SurfaceControl firstLeash = mAnimatable.mLeash;
@@ -114,13 +107,11 @@
 
         // First animation was finished, but this shouldn't cancel the second animation
         callbackCaptor.getValue().onAnimationFinished(mSpec);
-        waitUntilPrepareSurfaces();
         assertTrue(mAnimatable.mSurfaceAnimator.isAnimating());
 
         // Second animation was finished
         verify(mSpec2).startAnimation(any(), any(), callbackCaptor.capture());
         callbackCaptor.getValue().onAnimationFinished(mSpec2);
-        waitUntilPrepareSurfaces();
         assertNotAnimating(mAnimatable);
         assertTrue(mAnimatable.mFinishedCallbackCalled);
     }
@@ -157,10 +148,7 @@
         assertTrue(mAnimatable.mPendingDestroySurfaces.contains(mAnimatable.mLeash));
     }
 
-    // TODO: Tests are flaky, and timeout after 5 minutes. Instead of wasting everybody's time we
-    // mark them as ignore.
     @Test
-    @Ignore
     public void testTransferAnimation() throws Exception {
         mAnimatable.mSurfaceAnimator.startAnimation(mTransaction, mSpec, true /* hidden */);
 
@@ -175,7 +163,6 @@
         assertEquals(leash, mAnimatable2.mSurfaceAnimator.mLeash);
         assertFalse(mAnimatable.mPendingDestroySurfaces.contains(leash));
         callbackCaptor.getValue().onAnimationFinished(mSpec);
-        waitUntilPrepareSurfaces();
         assertNotAnimating(mAnimatable2);
         assertTrue(mAnimatable2.mFinishedCallbackCalled);
         assertTrue(mAnimatable2.mPendingDestroySurfaces.contains(leash));
@@ -191,14 +178,6 @@
         assertNull(animatable.mSurfaceAnimator.getAnimation());
     }
 
-    private void waitUntilPrepareSurfaces() throws Exception {
-        final CountDownLatch latch = new CountDownLatch(1);
-        synchronized (sWm.mWindowMap) {
-            sWm.mAnimator.addAfterPrepareSurfacesRunnable(latch::countDown);
-        }
-        latch.await();
-    }
-
     private class MyAnimatable implements Animatable {
 
         final SurfaceControl mParent;
@@ -219,7 +198,7 @@
                     .build();
             mFinishedCallbackCalled = false;
             mLeash = null;
-            mSurfaceAnimator = new SurfaceAnimator(this, mFinishedCallback, sWm);
+            mSurfaceAnimator = new SurfaceAnimator(this, mFinishedCallback, Runnable::run, sWm);
         }
 
         @Override
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowAnimationSpecTest.java b/services/tests/servicestests/src/com/android/server/wm/WindowAnimationSpecTest.java
index 9cdef16..f8db4fa 100644
--- a/services/tests/servicestests/src/com/android/server/wm/WindowAnimationSpecTest.java
+++ b/services/tests/servicestests/src/com/android/server/wm/WindowAnimationSpecTest.java
@@ -25,6 +25,7 @@
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.verify;
 
+import android.graphics.Point;
 import android.graphics.Rect;
 import android.platform.test.annotations.Presubmit;
 import android.support.test.filters.SmallTest;
@@ -72,6 +73,19 @@
     }
 
     @Test
+    public void testApply_clipAfterOffsetPosition() {
+        // Stack bounds is (0, 0, 10, 10) position is (20, 40)
+        WindowAnimationSpec windowAnimationSpec = new WindowAnimationSpec(mAnimation,
+                new Point(20, 40), mStackBounds, false /* canSkipFirstFrame */,
+                STACK_CLIP_AFTER_ANIM);
+        windowAnimationSpec.apply(mTransaction, mSurfaceControl, 0);
+        verify(mTransaction).setWindowCrop(eq(mSurfaceControl), argThat(Rect::isEmpty));
+        verify(mTransaction).setFinalCrop(eq(mSurfaceControl),
+                argThat(rect -> rect.left == 20 && rect.top == 40 && rect.right == 30
+                        && rect.bottom == 50));
+    }
+
+    @Test
     public void testApply_clipBeforeNoAnimationBounds() {
         // Stack bounds is (0, 0, 10, 10) animation clip is (0, 0, 0, 0)
         WindowAnimationSpec windowAnimationSpec = new WindowAnimationSpec(mAnimation, null,
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java b/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java
index ff840f3..c699a94 100644
--- a/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java
+++ b/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java
@@ -103,7 +103,6 @@
 
         context.getDisplay().getDisplayInfo(mDisplayInfo);
         mDisplayContent = createNewDisplay();
-        sWm.mAnimator.mInitialized = true;
         sWm.mDisplayEnabled = true;
         sWm.mDisplayReady = true;
 
diff --git a/services/usage/java/com/android/server/usage/AppIdleHistory.java b/services/usage/java/com/android/server/usage/AppIdleHistory.java
index a1f1810..0cbda28 100644
--- a/services/usage/java/com/android/server/usage/AppIdleHistory.java
+++ b/services/usage/java/com/android/server/usage/AppIdleHistory.java
@@ -23,7 +23,6 @@
 import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_ACTIVE;
 import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_NEVER;
 import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_RARE;
-import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_WORKING_SET;
 
 import android.app.usage.UsageStatsManager;
 import android.os.SystemClock;
@@ -66,13 +65,7 @@
 
     // History for all users and all packages
     private SparseArray<ArrayMap<String,AppUsageHistory>> mIdleHistory = new SparseArray<>();
-    private long mLastPeriod = 0;
     private static final long ONE_MINUTE = 60 * 1000;
-    private static final int HISTORY_SIZE = 100;
-    private static final int FLAG_LAST_STATE = 2;
-    private static final int FLAG_PARTIAL_ACTIVE = 1;
-    private static final long PERIOD_DURATION = UsageStatsService.COMPRESS_TIME ? ONE_MINUTE
-            : 60 * ONE_MINUTE;
 
     @VisibleForTesting
     static final String APP_IDLE_FILENAME = "app_idle_stats.xml";
@@ -89,6 +82,10 @@
     private static final String ATTR_CURRENT_BUCKET = "appLimitBucket";
     // The reason the app was put in the above bucket
     private static final String ATTR_BUCKETING_REASON = "bucketReason";
+    // The last time a job was run for this app
+    private static final String ATTR_LAST_RUN_JOB_TIME = "lastJobRunTime";
+    // The time when the forced active state can be overridden.
+    private static final String ATTR_BUCKET_TIMEOUT_TIME = "bucketTimeoutTime";
 
     // device on time = mElapsedDuration + (timeNow - mElapsedSnapshot)
     private long mElapsedSnapshot; // Elapsed time snapshot when last write of mDeviceOnDuration
@@ -103,8 +100,6 @@
     private boolean mScreenOn;
 
     static class AppUsageHistory {
-        // Debug
-        final byte[] recent = new byte[HISTORY_SIZE];
         // Last used time using elapsed timebase
         long lastUsedElapsedTime;
         // Last used time using screen_on timebase
@@ -118,6 +113,13 @@
         String bucketingReason;
         // In-memory only, last bucket for which the listeners were informed
         int lastInformedBucket;
+        // The last time a job was run for this app, using elapsed timebase
+        long lastJobRunTime;
+        // When should the bucket state timeout, in elapsed timebase, if greater than
+        // lastUsedElapsedTime.
+        // This is used to keep the app in a high bucket regardless of other timeouts and
+        // predictions.
+        long bucketTimeoutTime;
     }
 
     AppIdleHistory(File storageDir, long elapsedRealtime) {
@@ -195,81 +197,47 @@
         writeScreenOnTime();
     }
 
-    public int reportUsage(String packageName, int userId, long elapsedRealtime) {
+    /**
+     * Mark the app as used and update the bucket if necessary. If there is a timeout specified
+     * that's in the future, then the usage event is temporary and keeps the app in the specified
+     * bucket at least until the timeout is reached. This can be used to keep the app in an
+     * elevated bucket for a while until some important task gets to run.
+     * @param packageName
+     * @param userId
+     * @param bucket the bucket to set the app to
+     * @param elapsedRealtime mark as used time if non-zero
+     * @param timeout set the timeout of the specified bucket, if non-zero
+     * @return
+     */
+    public int reportUsage(String packageName, int userId, int bucket, long elapsedRealtime,
+            long timeout) {
         ArrayMap<String, AppUsageHistory> userHistory = getUserHistory(userId);
         AppUsageHistory appUsageHistory = getPackageHistory(userHistory, packageName,
                 elapsedRealtime, true);
 
-        shiftHistoryToNow(userHistory, elapsedRealtime);
+        if (elapsedRealtime != 0) {
+            appUsageHistory.lastUsedElapsedTime = mElapsedDuration
+                    + (elapsedRealtime - mElapsedSnapshot);
+            appUsageHistory.lastUsedScreenTime = getScreenOnTime(elapsedRealtime);
+        }
 
-        appUsageHistory.lastUsedElapsedTime = mElapsedDuration
-                + (elapsedRealtime - mElapsedSnapshot);
-        appUsageHistory.lastUsedScreenTime = getScreenOnTime(elapsedRealtime);
-        appUsageHistory.recent[HISTORY_SIZE - 1] = FLAG_LAST_STATE | FLAG_PARTIAL_ACTIVE;
-        if (appUsageHistory.currentBucket > STANDBY_BUCKET_ACTIVE) {
-            appUsageHistory.currentBucket = STANDBY_BUCKET_ACTIVE;
+        if (appUsageHistory.currentBucket > bucket) {
+            appUsageHistory.currentBucket = bucket;
             if (DEBUG) {
-                Slog.d(TAG, "Moved " + packageName + " to bucket=" + appUsageHistory.currentBucket
+                Slog.d(TAG, "Moved " + packageName + " to bucket=" + appUsageHistory
+                        .currentBucket
                         + ", reason=" + appUsageHistory.bucketingReason);
             }
+            if (timeout > elapsedRealtime) {
+                // Convert to elapsed timebase
+                appUsageHistory.bucketTimeoutTime = mElapsedDuration + (timeout - mElapsedSnapshot);
+            }
         }
         appUsageHistory.bucketingReason = REASON_USAGE;
 
         return appUsageHistory.currentBucket;
     }
 
-    public int reportMildUsage(String packageName, int userId, long elapsedRealtime) {
-        ArrayMap<String, AppUsageHistory> userHistory = getUserHistory(userId);
-        AppUsageHistory appUsageHistory = getPackageHistory(userHistory, packageName,
-                elapsedRealtime, true);
-        if (appUsageHistory.currentBucket > STANDBY_BUCKET_WORKING_SET) {
-            appUsageHistory.currentBucket = STANDBY_BUCKET_WORKING_SET;
-            if (DEBUG) {
-                Slog.d(TAG, "Moved " + packageName + " to bucket=" + appUsageHistory.currentBucket
-                        + ", reason=" + appUsageHistory.bucketingReason);
-            }
-        }
-        // TODO: Should this be a different reason for partial usage?
-        appUsageHistory.bucketingReason = REASON_USAGE;
-
-        return appUsageHistory.currentBucket;
-    }
-
-    public void setIdle(String packageName, int userId, long elapsedRealtime) {
-        ArrayMap<String, AppUsageHistory> userHistory = getUserHistory(userId);
-        AppUsageHistory appUsageHistory = getPackageHistory(userHistory, packageName,
-                elapsedRealtime, true);
-
-        shiftHistoryToNow(userHistory, elapsedRealtime);
-
-        appUsageHistory.recent[HISTORY_SIZE - 1] &= ~FLAG_LAST_STATE;
-    }
-
-    private void shiftHistoryToNow(ArrayMap<String, AppUsageHistory> userHistory,
-            long elapsedRealtime) {
-        long thisPeriod = elapsedRealtime / PERIOD_DURATION;
-        // Has the period switched over? Slide all users' package histories
-        if (mLastPeriod != 0 && mLastPeriod < thisPeriod
-                && (thisPeriod - mLastPeriod) < HISTORY_SIZE - 1) {
-            int diff = (int) (thisPeriod - mLastPeriod);
-            final int NUSERS = mIdleHistory.size();
-            for (int u = 0; u < NUSERS; u++) {
-                userHistory = mIdleHistory.valueAt(u);
-                for (AppUsageHistory idleState : userHistory.values()) {
-                    // Shift left
-                    System.arraycopy(idleState.recent, diff, idleState.recent, 0,
-                            HISTORY_SIZE - diff);
-                    // Replicate last state across the diff
-                    for (int i = 0; i < diff; i++) {
-                        idleState.recent[HISTORY_SIZE - i - 1] =
-                            (byte) (idleState.recent[HISTORY_SIZE - diff - 1] & FLAG_LAST_STATE);
-                    }
-                }
-            }
-        }
-        mLastPeriod = thisPeriod;
-    }
-
     private ArrayMap<String, AppUsageHistory> getUserHistory(int userId) {
         ArrayMap<String, AppUsageHistory> userHistory = mIdleHistory.get(userId);
         if (userHistory == null) {
@@ -291,6 +259,7 @@
             appUsageHistory.currentBucket = STANDBY_BUCKET_NEVER;
             appUsageHistory.bucketingReason = REASON_DEFAULT;
             appUsageHistory.lastInformedBucket = -1;
+            appUsageHistory.lastJobRunTime = Long.MIN_VALUE; // long long time ago
             userHistory.put(packageName, appUsageHistory);
         }
         return appUsageHistory;
@@ -338,6 +307,38 @@
         }
     }
 
+    /**
+     * Marks the last time a job was run, with the given elapsedRealtime. The time stored is
+     * based on the elapsed timebase.
+     * @param packageName
+     * @param userId
+     * @param elapsedRealtime
+     */
+    public void setLastJobRunTime(String packageName, int userId, long elapsedRealtime) {
+        ArrayMap<String, AppUsageHistory> userHistory = getUserHistory(userId);
+        AppUsageHistory appUsageHistory =
+                getPackageHistory(userHistory, packageName, elapsedRealtime, true);
+        appUsageHistory.lastJobRunTime = getElapsedTime(elapsedRealtime);
+    }
+
+    /**
+     * Returns the time since the last job was run for this app. This can be larger than the
+     * current elapsedRealtime, in case it happened before boot or a really large value if no jobs
+     * were ever run.
+     * @param packageName
+     * @param userId
+     * @param elapsedRealtime
+     * @return
+     */
+    public long getTimeSinceLastJobRun(String packageName, int userId, long elapsedRealtime) {
+        ArrayMap<String, AppUsageHistory> userHistory = getUserHistory(userId);
+        AppUsageHistory appUsageHistory =
+                getPackageHistory(userHistory, packageName, elapsedRealtime, true);
+        // Don't adjust the default, else it'll wrap around to a positive value
+        if (appUsageHistory.lastJobRunTime == Long.MIN_VALUE) return Long.MAX_VALUE;
+        return getElapsedTime(elapsedRealtime) - appUsageHistory.lastJobRunTime;
+    }
+
     public int getAppStandbyBucket(String packageName, int userId, long elapsedRealtime) {
         ArrayMap<String, AppUsageHistory> userHistory = getUserHistory(userId);
         AppUsageHistory appUsageHistory =
@@ -473,12 +474,8 @@
                                 Long.parseLong(parser.getAttributeValue(null, ATTR_ELAPSED_IDLE));
                         appUsageHistory.lastUsedScreenTime =
                                 Long.parseLong(parser.getAttributeValue(null, ATTR_SCREEN_IDLE));
-                        String lastPredictedTimeString = parser.getAttributeValue(null,
-                                ATTR_LAST_PREDICTED_TIME);
-                        if (lastPredictedTimeString != null) {
-                            appUsageHistory.lastPredictedTime =
-                                    Long.parseLong(lastPredictedTimeString);
-                        }
+                        appUsageHistory.lastPredictedTime = getLongValue(parser,
+                                ATTR_LAST_PREDICTED_TIME, 0L);
                         String currentBucketString = parser.getAttributeValue(null,
                                 ATTR_CURRENT_BUCKET);
                         appUsageHistory.currentBucket = currentBucketString == null
@@ -486,6 +483,10 @@
                                 : Integer.parseInt(currentBucketString);
                         appUsageHistory.bucketingReason =
                                 parser.getAttributeValue(null, ATTR_BUCKETING_REASON);
+                        appUsageHistory.lastJobRunTime = getLongValue(parser,
+                                ATTR_LAST_RUN_JOB_TIME, Long.MIN_VALUE);
+                        appUsageHistory.bucketTimeoutTime = getLongValue(parser,
+                                ATTR_BUCKET_TIMEOUT_TIME, 0L);
                         if (appUsageHistory.bucketingReason == null) {
                             appUsageHistory.bucketingReason = REASON_DEFAULT;
                         }
@@ -501,6 +502,12 @@
         }
     }
 
+    private long getLongValue(XmlPullParser parser, String attrName, long defValue) {
+        String value = parser.getAttributeValue(null, attrName);
+        if (value == null) return defValue;
+        return Long.parseLong(value);
+    }
+
     public void writeAppIdleTimes(int userId) {
         FileOutputStream fos = null;
         AtomicFile appIdleFile = new AtomicFile(getUserFile(userId));
@@ -531,6 +538,14 @@
                 xml.attribute(null, ATTR_CURRENT_BUCKET,
                         Integer.toString(history.currentBucket));
                 xml.attribute(null, ATTR_BUCKETING_REASON, history.bucketingReason);
+                if (history.bucketTimeoutTime > 0) {
+                    xml.attribute(null, ATTR_BUCKET_TIMEOUT_TIME, Long.toString(history
+                            .bucketTimeoutTime));
+                }
+                if (history.lastJobRunTime != Long.MIN_VALUE) {
+                    xml.attribute(null, ATTR_LAST_RUN_JOB_TIME, Long.toString(history
+                            .lastJobRunTime));
+                }
                 xml.endTag(null, TAG_PACKAGE);
             }
 
@@ -565,6 +580,10 @@
             TimeUtils.formatDuration(screenOnTime - appUsageHistory.lastUsedScreenTime, idpw);
             idpw.print(" lastPredictedTime=");
             TimeUtils.formatDuration(totalElapsedTime - appUsageHistory.lastPredictedTime, idpw);
+            idpw.print(" bucketTimeoutTime=");
+            TimeUtils.formatDuration(totalElapsedTime - appUsageHistory.bucketTimeoutTime, idpw);
+            idpw.print(" lastJobRunTime=");
+            TimeUtils.formatDuration(totalElapsedTime - appUsageHistory.lastJobRunTime, idpw);
             idpw.print(" idle=" + (isIdle(packageName, userId, elapsedRealtime) ? "y" : "n"));
             idpw.print(" bucket=" + appUsageHistory.currentBucket
                     + " reason=" + appUsageHistory.bucketingReason);
@@ -579,21 +598,4 @@
         idpw.println();
         idpw.decreaseIndent();
     }
-
-    public void dumpHistory(IndentingPrintWriter idpw, int userId) {
-        ArrayMap<String, AppUsageHistory> userHistory = mIdleHistory.get(userId);
-        final long elapsedRealtime = SystemClock.elapsedRealtime();
-        if (userHistory == null) return;
-        final int P = userHistory.size();
-        for (int p = 0; p < P; p++) {
-            final String packageName = userHistory.keyAt(p);
-            final byte[] history = userHistory.valueAt(p).recent;
-            for (int i = 0; i < HISTORY_SIZE; i++) {
-                idpw.print(history[i] == 0 ? '.' : 'A');
-            }
-            idpw.print(" idle=" + (isIdle(packageName, userId, elapsedRealtime) ? "y" : "n"));
-            idpw.print("  " + packageName);
-            idpw.println();
-        }
-    }
 }
diff --git a/services/usage/java/com/android/server/usage/AppStandbyController.java b/services/usage/java/com/android/server/usage/AppStandbyController.java
index 9b588fa..2ec218a 100644
--- a/services/usage/java/com/android/server/usage/AppStandbyController.java
+++ b/services/usage/java/com/android/server/usage/AppStandbyController.java
@@ -36,7 +36,6 @@
 import android.app.admin.DevicePolicyManager;
 import android.app.usage.UsageStatsManager.StandbyBuckets;
 import android.app.usage.UsageEvents;
-import android.app.usage.UsageStatsManager;
 import android.app.usage.UsageStatsManagerInternal.AppIdleStateChangeListener;
 import android.appwidget.AppWidgetManager;
 import android.content.BroadcastReceiver;
@@ -82,6 +81,8 @@
 
 import java.io.File;
 import java.io.PrintWriter;
+import java.time.Duration;
+import java.time.format.DateTimeParseException;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
@@ -263,8 +264,9 @@
                 }
                 if (!packageName.equals(providerPkgName)) {
                     synchronized (mAppIdleLock) {
-                        int newBucket = mAppIdleHistory.reportMildUsage(packageName, userId,
-                                    elapsedRealtime);
+                        int newBucket = mAppIdleHistory.reportUsage(packageName, userId,
+                                STANDBY_BUCKET_ACTIVE, elapsedRealtime,
+                                elapsedRealtime + 2 * ONE_HOUR);
                         maybeInformListeners(packageName, userId, elapsedRealtime,
                                 newBucket);
                     }
@@ -406,8 +408,10 @@
                         AppIdleHistory.AppUsageHistory app =
                                 mAppIdleHistory.getAppUsageHistory(packageName,
                                 userId, elapsedRealtime);
-                        // If the bucket was forced by the developer, leave it alone
-                        if (REASON_FORCED.equals(app.bucketingReason)) {
+                        // If the bucket was forced by the developer or the app is within the
+                        // temporary active period, leave it alone.
+                        if (REASON_FORCED.equals(app.bucketingReason)
+                                || !hasBucketTimeoutPassed(app, elapsedRealtime)) {
                             continue;
                         }
                         boolean predictionLate = false;
@@ -449,6 +453,11 @@
                     - app.lastPredictedTime > PREDICTION_TIMEOUT;
     }
 
+    private boolean hasBucketTimeoutPassed(AppIdleHistory.AppUsageHistory app,
+            long elapsedRealtime) {
+        return app.bucketTimeoutTime < mAppIdleHistory.getElapsedTime(elapsedRealtime);
+    }
+
     private void maybeInformListeners(String packageName, int userId,
             long elapsedRealtime, int bucket) {
         synchronized (mAppIdleLock) {
@@ -544,11 +553,13 @@
 
                 final int newBucket;
                 if (event.mEventType == UsageEvents.Event.NOTIFICATION_SEEN) {
-                    newBucket = mAppIdleHistory.reportMildUsage(event.mPackage, userId,
-                            elapsedRealtime);
+                    newBucket = mAppIdleHistory.reportUsage(event.mPackage, userId,
+                            STANDBY_BUCKET_WORKING_SET,
+                            elapsedRealtime, elapsedRealtime + 2 * ONE_HOUR);
                 } else {
                     newBucket = mAppIdleHistory.reportUsage(event.mPackage, userId,
-                            elapsedRealtime);
+                            STANDBY_BUCKET_ACTIVE,
+                            elapsedRealtime, elapsedRealtime + 2 * ONE_HOUR);
                 }
 
                 maybeInformListeners(event.mPackage, userId, elapsedRealtime,
@@ -592,6 +603,19 @@
         }
     }
 
+    public void setLastJobRunTime(String packageName, int userId, long elapsedRealtime) {
+        synchronized (mAppIdleLock) {
+            mAppIdleHistory.setLastJobRunTime(packageName, userId, elapsedRealtime);
+        }
+    }
+
+    public long getTimeSinceLastJobRun(String packageName, int userId) {
+        final long elapsedRealtime = mInjector.elapsedRealtime();
+        synchronized (mAppIdleLock) {
+            return mAppIdleHistory.getTimeSinceLastJobRun(packageName, userId, elapsedRealtime);
+        }
+    }
+
     public void onUserRemoved(int userId) {
         synchronized (mAppIdleLock) {
             mAppIdleHistory.onUserRemoved(userId);
@@ -805,16 +829,27 @@
             AppIdleHistory.AppUsageHistory app = mAppIdleHistory.getAppUsageHistory(packageName,
                     userId, elapsedRealtime);
             boolean predicted = reason != null && reason.startsWith(REASON_PREDICTED);
+
             // Don't allow changing bucket if higher than ACTIVE
             if (app.currentBucket < STANDBY_BUCKET_ACTIVE) return;
-            // Don't allow prediction to change from or to NEVER
+
+            // Don't allow prediction to change from/to NEVER
             if ((app.currentBucket == STANDBY_BUCKET_NEVER
                     || newBucket == STANDBY_BUCKET_NEVER)
                     && predicted) {
                 return;
             }
+
             // If the bucket was forced, don't allow prediction to override
             if (app.bucketingReason.equals(REASON_FORCED) && predicted) return;
+
+            // If the bucket is required to stay in a higher state for a specified duration, don't
+            // override unless the duration has passed
+            if (predicted && app.currentBucket < newBucket
+                    && !hasBucketTimeoutPassed(app, elapsedRealtime)) {
+                return;
+            }
+
             mAppIdleHistory.setAppStandbyBucket(packageName, userId, elapsedRealtime, newBucket,
                     reason);
         }
@@ -947,7 +982,10 @@
                 final PackageInfo pi = packages.get(i);
                 String packageName = pi.packageName;
                 if (pi.applicationInfo != null && pi.applicationInfo.isSystemApp()) {
-                    mAppIdleHistory.reportUsage(packageName, userId, elapsedRealtime);
+                    // Mark app as used for 4 hours. After that it can timeout to whatever the
+                    // past usage pattern was.
+                    mAppIdleHistory.reportUsage(packageName, userId, STANDBY_BUCKET_ACTIVE, 0,
+                            elapsedRealtime + 4 * ONE_HOUR);
                     if (isAppSpecial(packageName, UserHandle.getAppId(pi.applicationInfo.uid),
                             userId)) {
                         mAppIdleHistory.setAppStandbyBucket(packageName, userId, elapsedRealtime,
@@ -967,12 +1005,6 @@
                 .sendToTarget();
     }
 
-    void dumpHistory(IndentingPrintWriter idpw, int userId) {
-        synchronized (mAppIdleLock) {
-            mAppIdleHistory.dumpHistory(idpw, userId);
-        }
-    }
-
     void dumpUser(IndentingPrintWriter idpw, int userId, String pkg) {
         synchronized (mAppIdleLock) {
             mAppIdleHistory.dump(idpw, userId, pkg);
@@ -1278,10 +1310,10 @@
             synchronized (mAppIdleLock) {
 
                 // Default: 24 hours between paroles
-                mAppIdleParoleIntervalMillis = mParser.getLong(KEY_PAROLE_INTERVAL,
+                mAppIdleParoleIntervalMillis = mParser.getDurationMillis(KEY_PAROLE_INTERVAL,
                         COMPRESS_TIME ? ONE_MINUTE * 10 : 24 * 60 * ONE_MINUTE);
 
-                mAppIdleParoleDurationMillis = mParser.getLong(KEY_PAROLE_DURATION,
+                mAppIdleParoleDurationMillis = mParser.getDurationMillis(KEY_PAROLE_DURATION,
                         COMPRESS_TIME ? ONE_MINUTE : 10 * ONE_MINUTE); // 10 minutes
 
                 String screenThresholdsValue = mParser.getString(KEY_SCREEN_TIME_THRESHOLDS, null);
@@ -1308,7 +1340,15 @@
                 if (thresholds.length == THRESHOLD_BUCKETS.length) {
                     long[] array = new long[THRESHOLD_BUCKETS.length];
                     for (int i = 0; i < THRESHOLD_BUCKETS.length; i++) {
-                        array[i] = Long.parseLong(thresholds[i]);
+                        try {
+                            if (thresholds[i].startsWith("P") || thresholds[i].startsWith("p")) {
+                                array[i] = Duration.parse(thresholds[i]).toMillis();
+                            } else {
+                                array[i] = Long.parseLong(thresholds[i]);
+                            }
+                        } catch (NumberFormatException|DateTimeParseException e) {
+                            return defaults;
+                        }
                     }
                     return array;
                 } else {
diff --git a/services/usage/java/com/android/server/usage/StorageStatsService.java b/services/usage/java/com/android/server/usage/StorageStatsService.java
index d598649..2fec20a 100644
--- a/services/usage/java/com/android/server/usage/StorageStatsService.java
+++ b/services/usage/java/com/android/server/usage/StorageStatsService.java
@@ -168,8 +168,11 @@
     public boolean isReservedSupported(String volumeUuid, String callingPackage) {
         enforcePermission(Binder.getCallingUid(), callingPackage);
 
-        // TODO: implement as part of b/62024591
-        return false;
+        if (volumeUuid == StorageManager.UUID_PRIVATE_INTERNAL) {
+            return SystemProperties.getBoolean(StorageManager.PROP_HAS_RESERVED, false);
+        } else {
+            return false;
+        }
     }
 
     @Override
diff --git a/services/usage/java/com/android/server/usage/UsageStatsService.java b/services/usage/java/com/android/server/usage/UsageStatsService.java
index cdce448..463a26e 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsService.java
@@ -68,8 +68,6 @@
 import java.io.IOException;
 import java.io.PrintWriter;
 import java.util.Arrays;
-import java.util.Collection;
-import java.util.Collections;
 import java.util.List;
 import java.util.Map;
 
@@ -478,7 +476,6 @@
             IndentingPrintWriter idpw = new IndentingPrintWriter(pw, "  ");
 
             boolean checkin = false;
-            boolean history = false;
             String pkg = null;
 
             if (args != null) {
@@ -486,11 +483,6 @@
                     String arg = args[i];
                     if ("--checkin".equals(arg)) {
                         checkin = true;
-                    } else if ("--history".equals(arg)) {
-                        history = true;
-                    } else if ("history".equals(arg)) {
-                        history = true;
-                        break;
                     } else if ("flush".equals(arg)) {
                         flushToDiskLocked();
                         pw.println("Flushed stats to disk");
@@ -514,9 +506,6 @@
                 } else {
                     mUserState.valueAt(i).dump(idpw, pkg);
                     idpw.println();
-                    if (history) {
-                        mAppStandby.dumpHistory(idpw, userId);
-                    }
                 }
                 mAppStandby.dumpUser(idpw, userId, pkg);
                 idpw.decreaseIndent();
@@ -1021,5 +1010,15 @@
             return UsageStatsService.this.queryUsageStats(
                     userId, intervalType, beginTime, endTime, obfuscateInstantApps);
         }
+
+        @Override
+        public void setLastJobRunTime(String packageName, int userId, long elapsedRealtime) {
+            mAppStandby.setLastJobRunTime(packageName, userId, elapsedRealtime);
+        }
+
+        @Override
+        public long getTimeSinceLastJobRun(String packageName, int userId) {
+            return mAppStandby.getTimeSinceLastJobRun(packageName, userId);
+        }
     }
 }
diff --git a/telecomm/java/android/telecom/Log.java b/telecomm/java/android/telecom/Log.java
index 3361b5b..83ca470 100644
--- a/telecomm/java/android/telecom/Log.java
+++ b/telecomm/java/android/telecom/Log.java
@@ -340,24 +340,6 @@
         return sSessionManager;
     }
 
-    private static MessageDigest sMessageDigest;
-
-    public static void initMd5Sum() {
-        new AsyncTask<Void, Void, Void>() {
-            @Override
-            public Void doInBackground(Void... args) {
-                MessageDigest md;
-                try {
-                    md = MessageDigest.getInstance("SHA-1");
-                } catch (NoSuchAlgorithmException e) {
-                    md = null;
-                }
-                sMessageDigest = md;
-                return null;
-            }
-        }.execute();
-    }
-
     public static void setTag(String tag) {
         TAG = tag;
         DEBUG = isLoggable(android.util.Log.DEBUG);
@@ -425,44 +407,13 @@
     /**
      * Redact personally identifiable information for production users.
      * If we are running in verbose mode, return the original string,
-     * and return "****" if we are running on the user build, otherwise
-     * return a SHA-1 hash of the input string.
+     * and return "***" otherwise.
      */
     public static String pii(Object pii) {
         if (pii == null || VERBOSE) {
             return String.valueOf(pii);
         }
-        return "[" + secureHash(String.valueOf(pii).getBytes()) + "]";
-    }
-
-    private static String secureHash(byte[] input) {
-        // Refrain from logging user personal information in user build.
-        if (USER_BUILD) {
-            return "****";
-        }
-
-        if (sMessageDigest != null) {
-            sMessageDigest.reset();
-            sMessageDigest.update(input);
-            byte[] result = sMessageDigest.digest();
-            return encodeHex(result);
-        } else {
-            return "Uninitialized SHA1";
-        }
-    }
-
-    private static String encodeHex(byte[] bytes) {
-        StringBuffer hex = new StringBuffer(bytes.length * 2);
-
-        for (int i = 0; i < bytes.length; i++) {
-            int byteIntValue = bytes[i] & 0xff;
-            if (byteIntValue < 0x10) {
-                hex.append("0");
-            }
-            hex.append(Integer.toString(byteIntValue, 16));
-        }
-
-        return hex.toString();
+        return "***";
     }
 
     private static String getPrefixFromObject(Object obj) {
diff --git a/telecomm/java/android/telecom/TelecomManager.java b/telecomm/java/android/telecom/TelecomManager.java
index 15355ac..2d1fe50 100644
--- a/telecomm/java/android/telecom/TelecomManager.java
+++ b/telecomm/java/android/telecom/TelecomManager.java
@@ -653,7 +653,6 @@
             mContext = context;
         }
         mTelecomServiceOverride = telecomServiceImpl;
-        android.telecom.Log.initMd5Sum();
     }
 
     /**
diff --git a/telephony/java/android/telephony/RadioNetworkConstants.java b/telephony/java/android/telephony/AccessNetworkConstants.java
similarity index 95%
rename from telephony/java/android/telephony/RadioNetworkConstants.java
rename to telephony/java/android/telephony/AccessNetworkConstants.java
index 5f5dd82..fc814be 100644
--- a/telephony/java/android/telephony/RadioNetworkConstants.java
+++ b/telephony/java/android/telephony/AccessNetworkConstants.java
@@ -17,23 +17,23 @@
 package android.telephony;
 
 /**
- * Contains radio access network related constants.
+ * Contains access network related constants.
  */
-public final class RadioNetworkConstants {
+public final class AccessNetworkConstants {
 
-    public static final class RadioAccessNetworks {
+    public static final class AccessNetworkType {
         public static final int GERAN = 1;
         public static final int UTRAN = 2;
         public static final int EUTRAN = 3;
-        /** @hide */
         public static final int CDMA2000 = 4;
+        public static final int IWLAN = 5;
     }
 
     /**
      * Frenquency bands for GERAN.
      * http://www.etsi.org/deliver/etsi_ts/145000_145099/145005/14.00.00_60/ts_145005v140000p.pdf
      */
-    public static final class GeranBands {
+    public static final class GeranBand {
         public static final int BAND_T380 = 1;
         public static final int BAND_T410 = 2;
         public static final int BAND_450 = 3;
@@ -54,7 +54,7 @@
      * Frenquency bands for UTRAN.
      * http://www.etsi.org/deliver/etsi_ts/125100_125199/125104/13.03.00_60/ts_125104v130p.pdf
      */
-    public static final class UtranBands {
+    public static final class UtranBand {
         public static final int BAND_1 = 1;
         public static final int BAND_2 = 2;
         public static final int BAND_3 = 3;
@@ -83,7 +83,7 @@
      * Frenquency bands for EUTRAN.
      * http://www.etsi.org/deliver/etsi_ts/136100_136199/136101/14.03.00_60/ts_136101v140p.pdf
      */
-    public static final class EutranBands {
+    public static final class EutranBand {
         public static final int BAND_1 = 1;
         public static final int BAND_2 = 2;
         public static final int BAND_3 = 3;
diff --git a/telephony/java/android/telephony/RadioAccessSpecifier.java b/telephony/java/android/telephony/RadioAccessSpecifier.java
index 5412c61..85a4ed8 100644
--- a/telephony/java/android/telephony/RadioAccessSpecifier.java
+++ b/telephony/java/android/telephony/RadioAccessSpecifier.java
@@ -33,7 +33,7 @@
      *
      * This parameter must be provided or else the scan will be rejected.
      *
-     * See {@link RadioNetworkConstants.RadioAccessNetworks} for details.
+     * See {@link AccessNetworkConstants.AccessNetworkType} for details.
      */
     private int mRadioAccessNetwork;
 
@@ -43,7 +43,7 @@
      * When no specific bands are specified (empty array or null), all the frequency bands
      * supported by the modem will be scanned.
      *
-     * See {@link RadioNetworkConstants} for details.
+     * See {@link AccessNetworkConstants} for details.
      */
     private int[] mBands;
 
@@ -56,7 +56,7 @@
      * When no specific channels are specified (empty array or null), all the frequency channels
      * supported by the modem will be scanned.
      *
-     * See {@link RadioNetworkConstants} for details.
+     * See {@link AccessNetworkConstants} for details.
      */
     private int[] mChannels;
 
@@ -79,7 +79,7 @@
     /**
      * Returns the radio access network that needs to be scanned.
      *
-     * The returned value is define in {@link RadioNetworkConstants.RadioAccessNetworks};
+     * The returned value is define in {@link AccessNetworkConstants.AccessNetworkType};
      */
     public int getRadioAccessNetwork() {
         return mRadioAccessNetwork;
@@ -88,8 +88,8 @@
     /**
      * Returns the frequency bands that need to be scanned.
      *
-     * The returned value is defined in either of {@link RadioNetworkConstants.GeranBands},
-     * {@link RadioNetworkConstants.UtranBands} and {@link RadioNetworkConstants.EutranBands}, and
+     * The returned value is defined in either of {@link AccessNetworkConstants.GeranBand},
+     * {@link AccessNetworkConstants.UtranBand} and {@link AccessNetworkConstants.EutranBand}, and
      * it depends on the returned value of {@link #getRadioAccessNetwork()}.
      */
     public int[] getBands() {
diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java
index a494799..2396a9e 100644
--- a/telephony/java/android/telephony/SubscriptionManager.java
+++ b/telephony/java/android/telephony/SubscriptionManager.java
@@ -548,7 +548,8 @@
      */
     @Deprecated
     public static SubscriptionManager from(Context context) {
-        return context.getSystemService(SubscriptionManager.class);
+        return (SubscriptionManager) context
+                .getSystemService(Context.TELEPHONY_SUBSCRIPTION_SERVICE);
     }
 
     /**
diff --git a/telephony/java/android/telephony/euicc/EuiccManager.java b/telephony/java/android/telephony/euicc/EuiccManager.java
index 176057d..9b114a8 100644
--- a/telephony/java/android/telephony/euicc/EuiccManager.java
+++ b/telephony/java/android/telephony/euicc/EuiccManager.java
@@ -251,8 +251,8 @@
      *
      * @return the status of eUICC OTA. If {@link #isEnabled()} is false or the eUICC is not ready,
      *     {@link OtaStatus#EUICC_OTA_STATUS_UNAVAILABLE} will be returned.
+     * TODO(b/35851809): Make this a SystemApi.
      */
-    @SystemApi
     public int getOtaStatus() {
         if (!isEnabled()) {
             return EUICC_OTA_STATUS_UNAVAILABLE;
diff --git a/test-base/Android.bp b/test-base/Android.bp
index 30c9af1..0088962 100644
--- a/test-base/Android.bp
+++ b/test-base/Android.bp
@@ -63,6 +63,26 @@
     jarjar_rules: "jarjar-rules.txt",
 }
 
+// Build the android.test.base-minus-junit library
+// ===============================================
+// This contains the android.test classes from android.test.base plus
+// the com.android.internal.util.Predicate[s] classes. This is only
+// intended for inclusion in the android.test.legacy static library and
+// must not be used elsewhere.
+java_library_static {
+    name: "android.test.base-minus-junit",
+
+    srcs: [
+        "src/android/**/*.java",
+        "src/com/**/*.java",
+    ],
+
+    sdk_version: "current",
+    libs: [
+        "junit",
+    ],
+}
+
 // Build the legacy-android-test library
 // =====================================
 // This contains the android.test classes that were in Android API level 25,
diff --git a/test-mock/api/android-test-mock-current.txt b/test-mock/api/android-test-mock-current.txt
index 48a1f80..07acfef 100644
--- a/test-mock/api/android-test-mock-current.txt
+++ b/test-mock/api/android-test-mock-current.txt
@@ -1,5 +1,9 @@
 package android.test.mock {
 
+  public deprecated class MockAccountManager {
+    method public static android.accounts.AccountManager newMockAccountManager(android.content.Context);
+  }
+
   public deprecated class MockApplication extends android.app.Application {
     ctor public MockApplication();
   }
@@ -9,6 +13,7 @@
     ctor public MockContentProvider(android.content.Context);
     ctor public MockContentProvider(android.content.Context, java.lang.String, java.lang.String, android.content.pm.PathPermission[]);
     method public android.content.ContentProviderResult[] applyBatch(java.util.ArrayList<android.content.ContentProviderOperation>);
+    method public static deprecated void attachInfoForTesting(android.content.ContentProvider, android.content.Context, android.content.pm.ProviderInfo);
     method public int delete(android.net.Uri, java.lang.String, java.lang.String[]);
     method public java.lang.String getType(android.net.Uri);
     method public android.net.Uri insert(android.net.Uri, android.content.ContentValues);
@@ -289,5 +294,9 @@
     method public void updateConfiguration(android.content.res.Configuration, android.util.DisplayMetrics);
   }
 
+  public deprecated class MockService {
+    method public static <T extends android.app.Service> void attachForTesting(android.app.Service, android.content.Context, java.lang.String, android.app.Application);
+  }
+
 }
 
diff --git a/test-mock/src/android/test/mock/MockAccountManager.java b/test-mock/src/android/test/mock/MockAccountManager.java
new file mode 100644
index 0000000..c9b4c7b
--- /dev/null
+++ b/test-mock/src/android/test/mock/MockAccountManager.java
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.accounts.Account;
+import android.accounts.AccountManager;
+import android.accounts.AccountManagerCallback;
+import android.accounts.AccountManagerFuture;
+import android.accounts.AuthenticatorException;
+import android.accounts.OnAccountsUpdateListener;
+import android.accounts.OperationCanceledException;
+import android.content.Context;
+import android.os.Handler;
+import java.io.IOException;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * A mock {@link android.accounts.AccountManager} class.
+ *
+ * <p>Provided for use by {@code android.test.IsolatedContext}.
+ *
+ * @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 MockAccountManager {
+
+    /**
+     * Create a new mock {@link AccountManager} instance.
+     *
+     * @param context the {@link Context} to which the returned object belongs.
+     * @return the new instance.
+     */
+    public static AccountManager newMockAccountManager(Context context) {
+        return new MockAccountManagerImpl(context);
+    }
+
+    private MockAccountManager() {
+    }
+
+    private static class MockAccountManagerImpl extends AccountManager {
+
+        MockAccountManagerImpl(Context context) {
+            super(context, null /* IAccountManager */, null /* handler */);
+        }
+
+        public void addOnAccountsUpdatedListener(OnAccountsUpdateListener listener,
+                Handler handler, boolean updateImmediately) {
+            // do nothing
+        }
+
+        public Account[] getAccounts() {
+            return new Account[] {};
+        }
+
+        public AccountManagerFuture<Account[]> getAccountsByTypeAndFeatures(
+                final String type, final String[] features,
+                AccountManagerCallback<Account[]> callback, Handler handler) {
+            return new MockAccountManagerFuture<Account[]>(new Account[0]);
+        }
+
+        public String blockingGetAuthToken(Account account, String authTokenType,
+                boolean notifyAuthFailure)
+                throws OperationCanceledException, IOException, AuthenticatorException {
+            return null;
+        }
+    }
+
+    /**
+     * A very simple AccountManagerFuture class
+     * that returns what ever was passed in
+     */
+    private static class MockAccountManagerFuture<T>
+            implements AccountManagerFuture<T> {
+
+        T mResult;
+
+        MockAccountManagerFuture(T result) {
+            mResult = result;
+        }
+
+        public boolean cancel(boolean mayInterruptIfRunning) {
+            return false;
+        }
+
+        public boolean isCancelled() {
+            return false;
+        }
+
+        public boolean isDone() {
+            return true;
+        }
+
+        public T getResult()
+                throws OperationCanceledException, IOException, AuthenticatorException {
+            return mResult;
+        }
+
+        public T getResult(long timeout, TimeUnit unit)
+                throws OperationCanceledException, IOException, AuthenticatorException {
+            return getResult();
+        }
+    }
+}
diff --git a/test-mock/src/android/test/mock/MockContentProvider.java b/test-mock/src/android/test/mock/MockContentProvider.java
index d5f3ce8..b917fbd 100644
--- a/test-mock/src/android/test/mock/MockContentProvider.java
+++ b/test-mock/src/android/test/mock/MockContentProvider.java
@@ -277,4 +277,21 @@
     public final IContentProvider getIContentProvider() {
         return mIContentProvider;
     }
+
+    /**
+     * Like {@link #attachInfo(Context, android.content.pm.ProviderInfo)}, but for use
+     * when directly instantiating the provider for testing.
+     *
+     * <p>Provided for use by {@code android.test.ProviderTestCase2} and
+     * {@code android.test.RenamingDelegatingContext}.
+     *
+     * @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 static void attachInfoForTesting(
+            ContentProvider provider, Context context, ProviderInfo providerInfo) {
+        provider.attachInfoForTesting(context, providerInfo);
+    }
 }
diff --git a/test-mock/src/android/test/mock/MockService.java b/test-mock/src/android/test/mock/MockService.java
new file mode 100644
index 0000000..dbba4f3
--- /dev/null
+++ b/test-mock/src/android/test/mock/MockService.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.app.Application;
+import android.app.Service;
+import android.content.Context;
+
+/**
+ * A mock {@link android.app.Service} class.
+ *
+ * <p>Provided for use by {@code android.test.ServiceTestCase}.
+ *
+ * @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 MockService {
+
+    public static <T extends Service> void attachForTesting(Service service, Context context,
+            String serviceClassName,
+            Application application) {
+        service.attach(
+                context,
+                null,               // ActivityThread not actually used in Service
+                serviceClassName,
+                null,               // token not needed when not talking with the activity manager
+                application,
+                null                // mocked services don't talk with the activity manager
+        );
+    }
+
+    private MockService() {
+    }
+}
diff --git a/test-runner/Android.mk b/test-runner/Android.mk
index 706f636..f5c2bc6 100644
--- a/test-runner/Android.mk
+++ b/test-runner/Android.mk
@@ -117,5 +117,20 @@
 
 endif  # not TARGET_BUILD_APPS not TARGET_BUILD_PDK=true
 
+# Build the android.test.legacy library
+# =====================================
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := android.test.legacy
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src/android)
+
+LOCAL_SDK_VERSION := current
+
+LOCAL_JAVA_LIBRARIES := android.test.mock.stubs junit
+LOCAL_STATIC_JAVA_LIBRARIES := android.test.base-minus-junit
+
+include $(BUILD_STATIC_JAVA_LIBRARY)
+
 # additionally, build unit tests in a separate .apk
 include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/test-runner/src/android/test/IsolatedContext.java b/test-runner/src/android/test/IsolatedContext.java
index 0b77c00..6e4c41e 100644
--- a/test-runner/src/android/test/IsolatedContext.java
+++ b/test-runner/src/android/test/IsolatedContext.java
@@ -17,12 +17,6 @@
 package android.test;
 
 import android.accounts.AccountManager;
-import android.accounts.AccountManagerCallback;
-import android.accounts.AccountManagerFuture;
-import android.accounts.AuthenticatorException;
-import android.accounts.OnAccountsUpdateListener;
-import android.accounts.OperationCanceledException;
-import android.accounts.Account;
 import android.content.ContextWrapper;
 import android.content.ContentResolver;
 import android.content.Intent;
@@ -32,12 +26,10 @@
 import android.content.IntentFilter;
 import android.content.pm.PackageManager;
 import android.net.Uri;
-import android.os.Handler;
+import android.test.mock.MockAccountManager;
 
 import java.io.File;
-import java.io.IOException;
 import java.util.ArrayList;
-import java.util.concurrent.TimeUnit;
 import java.util.List;
 
 
@@ -52,7 +44,7 @@
 public class IsolatedContext extends ContextWrapper {
 
     private ContentResolver mResolver;
-    private final MockAccountManager mMockAccountManager;
+    private final AccountManager mMockAccountManager;
 
     private List<Intent> mBroadcastIntents = new ArrayList<>();
 
@@ -60,7 +52,7 @@
             ContentResolver resolver, Context targetContext) {
         super(targetContext);
         mResolver = resolver;
-        mMockAccountManager = new MockAccountManager();
+        mMockAccountManager = MockAccountManager.newMockAccountManager(IsolatedContext.this);
     }
 
     /** Returns the list of intents that were broadcast since the last call to this method. */
@@ -123,71 +115,6 @@
         return null;
     }
 
-    private class MockAccountManager extends AccountManager {
-        public MockAccountManager() {
-            super(IsolatedContext.this, null /* IAccountManager */, null /* handler */);
-        }
-
-        public void addOnAccountsUpdatedListener(OnAccountsUpdateListener listener,
-                Handler handler, boolean updateImmediately) {
-            // do nothing
-        }
-
-        public Account[] getAccounts() {
-            return new Account[]{};
-        }
-
-        public AccountManagerFuture<Account[]> getAccountsByTypeAndFeatures(
-                final String type, final String[] features,
-                AccountManagerCallback<Account[]> callback, Handler handler) {
-            return new MockAccountManagerFuture<Account[]>(new Account[0]);
-        }
-
-        public String blockingGetAuthToken(Account account, String authTokenType,
-                boolean notifyAuthFailure)
-                throws OperationCanceledException, IOException, AuthenticatorException {
-            return null;
-        }
-
-
-        /**
-         * A very simple AccountManagerFuture class
-         * that returns what ever was passed in
-         */
-        private class MockAccountManagerFuture<T>
-                implements AccountManagerFuture<T> {
-
-            T mResult;
-
-            public MockAccountManagerFuture(T result) {
-                mResult = result;
-            }
-
-            public boolean cancel(boolean mayInterruptIfRunning) {
-                return false;
-            }
-
-            public boolean isCancelled() {
-                return false;
-            }
-
-            public boolean isDone() {
-                return true;
-            }
-
-            public T getResult()
-                    throws OperationCanceledException, IOException, AuthenticatorException {
-                return mResult;
-            }
-
-            public T getResult(long timeout, TimeUnit unit)
-                    throws OperationCanceledException, IOException, AuthenticatorException {
-                return getResult();
-            }
-        }
-
-    }
-
     @Override
     public File getFilesDir() {
         return new File("/dev/null");
diff --git a/test-runner/src/android/test/ProviderTestCase2.java b/test-runner/src/android/test/ProviderTestCase2.java
index 1fa633e..be18b53 100644
--- a/test-runner/src/android/test/ProviderTestCase2.java
+++ b/test-runner/src/android/test/ProviderTestCase2.java
@@ -21,6 +21,7 @@
 import android.content.Context;
 import android.content.pm.ProviderInfo;
 import android.content.res.Resources;
+import android.test.mock.MockContentProvider;
 import android.test.mock.MockContext;
 import android.test.mock.MockContentResolver;
 import android.database.DatabaseUtils;
@@ -152,7 +153,7 @@
         T instance = providerClass.newInstance();
         ProviderInfo providerInfo = new ProviderInfo();
         providerInfo.authority = authority;
-        instance.attachInfoForTesting(context, providerInfo);
+        MockContentProvider.attachInfoForTesting(instance, context, providerInfo);
         return instance;
     }
 
diff --git a/test-runner/src/android/test/RenamingDelegatingContext.java b/test-runner/src/android/test/RenamingDelegatingContext.java
index fd33321..10ccebc 100644
--- a/test-runner/src/android/test/RenamingDelegatingContext.java
+++ b/test-runner/src/android/test/RenamingDelegatingContext.java
@@ -21,6 +21,7 @@
 import android.content.ContentProvider;
 import android.database.DatabaseErrorHandler;
 import android.database.sqlite.SQLiteDatabase;
+import android.test.mock.MockContentProvider;
 import android.util.Log;
 
 import java.io.File;
@@ -71,7 +72,7 @@
         if (allowAccessToExistingFilesAndDbs) {
             mContext.makeExistingFilesAndDbsAccessible();
         }
-        mProvider.attachInfoForTesting(mContext, null);
+        MockContentProvider.attachInfoForTesting(mProvider, mContext, null);
         return mProvider;
     }
 
diff --git a/test-runner/src/android/test/ServiceTestCase.java b/test-runner/src/android/test/ServiceTestCase.java
index c8ff0f9..cd54955 100644
--- a/test-runner/src/android/test/ServiceTestCase.java
+++ b/test-runner/src/android/test/ServiceTestCase.java
@@ -23,6 +23,7 @@
 import android.os.IBinder;
 import android.test.mock.MockApplication;
 
+import android.test.mock.MockService;
 import java.util.Random;
 
 /**
@@ -163,14 +164,8 @@
         if (getApplication() == null) {
             setApplication(new MockApplication());
         }
-        mService.attach(
-                getContext(),
-                null,               // ActivityThread not actually used in Service
-                mServiceClass.getName(),
-                null,               // token not needed when not talking with the activity manager
-                getApplication(),
-                null                // mocked services don't talk with the activity manager
-                );
+        MockService.attachForTesting(
+                mService, getContext(), mServiceClass.getName(), getApplication());
 
         assertNotNull(mService);
 
diff --git a/tests/net/java/com/android/server/IpSecServiceParameterizedTest.java b/tests/net/java/com/android/server/IpSecServiceParameterizedTest.java
index 80e42a3..2282c13 100644
--- a/tests/net/java/com/android/server/IpSecServiceParameterizedTest.java
+++ b/tests/net/java/com/android/server/IpSecServiceParameterizedTest.java
@@ -348,64 +348,6 @@
     }
 
     @Test
-    public void testCreateInvalidConfigAeadWithAuth() throws Exception {
-        IpSecConfig ipSecConfig = new IpSecConfig();
-        addDefaultSpisAndRemoteAddrToIpSecConfig(ipSecConfig);
-
-        for (int direction : DIRECTIONS) {
-            ipSecConfig.setAuthentication(direction, AUTH_ALGO);
-            ipSecConfig.setAuthenticatedEncryption(direction, AEAD_ALGO);
-        }
-
-        try {
-            mIpSecService.createTransportModeTransform(ipSecConfig, new Binder());
-            fail(
-                    "IpSecService should have thrown an error on authentication being"
-                            + " enabled with authenticated encryption");
-        } catch (IllegalArgumentException expected) {
-        }
-    }
-
-    @Test
-    public void testCreateInvalidConfigAeadWithCrypt() throws Exception {
-        IpSecConfig ipSecConfig = new IpSecConfig();
-        addDefaultSpisAndRemoteAddrToIpSecConfig(ipSecConfig);
-
-        for (int direction : DIRECTIONS) {
-            ipSecConfig.setEncryption(direction, CRYPT_ALGO);
-            ipSecConfig.setAuthenticatedEncryption(direction, AEAD_ALGO);
-        }
-
-        try {
-            mIpSecService.createTransportModeTransform(ipSecConfig, new Binder());
-            fail(
-                    "IpSecService should have thrown an error on encryption being"
-                            + " enabled with authenticated encryption");
-        } catch (IllegalArgumentException expected) {
-        }
-    }
-
-    @Test
-    public void testCreateInvalidConfigAeadWithAuthAndCrypt() throws Exception {
-        IpSecConfig ipSecConfig = new IpSecConfig();
-        addDefaultSpisAndRemoteAddrToIpSecConfig(ipSecConfig);
-
-        for (int direction : DIRECTIONS) {
-            ipSecConfig.setAuthentication(direction, AUTH_ALGO);
-            ipSecConfig.setEncryption(direction, CRYPT_ALGO);
-            ipSecConfig.setAuthenticatedEncryption(direction, AEAD_ALGO);
-        }
-
-        try {
-            mIpSecService.createTransportModeTransform(ipSecConfig, new Binder());
-            fail(
-                    "IpSecService should have thrown an error on authentication and encryption being"
-                            + " enabled with authenticated encryption");
-        } catch (IllegalArgumentException expected) {
-        }
-    }
-
-    @Test
     public void testDeleteTransportModeTransform() throws Exception {
         IpSecConfig ipSecConfig = new IpSecConfig();
         addDefaultSpisAndRemoteAddrToIpSecConfig(ipSecConfig);
diff --git a/tests/net/java/com/android/server/IpSecServiceTest.java b/tests/net/java/com/android/server/IpSecServiceTest.java
index 5d1e10e..0467989 100644
--- a/tests/net/java/com/android/server/IpSecServiceTest.java
+++ b/tests/net/java/com/android/server/IpSecServiceTest.java
@@ -35,6 +35,8 @@
 
 import android.content.Context;
 import android.net.INetd;
+import android.net.IpSecAlgorithm;
+import android.net.IpSecConfig;
 import android.net.IpSecManager;
 import android.net.IpSecSpiResponse;
 import android.net.IpSecTransform;
@@ -76,6 +78,36 @@
 
     private static final InetAddress INADDR_ANY;
 
+    private static final byte[] AEAD_KEY = {
+        0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+        0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
+        0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
+        0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F,
+        0x73, 0x61, 0x6C, 0x74
+    };
+    private static final byte[] CRYPT_KEY = {
+        0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+        0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
+        0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
+        0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F
+    };
+    private static final byte[] AUTH_KEY = {
+        0x7A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F,
+        0x7A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F
+    };
+
+    private static final IpSecAlgorithm AUTH_ALGO =
+            new IpSecAlgorithm(IpSecAlgorithm.AUTH_HMAC_SHA256, AUTH_KEY, AUTH_KEY.length * 4);
+    private static final IpSecAlgorithm CRYPT_ALGO =
+            new IpSecAlgorithm(IpSecAlgorithm.CRYPT_AES_CBC, CRYPT_KEY);
+    private static final IpSecAlgorithm AEAD_ALGO =
+            new IpSecAlgorithm(IpSecAlgorithm.AUTH_CRYPT_AES_GCM, AEAD_KEY, 128);
+
+    private static final int[] DIRECTIONS =
+            new int[] {IpSecTransform.DIRECTION_IN, IpSecTransform.DIRECTION_OUT};
+
     static {
         try {
             INADDR_ANY = InetAddress.getByAddress(new byte[] {0, 0, 0, 0});
@@ -270,6 +302,127 @@
     }
 
     @Test
+    public void testValidateAlgorithmsAuth() {
+        for (int direction : DIRECTIONS) {
+            // Validate that correct algorithm type succeeds
+            IpSecConfig config = new IpSecConfig();
+            config.setAuthentication(direction, AUTH_ALGO);
+            mIpSecService.validateAlgorithms(config, direction);
+
+            // Validate that incorrect algorithm types fails
+            for (IpSecAlgorithm algo : new IpSecAlgorithm[] {CRYPT_ALGO, AEAD_ALGO}) {
+                try {
+                    config = new IpSecConfig();
+                    config.setAuthentication(direction, algo);
+                    mIpSecService.validateAlgorithms(config, direction);
+                    fail("Did not throw exception on invalid algorithm type");
+                } catch (IllegalArgumentException expected) {
+                }
+            }
+        }
+    }
+
+    @Test
+    public void testValidateAlgorithmsCrypt() {
+        for (int direction : DIRECTIONS) {
+            // Validate that correct algorithm type succeeds
+            IpSecConfig config = new IpSecConfig();
+            config.setEncryption(direction, CRYPT_ALGO);
+            mIpSecService.validateAlgorithms(config, direction);
+
+            // Validate that incorrect algorithm types fails
+            for (IpSecAlgorithm algo : new IpSecAlgorithm[] {AUTH_ALGO, AEAD_ALGO}) {
+                try {
+                    config = new IpSecConfig();
+                    config.setEncryption(direction, algo);
+                    mIpSecService.validateAlgorithms(config, direction);
+                    fail("Did not throw exception on invalid algorithm type");
+                } catch (IllegalArgumentException expected) {
+                }
+            }
+        }
+    }
+
+    @Test
+    public void testValidateAlgorithmsAead() {
+        for (int direction : DIRECTIONS) {
+            // Validate that correct algorithm type succeeds
+            IpSecConfig config = new IpSecConfig();
+            config.setAuthenticatedEncryption(direction, AEAD_ALGO);
+            mIpSecService.validateAlgorithms(config, direction);
+
+            // Validate that incorrect algorithm types fails
+            for (IpSecAlgorithm algo : new IpSecAlgorithm[] {AUTH_ALGO, CRYPT_ALGO}) {
+                try {
+                    config = new IpSecConfig();
+                    config.setAuthenticatedEncryption(direction, algo);
+                    mIpSecService.validateAlgorithms(config, direction);
+                    fail("Did not throw exception on invalid algorithm type");
+                } catch (IllegalArgumentException expected) {
+                }
+            }
+        }
+    }
+
+    @Test
+    public void testValidateAlgorithmsAuthCrypt() {
+        for (int direction : DIRECTIONS) {
+            // Validate that correct algorithm type succeeds
+            IpSecConfig config = new IpSecConfig();
+            config.setAuthentication(direction, AUTH_ALGO);
+            config.setEncryption(direction, CRYPT_ALGO);
+            mIpSecService.validateAlgorithms(config, direction);
+        }
+    }
+
+    @Test
+    public void testValidateAlgorithmsNoAlgorithms() {
+        IpSecConfig config = new IpSecConfig();
+        try {
+            mIpSecService.validateAlgorithms(config, IpSecTransform.DIRECTION_IN);
+            fail("Expected exception; no algorithms specified");
+        } catch (IllegalArgumentException expected) {
+        }
+    }
+
+    @Test
+    public void testValidateAlgorithmsAeadWithAuth() {
+        IpSecConfig config = new IpSecConfig();
+        config.setAuthenticatedEncryption(IpSecTransform.DIRECTION_IN, AEAD_ALGO);
+        config.setAuthentication(IpSecTransform.DIRECTION_IN, AUTH_ALGO);
+        try {
+            mIpSecService.validateAlgorithms(config, IpSecTransform.DIRECTION_IN);
+            fail("Expected exception; both AEAD and auth algorithm specified");
+        } catch (IllegalArgumentException expected) {
+        }
+    }
+
+    @Test
+    public void testValidateAlgorithmsAeadWithCrypt() {
+        IpSecConfig config = new IpSecConfig();
+        config.setAuthenticatedEncryption(IpSecTransform.DIRECTION_IN, AEAD_ALGO);
+        config.setEncryption(IpSecTransform.DIRECTION_IN, CRYPT_ALGO);
+        try {
+            mIpSecService.validateAlgorithms(config, IpSecTransform.DIRECTION_IN);
+            fail("Expected exception; both AEAD and crypt algorithm specified");
+        } catch (IllegalArgumentException expected) {
+        }
+    }
+
+    @Test
+    public void testValidateAlgorithmsAeadWithAuthAndCrypt() {
+        IpSecConfig config = new IpSecConfig();
+        config.setAuthenticatedEncryption(IpSecTransform.DIRECTION_IN, AEAD_ALGO);
+        config.setAuthentication(IpSecTransform.DIRECTION_IN, AUTH_ALGO);
+        config.setEncryption(IpSecTransform.DIRECTION_IN, CRYPT_ALGO);
+        try {
+            mIpSecService.validateAlgorithms(config, IpSecTransform.DIRECTION_IN);
+            fail("Expected exception; AEAD, auth and crypt algorithm specified");
+        } catch (IllegalArgumentException expected) {
+        }
+    }
+
+    @Test
     public void testDeleteInvalidTransportModeTransform() throws Exception {
         try {
             mIpSecService.deleteTransportModeTransform(1);
diff --git a/tools/stats_log_api_gen/main.cpp b/tools/stats_log_api_gen/main.cpp
index 89749fb..bbe6d63 100644
--- a/tools/stats_log_api_gen/main.cpp
+++ b/tools/stats_log_api_gen/main.cpp
@@ -166,7 +166,15 @@
                     attributionDecl.fields.front().name.c_str());
                 fprintf(out, "        event.begin();\n");
                 for (const auto &chainField : attributionDecl.fields) {
-                    fprintf(out, "        event << %s[i];\n", chainField.name.c_str());
+                    if (chainField.javaType == JAVA_TYPE_STRING) {
+                        fprintf(out, "        if (%s[i] != NULL) {\n", chainField.name.c_str());
+                        fprintf(out, "           event << %s[i];\n", chainField.name.c_str());
+                        fprintf(out, "        } else {\n");
+                        fprintf(out, "           event << \"\";\n");
+                        fprintf(out, "        }\n");
+                    } else {
+                        fprintf(out, "        event << %s[i];\n", chainField.name.c_str());
+                    }
                 }
                 fprintf(out, "        event.end();\n");
                 fprintf(out, "    }\n");
@@ -589,13 +597,18 @@
                         fprintf(out, "        jstring jstr = "
                             "(jstring)env->GetObjectArrayElement(%s, i);\n",
                              chainField.name.c_str());
-                        fprintf(out, "        ScopedUtfChars* scoped_%s = "
+                        fprintf(out, "        if (jstr == NULL) {\n");
+                        fprintf(out, "            %s_vec.push_back(NULL);\n",
+                            chainField.name.c_str());
+                        fprintf(out, "        } else {\n");
+                        fprintf(out, "            ScopedUtfChars* scoped_%s = "
                             "new ScopedUtfChars(env, jstr);\n",
                              chainField.name.c_str());
-                        fprintf(out, "        %s_vec.push_back(scoped_%s->c_str());\n",
+                        fprintf(out, "            %s_vec.push_back(scoped_%s->c_str());\n",
                                 chainField.name.c_str(), chainField.name.c_str());
-                        fprintf(out, "        scoped_%s_vec.push_back(scoped_%s);\n",
+                        fprintf(out, "            scoped_%s_vec.push_back(scoped_%s);\n",
                                 chainField.name.c_str(), chainField.name.c_str());
+                        fprintf(out, "        }\n");
                         fprintf(out, "    }\n");
                     }
                     fprintf(out, "\n");
@@ -648,7 +661,7 @@
                         fprintf(out, "    env->ReleaseIntArrayElements(%s, %s_array, 0);\n",
                             chainField.name.c_str(), chainField.name.c_str());
                     } else if (chainField.javaType == JAVA_TYPE_STRING) {
-                        fprintf(out, "    for (size_t i = 0; i < %s_length; ++i) {\n",
+                        fprintf(out, "    for (size_t i = 0; i < scoped_%s_vec.size(); ++i) {\n",
                             chainField.name.c_str());
                         fprintf(out, "        delete scoped_%s_vec[i];\n", chainField.name.c_str());
                         fprintf(out, "    }\n");
diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java
index 3b118e7..267fd6f 100644
--- a/wifi/java/android/net/wifi/WifiManager.java
+++ b/wifi/java/android/net/wifi/WifiManager.java
@@ -1618,53 +1618,6 @@
     }
 
     /**
-     * startLocationRestrictedScan()
-     * Trigger a scan which will not make use of DFS channels and is thus not suitable for
-     * establishing wifi connection.
-     * @deprecated This API is nolonger supported.
-     * Use {@link android.net.wifi.WifiScanner} API
-     * @hide
-     * @removed
-     */
-    @Deprecated
-    @SystemApi
-    @SuppressLint("Doclava125")
-    public boolean startLocationRestrictedScan(WorkSource workSource) {
-        return false;
-    }
-
-    /**
-     * Check if the Batched Scan feature is supported.
-     *
-     * @return false if not supported.
-     * @deprecated This API is nolonger supported.
-     * Use {@link android.net.wifi.WifiScanner} API
-     * @hide
-     * @removed
-     */
-    @Deprecated
-    @SystemApi
-    @SuppressLint("Doclava125")
-    public boolean isBatchedScanSupported() {
-        return false;
-    }
-
-    /**
-     * Retrieve the latest batched scan result.  This should be called immediately after
-     * {@link BATCHED_SCAN_RESULTS_AVAILABLE_ACTION} is received.
-     * @deprecated This API is nolonger supported.
-     * Use {@link android.net.wifi.WifiScanner} API
-     * @hide
-     * @removed
-     */
-    @Deprecated
-    @SystemApi
-    @SuppressLint("Doclava125")
-    public List<BatchedScanResult> getBatchedScanResults() {
-        return null;
-    }
-
-    /**
      * Creates a configuration token describing the current network of MIME type
      * application/vnd.wfa.wsc. Can be used to configure WiFi networks via NFC.
      *
diff --git a/wifi/java/android/net/wifi/rtt/RangingRequest.java b/wifi/java/android/net/wifi/rtt/RangingRequest.java
index b4e3097..32f21b9 100644
--- a/wifi/java/android/net/wifi/rtt/RangingRequest.java
+++ b/wifi/java/android/net/wifi/rtt/RangingRequest.java
@@ -17,6 +17,7 @@
 package android.net.wifi.rtt;
 
 import android.annotation.NonNull;
+import android.annotation.SystemApi;
 import android.net.MacAddress;
 import android.net.wifi.ScanResult;
 import android.net.wifi.aware.AttachCallback;
@@ -41,8 +42,6 @@
  * The ranging request is a batch request - specifying a set of devices (specified using
  * {@link RangingRequest.Builder#addAccessPoint(ScanResult)} and
  * {@link RangingRequest.Builder#addAccessPoints(List)}).
- *
- * @hide RTT_API
  */
 public final class RangingRequest implements Parcelable {
     private static final int MAX_PEERS = 10;
@@ -198,7 +197,7 @@
             return addResponder(ResponderConfig.fromWifiAwarePeerHandleWithDefaults(peerHandle));
         }
 
-        /*
+        /**
          * Add the Responder device specified by the {@link ResponderConfig} to the list of devices
          * with which to measure range. The total number of peers added to the request cannot exceed
          * the limit specified by {@link #getMaxPeers()}.
@@ -206,8 +205,9 @@
          * @param responder Information on the RTT Responder.
          * @return The builder, to facilitate chaining {@code builder.setXXX(..).setXXX(..)}.
          *
-         * @hide (SystemApi)
+         * @hide
          */
+        @SystemApi
         public Builder addResponder(@NonNull ResponderConfig responder) {
             if (responder == null) {
                 throw new IllegalArgumentException("Null Responder!");
diff --git a/wifi/java/android/net/wifi/rtt/RangingResult.java b/wifi/java/android/net/wifi/rtt/RangingResult.java
index a380fae..d5ca8f7 100644
--- a/wifi/java/android/net/wifi/rtt/RangingResult.java
+++ b/wifi/java/android/net/wifi/rtt/RangingResult.java
@@ -18,6 +18,7 @@
 
 import android.annotation.IntDef;
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.net.MacAddress;
 import android.net.wifi.aware.PeerHandle;
 import android.os.Handler;
@@ -36,8 +37,6 @@
  * <p>
  * A ranging result is the distance measurement result for a single device specified in the
  * {@link RangingRequest}.
- *
- * @hide RTT_API
  */
 public final class RangingResult implements Parcelable {
     private static final String TAG = "RangingResult";
@@ -108,6 +107,7 @@
      * Will return a {@code null} for results corresponding to requests issued using a {@code
      * PeerHandle}, i.e. using the {@link RangingRequest.Builder#addWifiAwarePeer(PeerHandle)} API.
      */
+    @Nullable
     public MacAddress getMacAddress() {
         return mMac;
     }
@@ -119,7 +119,7 @@
      * <p>
      * Will return a {@code null} for results corresponding to requests issued using a MAC address.
      */
-    public PeerHandle getPeerHandle() {
+    @Nullable public PeerHandle getPeerHandle() {
         return mPeerHandle;
     }
 
@@ -182,13 +182,11 @@
         return mTimestamp;
     }
 
-    /** @hide */
     @Override
     public int describeContents() {
         return 0;
     }
 
-    /** @hide */
     @Override
     public void writeToParcel(Parcel dest, int flags) {
         dest.writeInt(mStatus);
@@ -210,7 +208,6 @@
         dest.writeLong(mTimestamp);
     }
 
-    /** @hide */
     public static final Creator<RangingResult> CREATOR = new Creator<RangingResult>() {
         @Override
         public RangingResult[] newArray(int size) {
diff --git a/wifi/java/android/net/wifi/rtt/RangingResultCallback.java b/wifi/java/android/net/wifi/rtt/RangingResultCallback.java
index c8aea3c..9639dc8 100644
--- a/wifi/java/android/net/wifi/rtt/RangingResultCallback.java
+++ b/wifi/java/android/net/wifi/rtt/RangingResultCallback.java
@@ -17,6 +17,7 @@
 package android.net.wifi.rtt;
 
 import android.annotation.IntDef;
+import android.annotation.NonNull;
 import android.os.Handler;
 
 import java.lang.annotation.Retention;
@@ -31,8 +32,6 @@
  * peers then the {@link #onRangingResults(List)} will be called with the set of results (@link
  * {@link RangingResult}, each of which has its own success/failure code
  * {@link RangingResult#getStatus()}.
- *
- * @hide RTT_API
  */
 public abstract class RangingResultCallback {
     /** @hide */
@@ -68,5 +67,5 @@
      *
      * @param results List of range measurements, one per requested device.
      */
-    public abstract void onRangingResults(List<RangingResult> results);
+    public abstract void onRangingResults(@NonNull List<RangingResult> results);
 }
diff --git a/wifi/java/android/net/wifi/rtt/ResponderConfig.java b/wifi/java/android/net/wifi/rtt/ResponderConfig.java
index c3e1007..fb723c5 100644
--- a/wifi/java/android/net/wifi/rtt/ResponderConfig.java
+++ b/wifi/java/android/net/wifi/rtt/ResponderConfig.java
@@ -18,6 +18,7 @@
 
 import android.annotation.IntDef;
 import android.annotation.NonNull;
+import android.annotation.SystemApi;
 import android.net.MacAddress;
 import android.net.wifi.ScanResult;
 import android.net.wifi.aware.PeerHandle;
@@ -35,8 +36,9 @@
  * A Responder configuration may be constructed from a {@link ScanResult} or manually (with the
  * data obtained out-of-band from a peer).
  *
- * @hide (@SystemApi)
+ * @hide
  */
+@SystemApi
 public final class ResponderConfig implements Parcelable {
     private static final int AWARE_BAND_2_DISCOVERY_CHANNEL = 2437;
 
@@ -290,7 +292,7 @@
         MacAddress macAddress = MacAddress.fromString(scanResult.BSSID);
         int responderType = RESPONDER_AP;
         boolean supports80211mc = scanResult.is80211mcResponder();
-        int channelWidth = translcateScanResultChannelWidth(scanResult.channelWidth);
+        int channelWidth = translateScanResultChannelWidth(scanResult.channelWidth);
         int frequency = scanResult.frequency;
         int centerFreq0 = scanResult.centerFreq0;
         int centerFreq1 = scanResult.centerFreq1;
@@ -454,7 +456,7 @@
     }
 
     /** @hide */
-    static int translcateScanResultChannelWidth(int scanResultChannelWidth) {
+    static int translateScanResultChannelWidth(int scanResultChannelWidth) {
         switch (scanResultChannelWidth) {
             case ScanResult.CHANNEL_WIDTH_20MHZ:
                 return CHANNEL_WIDTH_20MHZ;
@@ -468,7 +470,7 @@
                 return CHANNEL_WIDTH_80MHZ_PLUS_MHZ;
             default:
                 throw new IllegalArgumentException(
-                        "translcateScanResultChannelWidth: bad " + scanResultChannelWidth);
+                        "translateScanResultChannelWidth: bad " + scanResultChannelWidth);
         }
     }
 }
diff --git a/wifi/java/android/net/wifi/rtt/WifiRttManager.java b/wifi/java/android/net/wifi/rtt/WifiRttManager.java
index 240b3c1..ec6c46e 100644
--- a/wifi/java/android/net/wifi/rtt/WifiRttManager.java
+++ b/wifi/java/android/net/wifi/rtt/WifiRttManager.java
@@ -1,3 +1,19 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
 package android.net.wifi.rtt;
 
 import static android.Manifest.permission.ACCESS_COARSE_LOCATION;
@@ -5,6 +21,7 @@
 import static android.Manifest.permission.CHANGE_WIFI_STATE;
 import static android.Manifest.permission.LOCATION_HARDWARE;
 
+import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.RequiresPermission;
 import android.annotation.SdkConstant;
@@ -38,8 +55,6 @@
  *     changes in RTT usability register for the {@link #ACTION_WIFI_RTT_STATE_CHANGED}
  *     broadcast. Note that this broadcast is not sticky - you should register for it and then
  *     check the above API to avoid a race condition.
- *
- * @hide RTT_API
  */
 @SystemService(Context.WIFI_RTT_RANGING_SERVICE)
 public class WifiRttManager {
@@ -71,6 +86,8 @@
      * Returns the current status of RTT API: whether or not RTT is available. To track
      * changes in the state of RTT API register for the
      * {@link #ACTION_WIFI_RTT_STATE_CHANGED} broadcast.
+     * <p>Note: availability of RTT does not mean that the app can use the API. The app's
+     * permissions and platform Location Mode are validated at run-time.
      *
      * @return A boolean indicating whether the app can use the RTT API at this time (true) or
      * not (false).
@@ -95,8 +112,8 @@
      *                 will be used.
      */
     @RequiresPermission(allOf = {ACCESS_COARSE_LOCATION, CHANGE_WIFI_STATE, ACCESS_WIFI_STATE})
-    public void startRanging(RangingRequest request, RangingResultCallback callback,
-            @Nullable Handler handler) {
+    public void startRanging(@NonNull RangingRequest request,
+            @NonNull RangingResultCallback callback, @Nullable Handler handler) {
         startRanging(null, request, callback, handler);
     }
 
@@ -112,12 +129,13 @@
      *                 callback} object. If a null is provided then the application's main thread
      *                 will be used.
      *
-     * @hide (@SystemApi)
+     * @hide
      */
+    @SystemApi
     @RequiresPermission(allOf = {LOCATION_HARDWARE, ACCESS_COARSE_LOCATION, CHANGE_WIFI_STATE,
             ACCESS_WIFI_STATE})
-    public void startRanging(@Nullable WorkSource workSource, RangingRequest request,
-            RangingResultCallback callback, @Nullable Handler handler) {
+    public void startRanging(@Nullable WorkSource workSource, @NonNull RangingRequest request,
+            @NonNull RangingResultCallback callback, @Nullable Handler handler) {
         if (VDBG) {
             Log.v(TAG, "startRanging: workSource=" + workSource + ", request=" + request
                     + ", callback=" + callback + ", handler=" + handler);
@@ -143,10 +161,11 @@
      *
      * @param workSource The work-sources of the requesters.
      *
-     * @hide (@SystemApi)
+     * @hide
      */
+    @SystemApi
     @RequiresPermission(allOf = {LOCATION_HARDWARE})
-    public void cancelRanging(WorkSource workSource) {
+    public void cancelRanging(@Nullable WorkSource workSource) {
         if (VDBG) {
             Log.v(TAG, "cancelRanging: workSource=" + workSource);
         }
diff --git a/wifi/java/android/net/wifi/rtt/package.html b/wifi/java/android/net/wifi/rtt/package.html
index a0d407a..11ac058 100644
--- a/wifi/java/android/net/wifi/rtt/package.html
+++ b/wifi/java/android/net/wifi/rtt/package.html
@@ -13,6 +13,8 @@
     <li>{@link android.Manifest.permission#CHANGE_WIFI_STATE}</li>
     <li>{@link android.Manifest.permission#ACCESS_COARSE_LOCATION}</li>
 </ul>
+<p>Usage of the API is also gated by the device's Location Mode: whether it permits Wi-Fi based
+location to be queried.</p>
 
 <p class="note"><strong>Note:</strong> Not all Android-powered devices support Wi-Fi RTT
     functionality.