Merge "[WIFI] Update instructions for running tests"
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java
index a0e83da..bb94275 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java
@@ -144,14 +144,18 @@
     public void startTrackingRestrictedJobLocked(JobStatus jobStatus) {
         // Don't need to start tracking the job. If the job needed network, it would already be
         // tracked.
-        updateConstraintsSatisfied(jobStatus);
+        if (jobStatus.hasConnectivityConstraint()) {
+            updateConstraintsSatisfied(jobStatus);
+        }
     }
 
     @Override
     public void stopTrackingRestrictedJobLocked(JobStatus jobStatus) {
         // Shouldn't stop tracking the job here. If the job was tracked, it still needs network,
         // even after being unrestricted.
-        updateConstraintsSatisfied(jobStatus);
+        if (jobStatus.hasConnectivityConstraint()) {
+            updateConstraintsSatisfied(jobStatus);
+        }
     }
 
     /**
diff --git a/apex/media/framework/java/android/media/MediaParser.java b/apex/media/framework/java/android/media/MediaParser.java
index ac66d1b..d59270c 100644
--- a/apex/media/framework/java/android/media/MediaParser.java
+++ b/apex/media/framework/java/android/media/MediaParser.java
@@ -223,7 +223,7 @@
     public static final class SeekPoint {
 
         /** A {@link SeekPoint} whose time and byte offset are both set to 0. */
-        public static final @NonNull SeekPoint START = new SeekPoint(0, 0);
+        @NonNull public static final SeekPoint START = new SeekPoint(0, 0);
 
         /** The time of the seek point, in microseconds. */
         public final long timeUs;
@@ -241,7 +241,8 @@
         }
 
         @Override
-        public @NonNull String toString() {
+        @NonNull
+        public String toString() {
             return "[timeUs=" + timeUs + ", position=" + position + "]";
         }
 
@@ -414,7 +415,8 @@
      * @return A new instance.
      * @throws IllegalArgumentException If an invalid name is provided.
      */
-    public static @NonNull MediaParser createByName(
+    @NonNull
+    public static MediaParser createByName(
             @NonNull String name, @NonNull OutputConsumer outputConsumer) {
         String[] nameAsArray = new String[] {name};
         assertValidNames(nameAsArray);
@@ -431,7 +433,8 @@
      *     default array of names is used.
      * @return A new instance.
      */
-    public static @NonNull MediaParser create(
+    @NonNull
+    public static MediaParser create(
             @NonNull OutputConsumer outputConsumer, @NonNull String... extractorNames) {
         assertValidNames(extractorNames);
         if (extractorNames.length == 0) {
@@ -448,7 +451,8 @@
      *
      * <p>TODO: List which properties are taken into account. E.g. MimeType.
      */
-    public static @NonNull List<String> getExtractorNames(@NonNull MediaFormat mediaFormat) {
+    @NonNull
+    public static List<String> getExtractorNames(@NonNull MediaFormat mediaFormat) {
         throw new UnsupportedOperationException();
     }
 
@@ -479,7 +483,8 @@
      * @return The name of the backing extractor implementation, or null if the backing extractor
      *     implementation has not yet been selected.
      */
-    public @Nullable String getExtractorName() {
+    @Nullable
+    public String getExtractorName() {
         return mExtractorName;
     }
 
diff --git a/apex/sdkextensions/framework/Android.bp b/apex/sdkextensions/framework/Android.bp
index dd17473..245a96b 100644
--- a/apex/sdkextensions/framework/Android.bp
+++ b/apex/sdkextensions/framework/Android.bp
@@ -32,6 +32,7 @@
     libs: [ "framework-annotations-lib" ],
     permitted_packages: [ "android.os.ext" ],
     installable: true,
+    plugins: ["java_api_finder"],
     visibility: [
         "//frameworks/base/apex/sdkextensions",
         "//frameworks/base/apex/sdkextensions/testing",
diff --git a/api/current.txt b/api/current.txt
index b3d136d..e614f17 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -12017,6 +12017,7 @@
     field public static final String FEATURE_CAMERA_CAPABILITY_MANUAL_POST_PROCESSING = "android.hardware.camera.capability.manual_post_processing";
     field public static final String FEATURE_CAMERA_CAPABILITY_MANUAL_SENSOR = "android.hardware.camera.capability.manual_sensor";
     field public static final String FEATURE_CAMERA_CAPABILITY_RAW = "android.hardware.camera.capability.raw";
+    field public static final String FEATURE_CAMERA_CONCURRENT = "android.hardware.camera.concurrent";
     field public static final String FEATURE_CAMERA_EXTERNAL = "android.hardware.camera.external";
     field public static final String FEATURE_CAMERA_FLASH = "android.hardware.camera.flash";
     field public static final String FEATURE_CAMERA_FRONT = "android.hardware.camera.front";
@@ -17174,6 +17175,7 @@
     field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<java.lang.Float> SCALER_AVAILABLE_MAX_DIGITAL_ZOOM;
     field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<int[]> SCALER_AVAILABLE_ROTATE_AND_CROP_MODES;
     field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<java.lang.Integer> SCALER_CROPPING_TYPE;
+    field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<android.hardware.camera2.params.MandatoryStreamCombination[]> SCALER_MANDATORY_CONCURRENT_STREAM_COMBINATIONS;
     field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<android.hardware.camera2.params.MandatoryStreamCombination[]> SCALER_MANDATORY_STREAM_COMBINATIONS;
     field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<android.hardware.camera2.params.StreamConfigurationMap> SCALER_STREAM_CONFIGURATION_MAP;
     field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<int[]> SENSOR_AVAILABLE_TEST_PATTERN_MODES;
@@ -17263,6 +17265,8 @@
   public final class CameraManager {
     method @NonNull public android.hardware.camera2.CameraCharacteristics getCameraCharacteristics(@NonNull String) throws android.hardware.camera2.CameraAccessException;
     method @NonNull public String[] getCameraIdList() throws android.hardware.camera2.CameraAccessException;
+    method @NonNull public java.util.Set<java.util.Set<java.lang.String>> getConcurrentStreamingCameraIds() throws android.hardware.camera2.CameraAccessException;
+    method @RequiresPermission(android.Manifest.permission.CAMERA) public boolean isConcurrentSessionConfigurationSupported(@NonNull java.util.Map<java.lang.String,android.hardware.camera2.params.SessionConfiguration>) throws android.hardware.camera2.CameraAccessException;
     method @RequiresPermission(android.Manifest.permission.CAMERA) public void openCamera(@NonNull String, @NonNull android.hardware.camera2.CameraDevice.StateCallback, @Nullable android.os.Handler) throws android.hardware.camera2.CameraAccessException;
     method @RequiresPermission(android.Manifest.permission.CAMERA) public void openCamera(@NonNull String, @NonNull java.util.concurrent.Executor, @NonNull android.hardware.camera2.CameraDevice.StateCallback) throws android.hardware.camera2.CameraAccessException;
     method public void registerAvailabilityCallback(@NonNull android.hardware.camera2.CameraManager.AvailabilityCallback, @Nullable android.os.Handler);
@@ -45213,6 +45217,7 @@
     method public void registerCallback(android.telecom.Call.Callback);
     method public void registerCallback(android.telecom.Call.Callback, android.os.Handler);
     method public void reject(boolean, String);
+    method public void reject(int);
     method public void removeExtras(java.util.List<java.lang.String>);
     method public void removeExtras(java.lang.String...);
     method public void respondToRttRequest(int, boolean);
@@ -45228,6 +45233,8 @@
     field public static final String EXTRA_LAST_EMERGENCY_CALLBACK_TIME_MILLIS = "android.telecom.extra.LAST_EMERGENCY_CALLBACK_TIME_MILLIS";
     field public static final String EXTRA_SILENT_RINGING_REQUESTED = "android.telecom.extra.SILENT_RINGING_REQUESTED";
     field public static final String EXTRA_SUGGESTED_PHONE_ACCOUNTS = "android.telecom.extra.SUGGESTED_PHONE_ACCOUNTS";
+    field public static final int REJECT_REASON_DECLINED = 1; // 0x1
+    field public static final int REJECT_REASON_UNWANTED = 2; // 0x2
     field public static final int STATE_ACTIVE = 4; // 0x4
     field public static final int STATE_AUDIO_PROCESSING = 12; // 0xc
     field public static final int STATE_CONNECTING = 9; // 0x9
@@ -45495,6 +45502,7 @@
     method public void onPostDialContinue(boolean);
     method public void onPullExternalCall();
     method public void onReject();
+    method public void onReject(int);
     method public void onReject(String);
     method public void onSeparate();
     method public void onShowIncomingCallUi();
diff --git a/api/system-current.txt b/api/system-current.txt
index 82924d7d..cc4b122 100755
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -2519,7 +2519,7 @@
     method @Nullable public android.hardware.display.BrightnessCorrection getCorrectionByPackageName(@NonNull String);
     method public android.util.Pair<float[],float[]> getCurve();
     method public float getShortTermModelLowerLuxMultiplier();
-    method public long getShortTermModelTimeout();
+    method public long getShortTermModelTimeoutMillis();
     method public float getShortTermModelUpperLuxMultiplier();
     method public boolean shouldCollectColorSamples();
     method public void writeToParcel(android.os.Parcel, int);
@@ -2536,7 +2536,7 @@
     method public int getMaxCorrectionsByPackageName();
     method @NonNull public android.hardware.display.BrightnessConfiguration.Builder setDescription(@Nullable String);
     method @NonNull public android.hardware.display.BrightnessConfiguration.Builder setShortTermModelLowerLuxMultiplier(@FloatRange(from=0.0f) float);
-    method @NonNull public android.hardware.display.BrightnessConfiguration.Builder setShortTermModelTimeout(long);
+    method @NonNull public android.hardware.display.BrightnessConfiguration.Builder setShortTermModelTimeoutMillis(long);
     method @NonNull public android.hardware.display.BrightnessConfiguration.Builder setShortTermModelUpperLuxMultiplier(@FloatRange(from=0.0f) float);
     method @NonNull public android.hardware.display.BrightnessConfiguration.Builder setShouldCollectColorSamples(boolean);
   }
@@ -7571,12 +7571,12 @@
   }
 
   public class WifiInfo implements android.os.Parcelable {
-    method @Nullable public String getAppPackageName();
-    method public double getRxSuccessRate();
+    method public double getLostTxPacketsPerSecond();
+    method @Nullable public String getRequestingPackageName();
+    method public double getRetriedTxPacketsPerSecond();
     method public int getScore();
-    method public double getTxBadRate();
-    method public double getTxRetriesRate();
-    method public double getTxSuccessRate();
+    method public double getSuccessfulRxPacketsPerSecond();
+    method public double getSuccessfulTxPacketsPerSecond();
     method public boolean isEphemeral();
     method public boolean isOsuAp();
     method public boolean isPasspointAp();
@@ -7604,7 +7604,6 @@
     method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD, android.Manifest.permission.NETWORK_STACK}) public void connect(int, @Nullable android.net.wifi.WifiManager.ActionListener);
     method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD, android.Manifest.permission.NETWORK_STACK}) public void disable(int, @Nullable android.net.wifi.WifiManager.ActionListener);
     method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_STACK}) public void disableEphemeralNetwork(@NonNull String);
-    method @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public void enableVerboseLogging(int);
     method @RequiresPermission(android.Manifest.permission.CONNECTIVITY_INTERNAL) public void factoryReset();
     method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD, android.Manifest.permission.NETWORK_STACK}) public void forget(int, @Nullable android.net.wifi.WifiManager.ActionListener);
     method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD}) public java.util.List<android.util.Pair<android.net.wifi.WifiConfiguration,java.util.Map<java.lang.Integer,java.util.List<android.net.wifi.ScanResult>>>> getAllMatchingWifiConfigs(@NonNull java.util.List<android.net.wifi.ScanResult>);
@@ -7616,7 +7615,6 @@
     method @NonNull @RequiresPermission(allOf={android.Manifest.permission.ACCESS_FINE_LOCATION, android.Manifest.permission.ACCESS_WIFI_STATE}) public java.util.Map<android.net.wifi.WifiNetworkSuggestion,java.util.List<android.net.wifi.ScanResult>> getMatchingScanResults(@NonNull java.util.List<android.net.wifi.WifiNetworkSuggestion>, @Nullable java.util.List<android.net.wifi.ScanResult>);
     method @RequiresPermission(allOf={android.Manifest.permission.ACCESS_FINE_LOCATION, android.Manifest.permission.ACCESS_WIFI_STATE, android.Manifest.permission.READ_WIFI_CREDENTIAL}) public java.util.List<android.net.wifi.WifiConfiguration> getPrivilegedConfiguredNetworks();
     method @NonNull @RequiresPermission(android.Manifest.permission.ACCESS_WIFI_STATE) public android.net.wifi.SoftApConfiguration getSoftApConfiguration();
-    method public int getVerboseLoggingLevel();
     method @RequiresPermission(android.Manifest.permission.ACCESS_WIFI_STATE) public void getWifiActivityEnergyInfoAsync(@NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.WifiManager.OnWifiActivityEnergyInfoListener);
     method @Deprecated @Nullable @RequiresPermission(android.Manifest.permission.ACCESS_WIFI_STATE) public android.net.wifi.WifiConfiguration getWifiApConfiguration();
     method @RequiresPermission(android.Manifest.permission.ACCESS_WIFI_STATE) public int getWifiApState();
@@ -7626,6 +7624,7 @@
     method @Deprecated public boolean isDeviceToDeviceRttSupported();
     method @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public boolean isDualModeSupported();
     method public boolean isPortableHotspotSupported();
+    method public boolean isVerboseLoggingEnabled();
     method @RequiresPermission(android.Manifest.permission.ACCESS_WIFI_STATE) public boolean isWifiApEnabled();
     method public boolean isWifiScannerSupported();
     method @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public void registerNetworkRequestMatchCallback(@NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.WifiManager.NetworkRequestMatchCallback);
@@ -7642,6 +7641,7 @@
     method @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public void setMacRandomizationSettingPasspointEnabled(@NonNull String, boolean);
     method @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public void setMeteredOverridePasspoint(@NonNull String, int);
     method @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public boolean setSoftApConfiguration(@NonNull android.net.wifi.SoftApConfiguration);
+    method @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public void setVerboseLoggingEnabled(boolean);
     method @Deprecated @RequiresPermission(android.Manifest.permission.CHANGE_WIFI_STATE) public boolean setWifiApConfiguration(android.net.wifi.WifiConfiguration);
     method @RequiresPermission(android.Manifest.permission.WIFI_UPDATE_USABILITY_STATS_SCORE) public boolean setWifiConnectedNetworkScorer(@NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.WifiManager.WifiConnectedNetworkScorer);
     method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD}) public void startEasyConnectAsConfiguratorInitiator(@NonNull String, int, int, @NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.EasyConnectStatusCallback);
@@ -8606,9 +8606,6 @@
     field public static final int STATUS_SUCCESS = 0; // 0x0
   }
 
-  @IntDef(prefix={"STATUS_"}, value={android.os.HwParcel.STATUS_SUCCESS}) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface HwParcel.Status {
-  }
-
   public interface IHwBinder {
     method public boolean linkToDeath(android.os.IHwBinder.DeathRecipient, long);
     method public android.os.IHwInterface queryLocalInterface(String);
@@ -9045,23 +9042,15 @@
   }
 
   public final class WifiActivityEnergyInfo implements android.os.Parcelable {
-    ctor public WifiActivityEnergyInfo(long, int, long, long, long, long);
+    ctor public WifiActivityEnergyInfo(long, int, @IntRange(from=0) long, @IntRange(from=0) long, @IntRange(from=0) long, @IntRange(from=0) long);
     method public int describeContents();
-    method public long getControllerEnergyUsedMicroJoules();
-    method public long getControllerIdleDurationMillis();
-    method public long getControllerRxDurationMillis();
-    method public long getControllerScanDurationMillis();
-    method public long getControllerTxDurationMillis();
+    method @IntRange(from=0) public long getControllerEnergyUsedMicroJoules();
+    method @IntRange(from=0) public long getControllerIdleDurationMillis();
+    method @IntRange(from=0) public long getControllerRxDurationMillis();
+    method @IntRange(from=0) public long getControllerScanDurationMillis();
+    method @IntRange(from=0) public long getControllerTxDurationMillis();
     method public int getStackState();
     method public long getTimeSinceBootMillis();
-    method public boolean isValid();
-    method public void setControllerEnergyUsedMicroJoules(long);
-    method public void setControllerIdleDurationMillis(long);
-    method public void setControllerRxDurationMillis(long);
-    method public void setControllerScanDurationMillis(long);
-    method public void setControllerTxDurationMillis(long);
-    method public void setStackState(int);
-    method public void setTimeSinceBootMillis(long);
     method public void writeToParcel(@NonNull android.os.Parcel, int);
     field @NonNull public static final android.os.Parcelable.Creator<android.os.connectivity.WifiActivityEnergyInfo> CREATOR;
     field public static final int STACK_STATE_INVALID = 0; // 0x0
diff --git a/api/test-current.txt b/api/test-current.txt
index 37b117b..b35bca8 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -1091,7 +1091,7 @@
     method @Nullable public android.hardware.display.BrightnessCorrection getCorrectionByPackageName(@NonNull String);
     method public android.util.Pair<float[],float[]> getCurve();
     method public float getShortTermModelLowerLuxMultiplier();
-    method public long getShortTermModelTimeout();
+    method public long getShortTermModelTimeoutMillis();
     method public float getShortTermModelUpperLuxMultiplier();
     method public boolean shouldCollectColorSamples();
     method public void writeToParcel(android.os.Parcel, int);
@@ -1108,7 +1108,7 @@
     method public int getMaxCorrectionsByPackageName();
     method @NonNull public android.hardware.display.BrightnessConfiguration.Builder setDescription(@Nullable String);
     method @NonNull public android.hardware.display.BrightnessConfiguration.Builder setShortTermModelLowerLuxMultiplier(@FloatRange(from=0.0f) float);
-    method @NonNull public android.hardware.display.BrightnessConfiguration.Builder setShortTermModelTimeout(long);
+    method @NonNull public android.hardware.display.BrightnessConfiguration.Builder setShortTermModelTimeoutMillis(long);
     method @NonNull public android.hardware.display.BrightnessConfiguration.Builder setShortTermModelUpperLuxMultiplier(@FloatRange(from=0.0f) float);
     method @NonNull public android.hardware.display.BrightnessConfiguration.Builder setShouldCollectColorSamples(boolean);
   }
@@ -2218,9 +2218,6 @@
     field public static final int STATUS_SUCCESS = 0; // 0x0
   }
 
-  @IntDef(prefix={"STATUS_"}, value={android.os.HwParcel.STATUS_SUCCESS}) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface HwParcel.Status {
-  }
-
   public interface IHwBinder {
     method public boolean linkToDeath(android.os.IHwBinder.DeathRecipient, long);
     method public android.os.IHwInterface queryLocalInterface(String);
diff --git a/api/test-lint-baseline.txt b/api/test-lint-baseline.txt
index a9c1836..f53548b 100644
--- a/api/test-lint-baseline.txt
+++ b/api/test-lint-baseline.txt
@@ -2451,8 +2451,6 @@
     
 
 
-PublicTypedef: android.os.HwParcel.Status:
-    
 PublicTypedef: android.telephony.ims.feature.MmTelFeature.MmTelCapabilities.MmTelCapability:
     
 PublicTypedef: android.telephony.ims.feature.MmTelFeature.ProcessCallResult:
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index d9d5be6..7011724 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -137,7 +137,7 @@
         DaveyOccurred davey_occurred = 58 [(allow_from_any_uid) = true];
         OverlayStateChanged overlay_state_changed = 59;
         ForegroundServiceStateChanged foreground_service_state_changed = 60;
-        CallStateChanged call_state_changed = 61;
+        CallStateChanged call_state_changed = 61 [(module) = "telecom"];
         KeyguardStateChanged keyguard_state_changed = 62 [(module) = "sysui"];
         KeyguardBouncerStateChanged keyguard_bouncer_state_changed = 63 [(module) = "sysui"];
         KeyguardBouncerPasswordEntered keyguard_bouncer_password_entered = 64 [(module) = "sysui"];
@@ -288,8 +288,8 @@
         MediametricsNuPlayerReported mediametrics_nuplayer_reported = 199;
         MediametricsRecorderReported mediametrics_recorder_reported = 200;
         MediametricsDrmManagerReported mediametrics_drmmanager_reported = 201;
-        CarPowerStateChanged car_power_state_changed = 203;
-        GarageModeInfo garage_mode_info = 204;
+        CarPowerStateChanged car_power_state_changed = 203 [(module) = "car"];
+        GarageModeInfo garage_mode_info = 204 [(module) = "car"];
         TestAtomReported test_atom_reported = 205 [(module) = "cts"];
         ContentCaptureCallerMismatchReported content_capture_caller_mismatch_reported = 206;
         ContentCaptureServiceEvents content_capture_service_events = 207;
@@ -324,7 +324,8 @@
         AppCompatibilityChangeReported app_compatibility_change_reported =
             228 [(allow_from_any_uid) = true];
         PerfettoUploaded perfetto_uploaded = 229 [(module) = "perfetto"];
-        VmsClientConnectionStateChanged vms_client_connection_state_changed = 230;
+        VmsClientConnectionStateChanged vms_client_connection_state_changed =
+                230 [(module) = "car"];
         MediaProviderScanEvent media_provider_scan_event = 233 [(module) = "mediaprovider"];
         MediaProviderDeletionEvent media_provider_deletion_event = 234 [(module) = "mediaprovider"];
         MediaProviderPermissionEvent media_provider_permission_event =
@@ -343,6 +344,10 @@
         NotificationChannelModified notification_panel_modified = 246;
         IntegrityCheckResultReported integrity_check_result_reported = 247;
         IntegrityRulesPushed integrity_rules_pushed = 248;
+        CellBroadcastMessageReported cb_message_reported =
+            249 [(module) = "cellbroadcast"];
+        CellBroadcastMessageError cb_message_error =
+            250 [(module) = "cellbroadcast"];
     }
 
     // Pulled events will start at field 10000.
@@ -412,7 +417,7 @@
         SurfaceflingerStatsGlobalInfo surfaceflinger_stats_global_info = 10062;
         SurfaceflingerStatsLayerInfo surfaceflinger_stats_layer_info = 10063;
         ProcessMemorySnapshot process_memory_snapshot = 10064;
-        VmsClientStats vms_client_stats = 10065;
+        VmsClientStats vms_client_stats = 10065 [(module) = "car"];
         NotificationRemoteViews notification_remote_views = 10066;
         DangerousPermissionStateSampled dangerous_permission_state_sampled = 10067;
         GraphicsStats graphics_stats = 10068;
@@ -8112,3 +8117,55 @@
     // identify the rules.
     optional string rule_version = 3;
 }
+
+/**
+ * Logs when a cell broadcast message is received on the device.
+ *
+ * Logged from CellBroadcastService module:
+ *   packages/modules/CellBroadcastService/src/com/android/cellbroadcastservice/
+ */
+message CellBroadcastMessageReported {
+    // The type of Cell Broadcast message
+    enum CbType {
+        UNKNOWN_TYPE = 0;
+        GSM = 1;
+        CDMA = 2;
+        CDMA_SPC = 3;
+    }
+
+    // GSM, CDMA, CDMA-SCP
+    optional CbType type = 1;
+}
+
+/**
+ * Logs when an error occurs while handling a cell broadcast message;
+ *
+ * Logged from CellBroadcastService module:
+ *   packages/modules/CellBroadcastService/src/com/android/cellbroadcastservice/
+ */
+message CellBroadcastMessageError {
+    // The type of error raised when trying to handle a cell broadcast message
+    enum ErrorType {
+        UNKNOWN_TYPE = 0;
+        CDMA_DECODING_ERROR = 1;
+        CDMA_SCP_EMPTY = 2;
+        CDMA_SCP_HANDLING_ERROR = 3;
+        GSM_INVALID_HEADER_LENGTH = 4;
+        GSM_UNSUPPORTED_HEADER_MESSAGE_TYPE = 5;
+        GSM_UNSUPPORTED_HEADER_DATA_CODING_SCHEME = 6;
+        GSM_INVALID_PDU = 7;
+        GSM_INVALID_GEO_FENCING_DATA = 8;
+        GSM_UMTS_INVALID_WAC = 9;
+        FAILED_TO_INSERT_TO_DB = 10;
+        UNEXPECTED_GEOMETRY_FROM_FWK = 11;
+        UNEXPECTED_GSM_MESSAGE_TYPE_FROM_FWK = 12;
+        UNEXPECTED_CDMA_MESSAGE_TYPE_FROM_FWK = 13;
+        UNEXPECTED_CDMA_SCP_MESSAGE_TYPE_FROM_FWK = 14;
+    }
+
+    // What kind of error occurred
+    optional ErrorType type = 1;
+
+    // Exception message (or log message) associated with the error (max 1000 chars)
+    optional string exception_message = 2;
+}
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 38ffb51..b64c001 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -1960,6 +1960,15 @@
 
     /**
      * Feature for {@link #getSystemAvailableFeatures} and
+     * {@link #hasSystemFeature}: The device's main front and back cameras can stream
+     * concurrently as described in  {@link
+     * android.hardware.camera2.CameraManager#getConcurrentStreamingCameraIds()}
+     */
+    @SdkConstant(SdkConstantType.FEATURE)
+    public static final String FEATURE_CAMERA_CONCURRENT = "android.hardware.camera.concurrent";
+
+    /**
+     * Feature for {@link #getSystemAvailableFeatures} and
      * {@link #hasSystemFeature}: The device is capable of communicating with
      * consumer IR devices.
      */
diff --git a/core/java/android/hardware/camera2/CameraCharacteristics.java b/core/java/android/hardware/camera2/CameraCharacteristics.java
index dfc4f0f..b3a1ee2 100644
--- a/core/java/android/hardware/camera2/CameraCharacteristics.java
+++ b/core/java/android/hardware/camera2/CameraCharacteristics.java
@@ -2875,7 +2875,28 @@
     @NonNull
     public static final Key<int[]> SCALER_AVAILABLE_ROTATE_AND_CROP_MODES =
             new Key<int[]>("android.scaler.availableRotateAndCropModes", int[].class);
-
+    /**
+     * <p>An array of mandatory concurrent stream combinations.
+     * This is an app-readable conversion of the concurrent mandatory stream combination
+     * {@link android.hardware.camera2.CameraDevice#createCaptureSession tables}.</p>
+     * <p>The array of
+     * {@link android.hardware.camera2.params.MandatoryStreamCombination combinations} is
+     * generated according to the documented
+     * {@link android.hardware.camera2.CameraDevice#createCaptureSession guideline} for each device
+     * which has its Id present in the set returned by
+     * {@link android.hardware.camera2.CameraManager#getConcurrentStreamingCameraIds}.
+     * Clients can use the array as a quick reference to find an appropriate camera stream
+     * combination.
+     * The mandatory stream combination array will be {@code null} in case the device is not a part
+     * of at least one set of combinations returned by
+     * {@link android.hardware.camera2.CameraManager#getConcurrentStreamingCameraIds}.</p>
+     * <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p>
+     */
+    @PublicKey
+    @NonNull
+    @SyntheticKey
+    public static final Key<android.hardware.camera2.params.MandatoryStreamCombination[]> SCALER_MANDATORY_CONCURRENT_STREAM_COMBINATIONS =
+            new Key<android.hardware.camera2.params.MandatoryStreamCombination[]>("android.scaler.mandatoryConcurrentStreamCombinations", android.hardware.camera2.params.MandatoryStreamCombination[].class);
     /**
      * <p>The area of the image sensor which corresponds to active pixels after any geometric
      * distortion correction has been applied.</p>
diff --git a/core/java/android/hardware/camera2/CameraDevice.java b/core/java/android/hardware/camera2/CameraDevice.java
index cc06681..24d9311 100644
--- a/core/java/android/hardware/camera2/CameraDevice.java
+++ b/core/java/android/hardware/camera2/CameraDevice.java
@@ -680,6 +680,25 @@
      * </table><br>
      * </p>
      *
+     *<p>Devices capable of streaming concurrently with other devices as described by
+     * {@link android.hardware.camera2.CameraManager#getConcurrentStreamingCameraIds} have the
+     * following guaranteed streams (when streaming concurrently with other devices)</p>
+     *
+     * <table>
+     * <tr><th colspan="5">Concurrent stream guaranteed configurations</th></tr>
+     * <tr><th colspan="2" id="rb">Target 1</th><th colspan="2" id="rb">Target 2</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> </tr>
+     * <tr> <td>{@code YUV}</td><td id="rb">{@code MAXIMUM}</td>  <td colspan="2" id="rb"></td> <td>In-app video / image processing.</td> </tr>
+     * <tr> <td>{@code PRIV}</td><td id="rb">{@code MAXIMUM}</td>  <td colspan="2" id="rb"></td> <td>In-app viewfinder analysis.</td> </tr>
+     * <tr> <td>{@code YUV}</td><td id="rb">{@code MAXIMUM}</td> <td>{@code YUV }</td><td id="rb">{@code MAXIMUM}</td> <td>In-app video / processing with preview.</td> </tr>
+     * <tr> <td>{@code YUV}</td><td id="rb">{@code MAXIMUM}</td> <td>{@code PRIV }</td><td id="rb">{@code MAXIMUM}</td> <td>In-app video / processing with preview.</td> </tr>
+     * <tr> <td>{@code PRIV}</td><td id="rb">{@code MAXIMUM}</td> <td>{@code PRIV }</td><td id="rb">{@code MAXIMUM}</td> <td>Standard Recording.</td> </tr>
+     * </table><br>
+     * </p>
+     *
+     * <p> For guaranteed concurrent stream configurations, MAXIMUM refers to the camera device's
+     * resolution for that format from {@link StreamConfigurationMap#getOutputSizes} or
+     * 720p(1280X720) whichever is lower. </p>
      * <p>MONOCHROME-capability ({@link CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES}
      * includes {@link CameraMetadata#REQUEST_AVAILABLE_CAPABILITIES_MONOCHROME MONOCHROME}) devices
      * supporting {@link android.graphics.ImageFormat#Y8 Y8} support substituting {@code YUV}
diff --git a/core/java/android/hardware/camera2/CameraManager.java b/core/java/android/hardware/camera2/CameraManager.java
index 55025f0..9ee56a9 100644
--- a/core/java/android/hardware/camera2/CameraManager.java
+++ b/core/java/android/hardware/camera2/CameraManager.java
@@ -31,6 +31,9 @@
 import android.hardware.camera2.impl.CameraMetadataNative;
 import android.hardware.camera2.legacy.CameraDeviceUserShim;
 import android.hardware.camera2.legacy.LegacyMetadataMapper;
+import android.hardware.camera2.params.SessionConfiguration;
+import android.hardware.camera2.utils.CameraIdAndSessionConfiguration;
+import android.hardware.camera2.utils.ConcurrentCameraIdCombination;
 import android.os.Binder;
 import android.os.DeadObjectException;
 import android.os.Handler;
@@ -40,6 +43,7 @@
 import android.os.ServiceSpecificException;
 import android.os.SystemProperties;
 import android.util.ArrayMap;
+import android.util.ArraySet;
 import android.util.Log;
 import android.util.Size;
 import android.view.Display;
@@ -48,6 +52,7 @@
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Comparator;
+import java.util.Map;
 import java.util.Set;
 import java.util.concurrent.Executor;
 import java.util.concurrent.Executors;
@@ -126,6 +131,66 @@
     }
 
     /**
+     * Return the list of combinations of currently connected camera devices identifiers, which
+     * support configuring camera device sessions concurrently.
+     *
+     * <p>The set of combinations may include camera devices that may be in use by other camera API
+     * clients.</p>
+     *
+     * <p>The set of combinations doesn't contain physical cameras that can only be used as
+     * part of a logical multi-camera device.</p>
+     *
+     * @return The set of combinations of currently connected camera devices, that may have
+     *         sessions configured concurrently. The set of combinations will be empty if no such
+     *         combinations are supported by the camera subsystem.
+     *
+     * @throws CameraAccessException if the camera device has been disconnected.
+     */
+    @NonNull
+    public Set<Set<String>> getConcurrentStreamingCameraIds() throws CameraAccessException {
+        return CameraManagerGlobal.get().getConcurrentStreamingCameraIds();
+    }
+
+    /**
+     * Checks whether the provided set of camera devices and their corresponding
+     * {@link SessionConfiguration} can be configured concurrently.
+     *
+     * <p>This method performs a runtime check of the given {@link SessionConfiguration} and camera
+     * id combinations. The result confirms whether or not the passed session configurations can be
+     * successfully used to create camera capture sessions concurrently, on the given camera
+     * devices using {@link CameraDevice#createCaptureSession(SessionConfiguration)}.
+     * </p>
+     *
+     * <p>The method can be called at any point before, during and after active capture sessions.
+     * It will not impact normal camera behavior in any way and must complete significantly
+     * faster than creating a regular or constrained capture session.</p>
+     *
+     * <p>Although this method is faster than creating a new capture session, it is not intended
+     * to be used for exploring the entire space of supported concurrent stream combinations. The
+     * available mandatory concurrent stream combinations may be obtained by querying
+     * {@link #getCameraCharacteristics} for the key
+     * SCALER_MANDATORY_CONCURRENT_STREAM_COMBINATIONS. </p>
+     *
+     * <p>Note that session parameters will be ignored and calls to
+     * {@link SessionConfiguration#setSessionParameters} are not required.</p>
+     *
+     * @return {@code true} if the given combination of session configurations and corresponding
+     *                      camera ids are concurrently supported by the camera sub-system,
+     *         {@code false} otherwise.
+     *
+     * @throws IllegalArgumentException if the set of camera devices provided is not a subset of
+     *                                  those returned by getConcurrentStreamingCameraIds()
+     * @throws CameraAccessException if one of the camera devices queried is no longer connected.
+     */
+    @RequiresPermission(android.Manifest.permission.CAMERA)
+    public boolean isConcurrentSessionConfigurationSupported(
+            @NonNull Map<String, SessionConfiguration> cameraIdAndSessionConfig)
+            throws CameraAccessException {
+        return CameraManagerGlobal.get().isConcurrentSessionConfigurationSupported(
+                cameraIdAndSessionConfig);
+    }
+
+    /**
      * Register a callback to be notified about camera device availability.
      *
      * <p>Registering the same callback again will replace the handler with the
@@ -336,8 +401,10 @@
                     } catch (NumberFormatException e) {
                         Log.e(TAG, "Failed to parse camera Id " + cameraId + " to integer");
                     }
+                    boolean hasConcurrentStreams =
+                            CameraManagerGlobal.get().cameraIdHasConcurrentStreamsLocked(cameraId);
+                    info.setHasMandatoryConcurrentStreams(hasConcurrentStreams);
                     info.setDisplaySize(displaySize);
-
                     characteristics = new CameraCharacteristics(info);
                 }
             } catch (ServiceSpecificException e) {
@@ -964,6 +1031,9 @@
         private final ArrayMap<String, ArrayList<String>> mUnavailablePhysicalDevices =
                 new ArrayMap<String, ArrayList<String>>();
 
+        private final Set<Set<String>> mConcurrentCameraIdCombinations =
+                new ArraySet<Set<String>>();
+
         // Registered availablility callbacks and their executors
         private final ArrayMap<AvailabilityCallback, Executor> mCallbackMap =
             new ArrayMap<AvailabilityCallback, Executor>();
@@ -1068,7 +1138,22 @@
             } catch (RemoteException e) {
                 // Camera service is now down, leave mCameraService as null
             }
+
+            try {
+                ConcurrentCameraIdCombination[] cameraIdCombinations =
+                        cameraService.getConcurrentStreamingCameraIds();
+                for (ConcurrentCameraIdCombination comb : cameraIdCombinations) {
+                    mConcurrentCameraIdCombinations.add(comb.getConcurrentCameraIdCombination());
+                }
+            } catch (ServiceSpecificException e) {
+                // Unexpected failure
+                throw new IllegalStateException("Failed to get concurrent camera id combinations",
+                        e);
+            } catch (RemoteException e) {
+                // Camera service died in all probability
+            }
         }
+
         private String[] extractCameraIdListLocked() {
             String[] cameraIds = null;
             int idCount = 0;
@@ -1089,6 +1174,31 @@
             }
             return cameraIds;
         }
+
+        private Set<Set<String>> extractConcurrentCameraIdListLocked() {
+            Set<Set<String>> concurrentCameraIds = new ArraySet<Set<String>>();
+            for (Set<String> cameraIds : mConcurrentCameraIdCombinations) {
+                Set<String> extractedCameraIds = new ArraySet<String>();
+                for (String cameraId : cameraIds) {
+                    // if the camera id status is NOT_PRESENT or ENUMERATING; skip the device.
+                    // TODO: Would a device status NOT_PRESENT ever be in the map ? it gets removed
+                    // in the callback anyway.
+                    Integer status = mDeviceStatus.get(cameraId);
+                    if (status == null) {
+                        // camera id not present
+                        continue;
+                    }
+                    if (status == ICameraServiceListener.STATUS_ENUMERATING
+                            || status == ICameraServiceListener.STATUS_NOT_PRESENT) {
+                        continue;
+                    }
+                    extractedCameraIds.add(cameraId);
+                }
+                concurrentCameraIds.add(extractedCameraIds);
+            }
+            return concurrentCameraIds;
+        }
+
         private static void sortCameraIds(String[] cameraIds) {
             // The sort logic must match the logic in
             // libcameraservice/common/CameraProviderManager.cpp::getAPI1CompatibleCameraDeviceIds
@@ -1214,6 +1324,88 @@
             return cameraIds;
         }
 
+        public @NonNull Set<Set<String>> getConcurrentStreamingCameraIds() {
+            Set<Set<String>> concurrentStreamingCameraIds = null;
+            synchronized (mLock) {
+                // Try to make sure we have an up-to-date list of concurrent camera devices.
+                connectCameraServiceLocked();
+                concurrentStreamingCameraIds = extractConcurrentCameraIdListLocked();
+            }
+            // TODO: Some sort of sorting  ?
+            return concurrentStreamingCameraIds;
+        }
+
+        public boolean isConcurrentSessionConfigurationSupported(
+                @NonNull Map<String, SessionConfiguration> cameraIdsAndSessionConfigurations)
+                throws CameraAccessException {
+
+            if (cameraIdsAndSessionConfigurations == null) {
+                throw new IllegalArgumentException("cameraIdsAndSessionConfigurations was null");
+            }
+
+            int size = cameraIdsAndSessionConfigurations.size();
+            if (size == 0) {
+                throw new IllegalArgumentException("camera id and session combination is empty");
+            }
+
+            synchronized (mLock) {
+                // Go through all the elements and check if the camera ids are valid at least /
+                // belong to one of the combinations returned by getConcurrentStreamingCameraIds()
+                boolean subsetFound = false;
+                for (Set<String> combination : mConcurrentCameraIdCombinations) {
+                    if (combination.containsAll(cameraIdsAndSessionConfigurations.keySet())) {
+                        subsetFound = true;
+                    }
+                }
+                if (!subsetFound) {
+                    throw new IllegalArgumentException(
+                            "The set of camera ids provided is not a subset of"
+                            + "getConcurrentStreamingCameraIds");
+                }
+                CameraIdAndSessionConfiguration [] cameraIdsAndConfigs =
+                        new CameraIdAndSessionConfiguration[size];
+                int i = 0;
+                for (Map.Entry<String, SessionConfiguration> pair :
+                        cameraIdsAndSessionConfigurations.entrySet()) {
+                    cameraIdsAndConfigs[i] =
+                            new CameraIdAndSessionConfiguration(pair.getKey(), pair.getValue());
+                    i++;
+                }
+                try {
+                    return mCameraService.isConcurrentSessionConfigurationSupported(
+                            cameraIdsAndConfigs);
+                } catch (ServiceSpecificException e) {
+                   throwAsPublicException(e);
+                } catch (RemoteException e) {
+                  // Camera service died - act as if the camera was disconnected
+                  throw new CameraAccessException(CameraAccessException.CAMERA_DISCONNECTED,
+                          "Camera service is currently unavailable", e);
+                }
+            }
+
+            return false;
+        }
+
+      /**
+        * Helper function to find out if a camera id is in the set of combinations returned by
+        * getConcurrentStreamingCameraIds()
+        * @param cameraId the unique identifier of the camera device to query
+        * @return Whether the camera device was found in the set of combinations returned by
+        *         getConcurrentStreamingCameraIds
+        */
+        public boolean cameraIdHasConcurrentStreamsLocked(String cameraId) {
+            if (!mDeviceStatus.containsKey(cameraId)) {
+                Log.e(TAG, "cameraIdHasConcurrentStreamsLocked called on non existing camera id");
+                return false;
+            }
+            for (Set<String> comb : mConcurrentCameraIdCombinations) {
+                if (comb.contains(cameraId)) {
+                    return true;
+                }
+            }
+            return false;
+        }
+
         public void setTorchMode(String cameraId, boolean enabled) throws CameraAccessException {
             synchronized(mLock) {
 
@@ -1698,6 +1890,8 @@
                             cameraId);
                 }
 
+                mConcurrentCameraIdCombinations.clear();
+
                 scheduleCameraServiceReconnectionLocked();
             }
         }
diff --git a/core/java/android/hardware/camera2/impl/CameraMetadataNative.java b/core/java/android/hardware/camera2/impl/CameraMetadataNative.java
index 3ae3d78..aefe66f 100644
--- a/core/java/android/hardware/camera2/impl/CameraMetadataNative.java
+++ b/core/java/android/hardware/camera2/impl/CameraMetadataNative.java
@@ -621,6 +621,16 @@
                     }
                 });
         sGetCommandMap.put(
+                CameraCharacteristics.SCALER_MANDATORY_CONCURRENT_STREAM_COMBINATIONS.getNativeKey(),
+                        new GetCommand() {
+                    @Override
+                    @SuppressWarnings("unchecked")
+                    public <T> T getValue(CameraMetadataNative metadata, Key<T> key) {
+                        return (T) metadata.getMandatoryConcurrentStreamCombinations();
+                    }
+                });
+
+        sGetCommandMap.put(
                 CameraCharacteristics.CONTROL_MAX_REGIONS_AE.getNativeKey(), new GetCommand() {
                     @Override
                     @SuppressWarnings("unchecked")
@@ -1247,7 +1257,8 @@
         return ret;
     }
 
-    private MandatoryStreamCombination[] getMandatoryStreamCombinations() {
+    private MandatoryStreamCombination[] getMandatoryStreamCombinationsHelper(
+            boolean getConcurrent) {
         int[] capabilities = getBase(CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES);
         ArrayList<Integer> caps = new ArrayList<Integer>();
         caps.ensureCapacity(capabilities.length);
@@ -1257,7 +1268,13 @@
         int hwLevel = getBase(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL);
         MandatoryStreamCombination.Builder build = new MandatoryStreamCombination.Builder(
                 mCameraId, hwLevel, mDisplaySize, caps, getStreamConfigurationMap());
-        List<MandatoryStreamCombination> combs = build.getAvailableMandatoryStreamCombinations();
+
+        List<MandatoryStreamCombination> combs = null;
+        if (getConcurrent) {
+            combs = build.getAvailableMandatoryConcurrentStreamCombinations();
+        } else {
+            combs = build.getAvailableMandatoryStreamCombinations();
+        }
         if ((combs != null) && (!combs.isEmpty())) {
             MandatoryStreamCombination[] combArray = new MandatoryStreamCombination[combs.size()];
             combArray = combs.toArray(combArray);
@@ -1267,6 +1284,17 @@
         return null;
     }
 
+    private MandatoryStreamCombination[] getMandatoryConcurrentStreamCombinations() {
+        if (!mHasMandatoryConcurrentStreams) {
+            return null;
+        }
+        return getMandatoryStreamCombinationsHelper(true);
+    }
+
+    private MandatoryStreamCombination[] getMandatoryStreamCombinations() {
+        return getMandatoryStreamCombinationsHelper(false);
+    }
+
     private StreamConfigurationMap getStreamConfigurationMap() {
         StreamConfiguration[] configurations = getBase(
                 CameraCharacteristics.SCALER_AVAILABLE_STREAM_CONFIGURATIONS);
@@ -1614,6 +1642,7 @@
     }
 
     private int mCameraId = -1;
+    private boolean mHasMandatoryConcurrentStreams = false;
     private Size mDisplaySize = new Size(0, 0);
 
     /**
@@ -1628,6 +1657,18 @@
     }
 
     /**
+     * Set the current camera Id.
+     *
+     * @param hasMandatoryConcurrentStreams whether the metadata advertises mandatory concurrent
+     *        streams.
+     *
+     * @hide
+     */
+    public void setHasMandatoryConcurrentStreams(boolean hasMandatoryConcurrentStreams) {
+        mHasMandatoryConcurrentStreams = hasMandatoryConcurrentStreams;
+    }
+
+    /**
      * Set the current display size.
      *
      * @param displaySize The current display size.
@@ -1682,6 +1723,7 @@
     public void swap(CameraMetadataNative other) {
         nativeSwap(other);
         mCameraId = other.mCameraId;
+        mHasMandatoryConcurrentStreams = other.mHasMandatoryConcurrentStreams;
         mDisplaySize = other.mDisplaySize;
     }
 
diff --git a/core/java/android/hardware/camera2/params/MandatoryStreamCombination.java b/core/java/android/hardware/camera2/params/MandatoryStreamCombination.java
index 23f18a8..41e1443 100644
--- a/core/java/android/hardware/camera2/params/MandatoryStreamCombination.java
+++ b/core/java/android/hardware/camera2/params/MandatoryStreamCombination.java
@@ -16,30 +16,27 @@
 
 package android.hardware.camera2.params;
 
-import static com.android.internal.util.Preconditions.*;
 import static android.hardware.camera2.params.StreamConfigurationMap.checkArgumentFormat;
 
-import android.annotation.IntRange;
+import static com.android.internal.util.Preconditions.*;
+
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.content.Context;
 import android.graphics.ImageFormat;
 import android.graphics.ImageFormat.Format;
 import android.hardware.camera2.CameraCharacteristics;
-import android.hardware.camera2.CameraCharacteristics.Key;
 import android.hardware.camera2.CameraDevice;
 import android.hardware.camera2.CameraManager;
 import android.hardware.camera2.CameraMetadata;
 import android.hardware.camera2.params.StreamConfigurationMap;
 import android.hardware.camera2.utils.HashCodeHelpers;
-import android.graphics.PixelFormat;
 import android.media.CamcorderProfile;
-import android.util.Size;
 import android.util.Log;
 import android.util.Pair;
+import android.util.Size;
 
-import java.util.Arrays;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collections;
 import java.util.Comparator;
 import java.util.HashMap;
@@ -200,7 +197,6 @@
         mDescription = description;
         mIsReprocessable = isReprocessable;
     }
-
     /**
      * Get the mandatory stream combination description.
      *
@@ -271,7 +267,7 @@
                 mStreamsInformation.hashCode());
     }
 
-    private static enum SizeThreshold { VGA, PREVIEW, RECORD, MAXIMUM }
+    private static enum SizeThreshold { VGA, PREVIEW, RECORD, MAXIMUM, s720p }
     private static enum ReprocessType { NONE, PRIVATE, YUV }
     private static final class StreamTemplate {
         public int mFormat;
@@ -653,6 +649,27 @@
                 /*reprocessType*/ ReprocessType.YUV),
     };
 
+    private static StreamCombinationTemplate sConcurrentStreamCombinations[] = {
+        new StreamCombinationTemplate(new StreamTemplate [] {
+                new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.s720p) },
+                "In-app video / image processing"),
+        new StreamCombinationTemplate(new StreamTemplate [] {
+                new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.s720p) },
+                "preview / preview to GPU"),
+        new StreamCombinationTemplate(new StreamTemplate [] {
+                new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.s720p),
+                new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.s720p)},
+                "In-app video / image processing with preview"),
+        new StreamCombinationTemplate(new StreamTemplate [] {
+                new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.s720p),
+                new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.s720p)},
+                "In-app video / image processing with preview"),
+        new StreamCombinationTemplate(new StreamTemplate [] {
+                new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.s720p),
+                new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.s720p)},
+                "Standard Recording"),
+    };
+
     /**
      * Helper builder class to generate a list of available mandatory stream combinations.
      * @hide
@@ -687,6 +704,64 @@
         }
 
         /**
+          * Retrieve a list of all available mandatory concurrent stream combinations.
+          * This method should only be called for devices which are listed in combinations returned
+          * by CameraManager.getConcurrentStreamingCameraIds.
+          *
+          * @return a non-modifiable list of supported mandatory concurrent stream combinations.
+          */
+        public @NonNull List<MandatoryStreamCombination>
+                getAvailableMandatoryConcurrentStreamCombinations() {
+            // Since concurrent streaming support is optional, we mandate these stream
+            // combinations regardless of camera device capabilities.
+            if (!isColorOutputSupported()) {
+                Log.v(TAG, "Device is not backward compatible!");
+                throw new IllegalArgumentException("Camera device which is not BACKWARD_COMPATIBLE"
+                         + " cannot have mandatory concurrent streams");
+            }
+            Size size720p = new Size(1280, 720);
+
+            ArrayList<MandatoryStreamCombination> availableConcurrentStreamCombinations =
+                    new ArrayList<MandatoryStreamCombination>();
+            availableConcurrentStreamCombinations.ensureCapacity(
+                    sConcurrentStreamCombinations.length);
+            for (StreamCombinationTemplate combTemplate : sConcurrentStreamCombinations) {
+                ArrayList<MandatoryStreamInformation> streamsInfo =
+                        new ArrayList<MandatoryStreamInformation>();
+                streamsInfo.ensureCapacity(combTemplate.mStreamTemplates.length);
+                for (StreamTemplate template : combTemplate.mStreamTemplates) {
+                    MandatoryStreamInformation streamInfo;
+                    List<Size> sizes = new ArrayList<Size>();
+                    Size sizeChosen =
+                            getMinSize(size720p,
+                                    getMaxSize(mStreamConfigMap.getOutputSizes(template.mFormat)));
+                    sizes.add(sizeChosen);
+                    try {
+                        streamInfo = new MandatoryStreamInformation(sizes, template.mFormat);
+                    } catch (IllegalArgumentException e) {
+                        String cause = "No available sizes found for format: " + template.mFormat
+                                + " size threshold: " + template.mSizeThreshold + " combination: "
+                                + combTemplate.mDescription;
+                        throw new RuntimeException(cause, e);
+                    }
+                    streamsInfo.add(streamInfo);
+                }
+
+                MandatoryStreamCombination streamCombination;
+                try {
+                    streamCombination = new MandatoryStreamCombination(streamsInfo,
+                            combTemplate.mDescription, /*isReprocess*/false);
+                } catch (IllegalArgumentException e) {
+                    String cause =  "No stream information for mandatory combination: "
+                            + combTemplate.mDescription;
+                    throw new RuntimeException(cause, e);
+                }
+                availableConcurrentStreamCombinations.add(streamCombination);
+            }
+            return Collections.unmodifiableList(availableConcurrentStreamCombinations);
+        }
+
+        /**
          * Retrieve a list of all available mandatory stream combinations.
          *
          * @return a non-modifiable list of supported mandatory stream combinations or
@@ -965,6 +1040,18 @@
         }
 
         /**
+         * Return the lower size
+         */
+        public static @Nullable Size getMinSize(Size a, Size b) {
+            if (a == null || b == null) {
+                throw new IllegalArgumentException("sizes was empty");
+            }
+            if (a.getWidth() * a.getHeight() < b.getHeight() * b.getWidth()) {
+                return a;
+            }
+            return b;
+        }
+        /**
          * Get the largest size by area.
          *
          * @param sizes an array of sizes, must have at least 1 element
diff --git a/core/java/android/hardware/camera2/utils/CameraIdAndSessionConfiguration.java b/core/java/android/hardware/camera2/utils/CameraIdAndSessionConfiguration.java
new file mode 100644
index 0000000..cdc037c
--- /dev/null
+++ b/core/java/android/hardware/camera2/utils/CameraIdAndSessionConfiguration.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.hardware.camera2.utils;
+
+import android.annotation.NonNull;
+import android.hardware.camera2.params.SessionConfiguration;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * CameraIdAndSessionConfiguration
+ *
+ * Includes the pair of a cameraId and its corresponding SessionConfiguration, to be used with
+ * ICameraService.isConcurrentSessionConfigurationSupported.
+ * @hide
+ */
+public class CameraIdAndSessionConfiguration implements Parcelable {
+
+    private String mCameraId;
+    private SessionConfiguration mSessionConfiguration;
+
+    public CameraIdAndSessionConfiguration(@NonNull String cameraId,
+            @NonNull SessionConfiguration sessionConfiguration) {
+        mCameraId = cameraId;
+        mSessionConfiguration = sessionConfiguration;
+    }
+
+    public static final @NonNull
+            Parcelable.Creator<CameraIdAndSessionConfiguration> CREATOR =
+            new Parcelable.Creator<CameraIdAndSessionConfiguration>() {
+        @Override
+        public CameraIdAndSessionConfiguration createFromParcel(Parcel in) {
+            return new CameraIdAndSessionConfiguration(in);
+        }
+
+        @Override
+        public CameraIdAndSessionConfiguration[] newArray(int size) {
+            return new CameraIdAndSessionConfiguration[size];
+        }
+    };
+
+    private CameraIdAndSessionConfiguration(Parcel in) {
+        readFromParcel(in);
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeString(mCameraId);
+        mSessionConfiguration.writeToParcel(dest, flags);
+    }
+
+    /**
+     * helper for CREATOR
+     */
+    public void readFromParcel(Parcel in) {
+        mCameraId = in.readString();
+        mSessionConfiguration = SessionConfiguration.CREATOR.createFromParcel(in);
+    }
+
+    public @NonNull String getCameraId() {
+        return mCameraId;
+    }
+
+    public @NonNull SessionConfiguration getSessionConfiguration() {
+        return mSessionConfiguration;
+    }
+}
diff --git a/core/java/android/hardware/camera2/utils/ConcurrentCameraIdCombination.java b/core/java/android/hardware/camera2/utils/ConcurrentCameraIdCombination.java
new file mode 100644
index 0000000..8f4d636
--- /dev/null
+++ b/core/java/android/hardware/camera2/utils/ConcurrentCameraIdCombination.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.hardware.camera2.utils;
+
+import android.annotation.NonNull;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * ConcurrentCameraIdCombination
+ *
+ * Includes a list of camera ids that may have sessions configured concurrently.
+ * @hide
+ */
+public class ConcurrentCameraIdCombination implements Parcelable {
+
+    private Set<String> mConcurrentCameraIds = new HashSet<>();
+
+    public static final @NonNull
+            Parcelable.Creator<ConcurrentCameraIdCombination> CREATOR =
+            new Parcelable.Creator<ConcurrentCameraIdCombination>() {
+        @Override
+        public ConcurrentCameraIdCombination createFromParcel(Parcel in) {
+            return new ConcurrentCameraIdCombination(in);
+        }
+
+        @Override
+        public ConcurrentCameraIdCombination[] newArray(int size) {
+            return new ConcurrentCameraIdCombination[size];
+        }
+    };
+
+    private ConcurrentCameraIdCombination(Parcel in) {
+        readFromParcel(in);
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeInt(mConcurrentCameraIds.size());
+        for (String cameraId : mConcurrentCameraIds) {
+            dest.writeString(cameraId);
+        }
+    }
+
+    /**
+     * helper for CREATOR
+     */
+    public void readFromParcel(Parcel in) {
+        mConcurrentCameraIds.clear();
+        int cameraCombinationSize = in.readInt();
+        if (cameraCombinationSize < 0) {
+            throw new RuntimeException("cameraCombinationSize " + cameraCombinationSize
+                    + " should not be negative");
+        }
+        for (int i = 0; i < cameraCombinationSize; i++) {
+            String cameraId = in.readString();
+            if (cameraId == null) {
+                throw new RuntimeException("Failed to read camera id from Parcel");
+            }
+            mConcurrentCameraIds.add(cameraId);
+        }
+    }
+
+    /**
+     * Get this concurrent camera id combination.
+     */
+    public Set<String> getConcurrentCameraIdCombination() {
+        return mConcurrentCameraIds;
+    }
+}
diff --git a/core/java/android/hardware/display/BrightnessConfiguration.java b/core/java/android/hardware/display/BrightnessConfiguration.java
index 13122d2..6412a0c 100644
--- a/core/java/android/hardware/display/BrightnessConfiguration.java
+++ b/core/java/android/hardware/display/BrightnessConfiguration.java
@@ -61,7 +61,7 @@
     private static final String ATTR_MODEL_LOWER_BOUND = "model-lower-bound";
     private static final String ATTR_MODEL_UPPER_BOUND = "model-upper-bound";
     /**
-     * Returned from {@link #getShortTermModelTimeout()} if no timeout has been set.
+     * Returned from {@link #getShortTermModelTimeoutMillis()} if no timeout has been set.
      * In this case the device will use the default timeout available in the
      * {@link BrightnessConfiguration} returned from
      * {@link DisplayManager#getDefaultBrightnessConfiguration()}.
@@ -160,7 +160,7 @@
      * {@link #getShortTermModelUpperLuxMultiplier()} to decide whether to keep any adjustment
      * the user has made to adaptive brightness.
      */
-    public long getShortTermModelTimeout() {
+    public long getShortTermModelTimeoutMillis() {
         return mShortTermModelTimeout;
     }
 
@@ -326,7 +326,7 @@
             builder.setDescription(description);
             final boolean shouldCollectColorSamples = in.readBoolean();
             builder.setShouldCollectColorSamples(shouldCollectColorSamples);
-            builder.setShortTermModelTimeout(in.readLong());
+            builder.setShortTermModelTimeoutMillis(in.readLong());
             builder.setShortTermModelLowerLuxMultiplier(in.readFloat());
             builder.setShortTermModelUpperLuxMultiplier(in.readFloat());
             return builder.build();
@@ -487,7 +487,7 @@
             builder.addCorrectionByCategory(category, correction);
         }
         builder.setShouldCollectColorSamples(shouldCollectColorSamples);
-        builder.setShortTermModelTimeout(shortTermModelTimeout);
+        builder.setShortTermModelTimeoutMillis(shortTermModelTimeout);
         builder.setShortTermModelLowerLuxMultiplier(shortTermModelLowerLuxMultiplier);
         builder.setShortTermModelUpperLuxMultiplier(shortTermModelUpperLuxMultiplier);
         return builder.build();
@@ -673,8 +673,8 @@
          * adjustment the user has made to adaptive brightness.
          */
         @NonNull
-        public Builder setShortTermModelTimeout(long shortTermModelTimeout) {
-            mShortTermModelTimeout = shortTermModelTimeout;
+        public Builder setShortTermModelTimeoutMillis(long shortTermModelTimeoutMillis) {
+            mShortTermModelTimeout = shortTermModelTimeoutMillis;
             return this;
         }
 
diff --git a/core/java/android/os/HwParcel.java b/core/java/android/os/HwParcel.java
index fb36e6f..228548a 100644
--- a/core/java/android/os/HwParcel.java
+++ b/core/java/android/os/HwParcel.java
@@ -38,6 +38,7 @@
 public class HwParcel {
     private static final String TAG = "HwParcel";
 
+    /** @hide */
     @IntDef(prefix = { "STATUS_" }, value = {
         STATUS_SUCCESS,
     })
diff --git a/core/java/android/os/connectivity/WifiActivityEnergyInfo.java b/core/java/android/os/connectivity/WifiActivityEnergyInfo.java
index 664b6c8..016cc2f 100644
--- a/core/java/android/os/connectivity/WifiActivityEnergyInfo.java
+++ b/core/java/android/os/connectivity/WifiActivityEnergyInfo.java
@@ -16,7 +16,9 @@
 
 package android.os.connectivity;
 
+import android.annotation.ElapsedRealtimeLong;
 import android.annotation.IntDef;
+import android.annotation.IntRange;
 import android.annotation.NonNull;
 import android.annotation.SystemApi;
 import android.app.ActivityThread;
@@ -37,14 +39,20 @@
  */
 @SystemApi
 public final class WifiActivityEnergyInfo implements Parcelable {
-    private long mTimeSinceBootMillis;
+    @ElapsedRealtimeLong
+    private final long mTimeSinceBootMillis;
     @StackState
-    private int mStackState;
-    private long mControllerTxDurationMillis;
-    private long mControllerRxDurationMillis;
-    private long mControllerScanDurationMillis;
-    private long mControllerIdleDurationMillis;
-    private long mControllerEnergyUsedMicroJoules;
+    private final int mStackState;
+    @IntRange(from = 0)
+    private final long mControllerTxDurationMillis;
+    @IntRange(from = 0)
+    private final long mControllerRxDurationMillis;
+    @IntRange(from = 0)
+    private final long mControllerScanDurationMillis;
+    @IntRange(from = 0)
+    private final long mControllerIdleDurationMillis;
+    @IntRange(from = 0)
+    private final long mControllerEnergyUsedMicroJoules;
 
     /** @hide */
     @Retention(RetentionPolicy.SOURCE)
@@ -67,7 +75,7 @@
     /**
      * Constructor.
      *
-     * @param timeSinceBootMillis the time since boot, in milliseconds.
+     * @param timeSinceBootMillis the elapsed real time since boot, in milliseconds.
      * @param stackState The current state of the Wifi Stack. One of {@link #STACK_STATE_INVALID},
      *                   {@link #STACK_STATE_STATE_ACTIVE}, {@link #STACK_STATE_STATE_SCANNING},
      *                   or {@link #STACK_STATE_STATE_IDLE}.
@@ -78,23 +86,27 @@
      *                       receiving.
      */
     public WifiActivityEnergyInfo(
-            long timeSinceBootMillis,
+            @ElapsedRealtimeLong long timeSinceBootMillis,
             @StackState int stackState,
-            long txDurationMillis,
-            long rxDurationMillis,
-            long scanDurationMillis,
-            long idleDurationMillis) {
-        mTimeSinceBootMillis = timeSinceBootMillis;
-        mStackState = stackState;
-        mControllerTxDurationMillis = txDurationMillis;
-        mControllerRxDurationMillis = rxDurationMillis;
-        mControllerScanDurationMillis = scanDurationMillis;
-        mControllerIdleDurationMillis = idleDurationMillis;
+            @IntRange(from = 0) long txDurationMillis,
+            @IntRange(from = 0) long rxDurationMillis,
+            @IntRange(from = 0) long scanDurationMillis,
+            @IntRange(from = 0) long idleDurationMillis) {
 
+        this(timeSinceBootMillis,
+                stackState,
+                txDurationMillis,
+                rxDurationMillis,
+                scanDurationMillis,
+                idleDurationMillis,
+                calculateEnergyMicroJoules(txDurationMillis, rxDurationMillis, idleDurationMillis));
+    }
+
+    private static long calculateEnergyMicroJoules(
+            long txDurationMillis, long rxDurationMillis, long idleDurationMillis) {
         final Context context = ActivityThread.currentActivityThread().getSystemContext();
         if (context == null) {
-            mControllerEnergyUsedMicroJoules = 0L;
-            return;
+            return 0L;
         }
         // Calculate energy used using PowerProfile.
         PowerProfile powerProfile = new PowerProfile(context);
@@ -106,10 +118,28 @@
                 PowerProfile.POWER_WIFI_CONTROLLER_TX);
         final double voltage = powerProfile.getAveragePower(
                 PowerProfile.POWER_WIFI_CONTROLLER_OPERATING_VOLTAGE) / 1000.0;
-        final long energyUsedMicroJoules = (long) ((mControllerTxDurationMillis * txCurrent
-                + mControllerRxDurationMillis * rxCurrent
-                + mControllerIdleDurationMillis * rxIdleCurrent)
+
+        return (long) ((txDurationMillis * txCurrent
+                + rxDurationMillis * rxCurrent
+                + idleDurationMillis * rxIdleCurrent)
                 * voltage);
+    }
+
+    /** @hide */
+    public WifiActivityEnergyInfo(
+            @ElapsedRealtimeLong long timeSinceBootMillis,
+            @StackState int stackState,
+            @IntRange(from = 0) long txDurationMillis,
+            @IntRange(from = 0) long rxDurationMillis,
+            @IntRange(from = 0) long scanDurationMillis,
+            @IntRange(from = 0) long idleDurationMillis,
+            @IntRange(from = 0) long energyUsedMicroJoules) {
+        mTimeSinceBootMillis = timeSinceBootMillis;
+        mStackState = stackState;
+        mControllerTxDurationMillis = txDurationMillis;
+        mControllerRxDurationMillis = rxDurationMillis;
+        mControllerScanDurationMillis = scanDurationMillis;
+        mControllerIdleDurationMillis = idleDurationMillis;
         mControllerEnergyUsedMicroJoules = energyUsedMicroJoules;
     }
 
@@ -158,16 +188,12 @@
         return 0;
     }
 
-    /** Get the timestamp (milliseconds since boot) of record creation. */
+    /** Get the timestamp (elapsed real time milliseconds since boot) of record creation. */
+    @ElapsedRealtimeLong
     public long getTimeSinceBootMillis() {
         return mTimeSinceBootMillis;
     }
 
-    /** Set the timestamp (milliseconds since boot) of record creation. */
-    public void setTimeSinceBootMillis(long timeSinceBootMillis) {
-        mTimeSinceBootMillis = timeSinceBootMillis;
-    }
-
     /**
      * Get the Wifi stack reported state. One of {@link #STACK_STATE_INVALID},
      * {@link #STACK_STATE_STATE_ACTIVE}, {@link #STACK_STATE_STATE_SCANNING},
@@ -178,66 +204,40 @@
         return mStackState;
     }
 
-    /**
-     * Set the Wifi stack reported state. One of {@link #STACK_STATE_INVALID},
-     * {@link #STACK_STATE_STATE_ACTIVE}, {@link #STACK_STATE_STATE_SCANNING},
-     * {@link #STACK_STATE_STATE_IDLE}.
-     */
-    public void setStackState(@StackState int stackState) {
-        mStackState = stackState;
-    }
-
     /** Get the Wifi transmission duration, in milliseconds. */
+    @IntRange(from = 0)
     public long getControllerTxDurationMillis() {
         return mControllerTxDurationMillis;
     }
 
-    /** Set the Wifi transmission duration, in milliseconds. */
-    public void setControllerTxDurationMillis(long controllerTxDurationMillis) {
-        mControllerTxDurationMillis = controllerTxDurationMillis;
-    }
-
     /** Get the Wifi receive duration, in milliseconds. */
+    @IntRange(from = 0)
     public long getControllerRxDurationMillis() {
         return mControllerRxDurationMillis;
     }
 
-    /** Set the Wifi receive duration, in milliseconds. */
-    public void setControllerRxDurationMillis(long controllerRxDurationMillis) {
-        mControllerRxDurationMillis = controllerRxDurationMillis;
-    }
-
     /** Get the Wifi scan duration, in milliseconds. */
+    @IntRange(from = 0)
     public long getControllerScanDurationMillis() {
         return mControllerScanDurationMillis;
     }
 
-    /** Set the Wifi scan duration, in milliseconds. */
-    public void setControllerScanDurationMillis(long controllerScanDurationMillis) {
-        mControllerScanDurationMillis = controllerScanDurationMillis;
-    }
-
     /** Get the Wifi idle duration, in milliseconds. */
+    @IntRange(from = 0)
     public long getControllerIdleDurationMillis() {
         return mControllerIdleDurationMillis;
     }
 
-    /** Set the Wifi idle duration, in milliseconds. */
-    public void setControllerIdleDurationMillis(long controllerIdleDurationMillis) {
-        mControllerIdleDurationMillis = controllerIdleDurationMillis;
-    }
-
     /** Get the energy consumed by Wifi, in microjoules. */
+    @IntRange(from = 0)
     public long getControllerEnergyUsedMicroJoules() {
         return mControllerEnergyUsedMicroJoules;
     }
 
-    /** Set the energy consumed by Wifi, in microjoules. */
-    public void setControllerEnergyUsedMicroJoules(long controllerEnergyUsedMicroJoules) {
-        mControllerEnergyUsedMicroJoules = controllerEnergyUsedMicroJoules;
-    }
-
-    /** Returns true if the record is valid, false otherwise. */
+    /**
+     * Returns true if the record is valid, false otherwise.
+     * @hide
+     */
     public boolean isValid() {
         return mControllerTxDurationMillis >= 0
                 && mControllerRxDurationMillis >= 0
diff --git a/core/java/android/service/controls/TokenProvider.aidl b/core/java/android/service/controls/TokenProvider.aidl
new file mode 100644
index 0000000..8f4b795
--- /dev/null
+++ b/core/java/android/service/controls/TokenProvider.aidl
@@ -0,0 +1,7 @@
+package android.service.controls;
+
+/** @hide */
+interface TokenProvider {
+    void setAuthToken(String token);
+    String getAccountName();
+}
\ No newline at end of file
diff --git a/core/java/android/speech/tts/TextToSpeechService.java b/core/java/android/speech/tts/TextToSpeechService.java
index 7815864..26c8314 100644
--- a/core/java/android/speech/tts/TextToSpeechService.java
+++ b/core/java/android/speech/tts/TextToSpeechService.java
@@ -1259,6 +1259,7 @@
     @Override
     public IBinder onBind(Intent intent) {
         if (TextToSpeech.Engine.INTENT_ACTION_TTS_SERVICE.equals(intent.getAction())) {
+            Binder.allowBlocking(mBinder.asBinder());
             return mBinder;
         }
         return null;
diff --git a/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java b/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java
index de64573..b232efc 100644
--- a/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java
+++ b/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java
@@ -364,6 +364,12 @@
      */
     public static final String NAV_BAR_HANDLE_FORCE_OPAQUE = "nav_bar_handle_force_opaque";
 
+    /**
+     * (boolean) Whether to force the Nav Bar handle to remain visible over the lockscreen.
+     */
+    public static final String NAV_BAR_HANDLE_SHOW_OVER_LOCKSCREEN =
+            "nav_bar_handle_show_over_lockscreen";
+
     private SystemUiDeviceConfigFlags() {
     }
 }
diff --git a/core/jni/android/graphics/Bitmap.cpp b/core/jni/android/graphics/Bitmap.cpp
index eb7d432..30e914d 100755
--- a/core/jni/android/graphics/Bitmap.cpp
+++ b/core/jni/android/graphics/Bitmap.cpp
@@ -34,7 +34,6 @@
 #include <string>
 
 #define DEBUG_PARCEL 0
-#define ASHMEM_BITMAP_MIN_SIZE (128 * (1 << 10))
 
 static jclass   gBitmap_class;
 static jfieldID gBitmap_nativePtr;
@@ -587,7 +586,6 @@
 
     android::Parcel* p = android::parcelForJavaObject(env, parcel);
 
-    const bool        isMutable = p->readInt32() != 0;
     const SkColorType colorType = (SkColorType)p->readInt32();
     const SkAlphaType alphaType = (SkAlphaType)p->readInt32();
     const uint32_t    colorSpaceSize = p->readUint32();
@@ -636,11 +634,10 @@
 
     // Map the bitmap in place from the ashmem region if possible otherwise copy.
     sk_sp<Bitmap> nativeBitmap;
-    if (blob.fd() >= 0 && (blob.isMutable() || !isMutable) && (size >= ASHMEM_BITMAP_MIN_SIZE)) {
+    if (blob.fd() >= 0 && !blob.isMutable()) {
 #if DEBUG_PARCEL
-        ALOGD("Bitmap.createFromParcel: mapped contents of %s bitmap from %s blob "
+        ALOGD("Bitmap.createFromParcel: mapped contents of bitmap from %s blob "
                 "(fds %s)",
-                isMutable ? "mutable" : "immutable",
                 blob.isMutable() ? "mutable" : "immutable",
                 p->allowFds() ? "allowed" : "forbidden");
 #endif
@@ -657,7 +654,7 @@
         // Map the pixels in place and take ownership of the ashmem region. We must also respect the
         // rowBytes value already set on the bitmap instead of attempting to compute our own.
         nativeBitmap = Bitmap::createFrom(bitmap->info(), bitmap->rowBytes(), dupFd,
-                                          const_cast<void*>(blob.data()), size, !isMutable);
+                                          const_cast<void*>(blob.data()), size, true);
         if (!nativeBitmap) {
             close(dupFd);
             blob.release();
@@ -695,7 +692,7 @@
     }
 
     return createBitmap(env, nativeBitmap.release(),
-            getPremulBitmapCreateFlags(isMutable), NULL, NULL, density);
+            getPremulBitmapCreateFlags(false), NULL, NULL, density);
 #else
     doThrowRE(env, "Cannot use parcels outside of Android");
     return NULL;
@@ -703,9 +700,7 @@
 }
 
 static jboolean Bitmap_writeToParcel(JNIEnv* env, jobject,
-                                     jlong bitmapHandle,
-                                     jboolean isMutable, jint density,
-                                     jobject parcel) {
+                                     jlong bitmapHandle, jint density, jobject parcel) {
 #ifdef __ANDROID__ // Layoutlib does not support parcel
     if (parcel == NULL) {
         SkDebugf("------- writeToParcel null parcel\n");
@@ -718,7 +713,6 @@
     auto bitmapWrapper = reinterpret_cast<BitmapWrapper*>(bitmapHandle);
     bitmapWrapper->getSkBitmap(&bitmap);
 
-    p->writeInt32(isMutable);
     p->writeInt32(bitmap.colorType());
     p->writeInt32(bitmap.alphaType());
     SkColorSpace* colorSpace = bitmap.colorSpace();
@@ -745,7 +739,7 @@
     // Transfer the underlying ashmem region if we have one and it's immutable.
     android::status_t status;
     int fd = bitmapWrapper->bitmap().getAshmemFd();
-    if (fd >= 0 && !isMutable && p->allowFds()) {
+    if (fd >= 0 && p->allowFds()) {
 #if DEBUG_PARCEL
         ALOGD("Bitmap.writeToParcel: transferring immutable bitmap's ashmem fd as "
                 "immutable blob (fds %s)",
@@ -761,17 +755,14 @@
     }
 
     // Copy the bitmap to a new blob.
-    bool mutableCopy = isMutable;
 #if DEBUG_PARCEL
-    ALOGD("Bitmap.writeToParcel: copying %s bitmap into new %s blob (fds %s)",
-            isMutable ? "mutable" : "immutable",
-            mutableCopy ? "mutable" : "immutable",
+    ALOGD("Bitmap.writeToParcel: copying bitmap into new blob (fds %s)",
             p->allowFds() ? "allowed" : "forbidden");
 #endif
 
     size_t size = bitmap.computeByteSize();
     android::Parcel::WritableBlob blob;
-    status = p->writeBlob(size, mutableCopy, &blob);
+    status = p->writeBlob(size, false, &blob);
     if (status) {
         doThrowRE(env, "Could not copy bitmap to parcel blob.");
         return JNI_FALSE;
@@ -1109,7 +1100,7 @@
     {   "nativeCreateFromParcel",
         "(Landroid/os/Parcel;)Landroid/graphics/Bitmap;",
         (void*)Bitmap_createFromParcel },
-    {   "nativeWriteToParcel",      "(JZILandroid/os/Parcel;)Z",
+    {   "nativeWriteToParcel",      "(JILandroid/os/Parcel;)Z",
         (void*)Bitmap_writeToParcel },
     {   "nativeExtractAlpha",       "(JJ[I)Landroid/graphics/Bitmap;",
         (void*)Bitmap_extractAlpha },
diff --git a/core/tests/coretests/src/android/hardware/display/BrightnessConfigurationTest.java b/core/tests/coretests/src/android/hardware/display/BrightnessConfigurationTest.java
index 895b22c..4370462 100644
--- a/core/tests/coretests/src/android/hardware/display/BrightnessConfigurationTest.java
+++ b/core/tests/coretests/src/android/hardware/display/BrightnessConfigurationTest.java
@@ -132,7 +132,7 @@
         BrightnessConfiguration.Builder builder =
                 new BrightnessConfiguration.Builder(LUX_LEVELS, NITS_LEVELS);
         builder.setShouldCollectColorSamples(true);
-        builder.setShortTermModelTimeout(1234L);
+        builder.setShortTermModelTimeoutMillis(1234L);
         builder.setShortTermModelLowerLuxMultiplier(0.9f);
         builder.setShortTermModelUpperLuxMultiplier(0.2f);
         builder.addCorrectionByCategory(3,
@@ -153,7 +153,7 @@
         BrightnessConfiguration.Builder builder =
                 new BrightnessConfiguration.Builder(LUX_LEVELS, NITS_LEVELS);
         builder.setShouldCollectColorSamples(true);
-        builder.setShortTermModelTimeout(123L);
+        builder.setShortTermModelTimeoutMillis(123L);
         builder.setShortTermModelLowerLuxMultiplier(0.4f);
         builder.setShortTermModelUpperLuxMultiplier(0.8f);
         builder.addCorrectionByCategory(3,
@@ -236,7 +236,7 @@
         assertNotEquals(baseConfig, colorCollectionDiffers);
 
         builder = new BrightnessConfiguration.Builder(LUX_LEVELS, NITS_LEVELS);
-        builder.setShortTermModelTimeout(300L);
+        builder.setShortTermModelTimeoutMillis(300L);
         BrightnessConfiguration timeoutDiffers = builder.build();
         assertNotEquals(baseConfig, timeoutDiffers);
 
diff --git a/core/tests/hosttests/test-apps/DownloadManagerTestApp/src/com/android/frameworks/downloadmanagertests/DownloadManagerTestRunner.java b/core/tests/hosttests/test-apps/DownloadManagerTestApp/src/com/android/frameworks/downloadmanagertests/DownloadManagerTestRunner.java
index 70c266a..ac63853 100644
--- a/core/tests/hosttests/test-apps/DownloadManagerTestApp/src/com/android/frameworks/downloadmanagertests/DownloadManagerTestRunner.java
+++ b/core/tests/hosttests/test-apps/DownloadManagerTestApp/src/com/android/frameworks/downloadmanagertests/DownloadManagerTestRunner.java
@@ -58,7 +58,7 @@
         }
         // enable verbose wifi logging
         ((WifiManager)getContext().getSystemService(Context.WIFI_SERVICE))
-            .enableVerboseLogging(1);
+                .setVerboseLoggingEnabled(true);
         super.onCreate(icicle);
     }
 
diff --git a/graphics/java/android/graphics/Bitmap.java b/graphics/java/android/graphics/Bitmap.java
index ac094ba..9c2e95f 100644
--- a/graphics/java/android/graphics/Bitmap.java
+++ b/graphics/java/android/graphics/Bitmap.java
@@ -2132,7 +2132,7 @@
     public void writeToParcel(Parcel p, int flags) {
         checkRecycled("Can't parcel a recycled bitmap");
         noteHardwareBitmapSlowCall();
-        if (!nativeWriteToParcel(mNativePtr, isMutable(), mDensity, p)) {
+        if (!nativeWriteToParcel(mNativePtr, mDensity, p)) {
             throw new RuntimeException("native writeToParcel failed");
         }
     }
@@ -2285,7 +2285,6 @@
     private static native Bitmap nativeCreateFromParcel(Parcel p);
     // returns true on success
     private static native boolean nativeWriteToParcel(long nativeBitmap,
-                                                      boolean isMutable,
                                                       int density,
                                                       Parcel p);
     // returns a new bitmap built from the native bitmap's alpha, and the paint
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
index 9d4c24e..84dde05 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
@@ -1141,7 +1141,7 @@
         } else if (isActive()) {
             summary.append(getSummary(mContext, /* ssid */ null, getDetailedState(),
                     mInfo != null && mInfo.isEphemeral(),
-                    mInfo != null ? mInfo.getAppPackageName() : null));
+                    mInfo != null ? mInfo.getRequestingPackageName() : null));
         } else { // not active
             if (mConfig != null && mConfig.hasNoInternetAccess()) {
                 int messageID = mConfig.getNetworkSelectionStatus().isNetworkPermanentlyDisabled()
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java
index ed4ff08..26abf71 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java
@@ -226,7 +226,7 @@
         mConnectivityManager = connectivityManager;
 
         // check if verbose logging developer option has been turned on or off
-        sVerboseLogging = mWifiManager != null && (mWifiManager.getVerboseLoggingLevel() > 0);
+        sVerboseLogging = mWifiManager != null && mWifiManager.isVerboseLoggingEnabled();
 
         mFilter = filter;
 
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiUtils.java b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiUtils.java
index b93b000..78ccba0 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiUtils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiUtils.java
@@ -102,10 +102,10 @@
             if (accessPoint.getSpeed() != AccessPoint.Speed.NONE) {
                 visibility.append(" speed=").append(accessPoint.getSpeedLabel());
             }
-            visibility.append(String.format(" tx=%.1f,", info.getTxSuccessRate()));
-            visibility.append(String.format("%.1f,", info.getTxRetriesRate()));
-            visibility.append(String.format("%.1f ", info.getTxBadRate()));
-            visibility.append(String.format("rx=%.1f", info.getRxSuccessRate()));
+            visibility.append(String.format(" tx=%.1f,", info.getSuccessfulTxPacketsPerSecond()));
+            visibility.append(String.format("%.1f,", info.getRetriedTxPacketsPerSecond()));
+            visibility.append(String.format("%.1f ", info.getLostTxPacketsPerSecond()));
+            visibility.append(String.format("rx=%.1f", info.getSuccessfulRxPacketsPerSecond()));
         }
 
         int maxRssi5 = INVALID_RSSI;
diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/AccessPointTest.java b/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/AccessPointTest.java
index 03201ae..42f3cbb 100644
--- a/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/AccessPointTest.java
+++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/AccessPointTest.java
@@ -507,7 +507,7 @@
         WifiInfo wifiInfo = new WifiInfo();
         wifiInfo.setSSID(WifiSsid.createFromAsciiEncoded(TEST_SSID));
         wifiInfo.setEphemeral(true);
-        wifiInfo.setAppPackageName(appPackageName);
+        wifiInfo.setRequestingPackageName(appPackageName);
         wifiInfo.setRssi(rssi);
 
         Context context = mock(Context.class);
diff --git a/packages/SystemUI/res/color/light_background.xml b/packages/SystemUI/res/color/light_background.xml
new file mode 100644
index 0000000..2effd99
--- /dev/null
+++ b/packages/SystemUI/res/color/light_background.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+  <item android:state_enabled="false"
+        android:color="@color/control_default_background" />
+  <item android:color="@color/GM2_yellow_50" />
+</selector>
diff --git a/packages/SystemUI/res/color/light_foreground.xml b/packages/SystemUI/res/color/light_foreground.xml
new file mode 100644
index 0000000..8143028
--- /dev/null
+++ b/packages/SystemUI/res/color/light_foreground.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+  <item android:state_enabled="false"
+        android:color="@color/control_default_foreground" />
+  <item android:color="@color/GM2_orange_900" />
+</selector>
diff --git a/packages/SystemUI/res/color/lock_background.xml b/packages/SystemUI/res/color/lock_background.xml
new file mode 100644
index 0000000..646fe5d
--- /dev/null
+++ b/packages/SystemUI/res/color/lock_background.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+  <item android:state_enabled="false"
+        android:color="@color/control_default_background" />
+  <item android:color="@color/GM2_blue_50" />
+</selector>
diff --git a/packages/SystemUI/res/color/lock_foreground.xml b/packages/SystemUI/res/color/lock_foreground.xml
new file mode 100644
index 0000000..3e05653
--- /dev/null
+++ b/packages/SystemUI/res/color/lock_foreground.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+  <item android:state_enabled="false"
+        android:color="@color/control_default_foreground" />
+  <item android:color="@color/GM2_blue_700" />
+</selector>
diff --git a/packages/SystemUI/res/color/unknown_foreground.xml b/packages/SystemUI/res/color/unknown_foreground.xml
new file mode 100644
index 0000000..bf028f1
--- /dev/null
+++ b/packages/SystemUI/res/color/unknown_foreground.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+   <item android:state_enabled="false"
+         android:color="@color/control_default_foreground" />
+   <item android:color="@color/GM2_blue_700" />
+ </selector>
diff --git a/packages/SystemUI/res/drawable/control_background.xml b/packages/SystemUI/res/drawable/control_background.xml
new file mode 100644
index 0000000..b246ea0
--- /dev/null
+++ b/packages/SystemUI/res/drawable/control_background.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+* Copyright 2020, The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*     http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+-->
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
+  <item>
+    <shape>
+      <solid android:color="?android:attr/colorBackgroundFloating"/>
+      <corners android:radius="@dimen/control_corner_radius" />
+    </shape>
+  </item>
+  <item
+      android:id="@+id/clip_layer">
+    <clip
+        android:clipOrientation="horizontal"
+        android:drawable="@drawable/control_layer"/>
+  </item>
+</layer-list>
diff --git a/packages/SystemUI/res/drawable/control_layer.xml b/packages/SystemUI/res/drawable/control_layer.xml
new file mode 100644
index 0000000..fe8c4a4
--- /dev/null
+++ b/packages/SystemUI/res/drawable/control_layer.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+* Copyright 2020, The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*     http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+-->
+<shape xmlns:android="http://schemas.android.com/apk/res/android">
+  <solid android:color="@android:color/transparent"/>
+  <corners android:radius="@dimen/control_corner_radius" />
+</shape>
diff --git a/packages/SystemUI/res/drawable/control_no_favorites_background.xml b/packages/SystemUI/res/drawable/control_no_favorites_background.xml
new file mode 100644
index 0000000..1e282ad
--- /dev/null
+++ b/packages/SystemUI/res/drawable/control_no_favorites_background.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+* Copyright 2020, The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*     http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+-->
+<shape xmlns:android="http://schemas.android.com/apk/res/android">
+  <stroke android:width="1dp" android:color="?android:attr/colorBackgroundFloating"/>
+  <corners android:radius="@dimen/control_corner_radius" />
+</shape>
diff --git a/packages/SystemUI/res/drawable/ic_device_thermostat_gm2_24px.xml b/packages/SystemUI/res/drawable/ic_device_thermostat_gm2_24px.xml
new file mode 100644
index 0000000..45a658f
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_device_thermostat_gm2_24px.xml
@@ -0,0 +1,9 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24"
+    android:viewportHeight="24">
+  <path
+      android:fillColor="#FF000000"
+      android:pathData="M18,9h-5v2h5m3,-6h-8v2h8m-9,11.97c0.62,-0.83 1,-1.85 1,-2.97 0,-1.63 -0.79,-3.09 -2,-4V6c0,-1.66 -1.34,-3 -3,-3S5,4.34 5,6v6c-1.21,0.91 -2,2.37 -2,4 0,1.12 0.38,2.14 1,2.97V19h0.02c0.91,1.21 2.35,2 3.98,2s3.06,-0.79 3.98,-2H12v-0.03zM6.2,13.6L7,13V6c0,-0.55 0.45,-1 1,-1s1,0.45 1,1v7l0.8,0.6c0.75,0.57 1.2,1.46 1.2,2.4H5c0,-0.94 0.45,-1.84 1.2,-2.4z"/>
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_light_off_gm2_24px.xml b/packages/SystemUI/res/drawable/ic_light_off_gm2_24px.xml
new file mode 100644
index 0000000..78c3cc5
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_light_off_gm2_24px.xml
@@ -0,0 +1,19 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24"
+    android:viewportHeight="24">
+  <path
+      android:fillColor="#FF000000"
+      android:pathData="M9,21v-1h6v1c0,0.55 -0.45,1 -1,1h-4c-0.55,0 -1,-0.45 -1,-1z"/>
+  <group>
+    <clip-path android:pathData="M0,0h24v24H0z M 0,0"/>
+  </group>
+  <path
+      android:fillColor="#FF000000"
+      android:pathData="M12,2c-1.89,0 -3.6,0.75 -4.86,1.97l1.41,1.41C9.45,4.53 10.67,4 12,4c2.76,0 5,2.24 5,5 0,1.28 -0.5,2.5 -1.36,3.42l-0.02,0.02 1.41,1.41C18.25,12.6 19,10.89 19,9c0,-3.86 -3.14,-7 -7,-7z"
+      android:fillType="evenOdd"/>
+  <path
+      android:fillColor="#FF000000"
+      android:pathData="M2.92,2.29L1.65,3.57l3.59,3.59C5.09,7.75 5,8.36 5,9c0,2.38 1.19,4.47 3,5.74V17c0,0.55 0.45,1 1,1h6c0.3,0 0.57,-0.13 0.75,-0.34L20.09,22l1.27,-1.27L2.92,2.29zM10,16v-2.3l-0.85,-0.6C7.8,12.16 7,10.63 7,9v-0.08L14.09,16H10z"/>
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_lock_gm2_24px.xml b/packages/SystemUI/res/drawable/ic_lock_gm2_24px.xml
new file mode 100644
index 0000000..f4299e6
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_lock_gm2_24px.xml
@@ -0,0 +1,9 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24"
+    android:viewportHeight="24">
+  <path
+      android:fillColor="#FF000000"
+      android:pathData="M18,8h-1L17,6c0,-2.76 -2.24,-5 -5,-5S7,3.24 7,6v2L6,8c-1.1,0 -2,0.9 -2,2v10c0,1.1 0.9,2 2,2h12c1.1,0 2,-0.9 2,-2L20,10c0,-1.1 -0.9,-2 -2,-2zM9,6c0,-1.66 1.34,-3 3,-3s3,1.34 3,3v2L9,8L9,6zM18,20L6,20L6,10h12v10zM12,17c1.1,0 2,-0.9 2,-2s-0.9,-2 -2,-2 -2,0.9 -2,2 0.9,2 2,2z"/>
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_lock_open_gm2_24px.xml b/packages/SystemUI/res/drawable/ic_lock_open_gm2_24px.xml
new file mode 100644
index 0000000..59fe0a9
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_lock_open_gm2_24px.xml
@@ -0,0 +1,12 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24"
+    android:viewportHeight="24">
+  <path
+      android:fillColor="#FF000000"
+      android:pathData="M18,8h-1L17,6c0,-2.76 -2.24,-5 -5,-5S7,3.24 7,6h2c0,-1.66 1.34,-3 3,-3s3,1.34 3,3v2L6,8c-1.1,0 -2,0.9 -2,2v10c0,1.1 0.9,2 2,2h12c1.1,0 2,-0.9 2,-2L20,10c0,-1.1 -0.9,-2 -2,-2zM18,20L6,20L6,10h12v10z"/>
+  <path
+      android:fillColor="#FF000000"
+      android:pathData="M12,15m-2,0a2,2 0,1 1,4 0a2,2 0,1 1,-4 0"/>
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_more_vert.xml b/packages/SystemUI/res/drawable/ic_more_vert.xml
new file mode 100644
index 0000000..1309fa8
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_more_vert.xml
@@ -0,0 +1,24 @@
+<!--
+    Copyright (C) 2020 The Android Open Source Project
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24"
+    android:viewportHeight="24">
+  <path
+      android:fillColor="#FF000000"
+      android:pathData="M12,8c1.1,0 2,-0.9 2,-2s-0.9,-2 -2,-2 -2,0.9 -2,2 0.9,2 2,2zM12,10c-1.1,0 -2,0.9 -2,2s0.9,2 2,2 2,-0.9 2,-2 -0.9,-2 -2,-2zM12,16c-1.1,0 -2,0.9 -2,2s0.9,2 2,2 2,-0.9 2,-2 -0.9,-2 -2,-2z"/>
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_power_gm2_24px.xml b/packages/SystemUI/res/drawable/ic_power_gm2_24px.xml
new file mode 100644
index 0000000..cd95719
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_power_gm2_24px.xml
@@ -0,0 +1,9 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24"
+    android:viewportHeight="24">
+  <path
+      android:fillColor="#FF000000"
+      android:pathData="M16,9v4.66l-3.5,3.51V19h-1v-1.83L8,13.65V9h8m0,-6h-2v4h-4V3H8v4h-0.01C6.9,6.99 6,7.89 6,8.98v5.52L9.5,18v3h5v-3l3.5,-3.51V9c0,-1.1 -0.9,-2 -2,-2V3z"/>
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_power_off_gm2_24px.xml b/packages/SystemUI/res/drawable/ic_power_off_gm2_24px.xml
new file mode 100644
index 0000000..3eb7dd6
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_power_off_gm2_24px.xml
@@ -0,0 +1,9 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24"
+    android:viewportHeight="24">
+  <path
+      android:fillColor="#FF000000"
+      android:pathData="M21.19,21.19L2.81,2.81 1.39,4.22l4.63,4.63L6,14.5 9.5,18v3h5v-3l0.34,-0.34 4.94,4.94 1.41,-1.41zM12.5,17.17L12.5,19h-1v-1.83L8,13.65v-2.83l5.42,5.42 -0.92,0.93zM11.83,9L8,5.17L8,3h2v4h4L14,3h2v4c1.1,0 2,0.9 2,2v5.49l-0.34,0.34L16,13.17L16,9h-4.17z"/>
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_power_settings_new_gm2_24px.xml b/packages/SystemUI/res/drawable/ic_power_settings_new_gm2_24px.xml
new file mode 100644
index 0000000..f4edd87
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_power_settings_new_gm2_24px.xml
@@ -0,0 +1,9 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24"
+    android:viewportHeight="24">
+  <path
+      android:fillColor="#FF000000"
+      android:pathData="M11,2h2v10h-2zM18.37,5.64l-1.41,1.41c2.73,2.73 2.72,7.16 -0.01,9.89 -2.73,2.73 -7.17,2.73 -9.89,0.01 -2.73,-2.73 -2.74,-7.18 -0.01,-9.91l-1.41,-1.4c-3.51,3.51 -3.51,9.21 0.01,12.73 3.51,3.51 9.21,3.51 12.72,-0.01 3.51,-3.51 3.51,-9.2 0,-12.72z"/>
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_switches_gm2_24px.xml b/packages/SystemUI/res/drawable/ic_switches_gm2_24px.xml
new file mode 100644
index 0000000..bb535ce
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_switches_gm2_24px.xml
@@ -0,0 +1,9 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24"
+    android:viewportHeight="24">
+  <path
+      android:fillColor="#FF000000"
+      android:pathData="M19,9h-8.02C10.06,7.79 8.63,7 7,7c-2.76,0 -5,2.24 -5,5s2.24,5 5,5c1.63,0 3.06,-0.79 3.98,-2H19c1.66,0 3,-1.34 3,-3S20.66,9 19,9zM19,13h-7.1c0.07,-0.32 0.1,-0.66 0.1,-1s-0.04,-0.68 -0.1,-1H19c0.55,0 1,0.45 1,1S19.55,13 19,13z"/>
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_vacuum_gm2_24px.xml b/packages/SystemUI/res/drawable/ic_vacuum_gm2_24px.xml
new file mode 100644
index 0000000..86b9591
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_vacuum_gm2_24px.xml
@@ -0,0 +1,9 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24"
+    android:viewportHeight="24">
+  <path
+      android:fillColor="#FF000000"
+      android:pathData="M4,16c-1.66,0 -3,1.34 -3,3s1.34,3 3,3 3,-1.34 3,-3 -1.34,-3 -3,-3zM4,20c-0.55,0 -1,-0.45 -1,-1s0.45,-1 1,-1 1,0.45 1,1 -0.45,1 -1,1zM23,20v2h-7v-2h2.49L12.01,4.59C11.6,3.63 10.66,3 9.61,3 8.17,3 7,4.17 7,5.61L7,9h2c2.21,0 4,1.79 4,4v9L7.99,22c0.44,-0.58 0.76,-1.26 0.91,-2L11,20v-7c0,-1.1 -0.9,-2 -2,-2L4,11v3c-0.71,0 -1.39,0.15 -2,0.42L2,9h3L5,5.61C5,3.07 7.07,1 9.61,1c1.86,0 3.53,1.11 4.25,2.82L20.66,20L23,20z"/>
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_videocam_gm2_24px.xml b/packages/SystemUI/res/drawable/ic_videocam_gm2_24px.xml
new file mode 100644
index 0000000..687c9c4
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_videocam_gm2_24px.xml
@@ -0,0 +1,9 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24"
+    android:viewportHeight="24">
+  <path
+      android:fillColor="#FF000000"
+      android:pathData="M18,10.48L18,6c0,-1.1 -0.9,-2 -2,-2L4,4c-1.1,0 -2,0.9 -2,2v12c0,1.1 0.9,2 2,2h12c1.1,0 2,-0.9 2,-2v-4.48l4,3.98v-11l-4,3.98zM16,9.69L16,18L4,18L4,6h12v3.69z"/>
+</vector>
diff --git a/packages/SystemUI/res/layout/controls_base_item.xml b/packages/SystemUI/res/layout/controls_base_item.xml
new file mode 100644
index 0000000..3c4c61e
--- /dev/null
+++ b/packages/SystemUI/res/layout/controls_base_item.xml
@@ -0,0 +1,79 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2020 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<androidx.constraintlayout.widget.ConstraintLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    android:layout_width="0dp"
+    android:layout_weight="1"
+    android:layout_height="@dimen/control_height"
+    android:padding="@dimen/control_padding"
+    android:clickable="true"
+    android:focusable="true"
+    android:layout_marginLeft="3dp"
+    android:layout_marginRight="3dp"
+    android:background="@drawable/control_background">
+
+    <ImageView
+        android:id="@+id/icon"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintTop_toTopOf="parent" />
+
+    <TextView
+        android:id="@+id/status"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:textSize="@dimen/control_status_normal"
+        android:textColor="?android:attr/textColorPrimary"
+        android:fontFamily="@*android:string/config_bodyFontFamily"
+        android:paddingLeft="3dp"
+        app:layout_constraintBottom_toBottomOf="@+id/icon"
+        app:layout_constraintStart_toEndOf="@+id/icon" />
+
+    <TextView
+        android:id="@+id/status_extra"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:textSize="@dimen/control_status_normal"
+        android:textColor="?android:attr/textColorPrimary"
+        android:fontFamily="@*android:string/config_bodyFontFamily"
+        android:paddingLeft="3dp"
+        app:layout_constraintBottom_toBottomOf="@+id/icon"
+        app:layout_constraintStart_toEndOf="@+id/status" />
+
+    <TextView
+        android:id="@+id/title"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:textSize="18sp"
+        android:textColor="?android:attr/textColorPrimary"
+        android:fontFamily="@*android:string/config_headlineFontFamily"
+        app:layout_constraintBottom_toTopOf="@+id/subtitle"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintTop_toBottomOf="@+id/icon" />
+
+    <TextView
+        android:id="@+id/subtitle"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:textSize="16sp"
+        android:textColor="?android:attr/textColorSecondary"
+        android:fontFamily="@*android:string/config_headlineFontFamily"
+        app:layout_constraintBottom_toBottomOf="parent"
+        app:layout_constraintStart_toStartOf="parent" />
+</androidx.constraintlayout.widget.ConstraintLayout>
diff --git a/packages/SystemUI/res/layout/controls_no_favorites.xml b/packages/SystemUI/res/layout/controls_no_favorites.xml
new file mode 100644
index 0000000..79672ca
--- /dev/null
+++ b/packages/SystemUI/res/layout/controls_no_favorites.xml
@@ -0,0 +1,18 @@
+<merge
+    xmlns:android="http://schemas.android.com/apk/res/android">
+  <TextView
+      android:id="@+id/controls_title"
+      android:text="@string/quick_controls_title"
+      android:layout_width="match_parent"
+      android:layout_height="wrap_content"
+      android:singleLine="true"
+      android:gravity="center"
+      android:textSize="25dp"
+      android:paddingTop="40dp"
+      android:paddingBottom="40dp"
+      android:layout_marginLeft="10dp"
+      android:layout_marginRight="10dp"
+      android:textColor="?android:attr/textColorPrimary"
+      android:fontFamily="@*android:string/config_headlineFontFamily"
+      android:background="@drawable/control_no_favorites_background"/>
+</merge>
diff --git a/packages/SystemUI/res/layout/controls_row.xml b/packages/SystemUI/res/layout/controls_row.xml
new file mode 100644
index 0000000..13a6b36
--- /dev/null
+++ b/packages/SystemUI/res/layout/controls_row.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2020 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    orientation="horizontal"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:layout_marginBottom="@dimen/control_spacing" />
diff --git a/packages/SystemUI/res/layout/controls_with_favorites.xml b/packages/SystemUI/res/layout/controls_with_favorites.xml
new file mode 100644
index 0000000..7804fe6
--- /dev/null
+++ b/packages/SystemUI/res/layout/controls_with_favorites.xml
@@ -0,0 +1,35 @@
+<merge
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto">
+
+  <androidx.constraintlayout.widget.ConstraintLayout
+      android:layout_width="match_parent"
+      android:layout_height="wrap_content">
+
+    <TextView
+        android:text="@string/quick_controls_title"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:singleLine="true"
+        android:gravity="center"
+        android:textSize="25dp"
+        android:textColor="?android:attr/textColorPrimary"
+        android:fontFamily="@*android:string/config_headlineFontFamily"
+        app:layout_constraintLeft_toLeftOf="parent"
+        app:layout_constraintRight_toRightOf="parent"/>
+    <ImageView
+        android:id="@+id/controls_more"
+        android:src="@drawable/ic_more_vert"
+        android:layout_width="34dp"
+        android:layout_height="24dp" 
+        android:layout_marginEnd="10dp"
+        app:layout_constraintEnd_toEndOf="parent"/>
+
+  </androidx.constraintlayout.widget.ConstraintLayout>
+
+  <LinearLayout
+      android:id="@+id/global_actions_controls_list"
+      android:layout_width="match_parent"
+      android:layout_height="wrap_content"
+      android:orientation="vertical" />
+</merge>
diff --git a/packages/SystemUI/res/layout/global_actions_grid_v2.xml b/packages/SystemUI/res/layout/global_actions_grid_v2.xml
index 4cfb47e..6741484 100644
--- a/packages/SystemUI/res/layout/global_actions_grid_v2.xml
+++ b/packages/SystemUI/res/layout/global_actions_grid_v2.xml
@@ -111,20 +111,7 @@
         app:layout_constraintLeft_toLeftOf="parent"
         app:layout_constraintRight_toRightOf="parent"
         app:layout_constraintTop_toBottomOf="@id/global_actions_panel">
-      <TextView
-          android:text="Home"
-          android:layout_width="match_parent"
-          android:layout_height="wrap_content"
-          android:singleLine="true"
-          android:gravity="center"
-          android:textSize="25dp"
-          android:textColor="?android:attr/textColorPrimary"
-          android:fontFamily="@*android:string/config_headlineFontFamily" />
-    <LinearLayout
-        android:id="@+id/global_actions_controls_list"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:orientation="vertical" />
+
     </LinearLayout>
   </androidx.constraintlayout.widget.ConstraintLayout>
 </ScrollView>
diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml
index 8dd2a8b..09058f2 100644
--- a/packages/SystemUI/res/values/colors.xml
+++ b/packages/SystemUI/res/values/colors.xml
@@ -200,18 +200,28 @@
     <color name="GM2_grey_800">#3C4043</color>
     <color name="GM2_grey_900">#202124</color>
 
+    <color name="GM2_red_50">#FCE8E6</color>
     <color name="GM2_red_300">#F28B82</color>
     <color name="GM2_red_500">#B71C1C</color>
     <color name="GM2_red_700">#C5221F</color>
 
+    <color name="GM2_blue_50">#E8F0FE</color>
     <color name="GM2_blue_200">#AECBFA</color>
     <color name="GM2_blue_300">#8AB4F8</color>
+    <color name="GM2_blue_500">#FF4285F4</color>
     <color name="GM2_blue_600">#1A73E8</color>
     <color name="GM2_blue_700">#1967D2</color>
 
+    <color name="GM2_yellow_50">#FEF7E0</color>
     <color name="GM2_yellow_500">#FFFBBC04</color>
+
     <color name="GM2_green_500">#FF34A853</color>
-    <color name="GM2_blue_500">#FF4285F4</color>
+
+    <color name="GM2_orange_900">#B06000</color>
 
     <color name="magnification_border_color">#FF9900</color>
+
+    <!-- controls -->
+    <color name="control_default_foreground">?android:attr/textColorPrimary</color>
+    <color name="control_default_background">?android:attr/colorBackgroundFloating</color>
 </resources>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index b40c5c0..cc58b20 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -1181,5 +1181,12 @@
     <dimen name="magnifier_up_down_controls_width">45dp</dimen>
     <dimen name="magnifier_up_down_controls_height">40dp</dimen>
 
+    <!-- Home Controls -->
+    <dimen name="control_spacing">5dp</dimen>
+    <dimen name="control_corner_radius">15dp</dimen>
+    <dimen name="control_height">100dp</dimen>
+    <dimen name="control_padding">15dp</dimen>
+    <dimen name="control_status_normal">12dp</dimen>
+    <dimen name="control_status_expanded">18dp</dimen>
     <dimen name="app_icon_size">32dp</dimen>
 </resources>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 44a7fda..1f13f8d 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -2546,4 +2546,7 @@
     <string name="magnification_window_title">Magnification Window</string>
     <!-- Title for Magnification Controls Window [CHAR LIMIT=NONE] -->
     <string name="magnification_controls_title">Magnification Window Controls</string>
+
+    <!-- Quick Controls strings [CHAR LIMIT=30] -->
+    <string name="quick_controls_title">Quick Controls</string>
 </resources>
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlViewHolder.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlViewHolder.kt
new file mode 100644
index 0000000..81b5f36
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlViewHolder.kt
@@ -0,0 +1,400 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.controls.ui
+
+import android.content.Context
+import android.graphics.drawable.ClipDrawable
+import android.graphics.drawable.Drawable
+import android.graphics.drawable.GradientDrawable
+import android.graphics.drawable.Icon
+import android.graphics.drawable.LayerDrawable
+import android.service.controls.Control
+import android.service.controls.DeviceTypes
+import android.service.controls.actions.BooleanAction
+import android.service.controls.actions.ControlAction
+import android.service.controls.actions.FloatAction
+import android.service.controls.templates.ControlTemplate
+import android.service.controls.templates.RangeTemplate
+import android.service.controls.templates.ToggleRangeTemplate
+import android.service.controls.templates.ToggleTemplate
+import android.util.TypedValue
+import android.view.MotionEvent
+import android.view.View
+import android.view.ViewGroup
+import android.widget.ImageView
+import android.widget.TextView
+
+import com.android.systemui.controls.controller.ControlsController
+import com.android.systemui.R
+
+private const val MIN_LEVEL = 0
+private const val MAX_LEVEL = 10000
+
+class ControlViewHolder(
+    val layout: ViewGroup,
+    val controlsController: ControlsController
+) {
+    val icon: ImageView = layout.requireViewById(R.id.icon)
+    val status: TextView = layout.requireViewById(R.id.status)
+    val statusExtra: TextView = layout.requireViewById(R.id.status_extra)
+    val title: TextView = layout.requireViewById(R.id.title)
+    val subtitle: TextView = layout.requireViewById(R.id.subtitle)
+    val context: Context = layout.getContext()
+    val clipLayer: ClipDrawable
+    val gd: GradientDrawable
+    lateinit var cws: ControlWithState
+
+    init {
+        val ld = layout.getBackground() as LayerDrawable
+        ld.mutate()
+        clipLayer = ld.findDrawableByLayerId(R.id.clip_layer) as ClipDrawable
+        gd = clipLayer.getDrawable() as GradientDrawable
+    }
+
+    fun bindData(cws: ControlWithState) {
+        this.cws = cws
+
+        val (status, template) = cws.control?.let {
+            title.setText(it.getTitle())
+            subtitle.setText(it.getSubtitle())
+            Pair(it.getStatus(), it.getControlTemplate())
+        } ?: run {
+            title.setText(cws.ci.controlTitle)
+            subtitle.setText("")
+            Pair(Control.STATUS_UNKNOWN, ControlTemplate.NO_TEMPLATE)
+        }
+
+        findBehavior(status, template).apply(this, cws)
+    }
+
+    fun action(action: ControlAction) {
+        controlsController.action(cws.ci, action)
+    }
+
+    private fun findBehavior(status: Int, template: ControlTemplate): Behavior {
+        return when {
+            status == Control.STATUS_UNKNOWN -> UnknownBehavior()
+            template is ToggleTemplate -> ToggleTemplateBehavior()
+            template is ToggleRangeTemplate -> ToggleRangeTemplateBehavior()
+            else -> {
+                object : Behavior {
+                    override fun apply(cvh: ControlViewHolder, cws: ControlWithState) {
+                        cvh.status.setText(cws.control?.getStatusText())
+                        cvh.applyRenderInfo(findRenderInfo(cws.ci.deviceType, false))
+                    }
+                }
+            }
+        }
+    }
+
+    internal fun applyRenderInfo(ri: RenderInfo) {
+        val fg = context.getResources().getColorStateList(ri.foreground, context.getTheme())
+        val bg = context.getResources().getColorStateList(ri.background, context.getTheme())
+        status.setTextColor(fg)
+        statusExtra.setTextColor(fg)
+
+        icon.setImageIcon(Icon.createWithResource(context, ri.iconResourceId))
+        icon.setImageTintList(fg)
+
+        gd.setColor(bg)
+    }
+
+    fun setEnabled(enabled: Boolean) {
+        status.setEnabled(enabled)
+        icon.setEnabled(enabled)
+    }
+}
+
+private interface Behavior {
+    fun apply(cvh: ControlViewHolder, cws: ControlWithState)
+
+    fun findRenderInfo(deviceType: Int, isActive: Boolean): RenderInfo =
+        deviceRenderMap.getOrDefault(deviceType, unknownDeviceMap).getValue(isActive)
+}
+
+private class UnknownBehavior : Behavior {
+    override fun apply(cvh: ControlViewHolder, cws: ControlWithState) {
+        cvh.status.setText("Loading...")
+        cvh.applyRenderInfo(findRenderInfo(cws.ci.deviceType, false))
+    }
+}
+
+private class ToggleRangeTemplateBehavior : Behavior {
+    lateinit var clipLayer: Drawable
+    lateinit var template: ToggleRangeTemplate
+    lateinit var control: Control
+    lateinit var cvh: ControlViewHolder
+    lateinit var rangeTemplate: RangeTemplate
+    lateinit var statusExtra: TextView
+    lateinit var status: TextView
+    lateinit var context: Context
+
+    override fun apply(cvh: ControlViewHolder, cws: ControlWithState) {
+        this.control = cws.control!!
+        this.cvh = cvh
+
+        statusExtra = cvh.statusExtra
+        status = cvh.status
+
+        status.setText(control.getStatusText())
+
+        context = status.getContext()
+
+        cvh.layout.setOnTouchListener(ToggleRangeTouchListener())
+
+        val ld = cvh.layout.getBackground() as LayerDrawable
+        clipLayer = ld.findDrawableByLayerId(R.id.clip_layer)
+
+        template = control.getControlTemplate() as ToggleRangeTemplate
+        rangeTemplate = template.getRange()
+
+        val checked = template.isChecked()
+        val deviceType = control.getDeviceType()
+
+        updateRange((rangeTemplate.getCurrentValue() / 100.0f), checked)
+
+        cvh.setEnabled(checked)
+        cvh.applyRenderInfo(findRenderInfo(deviceType, checked))
+    }
+
+    fun toggle() {
+        cvh.action(BooleanAction(template.getTemplateId(), !template.isChecked()))
+
+        val nextLevel = if (template.isChecked()) MIN_LEVEL else MAX_LEVEL
+        clipLayer.setLevel(nextLevel)
+    }
+
+    fun beginUpdateRange() {
+        status.setVisibility(View.GONE)
+        statusExtra.setTextSize(TypedValue.COMPLEX_UNIT_PX, context.getResources()
+                .getDimensionPixelSize(R.dimen.control_status_expanded).toFloat())
+    }
+
+    fun updateRange(f: Float, checked: Boolean) {
+        clipLayer.setLevel(if (checked) (MAX_LEVEL * f).toInt() else MIN_LEVEL)
+
+        if (checked && f < 100.0f && f > 0.0f) {
+            statusExtra.setText("" + (f * 100.0).toInt() + "%")
+            statusExtra.setVisibility(View.VISIBLE)
+        } else {
+            statusExtra.setText("")
+            statusExtra.setVisibility(View.GONE)
+        }
+    }
+
+    fun endUpdateRange(f: Float) {
+        statusExtra.setText(" - " + (f * 100.0).toInt() + "%")
+
+        val newValue = rangeTemplate.getMinValue() +
+            (f * (rangeTemplate.getMaxValue() - rangeTemplate.getMinValue()))
+
+        statusExtra.setTextSize(TypedValue.COMPLEX_UNIT_PX, context.getResources()
+                .getDimensionPixelSize(R.dimen.control_status_normal).toFloat())
+        status.setVisibility(View.VISIBLE)
+
+        cvh.action(FloatAction(rangeTemplate.getTemplateId(), findNearestStep(newValue)))
+    }
+
+    fun findNearestStep(value: Float): Float {
+        var minDiff = 1000f
+
+        var f = rangeTemplate.getMinValue()
+        while (f <= rangeTemplate.getMaxValue()) {
+            val currentDiff = Math.abs(value - f)
+            if (currentDiff < minDiff) {
+                minDiff = currentDiff
+            } else {
+                return f - rangeTemplate.getStepValue()
+            }
+
+            f += rangeTemplate.getStepValue()
+        }
+
+        return rangeTemplate.getMaxValue()
+    }
+
+    inner class ToggleRangeTouchListener() : View.OnTouchListener {
+        private var initialTouchX: Float = 0.0f
+        private var initialTouchY: Float = 0.0f
+        private var isDragging: Boolean = false
+        private val minDragDiff = 20
+
+        override fun onTouch(v: View, e: MotionEvent): Boolean {
+            when (e.getActionMasked()) {
+                MotionEvent.ACTION_DOWN -> setupTouch(e)
+                MotionEvent.ACTION_MOVE -> detectDrag(v, e)
+                MotionEvent.ACTION_UP -> endTouch(v, e)
+            }
+
+            return true
+        }
+
+        private fun setupTouch(e: MotionEvent) {
+            initialTouchX = e.getX()
+            initialTouchY = e.getY()
+        }
+
+        private fun detectDrag(v: View, e: MotionEvent) {
+            val xDiff = Math.abs(e.getX() - initialTouchX)
+            val yDiff = Math.abs(e.getY() - initialTouchY)
+
+            if (xDiff < minDragDiff) {
+                isDragging = false
+            } else {
+                if (!isDragging) {
+                    this@ToggleRangeTemplateBehavior.beginUpdateRange()
+                }
+                v.getParent().requestDisallowInterceptTouchEvent(true)
+                isDragging = true
+                if (yDiff > xDiff) {
+                    endTouch(v, e)
+                } else {
+                    val percent = Math.max(0.0f, Math.min(1.0f, e.getX() / v.getWidth()))
+                    this@ToggleRangeTemplateBehavior.updateRange(percent, true)
+                }
+            }
+        }
+
+        private fun endTouch(v: View, e: MotionEvent) {
+            if (!isDragging) {
+                this@ToggleRangeTemplateBehavior.toggle()
+            } else {
+                val percent = Math.max(0.0f, Math.min(1.0f, e.getX() / v.getWidth()))
+                this@ToggleRangeTemplateBehavior.endUpdateRange(percent)
+            }
+
+            initialTouchX = 0.0f
+            initialTouchY = 0.0f
+            isDragging = false
+        }
+    }
+}
+
+private class ToggleTemplateBehavior : Behavior {
+    lateinit var clipLayer: Drawable
+    lateinit var template: ToggleTemplate
+    lateinit var control: Control
+    lateinit var cvh: ControlViewHolder
+    lateinit var context: Context
+    lateinit var status: TextView
+
+    override fun apply(cvh: ControlViewHolder, cws: ControlWithState) {
+        this.control = cws.control!!
+        this.cvh = cvh
+        status = cvh.status
+
+        status.setText(control.getStatusText())
+
+        cvh.layout.setOnClickListener(View.OnClickListener() { toggle() })
+
+        val ld = cvh.layout.getBackground() as LayerDrawable
+        clipLayer = ld.findDrawableByLayerId(R.id.clip_layer)
+
+        template = control.getControlTemplate() as ToggleTemplate
+
+        val checked = template.isChecked()
+        val deviceType = control.getDeviceType()
+
+        clipLayer.setLevel(if (checked) MAX_LEVEL else MIN_LEVEL)
+        cvh.setEnabled(checked)
+        cvh.applyRenderInfo(findRenderInfo(deviceType, checked))
+    }
+
+    fun toggle() {
+        cvh.action(BooleanAction(template.getTemplateId(), !template.isChecked()))
+
+        val nextLevel = if (template.isChecked()) MIN_LEVEL else MAX_LEVEL
+        clipLayer.setLevel(nextLevel)
+    }
+}
+
+internal data class RenderInfo(val iconResourceId: Int, val foreground: Int, val background: Int)
+
+private val unknownDeviceMap = mapOf(
+    false to RenderInfo(
+        R.drawable.ic_light_off_gm2_24px,
+        R.color.unknown_foreground,
+        R.color.unknown_foreground),
+    true to RenderInfo(
+        R.drawable.ic_lightbulb_outline_gm2_24px,
+        R.color.unknown_foreground,
+        R.color.unknown_foreground)
+)
+
+private val deviceRenderMap = mapOf<Int, Map<Boolean, RenderInfo>>(
+    DeviceTypes.TYPE_UNKNOWN to unknownDeviceMap,
+    DeviceTypes.TYPE_LIGHT to mapOf(
+        false to RenderInfo(
+            R.drawable.ic_light_off_gm2_24px,
+            R.color.light_foreground,
+            R.color.light_background),
+        true to RenderInfo(
+            R.drawable.ic_lightbulb_outline_gm2_24px,
+            R.color.light_foreground,
+            R.color.light_background)
+    ),
+    DeviceTypes.TYPE_THERMOSTAT to mapOf(
+        false to RenderInfo(
+            R.drawable.ic_device_thermostat_gm2_24px,
+            R.color.light_foreground,
+            R.color.light_background),
+        true to RenderInfo(
+            R.drawable.ic_device_thermostat_gm2_24px,
+            R.color.light_foreground,
+            R.color.light_background)
+    ),
+    DeviceTypes.TYPE_CAMERA to mapOf(
+        false to RenderInfo(
+            R.drawable.ic_videocam_gm2_24px,
+            R.color.light_foreground,
+            R.color.light_background),
+        true to RenderInfo(
+            R.drawable.ic_videocam_gm2_24px,
+            R.color.light_foreground,
+            R.color.light_background)
+    ),
+    DeviceTypes.TYPE_LOCK to mapOf(
+        false to RenderInfo(
+            R.drawable.ic_lock_open_gm2_24px,
+            R.color.lock_foreground,
+            R.color.lock_background),
+        true to RenderInfo(
+            R.drawable.ic_lock_gm2_24px,
+            R.color.lock_foreground,
+            R.color.lock_background)
+    ),
+    DeviceTypes.TYPE_SWITCH to mapOf(
+        false to RenderInfo(
+            R.drawable.ic_switches_gm2_24px,
+            R.color.lock_foreground,
+            R.color.lock_background),
+        true to RenderInfo(
+            R.drawable.ic_switches_gm2_24px,
+            R.color.lock_foreground,
+            R.color.lock_background)
+    ),
+    DeviceTypes.TYPE_OUTLET to mapOf(
+        false to RenderInfo(
+            R.drawable.ic_power_off_gm2_24px,
+            R.color.lock_foreground,
+            R.color.lock_background),
+        true to RenderInfo(
+            R.drawable.ic_power_gm2_24px,
+            R.color.lock_foreground,
+            R.color.lock_background)
+    )
+)
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlWithState.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlWithState.kt
new file mode 100644
index 0000000..816f0b2
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlWithState.kt
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.controls.ui
+
+import android.service.controls.Control
+
+import com.android.systemui.controls.controller.ControlInfo
+
+/**
+ * A container for:
+ * <ul>
+ *  <li>ControlInfo - Basic cached info about a Control
+ *  <li>Control - Actual Control parcelable received directly from
+ *  the participating application
+ * </ul>
+ */
+data class ControlWithState(val ci: ControlInfo, val control: Control?)
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiController.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiController.kt
index 0270c2b..b07a75d 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiController.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiController.kt
@@ -19,12 +19,15 @@
 import android.content.ComponentName
 import android.service.controls.Control
 import android.service.controls.actions.ControlAction
+import android.view.ViewGroup
 
 interface ControlsUiController {
+    fun show(parent: ViewGroup)
+    fun hide()
     fun onRefreshState(componentName: ComponentName, controls: List<Control>)
     fun onActionResponse(
         componentName: ComponentName,
         controlId: String,
         @ControlAction.ResponseResult response: Int
     )
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt
index 0ace126..926fb6e 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt
@@ -16,19 +16,204 @@
 
 package com.android.systemui.controls.ui
 
+import android.accounts.Account
+import android.accounts.AccountManager
 import android.content.ComponentName
+import android.content.Context
+import android.content.Intent
+import android.content.ServiceConnection
+import android.os.IBinder
 import android.service.controls.Control
+import android.service.controls.TokenProvider
+import android.util.Log
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import android.widget.TextView
+
+import com.android.systemui.controls.controller.ControlsController
+import com.android.systemui.controls.controller.ControlInfo
+import com.android.systemui.controls.management.ControlsProviderSelectorActivity
+import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.R
+
+import dagger.Lazy
+
+import java.util.concurrent.Executor
 import javax.inject.Inject
 import javax.inject.Singleton
 
+private const val TAG = "ControlsUi"
+
+// TEMP CODE for MOCK
+private const val TOKEN = "https://www.googleapis.com/auth/assistant"
+private const val SCOPE = "oauth2:" + TOKEN
+private var tokenProviderConnection: TokenProviderConnection? = null
+class TokenProviderConnection(val cc: ControlsController, val context: Context)
+    : ServiceConnection {
+    private var mTokenProvider: TokenProvider? = null
+
+    override fun onServiceConnected(cName: ComponentName, binder: IBinder) {
+        Thread({
+            Log.i(TAG, "TokenProviderConnection connected")
+            mTokenProvider = TokenProvider.Stub.asInterface(binder)
+
+            val mLastAccountName = mTokenProvider?.getAccountName()
+
+            if (mLastAccountName == null || mLastAccountName.isEmpty()) {
+                Log.e(TAG, "NO ACCOUNT IS SET. Open HomeMock app")
+            } else {
+                mTokenProvider?.setAuthToken(getAuthToken(mLastAccountName))
+                cc.subscribeToFavorites()
+            }
+        }, "TokenProviderThread").start()
+    }
+
+    override fun onServiceDisconnected(cName: ComponentName) {
+        mTokenProvider = null
+    }
+
+    fun getAuthToken(accountName: String): String? {
+        val am = AccountManager.get(context)
+        val accounts = am.getAccountsByType("com.google")
+        if (accounts == null || accounts.size == 0) {
+            Log.w(TAG, "No com.google accounts found")
+            return null
+        }
+
+        var account: Account? = null
+        for (a in accounts) {
+            if (a.name.equals(accountName)) {
+                account = a
+                break
+            }
+        }
+
+        if (account == null) {
+            account = accounts[0]
+        }
+
+        try {
+            return am.blockingGetAuthToken(account!!, SCOPE, true)
+        } catch (e: Throwable) {
+            Log.e(TAG, "Error getting auth token", e)
+            return null
+        }
+    }
+}
+
 @Singleton
-class ControlsUiControllerImpl @Inject constructor() : ControlsUiController {
+class ControlsUiControllerImpl @Inject constructor (
+    val controlsController: Lazy<ControlsController>,
+    val context: Context,
+    @Main val uiExecutor: Executor
+) : ControlsUiController {
+
+    private lateinit var controlInfos: List<ControlInfo>
+    private val controlsById = mutableMapOf<Pair<ComponentName, String>, ControlWithState>()
+    private val controlViewsById = mutableMapOf<String, ControlViewHolder>()
+    private lateinit var parent: ViewGroup
+
+    override fun show(parent: ViewGroup) {
+        Log.d(TAG, "show()")
+
+        this.parent = parent
+
+        controlInfos = controlsController.get().getFavoriteControls()
+
+        controlInfos.map {
+            ControlWithState(it, null)
+        }.associateByTo(controlsById) { Pair(it.ci.component, it.ci.controlId) }
+
+        if (controlInfos.isEmpty()) {
+            showInitialSetupView()
+        } else {
+            showControlsView()
+        }
+
+        // Temp code to pass auth
+        tokenProviderConnection = TokenProviderConnection(controlsController.get(), context)
+        val serviceIntent = Intent()
+        serviceIntent.setComponent(ComponentName("com.android.systemui.home.mock",
+                "com.android.systemui.home.mock.AuthService"))
+        context.bindService(serviceIntent, tokenProviderConnection!!, Context.BIND_AUTO_CREATE)
+    }
+
+    private fun showInitialSetupView() {
+        val inflater = LayoutInflater.from(context)
+        inflater.inflate(R.layout.controls_no_favorites, parent, true)
+
+        val textView = parent.requireViewById(R.id.controls_title) as TextView
+        textView.setOnClickListener {
+            val i = Intent()
+            i.setComponent(ComponentName(context, ControlsProviderSelectorActivity::class.java))
+            i.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
+            context.startActivity(i)
+        }
+    }
+
+    private fun showControlsView() {
+        val inflater = LayoutInflater.from(context)
+        inflater.inflate(R.layout.controls_with_favorites, parent, true)
+
+        val listView = parent.requireViewById(R.id.global_actions_controls_list) as ViewGroup
+        var lastRow: ViewGroup = createRow(inflater, listView)
+        controlInfos.forEach {
+            Log.d(TAG, "favorited control id: " + it.controlId)
+            if (lastRow.getChildCount() == 2) {
+                lastRow = createRow(inflater, listView)
+            }
+            val item = inflater.inflate(
+                R.layout.controls_base_item, lastRow, false) as ViewGroup
+            lastRow.addView(item)
+            val cvh = ControlViewHolder(item, controlsController.get())
+            cvh.bindData(controlsById.get(Pair(it.component, it.controlId))!!)
+            controlViewsById.put(it.controlId, cvh)
+        }
+
+        val moreImageView = parent.requireViewById(R.id.controls_more) as View
+        moreImageView.setOnClickListener {
+            val i = Intent()
+            i.setComponent(ComponentName(context, ControlsProviderSelectorActivity::class.java))
+            i.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
+            context.startActivity(i)
+        }
+    }
+
+    override fun hide() {
+        Log.d(TAG, "hide()")
+        controlsController.get().unsubscribe()
+        context.unbindService(tokenProviderConnection)
+        tokenProviderConnection = null
+
+        parent.removeAllViews()
+        controlsById.clear()
+        controlViewsById.clear()
+    }
 
     override fun onRefreshState(componentName: ComponentName, controls: List<Control>) {
-        TODO("not implemented")
+        Log.d(TAG, "onRefreshState()")
+        controls.forEach { c ->
+            controlsById.get(Pair(componentName, c.getControlId()))?.let {
+                Log.d(TAG, "onRefreshState() for id: " + c.getControlId())
+                val cws = ControlWithState(it.ci, c)
+                controlsById.put(Pair(componentName, c.getControlId()), cws)
+
+                uiExecutor.execute {
+                    controlViewsById.get(c.getControlId())?.bindData(cws)
+                }
+            }
+        }
     }
 
     override fun onActionResponse(componentName: ComponentName, controlId: String, response: Int) {
+        Log.d(TAG, "onActionResponse()")
         TODO("not implemented")
     }
-}
\ No newline at end of file
+
+    private fun createRow(inflater: LayoutInflater, parent: ViewGroup): ViewGroup {
+        val row = inflater.inflate(R.layout.controls_row, parent, false) as ViewGroup
+        parent.addView(row)
+        return row
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
index 83f6d45..80d776a 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
@@ -93,12 +93,13 @@
 import com.android.systemui.MultiListLayout.MultiListAdapter;
 import com.android.systemui.broadcast.BroadcastDispatcher;
 import com.android.systemui.colorextraction.SysuiColorExtractor;
+import com.android.systemui.controls.ui.ControlsUiController;
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.plugins.ActivityStarter;
 import com.android.systemui.plugins.GlobalActions.GlobalActionsManager;
 import com.android.systemui.plugins.GlobalActionsPanelPlugin;
-import com.android.systemui.statusbar.phone.NotificationShadeWindowController;
 import com.android.systemui.statusbar.BlurUtils;
+import com.android.systemui.statusbar.phone.NotificationShadeWindowController;
 import com.android.systemui.statusbar.phone.ScrimController;
 import com.android.systemui.statusbar.policy.ConfigurationController;
 import com.android.systemui.statusbar.policy.KeyguardStateController;
@@ -183,6 +184,7 @@
     private final IStatusBarService mStatusBarService;
     private final NotificationShadeWindowController mNotificationShadeWindowController;
     private GlobalActionsPanelPlugin mPanelPlugin;
+    private ControlsUiController mControlsUiController;
 
     /**
      * @param context everything needs a context :(
@@ -200,7 +202,8 @@
             TelecomManager telecomManager, MetricsLogger metricsLogger,
             BlurUtils blurUtils, SysuiColorExtractor colorExtractor,
             IStatusBarService statusBarService,
-            NotificationShadeWindowController notificationShadeWindowController) {
+            NotificationShadeWindowController notificationShadeWindowController,
+            ControlsUiController controlsUiController) {
         mContext = new ContextThemeWrapper(context, com.android.systemui.R.style.qs_theme);
         mWindowManagerFuncs = windowManagerFuncs;
         mAudioManager = audioManager;
@@ -220,6 +223,7 @@
         mSysuiColorExtractor = colorExtractor;
         mStatusBarService = statusBarService;
         mNotificationShadeWindowController = notificationShadeWindowController;
+        mControlsUiController = controlsUiController;
 
         // receive broadcasts
         IntentFilter filter = new IntentFilter();
@@ -455,9 +459,12 @@
                                 mKeyguardManager.isDeviceLocked())
                         : null;
 
+        boolean showControls = !mKeyguardManager.isDeviceLocked() && isControlsEnabled(mContext);
+
         ActionsDialog dialog = new ActionsDialog(mContext, mAdapter, panelViewController,
                 mBlurUtils, mSysuiColorExtractor, mStatusBarService,
-                mNotificationShadeWindowController, isControlsEnabled(mContext));
+                mNotificationShadeWindowController,
+                showControls ? mControlsUiController : null);
         dialog.setCanceledOnTouchOutside(false); // Handled by the custom class.
         dialog.setKeyguardShowing(mKeyguardShowing);
 
@@ -1543,13 +1550,15 @@
         private boolean mHadTopUi;
         private final NotificationShadeWindowController mNotificationShadeWindowController;
         private final BlurUtils mBlurUtils;
-        private final boolean mControlsEnabled;
+
+        private ControlsUiController mControlsUiController;
+        private ViewGroup mControlsView;
 
         ActionsDialog(Context context, MyAdapter adapter,
                 GlobalActionsPanelPlugin.PanelViewController plugin, BlurUtils blurUtils,
                 SysuiColorExtractor sysuiColorExtractor, IStatusBarService statusBarService,
                 NotificationShadeWindowController notificationShadeWindowController,
-                boolean controlsEnabled) {
+                ControlsUiController controlsUiController) {
             super(context, com.android.systemui.R.style.Theme_SystemUI_Dialog_GlobalActions);
             mContext = context;
             mAdapter = adapter;
@@ -1557,7 +1566,7 @@
             mColorExtractor = sysuiColorExtractor;
             mStatusBarService = statusBarService;
             mNotificationShadeWindowController = notificationShadeWindowController;
-            mControlsEnabled = controlsEnabled;
+            mControlsUiController = controlsUiController;
 
             // Window initialization
             Window window = getWindow();
@@ -1639,6 +1648,7 @@
         private void initializeLayout() {
             setContentView(getGlobalActionsLayoutId(mContext));
             fixNavBarClipping();
+            mControlsView = findViewById(com.android.systemui.R.id.global_actions_controls);
             mGlobalActionsLayout = findViewById(com.android.systemui.R.id.global_actions_view);
             mGlobalActionsLayout.setOutsideTouchListener(view -> dismiss());
             ((View) mGlobalActionsLayout.getParent()).setOnClickListener(view -> dismiss());
@@ -1674,7 +1684,7 @@
         }
 
         private int getGlobalActionsLayoutId(Context context) {
-            if (mControlsEnabled) {
+            if (mControlsUiController != null) {
                 return com.android.systemui.R.layout.global_actions_grid_v2;
             }
 
@@ -1758,6 +1768,9 @@
                                 mBlurUtils.radiusForRatio(animatedValue));
                     })
                     .start();
+            if (mControlsUiController != null) {
+                mControlsUiController.show(mControlsView);
+            }
         }
 
         @Override
@@ -1766,6 +1779,7 @@
                 return;
             }
             mShowing = false;
+            if (mControlsUiController != null) mControlsUiController.hide();
             mGlobalActionsLayout.setTranslationX(0);
             mGlobalActionsLayout.setTranslationY(0);
             mGlobalActionsLayout.setAlpha(1);
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index 2fc7a9c..14eec59 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -18,6 +18,7 @@
 
 import static android.provider.Settings.System.SCREEN_OFF_TIMEOUT;
 
+import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.NAV_BAR_HANDLE_SHOW_OVER_LOCKSCREEN;
 import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.SOME_AUTH_REQUIRED_AFTER_USER_REQUEST;
 import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_DPM_LOCK_NOW;
 import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_LOCKOUT;
@@ -53,6 +54,7 @@
 import android.os.Trace;
 import android.os.UserHandle;
 import android.os.UserManager;
+import android.provider.DeviceConfig;
 import android.provider.Settings;
 import android.telephony.SubscriptionManager;
 import android.telephony.TelephonyManager;
@@ -371,6 +373,7 @@
     private boolean mPulsing;
 
     private boolean mLockLater;
+    private boolean mShowHomeOverLockscreen;
 
     private boolean mWakeAndUnlocking;
     private IKeyguardDrawnCallback mDrawnCallback;
@@ -703,6 +706,20 @@
         mStatusBarKeyguardViewManagerLazy = statusBarKeyguardViewManagerLazy;
         mDismissCallbackRegistry = dismissCallbackRegistry;
         mUiBgExecutor = uiBgExecutor;
+        mShowHomeOverLockscreen = DeviceConfig.getBoolean(
+                DeviceConfig.NAMESPACE_SYSTEMUI,
+                NAV_BAR_HANDLE_SHOW_OVER_LOCKSCREEN,
+                /* defaultValue = */ true);
+        DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_SYSTEMUI, mHandler::post,
+                new DeviceConfig.OnPropertiesChangedListener() {
+                    @Override
+                    public void onPropertiesChanged(DeviceConfig.Properties properties) {
+                        if (properties.getKeyset().contains(NAV_BAR_HANDLE_SHOW_OVER_LOCKSCREEN)) {
+                            mShowHomeOverLockscreen = properties.getBoolean(
+                                    NAV_BAR_HANDLE_SHOW_OVER_LOCKSCREEN, true /* defaultValue */);
+                        }
+                    }
+                });
     }
 
     public void userActivity() {
@@ -1972,7 +1989,10 @@
             // windows that appear on top, ever
             int flags = StatusBarManager.DISABLE_NONE;
             if (forceHideHomeRecentsButtons || isShowingAndNotOccluded()) {
-                flags |= StatusBarManager.DISABLE_HOME | StatusBarManager.DISABLE_RECENT;
+                if (!mShowHomeOverLockscreen) {
+                    flags |= StatusBarManager.DISABLE_HOME;
+                }
+                flags |= StatusBarManager.DISABLE_RECENT;
             }
 
             if (DEBUG) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
index 3e3ef0c..9e64748 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
@@ -302,14 +302,14 @@
         mForceNavBarHandleOpaque = DeviceConfig.getBoolean(
                 DeviceConfig.NAMESPACE_SYSTEMUI,
                 NAV_BAR_HANDLE_FORCE_OPAQUE,
-                /* defaultValue = */ false);
+                /* defaultValue = */ true);
         DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_SYSTEMUI, mHandler::post,
                 new DeviceConfig.OnPropertiesChangedListener() {
                     @Override
                     public void onPropertiesChanged(DeviceConfig.Properties properties) {
                         if (properties.getKeyset().contains(NAV_BAR_HANDLE_FORCE_OPAQUE)) {
                             mForceNavBarHandleOpaque = properties.getBoolean(
-                                    NAV_BAR_HANDLE_FORCE_OPAQUE, /* defaultValue = */ false);
+                                    NAV_BAR_HANDLE_FORCE_OPAQUE, /* defaultValue = */ true);
                         }
                     }
                 });
diff --git a/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java b/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java
index cf996a5..fa91620 100644
--- a/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java
+++ b/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java
@@ -567,39 +567,41 @@
         final long lastRxMs = mLastInfo.getControllerRxDurationMillis();
         final long lastEnergy = mLastInfo.getControllerEnergyUsedMicroJoules();
 
-        // We will modify the last info object to be the delta, and store the new
-        // WifiActivityEnergyInfo object as our last one.
-        final WifiActivityEnergyInfo delta = mLastInfo;
-        delta.setTimeSinceBootMillis(latest.getTimeSinceBootMillis());
-        delta.setStackState(latest.getStackState());
+        final long deltaTimeSinceBootMillis = latest.getTimeSinceBootMillis();
+        final int deltaStackState = latest.getStackState();
+        final long deltaControllerTxDurationMillis;
+        final long deltaControllerRxDurationMillis;
+        final long deltaControllerScanDurationMillis;
+        final long deltaControllerIdleDurationMillis;
+        final long deltaControllerEnergyUsedMicroJoules;
 
         final long txTimeMs = latest.getControllerTxDurationMillis() - lastTxMs;
         final long rxTimeMs = latest.getControllerRxDurationMillis() - lastRxMs;
         final long idleTimeMs = latest.getControllerIdleDurationMillis() - lastIdleMs;
         final long scanTimeMs = latest.getControllerScanDurationMillis() - lastScanMs;
 
+        final boolean wasReset;
         if (txTimeMs < 0 || rxTimeMs < 0 || scanTimeMs < 0 || idleTimeMs < 0) {
             // The stats were reset by the WiFi system (which is why our delta is negative).
             // Returns the unaltered stats. The total on time should not exceed the time
-            // duartion between reports.
+            // duration between reports.
             final long totalOnTimeMs = latest.getControllerTxDurationMillis()
                     + latest.getControllerRxDurationMillis()
                     + latest.getControllerIdleDurationMillis();
             if (totalOnTimeMs <= timePeriodMs + MAX_WIFI_STATS_SAMPLE_ERROR_MILLIS) {
-                delta.setControllerEnergyUsedMicroJoules(
-                        latest.getControllerEnergyUsedMicroJoules());
-                delta.setControllerRxDurationMillis(latest.getControllerRxDurationMillis());
-                delta.setControllerTxDurationMillis(latest.getControllerTxDurationMillis());
-                delta.setControllerIdleDurationMillis(latest.getControllerIdleDurationMillis());
-                delta.setControllerScanDurationMillis(latest.getControllerScanDurationMillis());
+                deltaControllerEnergyUsedMicroJoules = latest.getControllerEnergyUsedMicroJoules();
+                deltaControllerRxDurationMillis = latest.getControllerRxDurationMillis();
+                deltaControllerTxDurationMillis = latest.getControllerTxDurationMillis();
+                deltaControllerIdleDurationMillis = latest.getControllerIdleDurationMillis();
+                deltaControllerScanDurationMillis = latest.getControllerScanDurationMillis();
             } else {
-                delta.setControllerEnergyUsedMicroJoules(0);
-                delta.setControllerRxDurationMillis(0);
-                delta.setControllerTxDurationMillis(0);
-                delta.setControllerIdleDurationMillis(0);
-                delta.setControllerScanDurationMillis(0);
+                deltaControllerEnergyUsedMicroJoules = 0;
+                deltaControllerRxDurationMillis = 0;
+                deltaControllerTxDurationMillis = 0;
+                deltaControllerIdleDurationMillis = 0;
+                deltaControllerScanDurationMillis = 0;
             }
-            Slog.v(TAG, "WiFi energy data was reset, new WiFi energy data is " + delta);
+            wasReset = true;
         } else {
             final long totalActiveTimeMs = txTimeMs + rxTimeMs;
             long maxExpectedIdleTimeMs;
@@ -634,21 +636,33 @@
                 maxExpectedIdleTimeMs = timePeriodMs - totalActiveTimeMs;
             }
             // These times seem to be the most reliable.
-            delta.setControllerTxDurationMillis(txTimeMs);
-            delta.setControllerRxDurationMillis(rxTimeMs);
-            delta.setControllerScanDurationMillis(scanTimeMs);
+            deltaControllerTxDurationMillis = txTimeMs;
+            deltaControllerRxDurationMillis = rxTimeMs;
+            deltaControllerScanDurationMillis = scanTimeMs;
             // WiFi calculates the idle time as a difference from the on time and the various
             // Rx + Tx times. There seems to be some missing time there because this sometimes
             // becomes negative. Just cap it at 0 and ensure that it is less than the expected idle
             // time from the difference in timestamps.
             // b/21613534
-            delta.setControllerIdleDurationMillis(
-                    Math.min(maxExpectedIdleTimeMs, Math.max(0, idleTimeMs)));
-            delta.setControllerEnergyUsedMicroJoules(
-                    Math.max(0, latest.getControllerEnergyUsedMicroJoules() - lastEnergy));
+            deltaControllerIdleDurationMillis =
+                    Math.min(maxExpectedIdleTimeMs, Math.max(0, idleTimeMs));
+            deltaControllerEnergyUsedMicroJoules =
+                    Math.max(0, latest.getControllerEnergyUsedMicroJoules() - lastEnergy);
+            wasReset = false;
         }
 
         mLastInfo = latest;
+        WifiActivityEnergyInfo delta = new WifiActivityEnergyInfo(
+                deltaTimeSinceBootMillis,
+                deltaStackState,
+                deltaControllerTxDurationMillis,
+                deltaControllerRxDurationMillis,
+                deltaControllerScanDurationMillis,
+                deltaControllerIdleDurationMillis,
+                deltaControllerEnergyUsedMicroJoules);
+        if (wasReset) {
+            Slog.v(TAG, "WiFi energy data was reset, new WiFi energy data is " + delta);
+        }
         return delta;
     }
 }
diff --git a/services/core/java/com/android/server/display/BrightnessMappingStrategy.java b/services/core/java/com/android/server/display/BrightnessMappingStrategy.java
index aa39926..6ff2767 100644
--- a/services/core/java/com/android/server/display/BrightnessMappingStrategy.java
+++ b/services/core/java/com/android/server/display/BrightnessMappingStrategy.java
@@ -87,7 +87,7 @@
             }
             BrightnessConfiguration.Builder builder = new BrightnessConfiguration.Builder(
                     luxLevels, brightnessLevelsNits);
-            builder.setShortTermModelTimeout(shortTermModelTimeout);
+            builder.setShortTermModelTimeoutMillis(shortTermModelTimeout);
             builder.setShortTermModelLowerLuxMultiplier(SHORT_TERM_MODEL_THRESHOLD_RATIO);
             builder.setShortTermModelUpperLuxMultiplier(SHORT_TERM_MODEL_THRESHOLD_RATIO);
             return new PhysicalMappingStrategy(builder.build(), nitsRange, backlightRange,
@@ -739,10 +739,10 @@
 
         @Override
         public long getShortTermModelTimeout() {
-            if (mConfig.getShortTermModelTimeout() >= 0) {
-                return mConfig.getShortTermModelTimeout();
+            if (mConfig.getShortTermModelTimeoutMillis() >= 0) {
+                return mConfig.getShortTermModelTimeoutMillis();
             } else {
-                return mDefaultConfig.getShortTermModelTimeout();
+                return mDefaultConfig.getShortTermModelTimeoutMillis();
             }
         }
 
diff --git a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
index 39d1a51..92e5a01 100644
--- a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
+++ b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
@@ -1268,6 +1268,7 @@
                 continue;
             }
             StatsEvent e = StatsEvent.newBuilder()
+                    .setAtomId(atomTag)
                     .writeInt(managedProcess.uid)
                     .writeString(managedProcess.processName)
                     .writeInt(managedProcess.pid)
diff --git a/services/tests/mockingservicestests/src/com/android/server/job/controllers/ConnectivityControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/job/controllers/ConnectivityControllerTest.java
index 1c88c40..e724e60 100644
--- a/services/tests/mockingservicestests/src/com/android/server/job/controllers/ConnectivityControllerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/job/controllers/ConnectivityControllerTest.java
@@ -20,6 +20,7 @@
 import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_CONGESTED;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED;
+import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
 
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
@@ -27,6 +28,9 @@
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.spy;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.when;
+import static com.android.server.job.JobSchedulerService.FREQUENT_INDEX;
+import static com.android.server.job.JobSchedulerService.RARE_INDEX;
+import static com.android.server.job.JobSchedulerService.RESTRICTED_INDEX;
 
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
@@ -568,6 +572,48 @@
         assertFalse(controller.isStandbyExceptionRequestedLocked(UID_RED));
     }
 
+    @Test
+    public void testRestrictedJobTracking() {
+        final JobStatus networked = createJobStatus(createJob()
+                .setEstimatedNetworkBytes(DataUnit.MEBIBYTES.toBytes(1), 0)
+                .setRequiredNetworkType(JobInfo.NETWORK_TYPE_CELLULAR), UID_RED);
+        final JobStatus unnetworked = createJobStatus(createJob(), UID_BLUE);
+        networked.setStandbyBucket(FREQUENT_INDEX);
+        unnetworked.setStandbyBucket(FREQUENT_INDEX);
+
+        final Network cellularNet = new Network(101);
+        final NetworkCapabilities cellularCaps =
+                createCapabilities().addTransportType(TRANSPORT_CELLULAR);
+        reset(mConnManager);
+        answerNetwork(UID_RED, cellularNet, cellularCaps);
+        answerNetwork(UID_BLUE, cellularNet, cellularCaps);
+
+        final ConnectivityController controller = new ConnectivityController(mService);
+        controller.maybeStartTrackingJobLocked(networked, null);
+        controller.maybeStartTrackingJobLocked(unnetworked, null);
+
+        assertTrue(networked.isConstraintSatisfied(JobStatus.CONSTRAINT_CONNECTIVITY));
+        assertFalse(unnetworked.isConstraintSatisfied(JobStatus.CONSTRAINT_CONNECTIVITY));
+
+        networked.setStandbyBucket(RESTRICTED_INDEX);
+        unnetworked.setStandbyBucket(RESTRICTED_INDEX);
+        controller.startTrackingRestrictedJobLocked(networked);
+        controller.startTrackingRestrictedJobLocked(unnetworked);
+        assertFalse(networked.isConstraintSatisfied(JobStatus.CONSTRAINT_CONNECTIVITY));
+        // Unnetworked shouldn't be affected by ConnectivityController since it doesn't have a
+        // connectivity constraint.
+        assertFalse(unnetworked.isConstraintSatisfied(JobStatus.CONSTRAINT_CONNECTIVITY));
+
+        networked.setStandbyBucket(RARE_INDEX);
+        unnetworked.setStandbyBucket(RARE_INDEX);
+        controller.stopTrackingRestrictedJobLocked(networked);
+        controller.stopTrackingRestrictedJobLocked(unnetworked);
+        assertTrue(networked.isConstraintSatisfied(JobStatus.CONSTRAINT_CONNECTIVITY));
+        // Unnetworked shouldn't be affected by ConnectivityController since it doesn't have a
+        // connectivity constraint.
+        assertFalse(unnetworked.isConstraintSatisfied(JobStatus.CONSTRAINT_CONNECTIVITY));
+    }
+
     private void answerNetwork(int uid, Network net, NetworkCapabilities caps) {
         when(mConnManager.getActiveNetworkForUid(eq(uid))).thenReturn(net);
         when(mConnManager.getNetworkCapabilities(eq(net))).thenReturn(caps);
diff --git a/services/tests/servicestests/src/com/android/server/WatchdogDiagnosticsTest.java b/services/tests/servicestests/src/com/android/server/WatchdogDiagnosticsTest.java
index 4d229ef..625766a 100644
--- a/services/tests/servicestests/src/com/android/server/WatchdogDiagnosticsTest.java
+++ b/services/tests/servicestests/src/com/android/server/WatchdogDiagnosticsTest.java
@@ -157,6 +157,8 @@
             String expected =
                     "TestThread2 annotated stack trace:\n" +
                     "    at java.lang.Object.wait(Native Method)\n" +
+                    "    at java.lang.Object.wait(Object.java:442)\n" +
+                    "    at java.lang.Object.wait(Object.java:568)\n" +
                     "    at com.android.server.WatchdogDiagnosticsTest$TestThread2.y(" +
                             "WatchdogDiagnosticsTest.java:91)\n" +
                     "    - locked <HASH> (a java.lang.String)\n" +
diff --git a/telecomm/java/android/telecom/Call.java b/telecomm/java/android/telecom/Call.java
index acf51f3..f54f8d1 100644
--- a/telecomm/java/android/telecom/Call.java
+++ b/telecomm/java/android/telecom/Call.java
@@ -265,6 +265,29 @@
     public static final String EVENT_HANDOVER_FAILED =
             "android.telecom.event.HANDOVER_FAILED";
 
+
+    /**
+     * Reject reason used with {@link #reject(int)} to indicate that the user is rejecting this
+     * call because they have declined to answer it.  This typically means that they are unable
+     * to answer the call at this time and would prefer it be sent to voicemail.
+     */
+    public static final int REJECT_REASON_DECLINED = 1;
+
+    /**
+     * Reject reason used with {@link #reject(int)} to indicate that the user is rejecting this
+     * call because it is an unwanted call.  This allows the user to indicate that they are
+     * rejecting a call because it is likely a nuisance call.
+     */
+    public static final int REJECT_REASON_UNWANTED = 2;
+
+    /**
+     * @hide
+     */
+    @IntDef(prefix = { "REJECT_REASON_" },
+            value = {REJECT_REASON_DECLINED, REJECT_REASON_UNWANTED})
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface RejectReason {};
+
     public static class Details {
         /** @hide */
         @Retention(RetentionPolicy.SOURCE)
@@ -1520,6 +1543,16 @@
     }
 
     /**
+     * Instructs the {@link ConnectionService} providing this {@link #STATE_RINGING} call that the
+     * user has chosen to reject the call and has indicated a reason why the call is being rejected.
+     *
+     * @param rejectReason the reason the call is being rejected.
+     */
+    public void reject(@RejectReason int rejectReason) {
+        mInCallAdapter.rejectCall(mTelecomCallId, rejectReason);
+    }
+
+    /**
      * Instructs this {@code Call} to disconnect.
      */
     public void disconnect() {
diff --git a/telecomm/java/android/telecom/Connection.java b/telecomm/java/android/telecom/Connection.java
index c934625..72c66d2 100644
--- a/telecomm/java/android/telecom/Connection.java
+++ b/telecomm/java/android/telecom/Connection.java
@@ -3037,6 +3037,17 @@
     public void onReject() {}
 
     /**
+     * Notifies this Connection, which is in {@link #STATE_RINGING}, of a request to reject.
+     * <p>
+     * For managed {@link ConnectionService}s, this will be called when the user rejects a call via
+     * the default dialer's {@link InCallService} using {@link Call#reject(int)}.
+     * @param rejectReason the reason the user provided for rejecting the call.
+     */
+    public void onReject(@android.telecom.Call.RejectReason int rejectReason) {
+        // to be implemented by ConnectionService.
+    }
+
+    /**
      * Notifies this Connection, which is in {@link #STATE_RINGING}, of
      * a request to reject with a message.
      */
diff --git a/telecomm/java/android/telecom/ConnectionService.java b/telecomm/java/android/telecom/ConnectionService.java
index 440f044..00c2918 100644
--- a/telecomm/java/android/telecom/ConnectionService.java
+++ b/telecomm/java/android/telecom/ConnectionService.java
@@ -194,6 +194,7 @@
     private static final int MSG_CREATE_CONFERENCE = 35;
     private static final int MSG_CREATE_CONFERENCE_COMPLETE = 36;
     private static final int MSG_CREATE_CONFERENCE_FAILED = 37;
+    private static final int MSG_REJECT_WITH_REASON = 38;
 
     private static Connection sNullConnection;
 
@@ -450,6 +451,21 @@
         }
 
         @Override
+        public void rejectWithReason(String callId,
+                @android.telecom.Call.RejectReason int rejectReason, Session.Info sessionInfo) {
+            Log.startSession(sessionInfo, SESSION_REJECT);
+            try {
+                SomeArgs args = SomeArgs.obtain();
+                args.arg1 = callId;
+                args.argi1 = rejectReason;
+                args.arg2 = Log.createSubsession();
+                mHandler.obtainMessage(MSG_REJECT_WITH_REASON, args).sendToTarget();
+            } finally {
+                Log.endSession();
+            }
+        }
+
+        @Override
         public void rejectWithMessage(String callId, String message, Session.Info sessionInfo) {
             Log.startSession(sessionInfo, SESSION_REJECT_MESSAGE);
             try {
@@ -1053,6 +1069,17 @@
                     }
                     break;
                 }
+                case MSG_REJECT_WITH_REASON: {
+                    SomeArgs args = (SomeArgs) msg.obj;
+                    Log.continueSession((Session) args.arg2, SESSION_HANDLER + SESSION_REJECT);
+                    try {
+                        reject((String) args.arg1, args.argi1);
+                    } finally {
+                        args.recycle();
+                        Log.endSession();
+                    }
+                    break;
+                }
                 case MSG_REJECT_WITH_MESSAGE: {
                     SomeArgs args = (SomeArgs) msg.obj;
                     Log.continueSession((Session) args.arg3,
@@ -1981,6 +2008,11 @@
         findConnectionForAction(callId, "reject").onReject(rejectWithMessage);
     }
 
+    private void reject(String callId, @android.telecom.Call.RejectReason int rejectReason) {
+        Log.d(this, "reject %s with reason %d", callId, rejectReason);
+        findConnectionForAction(callId, "reject").onReject(rejectReason);
+    }
+
     private void silence(String callId) {
         Log.d(this, "silence %s", callId);
         findConnectionForAction(callId, "silence").onSilence();
diff --git a/telecomm/java/android/telecom/InCallAdapter.java b/telecomm/java/android/telecom/InCallAdapter.java
index 2612468..594c1eb 100644
--- a/telecomm/java/android/telecom/InCallAdapter.java
+++ b/telecomm/java/android/telecom/InCallAdapter.java
@@ -89,6 +89,19 @@
     }
 
     /**
+     * Instructs Telecom to reject the specified call.
+     *
+     * @param callId The identifier of the call to reject.
+     * @param rejectReason The reason the call was rejected.
+     */
+    public void rejectCall(String callId, @Call.RejectReason int rejectReason) {
+        try {
+            mAdapter.rejectCallWithReason(callId, rejectReason);
+        } catch (RemoteException e) {
+        }
+    }
+
+    /**
      * Instructs Telecom to disconnect the specified call.
      *
      * @param callId The identifier of the call to disconnect.
diff --git a/telecomm/java/com/android/internal/telecom/IConnectionService.aidl b/telecomm/java/com/android/internal/telecom/IConnectionService.aidl
index 96f2483..4249dff 100644
--- a/telecomm/java/com/android/internal/telecom/IConnectionService.aidl
+++ b/telecomm/java/com/android/internal/telecom/IConnectionService.aidl
@@ -77,6 +77,8 @@
 
     void reject(String callId, in Session.Info sessionInfo);
 
+    void rejectWithReason(String callId, int rejectReason, in Session.Info sessionInfo);
+
     void rejectWithMessage(String callId, String message, in Session.Info sessionInfo);
 
     void disconnect(String callId, in Session.Info sessionInfo);
diff --git a/telecomm/java/com/android/internal/telecom/IInCallAdapter.aidl b/telecomm/java/com/android/internal/telecom/IInCallAdapter.aidl
index 60745e4..eb2d714 100644
--- a/telecomm/java/com/android/internal/telecom/IInCallAdapter.aidl
+++ b/telecomm/java/com/android/internal/telecom/IInCallAdapter.aidl
@@ -34,6 +34,8 @@
 
     void rejectCall(String callId, boolean rejectWithMessage, String textMessage);
 
+    void rejectCallWithReason(String callId, int rejectReason);
+
     void disconnectCall(String callId);
 
     void holdCall(String callId);
diff --git a/telephony/java/android/telephony/ims/stub/ImsCallSessionImplBase.java b/telephony/java/android/telephony/ims/stub/ImsCallSessionImplBase.java
index f4367da..e8f69ea 100644
--- a/telephony/java/android/telephony/ims/stub/ImsCallSessionImplBase.java
+++ b/telephony/java/android/telephony/ims/stub/ImsCallSessionImplBase.java
@@ -410,6 +410,13 @@
      * Rejects an incoming call or session update.
      *
      * @param reason reason code to reject an incoming call, defined in {@link ImsReasonInfo}.
+     *               The {@link android.telecom.InCallService} (dialer app) can use the
+     *               {@link android.telecom.Call#reject(int)} API to reject a call while specifying
+     *               a user-indicated reason for rejecting the call.
+     *               Normal call declines ({@link android.telecom.Call#REJECT_REASON_DECLINED}) will
+     *               map to {@link ImsReasonInfo#CODE_USER_DECLINE}.
+     *               Unwanted calls ({@link android.telecom.Call#REJECT_REASON_UNWANTED}) will map
+     *               to {@link ImsReasonInfo#CODE_SIP_USER_MARKED_UNWANTED}.
      * {@link ImsCallSession.Listener#callSessionStartFailed}
      */
     public void reject(int reason) {
diff --git a/wifi/java/android/net/wifi/WifiInfo.java b/wifi/java/android/net/wifi/WifiInfo.java
index 419bcb1..f8d48c5 100644
--- a/wifi/java/android/net/wifi/WifiInfo.java
+++ b/wifi/java/android/net/wifi/WifiInfo.java
@@ -178,7 +178,7 @@
      * If connected to a network suggestion or specifier, store the package name of the app,
      * else null.
      */
-    private String mAppPackageName;
+    private String mRequestingPackageName;
 
     /**
      * Running total count of lost (not ACKed) transmitted unicast data packets.
@@ -201,68 +201,68 @@
      */
     public long rxSuccess;
 
-    private double mTxBadRate;
+    private double mLostTxPacketsPerSecond;
 
     /**
      * Average rate of lost transmitted packets, in units of packets per second.
      * @hide
      */
     @SystemApi
-    public double getTxBadRate() {
-        return mTxBadRate;
+    public double getLostTxPacketsPerSecond() {
+        return mLostTxPacketsPerSecond;
     }
 
     /** @hide */
-    public void setTxBadRate(double txBadRate) {
-        mTxBadRate = txBadRate;
+    public void setLostTxPacketsPerSecond(double lostTxPacketsPerSecond) {
+        mLostTxPacketsPerSecond = lostTxPacketsPerSecond;
     }
 
-    private double mTxRetriesRate;
+    private double mTxRetriedTxPacketsPerSecond;
 
     /**
      * Average rate of transmitted retry packets, in units of packets per second.
      * @hide
      */
     @SystemApi
-    public double getTxRetriesRate() {
-        return mTxRetriesRate;
+    public double getRetriedTxPacketsPerSecond() {
+        return mTxRetriedTxPacketsPerSecond;
     }
 
     /** @hide */
-    public void setTxRetriesRate(double txRetriesRate) {
-        mTxRetriesRate = txRetriesRate;
+    public void setRetriedTxPacketsRate(double txRetriedTxPacketsPerSecond) {
+        mTxRetriedTxPacketsPerSecond = txRetriedTxPacketsPerSecond;
     }
 
-    private double mTxSuccessRate;
+    private double mSuccessfulTxPacketsPerSecond;
 
     /**
      * Average rate of successfully transmitted unicast packets, in units of packets per second.
      * @hide
      */
     @SystemApi
-    public double getTxSuccessRate() {
-        return mTxSuccessRate;
+    public double getSuccessfulTxPacketsPerSecond() {
+        return mSuccessfulTxPacketsPerSecond;
     }
 
     /** @hide */
-    public void setTxSuccessRate(double txSuccessRate) {
-        mTxSuccessRate = txSuccessRate;
+    public void setSuccessfulTxPacketsPerSecond(double successfulTxPacketsPerSecond) {
+        mSuccessfulTxPacketsPerSecond = successfulTxPacketsPerSecond;
     }
 
-    private double mRxSuccessRate;
+    private double mSuccessfulRxPacketsPerSecond;
 
     /**
      * Average rate of received unicast data packets, in units of packets per second.
      * @hide
      */
     @SystemApi
-    public double getRxSuccessRate() {
-        return mRxSuccessRate;
+    public double getSuccessfulRxPacketsPerSecond() {
+        return mSuccessfulRxPacketsPerSecond;
     }
 
     /** @hide */
-    public void setRxSuccessRate(double rxSuccessRate) {
-        mRxSuccessRate = rxSuccessRate;
+    public void setSuccessfulRxPacketsPerSecond(double successfulRxPacketsPerSecond) {
+        mSuccessfulRxPacketsPerSecond = successfulRxPacketsPerSecond;
     }
 
     /** @hide */
@@ -319,17 +319,17 @@
         setMeteredHint(false);
         setEphemeral(false);
         setOsuAp(false);
-        setAppPackageName(null);
+        setRequestingPackageName(null);
         setFQDN(null);
         setProviderFriendlyName(null);
         txBad = 0;
         txSuccess = 0;
         rxSuccess = 0;
         txRetries = 0;
-        mTxBadRate = 0;
-        mTxSuccessRate = 0;
-        mRxSuccessRate = 0;
-        mTxRetriesRate = 0;
+        mLostTxPacketsPerSecond = 0;
+        mSuccessfulTxPacketsPerSecond = 0;
+        mSuccessfulRxPacketsPerSecond = 0;
+        mTxRetriedTxPacketsPerSecond = 0;
         score = 0;
     }
 
@@ -353,8 +353,8 @@
             mMeteredHint = source.mMeteredHint;
             mEphemeral = source.mEphemeral;
             mTrusted = source.mTrusted;
-            mAppPackageName =
-                    source.mAppPackageName;
+            mRequestingPackageName =
+                    source.mRequestingPackageName;
             mOsuAp = source.mOsuAp;
             mFqdn = source.mFqdn;
             mProviderFriendlyName = source.mProviderFriendlyName;
@@ -362,10 +362,10 @@
             txRetries = source.txRetries;
             txSuccess = source.txSuccess;
             rxSuccess = source.rxSuccess;
-            mTxBadRate = source.mTxBadRate;
-            mTxRetriesRate = source.mTxRetriesRate;
-            mTxSuccessRate = source.mTxSuccessRate;
-            mRxSuccessRate = source.mRxSuccessRate;
+            mLostTxPacketsPerSecond = source.mLostTxPacketsPerSecond;
+            mTxRetriedTxPacketsPerSecond = source.mTxRetriedTxPacketsPerSecond;
+            mSuccessfulTxPacketsPerSecond = source.mSuccessfulTxPacketsPerSecond;
+            mSuccessfulRxPacketsPerSecond = source.mSuccessfulRxPacketsPerSecond;
             score = source.score;
             mWifiStandard = source.mWifiStandard;
             mMaxSupportedTxLinkSpeed = source.mMaxSupportedTxLinkSpeed;
@@ -777,8 +777,8 @@
     }
 
     /** {@hide} */
-    public void setAppPackageName(@Nullable String packageName) {
-        mAppPackageName = packageName;
+    public void setRequestingPackageName(@Nullable String packageName) {
+        mRequestingPackageName = packageName;
     }
 
     /**
@@ -788,8 +788,8 @@
      * @hide
      */
     @SystemApi
-    public @Nullable String getAppPackageName() {
-        return mAppPackageName;
+    public @Nullable String getRequestingPackageName() {
+        return mRequestingPackageName;
     }
 
 
@@ -956,16 +956,16 @@
         dest.writeInt(mTrusted ? 1 : 0);
         dest.writeInt(score);
         dest.writeLong(txSuccess);
-        dest.writeDouble(mTxSuccessRate);
+        dest.writeDouble(mSuccessfulTxPacketsPerSecond);
         dest.writeLong(txRetries);
-        dest.writeDouble(mTxRetriesRate);
+        dest.writeDouble(mTxRetriedTxPacketsPerSecond);
         dest.writeLong(txBad);
-        dest.writeDouble(mTxBadRate);
+        dest.writeDouble(mLostTxPacketsPerSecond);
         dest.writeLong(rxSuccess);
-        dest.writeDouble(mRxSuccessRate);
+        dest.writeDouble(mSuccessfulRxPacketsPerSecond);
         mSupplicantState.writeToParcel(dest, flags);
         dest.writeInt(mOsuAp ? 1 : 0);
-        dest.writeString(mAppPackageName);
+        dest.writeString(mRequestingPackageName);
         dest.writeString(mFqdn);
         dest.writeString(mProviderFriendlyName);
         dest.writeInt(mWifiStandard);
@@ -1000,16 +1000,16 @@
                 info.mTrusted = in.readInt() != 0;
                 info.score = in.readInt();
                 info.txSuccess = in.readLong();
-                info.mTxSuccessRate = in.readDouble();
+                info.mSuccessfulTxPacketsPerSecond = in.readDouble();
                 info.txRetries = in.readLong();
-                info.mTxRetriesRate = in.readDouble();
+                info.mTxRetriedTxPacketsPerSecond = in.readDouble();
                 info.txBad = in.readLong();
-                info.mTxBadRate = in.readDouble();
+                info.mLostTxPacketsPerSecond = in.readDouble();
                 info.rxSuccess = in.readLong();
-                info.mRxSuccessRate = in.readDouble();
+                info.mSuccessfulRxPacketsPerSecond = in.readDouble();
                 info.mSupplicantState = SupplicantState.CREATOR.createFromParcel(in);
                 info.mOsuAp = in.readInt() != 0;
-                info.mAppPackageName = in.readString();
+                info.mRequestingPackageName = in.readString();
                 info.mFqdn = in.readString();
                 info.mProviderFriendlyName = in.readString();
                 info.mWifiStandard = in.readInt();
diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java
index fb3e794..c7475ee 100644
--- a/wifi/java/android/net/wifi/WifiManager.java
+++ b/wifi/java/android/net/wifi/WifiManager.java
@@ -4854,13 +4854,19 @@
     /**
      * Set Wi-Fi verbose logging level from developer settings.
      *
-     * @param verbose the verbose logging level to set. 0 will disable verbose logging, a positive
-     *                integer will enable verbose logging.
+     * @param enable true to enable verbose logging, false to disable.
      *
      * @hide
      */
     @SystemApi
     @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS)
+    public void setVerboseLoggingEnabled(boolean enable) {
+        enableVerboseLogging(enable ? 1 : 0);
+    }
+
+    /** @hide */
+    @UnsupportedAppUsage
+    @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS)
     public void enableVerboseLogging (int verbose) {
         try {
             mService.enableVerboseLogging(verbose);
@@ -4871,15 +4877,23 @@
     }
 
     /**
-     * Get the persisted WiFi verbose logging level, set by {@link #enableVerboseLogging(int)}.
+     * Get the persisted Wi-Fi verbose logging level, set by
+     * {@link #setVerboseLoggingEnabled(boolean)}.
      * No permissions are required to call this method.
      *
-     * @return 0 to indicate that verbose logging is disabled, a positive integer to indicate that
-     * verbose logging is enabled.
+     * @return true to indicate that verbose logging is enabled, false to indicate that verbose
+     * logging is disabled.
      *
      * @hide
      */
     @SystemApi
+    public boolean isVerboseLoggingEnabled() {
+        return getVerboseLoggingLevel() > 0;
+    }
+
+    /** @hide */
+    // TODO(b/145484145): remove once SUW stops calling this via reflection
+    @UnsupportedAppUsage
     public int getVerboseLoggingLevel() {
         try {
             return mService.getVerboseLoggingLevel();
@@ -5204,7 +5218,7 @@
      * level from wifi service.
      */
     private void updateVerboseLoggingEnabledFromService() {
-        mVerboseLoggingEnabled = getVerboseLoggingLevel() > 0;
+        mVerboseLoggingEnabled = isVerboseLoggingEnabled();
     }
 
     /**
diff --git a/wifi/tests/src/android/net/wifi/WifiInfoTest.java b/wifi/tests/src/android/net/wifi/WifiInfoTest.java
index 04759ac..311bbc4 100644
--- a/wifi/tests/src/android/net/wifi/WifiInfoTest.java
+++ b/wifi/tests/src/android/net/wifi/WifiInfoTest.java
@@ -64,7 +64,7 @@
         writeWifiInfo.setOsuAp(true);
         writeWifiInfo.setFQDN(TEST_FQDN);
         writeWifiInfo.setProviderFriendlyName(TEST_PROVIDER_NAME);
-        writeWifiInfo.setAppPackageName(TEST_PACKAGE_NAME);
+        writeWifiInfo.setRequestingPackageName(TEST_PACKAGE_NAME);
         writeWifiInfo.setWifiStandard(TEST_WIFI_STANDARD);
         writeWifiInfo.setMaxSupportedTxLinkSpeedMbps(TEST_MAX_SUPPORTED_TX_LINK_SPEED_MBPS);
         writeWifiInfo.setMaxSupportedRxLinkSpeedMbps(TEST_MAX_SUPPORTED_RX_LINK_SPEED_MBPS);
@@ -83,7 +83,7 @@
         assertTrue(readWifiInfo.isTrusted());
         assertTrue(readWifiInfo.isOsuAp());
         assertTrue(readWifiInfo.isPasspointAp());
-        assertEquals(TEST_PACKAGE_NAME, readWifiInfo.getAppPackageName());
+        assertEquals(TEST_PACKAGE_NAME, readWifiInfo.getRequestingPackageName());
         assertEquals(TEST_FQDN, readWifiInfo.getPasspointFqdn());
         assertEquals(TEST_PROVIDER_NAME, readWifiInfo.getPasspointProviderFriendlyName());
         assertEquals(TEST_WIFI_STANDARD, readWifiInfo.getWifiStandard());