Merge "Add all .idea directories to .gitignore"
diff --git a/api/system-current.txt b/api/system-current.txt
index 3ff93f5..b0958a2 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -1635,7 +1635,7 @@
     method @NonNull @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS) public java.util.List<android.content.pm.ResolveInfo> queryIntentServicesAsUser(@NonNull android.content.Intent, int, @NonNull android.os.UserHandle);
     method public abstract void registerDexModule(@NonNull String, @Nullable android.content.pm.PackageManager.DexModuleRegisterCallback);
     method @RequiresPermission("android.permission.OBSERVE_GRANT_REVOKE_PERMISSIONS") public abstract void removeOnPermissionsChangeListener(@NonNull android.content.pm.PackageManager.OnPermissionsChangedListener);
-    method @Deprecated public void replacePreferredActivity(@NonNull android.content.IntentFilter, int, @NonNull java.util.List<android.content.ComponentName>, @NonNull android.content.ComponentName);
+    method public void replacePreferredActivity(@NonNull android.content.IntentFilter, int, @NonNull java.util.List<android.content.ComponentName>, @NonNull android.content.ComponentName);
     method @RequiresPermission(android.Manifest.permission.REVOKE_RUNTIME_PERMISSIONS) public abstract void revokeRuntimePermission(@NonNull String, @NonNull String, @NonNull android.os.UserHandle);
     method public void sendDeviceCustomizationReadyBroadcast();
     method @RequiresPermission(allOf={android.Manifest.permission.SET_PREFERRED_APPLICATIONS, android.Manifest.permission.INTERACT_ACROSS_USERS_FULL}) public abstract boolean setDefaultBrowserPackageNameAsUser(@Nullable String, int);
diff --git a/api/test-current.txt b/api/test-current.txt
index 0e71c94..1e5bb7e 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -3215,6 +3215,7 @@
     method public default void setShouldShowIme(int, boolean);
     method public default void setShouldShowSystemDecors(int, boolean);
     method public default void setShouldShowWithInsecureKeyguard(int, boolean);
+    method public default boolean shouldShowIme(int);
     method public default boolean shouldShowSystemDecors(int);
   }
 
diff --git a/cmds/statsd/src/StatsService.cpp b/cmds/statsd/src/StatsService.cpp
index 662be05..623a1f2 100644
--- a/cmds/statsd/src/StatsService.cpp
+++ b/cmds/statsd/src/StatsService.cpp
@@ -28,6 +28,7 @@
 
 #include <android-base/file.h>
 #include <android-base/stringprintf.h>
+#include <android-base/strings.h>
 #include <binder/IPCThreadState.h>
 #include <binder/IServiceManager.h>
 #include <binder/PermissionController.h>
@@ -394,6 +395,10 @@
             return cmd_log_app_breadcrumb(out, args);
         }
 
+        if (!args[0].compare(String8("log-binary-push"))) {
+            return cmd_log_binary_push(out, args);
+        }
+
         if (!args[0].compare(String8("clear-puller-cache"))) {
             return cmd_clear_puller_cache(out);
         }
@@ -461,6 +466,21 @@
     dprintf(out, "  STATE         Integer in [0, 3], as per atoms.proto.\n");
     dprintf(out, "\n");
     dprintf(out, "\n");
+    dprintf(out,
+            "usage: adb shell cmd stats log-binary-push NAME VERSION STAGING ROLLBACK_ENABLED "
+            "LOW_LATENCY STATE EXPERIMENT_IDS\n");
+    dprintf(out, "  Log a binary push state changed event.\n");
+    dprintf(out, "  NAME                The train name.\n");
+    dprintf(out, "  VERSION             The train version code.\n");
+    dprintf(out, "  STAGING             If this train requires a restart.\n");
+    dprintf(out, "  ROLLBACK_ENABLED    If rollback should be enabled for this install.\n");
+    dprintf(out, "  LOW_LATENCY         If the train requires low latency monitoring.\n");
+    dprintf(out, "  STATE               The status of the train push.\n");
+    dprintf(out, "                      Integer value of the enum in atoms.proto.\n");
+    dprintf(out, "  EXPERIMENT_IDS      Comma separated list of experiment ids.\n");
+    dprintf(out, "                      Leave blank for none.\n");
+    dprintf(out, "\n");
+    dprintf(out, "\n");
     dprintf(out, "usage: adb shell cmd stats config remove [UID] [NAME]\n");
     dprintf(out, "usage: adb shell cmd stats config update [UID] NAME\n");
     dprintf(out, "\n");
@@ -506,7 +526,6 @@
     dprintf(out, "  --configs     Send the list of configs in the name list instead of\n");
     dprintf(out, "                the currently active configs\n");
     dprintf(out, "  NAME LIST     List of configuration names to be included in the broadcast.\n");
-
     dprintf(out, "\n");
     dprintf(out, "\n");
     dprintf(out, "usage: adb shell cmd stats print-stats\n");
@@ -821,6 +840,39 @@
     return NO_ERROR;
 }
 
+status_t StatsService::cmd_log_binary_push(int out, const Vector<String8>& args) {
+    // Security checks are done in the sendBinaryPushStateChanged atom.
+    const int argCount = args.size();
+    if (argCount != 7 && argCount != 8) {
+        dprintf(out, "Incorrect number of argument supplied\n");
+        return UNKNOWN_ERROR;
+    }
+    android::String16 trainName = android::String16(args[1].c_str());
+    int64_t trainVersion = strtoll(args[2].c_str(), nullptr, 10);
+    int options = 0;
+    if (args[3] == "1") {
+        options = options | IStatsManager::FLAG_REQUIRE_STAGING;
+    }
+    if (args[4] == "1") {
+        options = options | IStatsManager::FLAG_ROLLBACK_ENABLED;
+    }
+    if (args[5] == "1") {
+        options = options | IStatsManager::FLAG_REQUIRE_LOW_LATENCY_MONITOR;
+    }
+    int32_t state = atoi(args[6].c_str());
+    vector<int64_t> experimentIds;
+    if (argCount == 8) {
+        vector<string> experimentIdsString = android::base::Split(string(args[7].c_str()), ",");
+        for (string experimentIdString : experimentIdsString) {
+            int64_t experimentId = strtoll(experimentIdString.c_str(), nullptr, 10);
+            experimentIds.push_back(experimentId);
+        }
+    }
+    dprintf(out, "Logging BinaryPushStateChanged\n");
+    sendBinaryPushStateChangedAtom(trainName, trainVersion, options, state, experimentIds);
+    return NO_ERROR;
+}
+
 status_t StatsService::cmd_print_pulled_metrics(int out, const Vector<String8>& args) {
     int s = atoi(args[1].c_str());
     vector<shared_ptr<LogEvent> > stats;
@@ -1207,23 +1259,26 @@
                                                     const int options,
                                                     const int32_t state,
                                                     const std::vector<int64_t>& experimentIdsIn) {
+    // Note: We skip the usage stats op check here since we do not have a package name.
+    // This is ok since we are overloading the usage_stats permission.
+    // This method only sends data, it does not receive it.
+    pid_t pid = IPCThreadState::self()->getCallingPid();
     uid_t uid = IPCThreadState::self()->getCallingUid();
-    // For testing
-    if (uid == AID_ROOT || uid == AID_SYSTEM || uid == AID_SHELL) {
-        return ok();
+        // Root, system, and shell always have access
+    if (uid != AID_ROOT && uid != AID_SYSTEM && uid != AID_SHELL) {
+        // Caller must be granted these permissions
+        if (!checkCallingPermission(String16(kPermissionDump))) {
+            return exception(binder::Status::EX_SECURITY,
+                             StringPrintf("UID %d / PID %d lacks permission %s", uid, pid,
+                                          kPermissionDump));
+        }
+        if (!checkCallingPermission(String16(kPermissionUsage))) {
+            return exception(binder::Status::EX_SECURITY,
+                             StringPrintf("UID %d / PID %d lacks permission %s", uid, pid,
+                                          kPermissionUsage));
+        }
     }
 
-    // Caller must be granted these permissions
-    if (!checkCallingPermission(String16(kPermissionDump))) {
-        return exception(binder::Status::EX_SECURITY,
-                         StringPrintf("UID %d lacks permission %s", uid, kPermissionDump));
-    }
-    if (!checkCallingPermission(String16(kPermissionUsage))) {
-        return exception(binder::Status::EX_SECURITY,
-                         StringPrintf("UID %d lacks permission %s", uid, kPermissionUsage));
-    }
-    // TODO: add verifier permission
-
     bool readTrainInfoSuccess = false;
     InstallTrainInfo trainInfoOnDisk;
     readTrainInfoSuccess = StorageManager::readTrainInfo(trainInfoOnDisk);
diff --git a/cmds/statsd/src/StatsService.h b/cmds/statsd/src/StatsService.h
index 0b6df8b..936f7db 100644
--- a/cmds/statsd/src/StatsService.h
+++ b/cmds/statsd/src/StatsService.h
@@ -331,6 +331,11 @@
     status_t cmd_log_app_breadcrumb(int outFd, const Vector<String8>& args);
 
     /**
+     * Write an BinaryPushStateChanged event, as if calling StatsLog.logBinaryPushStateChanged().
+     */
+    status_t cmd_log_binary_push(int outFd, const Vector<String8>& args);
+
+    /**
      * Print contents of a pulled metrics source.
      */
     status_t cmd_print_pulled_metrics(int outFd, const Vector<String8>& args);
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index ca05d9a..004eef7 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -41,13 +41,14 @@
 import "frameworks/base/core/proto/android/service/procstats_enum.proto";
 import "frameworks/base/core/proto/android/service/usb.proto";
 import "frameworks/base/core/proto/android/stats/connectivity/network_stack.proto";
-import "frameworks/base/core/proto/android/stats/connectivity/resolv_stats.proto";
+import "frameworks/base/core/proto/android/stats/dnsresolver/dns_resolver.proto";
 import "frameworks/base/core/proto/android/stats/devicepolicy/device_policy.proto";
 import "frameworks/base/core/proto/android/stats/devicepolicy/device_policy_enums.proto";
 import "frameworks/base/core/proto/android/stats/docsui/docsui_enums.proto";
 import "frameworks/base/core/proto/android/stats/enums.proto";
 import "frameworks/base/core/proto/android/stats/intelligence/enums.proto";
 import "frameworks/base/core/proto/android/stats/launcher/launcher.proto";
+import "frameworks/base/core/proto/android/stats/mediametrics/mediametrics.proto";
 import "frameworks/base/core/proto/android/stats/storage/storage_enums.proto";
 import "frameworks/base/core/proto/android/stats/style/style_enums.proto";
 import "frameworks/base/core/proto/android/telecomm/enums.proto";
@@ -283,6 +284,16 @@
         ThermalThrottlingSeverityStateChanged thermal_throttling_severity_state_changed = 189;
         RoleRequestResultReported role_request_result_reported =
             190 [(log_from_module) = "permissioncontroller"];
+        MediametricsAudiopolicyReported mediametrics_audiopolicy_reported = 191;
+        MediametricsAudiorecordReported mediametrics_audiorecord_reported = 192;
+        MediametricsAudiothreadReported mediametrics_audiothread_reported = 193;
+        MediametricsAudiotrackReported mediametrics_audiotrack_reported = 194;
+        MediametricsCodecReported mediametrics_codec_reported = 195;
+        MediametricsDrmWidevineReported mediametrics_drm_widevine_reported = 196;
+        MediametricsExtractorReported mediametrics_extractor_reported = 197;
+        MediametricsMediadrmReported mediametrics_mediadrm_reported = 198;
+        MediametricsNuPlayerReported mediametrics_nuplayer_reported = 199;
+        MediametricsRecorderReported mediametrics_recorder_reported = 200;
     }
 
     // Pulled events will start at field 10000.
@@ -3296,7 +3307,10 @@
         INSTALL_STAGED_NOT_READY = 3;
         INSTALL_STAGED_READY = 4;
         INSTALL_SUCCESS = 5;
-        INSTALL_FAILURE = 6;
+        // Replaced by INSTALL_FAILURE_DOWNLOAD, INSTALL_FAILURE_STATE_MISMATCH,
+        // and INSTALL_FAILURE_COMMIT.
+        INSTALL_FAILURE = 6  [deprecated = true];
+        // This enum is for installs that are manually cancelled via the Manual Update UI.
         INSTALL_CANCELLED = 7;
         INSTALLER_ROLLBACK_REQUESTED = 8;
         INSTALLER_ROLLBACK_INITIATED = 9;
@@ -3313,6 +3327,9 @@
         INSTALL_STAGED_CANCEL_REQUESTED = 20;
         INSTALL_STAGED_CANCEL_SUCCESS = 21;
         INSTALL_STAGED_CANCEL_FAILURE = 22;
+        INSTALL_FAILURE_DOWNLOAD = 23;
+        INSTALL_FAILURE_STATE_MISMATCH = 24;
+        INSTALL_FAILURE_COMMIT = 25;
     }
     optional State state = 6;
     // Possible experiment ids for monitoring this push.
@@ -5092,36 +5109,36 @@
  * Logs a DNS lookup operation initiated by the system resolver on behalf of an application
  * invoking native APIs such as getaddrinfo() or Java APIs such as Network#getAllByName().
  *
- * The top-level message represents the entire lookup operation, which may result one or more
- * queries to the recursive DNS resolvers. Those are individually logged in DnsQueryEvent to
- * enable computing error rates and network latency and timeouts broken up by query type,
- * transport, network interface, etc.
+ * The NetworkDnsEventReported message represents the entire lookup operation, which may
+ * result one or more queries to the recursive DNS resolvers. Those are individually logged
+ * in DnsQueryEvents to enable computing error rates and network latency and timeouts
+ * broken up by query type, transport, network interface, etc.
  */
 message NetworkDnsEventReported {
+    optional android.stats.dnsresolver.EventType event_type = 1;
 
-    optional android.stats.connectivity.EventType event_type = 1;
-
-    optional android.stats.connectivity.ReturnCode return_code = 2;
+    optional android.stats.dnsresolver.ReturnCode return_code = 2;
 
     // The latency in microseconds of the entire DNS lookup operation.
     optional int32 latency_micros = 3;
 
-    optional android.stats.connectivity.DnsQueryEventRe dns_query_event_re = 4 [(log_mode) = MODE_BYTES];
+    // Only valid for event_type = EVENT_GETADDRINFO.
+    optional int32 hints_ai_flags = 4;
 
-    // ResNSend flags defined in android/multinetwork.h
-    optional int32 flags = 5;
+    // Flags passed to android_res_nsend() defined in multinetwork.h
+    // Only valid for event_type = EVENT_RESNSEND.
+    optional int32 res_nsend_flags = 5;
 
-    optional android.net.NetworkCapabilitiesProto.Transport network_type = 6;
+    optional android.stats.dnsresolver.Transport network_type = 6;
 
     // The DNS over TLS mode on a specific netId.
-    optional android.stats.connectivity.PrivateDnsModes private_dns_modes = 7;
+    optional android.stats.dnsresolver.PrivateDnsModes private_dns_modes = 7;
 
     // Additional pass-through fields opaque to statsd.
     // The DNS resolver Mainline module can add new fields here without requiring an OS update.
-    optional android.stats.connectivity.DnsCallEvent dns_call_event = 8 [(log_mode) = MODE_BYTES];
+    optional android.stats.dnsresolver.DnsQueryEvents dns_query_events = 8 [(log_mode) = MODE_BYTES];
 }
 
-
 /**
  * Logs when a data stall event occurs.
  *
@@ -5809,6 +5826,160 @@
 }
 
 /**
+ * Track Media Codec usage
+ * Logged from:
+ *   frameworks/av/media/libstagefright/MediaCodec.cpp
+ *   frameworks/av/services/mediaanalytics/statsd_codec.cpp
+ */
+message MediametricsCodecReported {
+    optional int64 timestamp_nanos = 1;
+    optional string package_name = 2;
+    optional int64 package_version_code = 3;
+    optional int64 media_apex_version = 4;
+
+    optional android.stats.mediametrics.CodecData codec_data = 5 [(android.os.statsd.log_mode) = MODE_BYTES];
+}
+
+/**
+ * Track Media Extractor (pulling video/audio streams out of containers) usage
+ * Logged from:
+ *   frameworks/av/media/libstagefright/RemoteMediaExtractor.cpp
+ *   frameworks/av/services/mediaanalytics/statsd_extractor.cpp
+ */
+message MediametricsExtractorReported {
+    optional int64 timestamp_nanos = 1;
+    optional string package_name = 2;
+    optional int64 package_version_code = 3;
+    optional int64 media_apex_version = 4;
+
+    optional android.stats.mediametrics.ExtractorData extractor_data = 5 [(android.os.statsd.log_mode) = MODE_BYTES];
+}
+
+/**
+ * Track how we arbitrate between microphone/input requests.
+ * Logged from
+ *   frameworks/av/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp
+ *   frameworks/av/services/mediaanalytics/statsd_audiopolicy.cpp
+ */
+message MediametricsAudiopolicyReported {
+    optional int64 timestamp_nanos = 1;
+    optional string package_name = 2;
+    optional int64 package_version_code = 3;
+    optional int64 media_apex_version = 4;
+
+    optional android.stats.mediametrics.AudioPolicyData audiopolicy_data = 5 [(android.os.statsd.log_mode) = MODE_BYTES];
+}
+
+/**
+ * Track how we arbitrate between microphone requests.
+ * Logged from
+ *   frameworks/av/media/libaudioclient/AudioRecord.cpp
+ *   frameworks/av/services/mediaanalytics/statsd_audiorecord.cpp
+ */
+message MediametricsAudiorecordReported {
+    optional int64 timestamp_nanos = 1;
+    optional string package_name = 2;
+    optional int64 package_version_code = 3;
+    optional int64 media_apex_version = 4;
+
+    optional android.stats.mediametrics.AudioRecordData audiorecord_data = 5 [(android.os.statsd.log_mode) = MODE_BYTES];
+}
+
+/**
+ * Track how we arbitrate between microphone/input requests.
+ * Logged from
+ *   frameworks/av/media/libnblog/ReportPerformance.cpp
+ *   frameworks/av/services/mediaanalytics/statsd_audiothread.cpp
+ */
+message MediametricsAudiothreadReported {
+    optional int64 timestamp_nanos = 1;
+    optional string package_name = 2;
+    optional int64 package_version_code = 3;
+    optional int64 media_apex_version = 4;
+
+    optional android.stats.mediametrics.AudioThreadData audiothread_data = 5 [(android.os.statsd.log_mode) = MODE_BYTES];
+}
+
+/**
+ * Track how we arbitrate between microphone/input requests.
+ * Logged from
+ *   frameworks/av/media/libaudioclient/AudioTrack.cpp
+ *   frameworks/av/services/mediaanalytics/statsd_audiotrack.cpp
+ */
+message MediametricsAudiotrackReported {
+    optional int64 timestamp_nanos = 1;
+    optional string package_name = 2;
+    optional int64 package_version_code = 3;
+    optional int64 media_apex_version = 4;
+
+    optional android.stats.mediametrics.AudioTrackData audiotrack_data = 5 [(android.os.statsd.log_mode) = MODE_BYTES];
+}
+
+/**
+ * Track information about DRM framework performance
+ * Logged from
+ *   frameworks/av/drm/libmediadrm/DrmHal.cpp
+ *   frameworks/av/services/mediaanalytics/statsd_drm.cpp
+ */
+message MediametricsMediadrmReported {
+    optional int64 timestamp_nanos = 1;
+    optional string package_name = 2;
+    optional int64 package_version_code = 3;
+    optional int64 media_apex_version = 4;
+
+    // vendor+description tell about which DRM plugin is in use on this device
+    optional string vendor = 5;
+    optional string description = 6;
+    // from frameworks/av/drm/libmediadrm/protos/metrics.proto
+    optional bytes framework_stats = 7 [(android.os.statsd.log_mode) = MODE_BYTES];
+}
+
+/**
+ * Track information about the widevine DRM plugin performance
+ * Logged from
+ *   vendor/widevine/libwvdrmengine/cdm/metrics
+ *   frameworks/av/services/mediaanalytics/statsd_drm.cpp
+ */
+message MediametricsDrmWidevineReported {
+    optional int64 timestamp_nanos = 1;
+    optional string package_name = 2;
+    optional int64 package_version_code = 3;
+    optional int64 media_apex_version = 4;
+
+    optional bytes vendor_specific_stats = 5 [(android.os.statsd.log_mode) = MODE_BYTES];
+}
+
+/**
+ * Track information about recordings (e.g. camcorder)
+ * Logged from
+ *   frameworks/av/media/libmediaplayerservice/StagefrightRecorder.cpp
+ *   frameworks/av/services/mediaanalytics/statsd_recorder.cpp
+ */
+message MediametricsRecorderReported {
+    optional int64 timestamp_nanos = 1;
+    optional string package_name = 2;
+    optional int64 package_version_code = 3;
+    optional int64 media_apex_version = 4;
+
+    optional android.stats.mediametrics.RecorderData recorder_data = 5 [(android.os.statsd.log_mode) = MODE_BYTES];
+}
+
+/**
+ * Track Media Player usage
+ * Logged from:
+ *   frameworks/av/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp
+ *   frameworks/av/services/mediaanalytics/statsd_nuplayer.cpp
+ */
+message MediametricsNuPlayerReported {
+    optional int64 timestamp_nanos = 1;
+    optional string package_name = 2;
+    optional int64 package_version_code = 3;
+    optional int64 media_apex_version = 4;
+
+    optional android.stats.mediametrics.NuPlayerData nuplayer_data = 5 [(android.os.statsd.log_mode) = MODE_BYTES];
+}
+
+/**
  * State of a dangerous permission requested by a package
  */
 message DangerousPermissionState {
@@ -5863,7 +6034,10 @@
         INSTALL_STAGED_NOT_READY = 3;
         INSTALL_STAGED_READY = 4;
         INSTALL_SUCCESS = 5;
-        INSTALL_FAILURE = 6;
+        // Replaced by INSTALL_FAILURE_DOWNLOAD, INSTALL_FAILURE_STATE_MISMATCH,
+        // and INSTALL_FAILURE_COMMIT.
+        INSTALL_FAILURE = 6  [deprecated = true];
+        // This enum is for installs that are manually cancelled via the Manual Update UI.
         INSTALL_CANCELLED = 7;
         INSTALLER_ROLLBACK_REQUESTED = 8;
         INSTALLER_ROLLBACK_INITIATED = 9;
@@ -5880,6 +6054,9 @@
         INSTALL_STAGED_CANCEL_REQUESTED = 20;
         INSTALL_STAGED_CANCEL_SUCCESS = 21;
         INSTALL_STAGED_CANCEL_FAILURE = 22;
+        INSTALL_FAILURE_DOWNLOAD = 23;
+        INSTALL_FAILURE_STATE_MISMATCH = 24;
+        INSTALL_FAILURE_COMMIT = 25;
     }
     optional Status status = 4;
 }
diff --git a/cmds/statsd/tools/localtools/src/com/android/statsd/shelltools/testdrive/TestDrive.java b/cmds/statsd/tools/localtools/src/com/android/statsd/shelltools/testdrive/TestDrive.java
index fc7b778..3e705fd 100644
--- a/cmds/statsd/tools/localtools/src/com/android/statsd/shelltools/testdrive/TestDrive.java
+++ b/cmds/statsd/tools/localtools/src/com/android/statsd/shelltools/testdrive/TestDrive.java
@@ -57,6 +57,7 @@
         "AID_BLUETOOTH",
         "AID_LMKD",
         "com.android.managedprovisioning",
+        "AID_MEDIA",
         "AID_NETWORK_STACK"
     };
     private static final Logger LOGGER = Logger.getLogger(TestDrive.class.getName());
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 43531ed..4abf924 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -2349,7 +2349,7 @@
      *
      * @param cancellationSignal A signal to cancel the operation in progress.
      * @param callback The callback to send the action list. The actions list cannot
-     *     contain <code>null</code> elements.
+     *     contain <code>null</code> elements. You can call this on any thread.
      */
     public void onGetDirectActions(@NonNull CancellationSignal cancellationSignal,
             @NonNull Consumer<List<DirectAction>> callback) {
@@ -2360,10 +2360,13 @@
      * This is called to perform an action previously defined by the app.
      * Apps also have access to {@link #getVoiceInteractor()} to follow up on the action.
      *
-     * @param actionId The ID for the action
-     * @param arguments Any additional arguments provided by the caller
+     * @param actionId The ID for the action you previously reported via
+     *     {@link #onGetDirectActions(CancellationSignal, Consumer)}.
+     * @param arguments Any additional arguments provided by the caller that are
+     *     specific to the given action.
      * @param cancellationSignal A signal to cancel the operation in progress.
-     * @param resultListener The callback to provide the result back to the caller
+     * @param resultListener The callback to provide the result back to the caller.
+     *     You can call this on any thread. The result bundle is action specific.
      *
      * @see #onGetDirectActions(CancellationSignal, Consumer)
      */
diff --git a/core/java/android/app/DirectAction.java b/core/java/android/app/DirectAction.java
index d191f4b..ef3627b 100644
--- a/core/java/android/app/DirectAction.java
+++ b/core/java/android/app/DirectAction.java
@@ -22,12 +22,19 @@
 import android.os.IBinder;
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.view.accessibility.AccessibilityNodeInfo;
 
 import com.android.internal.util.Preconditions;
 
+import java.util.Objects;
+
 /**
  * Represents a abstract action that can be perform on this app. This are requested from
- * outside the app's UI (eg by SystemUI or assistant).
+ * outside the app's UI (eg by SystemUI or assistant). The semantics of these actions are
+ * not specified by the OS. This allows open-ended and scalable approach for defining how
+ * an app interacts with components that expose alternative interaction models to the user
+ * such as the assistant, SystemUI, etc. You can use {@link #equals(Object)} to compare
+ * instances of this class.
  */
 public final class DirectAction implements Parcelable {
 
@@ -91,7 +98,7 @@
     }
 
     /**
-     * Returns the ID for this action.
+     * @return the ID for this action.
      */
     @NonNull
     public String getId() {
@@ -99,7 +106,7 @@
     }
 
     /**
-     * Returns any extras associated with this action.
+     * @return any extras associated with this action.
      */
     @Nullable
     public Bundle getExtras() {
@@ -107,7 +114,7 @@
     }
 
     /**
-     * Returns the LocusId for the current state for the app
+     * @return the LocusId for the current state for the app
      */
     @Nullable
     public LocusId getLocusId() {
@@ -120,6 +127,28 @@
     }
 
     @Override
+    public int hashCode() {
+        return mID.hashCode();
+    }
+
+    @Override
+    public boolean equals(Object other) {
+        if (other == null) {
+            return false;
+        }
+
+        if (other == this) {
+            return true;
+        }
+
+        if (getClass() != other.getClass()) {
+            return false;
+        }
+
+        return mID.equals(((DirectAction) other).mID);
+    }
+
+    @Override
     public void writeToParcel(Parcel dest, int flags) {
         dest.writeInt(mTaskId);
         dest.writeStrongBinder(mActivityId);
@@ -139,7 +168,8 @@
         /**
          * Creates a new instance.
          *
-         * @param id The mandatory action id.
+         * @param id The mandatory action id which must be unique in the
+         *     current application state.
          */
         public Builder(@NonNull String id) {
             Preconditions.checkNotNull(id);
@@ -147,7 +177,9 @@
         }
 
         /**
-         * Sets the optional action extras.
+         * Sets the optional action extras. These extras are action specific
+         * and their semantics are open-ended potentially representing how
+         * the action is visualized, interpreted, what its arguments are, etc.
          *
          * @param extras The extras.
          * @return This builder.
@@ -158,7 +190,9 @@
         }
 
         /**
-         * Sets the optional locus id.
+         * Sets the optional locus id. This is an identifier of the application
+         * state from a user perspective. For example, a specific chat in a
+         * messaging app.
          *
          * @param locusId The locus id.
          * @return This builder.
diff --git a/core/java/android/app/role/RoleControllerManager.java b/core/java/android/app/role/RoleControllerManager.java
index 668dbd4..98b11a7 100644
--- a/core/java/android/app/role/RoleControllerManager.java
+++ b/core/java/android/app/role/RoleControllerManager.java
@@ -21,7 +21,6 @@
 import android.annotation.NonNull;
 import android.annotation.RequiresPermission;
 import android.annotation.SystemService;
-import android.annotation.UserIdInt;
 import android.app.ActivityThread;
 import android.content.ComponentName;
 import android.content.Context;
@@ -29,18 +28,18 @@
 import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
 import android.os.Binder;
+import android.os.Bundle;
 import android.os.Handler;
-import android.os.IBinder;
 import android.os.RemoteCallback;
-import android.os.RemoteException;
 import android.util.Log;
 import android.util.SparseArray;
 
 import com.android.internal.annotations.GuardedBy;
-import com.android.internal.infra.AbstractMultiplePendingRequestsRemoteService;
-import com.android.internal.infra.AbstractRemoteService;
+import com.android.internal.infra.AndroidFuture;
+import com.android.internal.infra.ServiceConnector;
 
 import java.util.concurrent.Executor;
+import java.util.concurrent.TimeUnit;
 import java.util.function.Consumer;
 
 /**
@@ -53,6 +52,8 @@
 
     private static final String LOG_TAG = RoleControllerManager.class.getSimpleName();
 
+    private static final long REQUEST_TIMEOUT_MILLIS = 15 * 1000;
+
     private static volatile ComponentName sRemoteServiceComponentName;
 
     private static final Object sRemoteServicesLock = new Object();
@@ -61,10 +62,11 @@
      * Global remote services (per user) used by all {@link RoleControllerManager managers}.
      */
     @GuardedBy("sRemoteServicesLock")
-    private static final SparseArray<RemoteService> sRemoteServices = new SparseArray<>();
+    private static final SparseArray<ServiceConnector<IRoleController>> sRemoteServices =
+            new SparseArray<>();
 
     @NonNull
-    private final RemoteService mRemoteService;
+    private final ServiceConnector<IRoleController> mRemoteService;
 
     /**
      * Initialize the remote service component name once so that we can avoid acquiring the
@@ -92,10 +94,19 @@
             @NonNull Handler handler, @NonNull Context context) {
         synchronized (sRemoteServicesLock) {
             int userId = context.getUserId();
-            RemoteService remoteService = sRemoteServices.get(userId);
+            ServiceConnector<IRoleController> remoteService = sRemoteServices.get(userId);
             if (remoteService == null) {
-                remoteService = new RemoteService(ActivityThread.currentApplication(),
-                        remoteServiceComponentName, handler, userId);
+                remoteService = new ServiceConnector.Impl<IRoleController>(
+                        ActivityThread.currentApplication(),
+                        new Intent(RoleControllerService.SERVICE_INTERFACE)
+                                .setComponent(remoteServiceComponentName),
+                        0 /* bindingFlags */, userId, IRoleController.Stub::asInterface) {
+
+                    @Override
+                    protected Handler getJobHandler() {
+                        return handler;
+                    }
+                };
                 sRemoteServices.put(userId, remoteService);
             }
             mRemoteService = remoteService;
@@ -120,8 +131,12 @@
      */
     public void grantDefaultRoles(@NonNull @CallbackExecutor Executor executor,
             @NonNull Consumer<Boolean> callback) {
-        mRemoteService.scheduleRequest(new GrantDefaultRolesRequest(mRemoteService, executor,
-                callback));
+        AndroidFuture<Bundle> operation = mRemoteService.postAsync(service -> {
+            AndroidFuture<Bundle> future = new AndroidFuture<>();
+            service.grantDefaultRoles(new RemoteCallback(future::complete));
+            return future;
+        });
+        propagateCallback(operation, "grantDefaultRoles", executor, callback);
     }
 
     /**
@@ -129,8 +144,13 @@
      */
     public void onAddRoleHolder(@NonNull String roleName, @NonNull String packageName,
             @RoleManager.ManageHoldersFlags int flags, @NonNull RemoteCallback callback) {
-        mRemoteService.scheduleRequest(new OnAddRoleHolderRequest(mRemoteService, roleName,
-                packageName, flags, callback));
+        AndroidFuture<Bundle> operation = mRemoteService.postAsync(service -> {
+            AndroidFuture<Bundle> future = new AndroidFuture<>();
+            service.onAddRoleHolder(roleName, packageName, flags,
+                    new RemoteCallback(future::complete));
+            return future;
+        });
+        propagateCallback(operation, "onAddRoleHolder", callback);
     }
 
     /**
@@ -138,8 +158,13 @@
      */
     public void onRemoveRoleHolder(@NonNull String roleName, @NonNull String packageName,
             @RoleManager.ManageHoldersFlags int flags, @NonNull RemoteCallback callback) {
-        mRemoteService.scheduleRequest(new OnRemoveRoleHolderRequest(mRemoteService, roleName,
-                packageName, flags, callback));
+        AndroidFuture<Bundle> operation = mRemoteService.postAsync(service -> {
+            AndroidFuture<Bundle> future = new AndroidFuture<>();
+            service.onRemoveRoleHolder(roleName, packageName, flags,
+                    new RemoteCallback(future::complete));
+            return future;
+        });
+        propagateCallback(operation, "onRemoveRoleHolder", callback);
     }
 
     /**
@@ -147,8 +172,13 @@
      */
     public void onClearRoleHolders(@NonNull String roleName,
             @RoleManager.ManageHoldersFlags int flags, @NonNull RemoteCallback callback) {
-        mRemoteService.scheduleRequest(new OnClearRoleHoldersRequest(mRemoteService, roleName,
-                flags, callback));
+        AndroidFuture<Bundle> operation = mRemoteService.postAsync(service -> {
+            AndroidFuture<Bundle> future = new AndroidFuture<>();
+            service.onClearRoleHolders(roleName, flags,
+                    new RemoteCallback(future::complete));
+            return future;
+        });
+        propagateCallback(operation, "onClearRoleHolders", callback);
     }
 
     /**
@@ -157,8 +187,13 @@
     @RequiresPermission(Manifest.permission.MANAGE_ROLE_HOLDERS)
     public void isApplicationQualifiedForRole(@NonNull String roleName, @NonNull String packageName,
             @NonNull @CallbackExecutor Executor executor, @NonNull Consumer<Boolean> callback) {
-        mRemoteService.scheduleRequest(new IsApplicationQualifiedForRoleRequest(mRemoteService,
-                roleName, packageName, executor, callback));
+        AndroidFuture<Bundle> operation = mRemoteService.postAsync(service -> {
+            AndroidFuture<Bundle> future = new AndroidFuture<>();
+            service.isApplicationQualifiedForRole(roleName, packageName,
+                    new RemoteCallback(future::complete));
+            return future;
+        });
+        propagateCallback(operation, "isApplicationQualifiedForRole", executor, callback);
     }
 
     /**
@@ -167,387 +202,48 @@
     @RequiresPermission(Manifest.permission.MANAGE_ROLE_HOLDERS)
     public void isRoleVisible(@NonNull String roleName,
             @NonNull @CallbackExecutor Executor executor, @NonNull Consumer<Boolean> callback) {
-        mRemoteService.scheduleRequest(new IsRoleVisibleRequest(mRemoteService, roleName, executor,
-                callback));
+        AndroidFuture<Bundle> operation = mRemoteService.postAsync(service -> {
+            AndroidFuture<Bundle> future = new AndroidFuture<>();
+            service.isRoleVisible(roleName, new RemoteCallback(future::complete));
+            return future;
+        });
+        propagateCallback(operation, "isRoleVisible", executor, callback);
     }
 
-    /**
-     * Connection to the remote service.
-     */
-    private static final class RemoteService extends AbstractMultiplePendingRequestsRemoteService<
-            RemoteService, IRoleController> {
-
-        private static final long UNBIND_DELAY_MILLIS = 15 * 1000;
-        private static final long REQUEST_TIMEOUT_MILLIS = 15 * 1000;
-
-        /**
-         * Create a connection to the remote service
-         *
-         * @param context the context to use
-         * @param componentName the component of the service to connect to
-         * @param handler the handler for binding service and callbacks
-         * @param userId the user whom remote service should be connected as
-         */
-        RemoteService(@NonNull Context context, @NonNull ComponentName componentName,
-                @NonNull Handler handler, @UserIdInt int userId) {
-            super(context, RoleControllerService.SERVICE_INTERFACE, componentName, userId,
-                    service -> Log.e(LOG_TAG, "RemoteService " + service + " died"), handler, 0,
-                    false, 1);
-        }
-
-        /**
-         * @return The default handler used by this service.
-         */
-        @NonNull
-        public Handler getHandler() {
-            return mHandler;
-        }
-
-        @Override
-        protected @NonNull IRoleController getServiceInterface(@NonNull IBinder binder) {
-            return IRoleController.Stub.asInterface(binder);
-        }
-
-        @Override
-        protected long getTimeoutIdleBindMillis() {
-            return UNBIND_DELAY_MILLIS;
-        }
-
-        @Override
-        protected long getRemoteRequestMillis() {
-            return REQUEST_TIMEOUT_MILLIS;
-        }
-
-        @Override
-        public void scheduleRequest(
-                @NonNull BasePendingRequest<RemoteService, IRoleController> pendingRequest) {
-            super.scheduleRequest(pendingRequest);
-        }
-
-        @Override
-        public void scheduleAsyncRequest(@NonNull AsyncRequest<IRoleController> request) {
-            super.scheduleAsyncRequest(request);
-        }
+    private void propagateCallback(AndroidFuture<Bundle> operation, String opName,
+            @CallbackExecutor @NonNull Executor executor,
+            Consumer<Boolean> destination) {
+        operation.orTimeout(REQUEST_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS)
+                .whenComplete((res, err) -> executor.execute(() -> {
+                    long token = Binder.clearCallingIdentity();
+                    try {
+                        if (err != null) {
+                            Log.e(LOG_TAG, "Error calling " + opName + "()", err);
+                            destination.accept(false);
+                        } else {
+                            destination.accept(res != null);
+                        }
+                    } finally {
+                        Binder.restoreCallingIdentity(token);
+                    }
+                }));
     }
 
-    /**
-     * Request for {@link #grantDefaultRoles(Executor, Consumer)}.
-     */
-    private static final class GrantDefaultRolesRequest
-            extends AbstractRemoteService.PendingRequest<RemoteService, IRoleController> {
-
-        @NonNull
-        private final Executor mExecutor;
-        @NonNull
-        private final Consumer<Boolean> mCallback;
-
-        @NonNull
-        private final RemoteCallback mRemoteCallback;
-
-        private GrantDefaultRolesRequest(@NonNull RemoteService service,
-                @NonNull @CallbackExecutor Executor executor, @NonNull Consumer<Boolean> callback) {
-            super(service);
-
-            mExecutor = executor;
-            mCallback = callback;
-
-            mRemoteCallback = new RemoteCallback(result -> mExecutor.execute(() -> {
-                long token = Binder.clearCallingIdentity();
-                try {
-                    boolean successful = result != null;
-                    mCallback.accept(successful);
-                } finally {
-                    Binder.restoreCallingIdentity(token);
-                    finish();
-                }
-            }));
-        }
-
-        @Override
-        protected void onTimeout(@NonNull RemoteService remoteService) {
-            mExecutor.execute(() -> mCallback.accept(false));
-        }
-
-        @Override
-        public void run() {
-            try {
-                getService().getServiceInterface().grantDefaultRoles(mRemoteCallback);
-            } catch (RemoteException e) {
-                Log.e(LOG_TAG, "Error calling grantDefaultRoles()", e);
-            }
-        }
-
-        @Override
-        protected void onFailed() {
-            mRemoteCallback.sendResult(null);
-        }
-    }
-
-    /**
-     * Request for {@link #onAddRoleHolder(String, String, int, RemoteCallback)}.
-     */
-    private static final class OnAddRoleHolderRequest
-            extends AbstractRemoteService.PendingRequest<RemoteService, IRoleController> {
-
-        @NonNull
-        private final String mRoleName;
-        @NonNull
-        private final String mPackageName;
-        @RoleManager.ManageHoldersFlags
-        private final int mFlags;
-        @NonNull
-        private final RemoteCallback mCallback;
-
-        @NonNull
-        private final RemoteCallback mRemoteCallback;
-
-        private OnAddRoleHolderRequest(@NonNull RemoteService service, @NonNull String roleName,
-                @NonNull String packageName, @RoleManager.ManageHoldersFlags int flags,
-                @NonNull RemoteCallback callback) {
-            super(service);
-
-            mRoleName = roleName;
-            mPackageName = packageName;
-            mFlags = flags;
-            mCallback = callback;
-
-            mRemoteCallback = new RemoteCallback(result -> {
-                long token = Binder.clearCallingIdentity();
-                try {
-                    mCallback.sendResult(result);
-                } finally {
-                    Binder.restoreCallingIdentity(token);
-                    finish();
-                }
-            });
-        }
-
-        @Override
-        protected void onTimeout(@NonNull RemoteService remoteService) {
-            mCallback.sendResult(null);
-        }
-
-        @Override
-        public void run() {
-            try {
-                getService().getServiceInterface().onAddRoleHolder(mRoleName, mPackageName, mFlags,
-                        mRemoteCallback);
-            } catch (RemoteException e) {
-                Log.e(LOG_TAG, "Error calling onAddRoleHolder()", e);
-            }
-        }
-    }
-
-    /**
-     * Request for {@link #onRemoveRoleHolder(String, String, int, RemoteCallback)}.
-     */
-    private static final class OnRemoveRoleHolderRequest
-            extends AbstractRemoteService.PendingRequest<RemoteService, IRoleController> {
-
-        @NonNull
-        private final String mRoleName;
-        @NonNull
-        private final String mPackageName;
-        @RoleManager.ManageHoldersFlags
-        private final int mFlags;
-        @NonNull
-        private final RemoteCallback mCallback;
-
-        @NonNull
-        private final RemoteCallback mRemoteCallback;
-
-        private OnRemoveRoleHolderRequest(@NonNull RemoteService service, @NonNull String roleName,
-                @NonNull String packageName, @RoleManager.ManageHoldersFlags int flags,
-                @NonNull RemoteCallback callback) {
-            super(service);
-
-            mRoleName = roleName;
-            mPackageName = packageName;
-            mFlags = flags;
-            mCallback = callback;
-
-            mRemoteCallback = new RemoteCallback(result -> {
-                long token = Binder.clearCallingIdentity();
-                try {
-                    mCallback.sendResult(result);
-                } finally {
-                    Binder.restoreCallingIdentity(token);
-                    finish();
-                }
-            });
-        }
-
-        @Override
-        protected void onTimeout(@NonNull RemoteService remoteService) {
-            mCallback.sendResult(null);
-        }
-
-        @Override
-        public void run() {
-            try {
-                getService().getServiceInterface().onRemoveRoleHolder(mRoleName, mPackageName,
-                        mFlags, mRemoteCallback);
-            } catch (RemoteException e) {
-                Log.e(LOG_TAG, "Error calling onRemoveRoleHolder()", e);
-            }
-        }
-    }
-
-    /**
-     * Request for {@link #onClearRoleHolders(String, int, RemoteCallback)}.
-     */
-    private static final class OnClearRoleHoldersRequest
-            extends AbstractRemoteService.PendingRequest<RemoteService, IRoleController> {
-
-        @NonNull
-        private final String mRoleName;
-        @RoleManager.ManageHoldersFlags
-        private final int mFlags;
-        @NonNull
-        private final RemoteCallback mCallback;
-
-        @NonNull
-        private final RemoteCallback mRemoteCallback;
-
-        private OnClearRoleHoldersRequest(@NonNull RemoteService service, @NonNull String roleName,
-                @RoleManager.ManageHoldersFlags int flags, @NonNull RemoteCallback callback) {
-            super(service);
-
-            mRoleName = roleName;
-            mFlags = flags;
-            mCallback = callback;
-
-            mRemoteCallback = new RemoteCallback(result -> {
-                long token = Binder.clearCallingIdentity();
-                try {
-                    mCallback.sendResult(result);
-                } finally {
-                    Binder.restoreCallingIdentity(token);
-                    finish();
-                }
-            });
-        }
-
-        @Override
-        protected void onTimeout(@NonNull RemoteService remoteService) {
-            mCallback.sendResult(null);
-        }
-
-        @Override
-        public void run() {
-            try {
-                getService().getServiceInterface().onClearRoleHolders(mRoleName, mFlags,
-                        mRemoteCallback);
-            } catch (RemoteException e) {
-                Log.e(LOG_TAG, "Error calling onClearRoleHolders()", e);
-            }
-        }
-    }
-
-    /**
-     * Request for {@link #isApplicationQualifiedForRole(String, String, Executor, Consumer)}
-     */
-    private static final class IsApplicationQualifiedForRoleRequest extends
-            AbstractRemoteService.PendingRequest<RemoteService, IRoleController> {
-
-        @NonNull
-        private final String mRoleName;
-        @NonNull
-        private final String mPackageName;
-        @NonNull
-        private final Executor mExecutor;
-        @NonNull
-        private final Consumer<Boolean> mCallback;
-
-        @NonNull
-        private final RemoteCallback mRemoteCallback;
-
-        private IsApplicationQualifiedForRoleRequest(@NonNull RemoteService service,
-                @NonNull String roleName, @NonNull String packageName,
-                @CallbackExecutor @NonNull Executor executor, @NonNull Consumer<Boolean> callback) {
-            super(service);
-
-            mRoleName = roleName;
-            mPackageName = packageName;
-            mExecutor = executor;
-            mCallback = callback;
-
-            mRemoteCallback = new RemoteCallback(result -> mExecutor.execute(() -> {
-                long token = Binder.clearCallingIdentity();
-                try {
-                    boolean qualified = result != null;
-                    mCallback.accept(qualified);
-                } finally {
-                    Binder.restoreCallingIdentity(token);
-                    finish();
-                }
-            }));
-        }
-
-        @Override
-        protected void onTimeout(RemoteService remoteService) {
-            mExecutor.execute(() -> mCallback.accept(false));
-        }
-
-        @Override
-        public void run() {
-            try {
-                getService().getServiceInterface().isApplicationQualifiedForRole(mRoleName,
-                        mPackageName, mRemoteCallback);
-            } catch (RemoteException e) {
-                Log.e(LOG_TAG, "Error calling isApplicationQualifiedForRole()", e);
-            }
-        }
-    }
-
-    /**
-     * Request for {@link #isRoleVisible(String, Executor, Consumer)}
-     */
-    private static final class IsRoleVisibleRequest
-            extends AbstractRemoteService.PendingRequest<RemoteService, IRoleController> {
-
-        @NonNull
-        private final String mRoleName;
-        @NonNull
-        private final Executor mExecutor;
-        @NonNull
-        private final Consumer<Boolean> mCallback;
-
-        @NonNull
-        private final RemoteCallback mRemoteCallback;
-
-        private IsRoleVisibleRequest(@NonNull RemoteService service, @NonNull String roleName,
-                @CallbackExecutor @NonNull Executor executor, @NonNull Consumer<Boolean> callback) {
-            super(service);
-
-            mRoleName = roleName;
-            mExecutor = executor;
-            mCallback = callback;
-
-            mRemoteCallback = new RemoteCallback(result -> mExecutor.execute(() -> {
-                long token = Binder.clearCallingIdentity();
-                try {
-                    boolean visible = result != null;
-                    mCallback.accept(visible);
-                } finally {
-                    Binder.restoreCallingIdentity(token);
-                    finish();
-                }
-            }));
-        }
-
-        @Override
-        protected void onTimeout(RemoteService remoteService) {
-            mExecutor.execute(() -> mCallback.accept(false));
-        }
-
-        @Override
-        public void run() {
-            try {
-                getService().getServiceInterface().isRoleVisible(mRoleName, mRemoteCallback);
-            } catch (RemoteException e) {
-                Log.e(LOG_TAG, "Error calling isRoleVisible()", e);
-            }
-        }
+    private void propagateCallback(AndroidFuture<Bundle> operation, String opName,
+            RemoteCallback destination) {
+        operation.orTimeout(REQUEST_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS)
+                .whenComplete((res, err) -> {
+                    long token = Binder.clearCallingIdentity();
+                    try {
+                        if (err != null) {
+                            Log.e(LOG_TAG, "Error calling " + opName + "()", err);
+                            destination.sendResult(null);
+                        } else {
+                            destination.sendResult(res);
+                        }
+                    } finally {
+                        Binder.restoreCallingIdentity(token);
+                    }
+                });
     }
 }
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 3dd510c..0112396 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -3009,7 +3009,14 @@
     /**
      * Variation of {@link #bindService} that, in the specific case of isolated
      * services, allows the caller to generate multiple instances of a service
-     * from a single component declaration.
+     * from a single component declaration.  In other words, you can use this to bind
+     * to a service that has specified {@link android.R.attr#isolatedProcess} and, in
+     * addition to the existing behavior of running in an isolated process, you can
+     * also through the arguments here have the system bring up multiple concurrent
+     * processes hosting their own instances of that service.  The <var>instanceName</var>
+     * you provide here identifies the different instances, and you can use
+     * {@link #updateServiceGroup(ServiceConnection, int, int)} to tell the system how it
+     * should manage each of these instances.
      *
      * @param service Identifies the service to connect to.  The Intent must
      *      specify an explicit component name.
@@ -3027,6 +3034,8 @@
      * @throws IllegalArgumentException If the instanceName is invalid.
      *
      * @see #bindService
+     * @see #updateServiceGroup
+     * @see android.R.attr#isolatedProcess
      */
     public boolean bindIsolatedService(@RequiresPermission @NonNull Intent service,
             @BindServiceFlags int flags, @NonNull String instanceName,
@@ -3082,10 +3091,16 @@
      *              are considered to be related.  Supplying 0 reverts to the default behavior
      *              of not grouping.
      * @param importance Additional importance of the processes within a group.  Upon calling
-     *                   here, this will override any previous group that was set for that
-     *                   process.  This fine-tunes process killing of all processes within
-     *                   a related groups -- higher importance values will be killed before
-     *                   lower ones.
+     *                   here, this will override any previous importance that was set for that
+     *                   process.  The most important process is 0, and higher values are
+     *                   successively less important.  You can view this as describing how
+     *                   to order the processes in an array, with the processes at the end of
+     *                   the array being the least important.  This value has no meaning besides
+     *                   indicating how processes should be ordered in that array one after the
+     *                   other.  This provides a way to fine-tune the system's process killing,
+     *                   guiding it to kill processes at the end of the array first.
+     *
+     * @see #bindIsolatedService
      */
     public void updateServiceGroup(@NonNull ServiceConnection conn, int group,
             int importance) {
diff --git a/core/java/android/content/om/IOverlayManager.aidl b/core/java/android/content/om/IOverlayManager.aidl
index 722c128..43a4fe5 100644
--- a/core/java/android/content/om/IOverlayManager.aidl
+++ b/core/java/android/content/om/IOverlayManager.aidl
@@ -152,4 +152,9 @@
      * @param userId The user for which to change the overlay.
      */
     boolean setLowestPriority(in String packageName, in int userId);
+
+    /**
+     * Returns the list of default overlay packages, or an empty array if there are none.
+     */
+    String[] getDefaultOverlayPackages();
 }
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index a95e094..33c0bb9 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -6178,15 +6178,7 @@
      * @param activity The component name of the activity that is to be preferred.
      *
      * @hide
-     *
-     * @deprecated This function no longer does anything. It is the platform's
-     * responsibility to assign preferred activities and this cannot be modified
-     * directly. To determine the activities resolved by the platform, use
-     * {@link #resolveActivity} or {@link #queryIntentActivities}. To configure
-     * an app to be responsible for a particular role and to check current role
-     * holders, see {@link android.app.role.RoleManager}.
      */
-    @Deprecated
     @SystemApi
     public void replacePreferredActivity(@NonNull IntentFilter filter, int match,
             @NonNull List<ComponentName> set, @NonNull ComponentName activity) {
diff --git a/core/java/android/database/MatrixCursor.java b/core/java/android/database/MatrixCursor.java
index a52e96e..b0d399c 100644
--- a/core/java/android/database/MatrixCursor.java
+++ b/core/java/android/database/MatrixCursor.java
@@ -18,6 +18,7 @@
 
 import android.annotation.UnsupportedAppUsage;
 import android.os.Build;
+
 import java.util.ArrayList;
 
 /**
@@ -240,6 +241,12 @@
             }
             return this;
         }
+
+        /** @hide */
+        public final RowBuilder add(int columnIndex, Object value) {
+            data[(row * columnCount) + columnIndex] = value;
+            return this;
+        }
     }
 
     // AbstractCursor implementation.
diff --git a/core/java/android/net/metrics/ApfProgramEvent.java b/core/java/android/net/metrics/ApfProgramEvent.java
index 2bd4378..e9c209c 100644
--- a/core/java/android/net/metrics/ApfProgramEvent.java
+++ b/core/java/android/net/metrics/ApfProgramEvent.java
@@ -176,7 +176,7 @@
         out.writeInt(filteredRas);
         out.writeInt(currentRas);
         out.writeInt(programLength);
-        out.writeInt(flags);
+        out.writeInt(this.flags);
     }
 
     /** @hide */
@@ -192,6 +192,18 @@
                 programLength, actualLifetime, lifetimeString, namesOf(flags));
     }
 
+    @Override
+    public boolean equals(Object obj) {
+        if (obj == null || !(obj.getClass().equals(ApfProgramEvent.class))) return false;
+        final ApfProgramEvent other = (ApfProgramEvent) obj;
+        return lifetime == other.lifetime
+                && actualLifetime == other.actualLifetime
+                && filteredRas == other.filteredRas
+                && currentRas == other.currentRas
+                && programLength == other.programLength
+                && flags == other.flags;
+    }
+
     /** @hide */
     public static final @android.annotation.NonNull Parcelable.Creator<ApfProgramEvent> CREATOR
             = new Parcelable.Creator<ApfProgramEvent>() {
diff --git a/core/java/android/net/metrics/ApfStats.java b/core/java/android/net/metrics/ApfStats.java
index 6c3b7af..b963777 100644
--- a/core/java/android/net/metrics/ApfStats.java
+++ b/core/java/android/net/metrics/ApfStats.java
@@ -275,6 +275,22 @@
                 .toString();
     }
 
+    @Override
+    public boolean equals(Object obj) {
+        if (obj == null || !(obj.getClass().equals(ApfStats.class))) return false;
+        final ApfStats other = (ApfStats) obj;
+        return durationMs == other.durationMs
+                && receivedRas == other.receivedRas
+                && matchingRas == other.matchingRas
+                && droppedRas == other.droppedRas
+                && zeroLifetimeRas == other.zeroLifetimeRas
+                && parseErrors == other.parseErrors
+                && programUpdates == other.programUpdates
+                && programUpdatesAll == other.programUpdatesAll
+                && programUpdatesAllowingMulticast == other.programUpdatesAllowingMulticast
+                && maxProgramSize == other.maxProgramSize;
+    }
+
     /** @hide */
     public static final @android.annotation.NonNull Parcelable.Creator<ApfStats> CREATOR = new Parcelable.Creator<ApfStats>() {
         public ApfStats createFromParcel(Parcel in) {
diff --git a/core/java/android/net/metrics/DhcpClientEvent.java b/core/java/android/net/metrics/DhcpClientEvent.java
index a3d0a20..2fed736 100644
--- a/core/java/android/net/metrics/DhcpClientEvent.java
+++ b/core/java/android/net/metrics/DhcpClientEvent.java
@@ -22,6 +22,7 @@
 import android.annotation.UnsupportedAppUsage;
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.text.TextUtils;
 
 /**
  * An event recorded when a DhcpClient state machine transitions to a new state.
@@ -101,6 +102,14 @@
         return String.format("DhcpClientEvent(%s, %dms)", msg, durationMs);
     }
 
+    @Override
+    public boolean equals(Object obj) {
+        if (obj == null || !(obj.getClass().equals(DhcpClientEvent.class))) return false;
+        final DhcpClientEvent other = (DhcpClientEvent) obj;
+        return TextUtils.equals(msg, other.msg)
+                && durationMs == other.durationMs;
+    }
+
     /** @hide */
     public static final @android.annotation.NonNull Parcelable.Creator<DhcpClientEvent> CREATOR
         = new Parcelable.Creator<DhcpClientEvent>() {
diff --git a/core/java/android/net/metrics/IpManagerEvent.java b/core/java/android/net/metrics/IpManagerEvent.java
index 9d358d1..ba05c59 100644
--- a/core/java/android/net/metrics/IpManagerEvent.java
+++ b/core/java/android/net/metrics/IpManagerEvent.java
@@ -101,6 +101,14 @@
                 Decoder.constants.get(eventType), durationMs);
     }
 
+    @Override
+    public boolean equals(Object obj) {
+        if (obj == null || !(obj.getClass().equals(IpManagerEvent.class))) return false;
+        final IpManagerEvent other = (IpManagerEvent) obj;
+        return eventType == other.eventType
+                && durationMs == other.durationMs;
+    }
+
     final static class Decoder {
         static final SparseArray<String> constants = MessageUtils.findMessageNames(
                 new Class[]{IpManagerEvent.class},
diff --git a/core/java/android/net/metrics/IpReachabilityEvent.java b/core/java/android/net/metrics/IpReachabilityEvent.java
index 80c8211..d4ba294 100644
--- a/core/java/android/net/metrics/IpReachabilityEvent.java
+++ b/core/java/android/net/metrics/IpReachabilityEvent.java
@@ -93,6 +93,13 @@
         return String.format("IpReachabilityEvent(%s:%02x)", eventName, lo);
     }
 
+    @Override
+    public boolean equals(Object obj) {
+        if (obj == null || !(obj.getClass().equals(IpReachabilityEvent.class))) return false;
+        final IpReachabilityEvent other = (IpReachabilityEvent) obj;
+        return eventType == other.eventType;
+    }
+
     final static class Decoder {
         static final SparseArray<String> constants =
                 MessageUtils.findMessageNames(new Class[]{IpReachabilityEvent.class},
diff --git a/core/java/android/net/metrics/NetworkEvent.java b/core/java/android/net/metrics/NetworkEvent.java
index bed914d..0c57ec6 100644
--- a/core/java/android/net/metrics/NetworkEvent.java
+++ b/core/java/android/net/metrics/NetworkEvent.java
@@ -121,6 +121,14 @@
                 Decoder.constants.get(eventType), durationMs);
     }
 
+    @Override
+    public boolean equals(Object obj) {
+        if (obj == null || !(obj.getClass().equals(NetworkEvent.class))) return false;
+        final NetworkEvent other = (NetworkEvent) obj;
+        return eventType == other.eventType
+                && durationMs == other.durationMs;
+    }
+
     final static class Decoder {
         static final SparseArray<String> constants = MessageUtils.findMessageNames(
                 new Class[]{NetworkEvent.class}, new String[]{"NETWORK_"});
diff --git a/core/java/android/net/metrics/RaEvent.java b/core/java/android/net/metrics/RaEvent.java
index b2f6585..3fd87c2 100644
--- a/core/java/android/net/metrics/RaEvent.java
+++ b/core/java/android/net/metrics/RaEvent.java
@@ -97,6 +97,18 @@
                 .toString();
     }
 
+    @Override
+    public boolean equals(Object obj) {
+        if (obj == null || !(obj.getClass().equals(RaEvent.class))) return false;
+        final RaEvent other = (RaEvent) obj;
+        return routerLifetime == other.routerLifetime
+                && prefixValidLifetime == other.prefixValidLifetime
+                && prefixPreferredLifetime == other.prefixPreferredLifetime
+                && routeInfoLifetime == other.routeInfoLifetime
+                && rdnssLifetime == other.rdnssLifetime
+                && dnsslLifetime == other.dnsslLifetime;
+    }
+
     /** @hide */
     public static final @android.annotation.NonNull Parcelable.Creator<RaEvent> CREATOR = new Parcelable.Creator<RaEvent>() {
         public RaEvent createFromParcel(Parcel in) {
diff --git a/core/java/android/net/metrics/ValidationProbeEvent.java b/core/java/android/net/metrics/ValidationProbeEvent.java
index c9d7b1b..1aaa50d 100644
--- a/core/java/android/net/metrics/ValidationProbeEvent.java
+++ b/core/java/android/net/metrics/ValidationProbeEvent.java
@@ -170,6 +170,15 @@
                 getProbeName(probeType), returnCode, getValidationStage(probeType), durationMs);
     }
 
+    @Override
+    public boolean equals(Object obj) {
+        if (obj == null || !(obj.getClass().equals(ValidationProbeEvent.class))) return false;
+        final ValidationProbeEvent other = (ValidationProbeEvent) obj;
+        return durationMs == other.durationMs
+                && probeType == other.probeType
+                && returnCode == other.returnCode;
+    }
+
     final static class Decoder {
         static final SparseArray<String> constants = MessageUtils.findMessageNames(
                 new Class[]{ValidationProbeEvent.class},
diff --git a/core/java/android/os/Build.java b/core/java/android/os/Build.java
index b437083..9bfdb72 100755
--- a/core/java/android/os/Build.java
+++ b/core/java/android/os/Build.java
@@ -979,11 +979,6 @@
          */
         public static final int Q = CUR_DEVELOPMENT;
 
-        /**
-         * Stub for a potential new API level after P.
-         * @hide
-         */
-        public static final int P0 = Q;
     }
 
     /** The type of build, like "user" or "eng". */
diff --git a/core/java/android/os/RecoverySystem.java b/core/java/android/os/RecoverySystem.java
index 7cc7ccd..1b41694 100644
--- a/core/java/android/os/RecoverySystem.java
+++ b/core/java/android/os/RecoverySystem.java
@@ -866,8 +866,8 @@
             if (storageManager.needsCheckpoint()) {
                 Log.i(TAG, "Rescue Party requested wipe. Aborting update instead.");
                 storageManager.abortChanges("rescueparty", false);
+                return;
             }
-            return;
         } catch (RemoteException e) {
             Log.i(TAG, "Failed to handle with checkpointing. Continuing with wipe.");
         }
diff --git a/core/java/android/permission/PermissionManager.java b/core/java/android/permission/PermissionManager.java
index 1aa5b06..2a41c20 100644
--- a/core/java/android/permission/PermissionManager.java
+++ b/core/java/android/permission/PermissionManager.java
@@ -23,11 +23,8 @@
 import android.annotation.SystemApi;
 import android.annotation.SystemService;
 import android.annotation.TestApi;
-import android.annotation.UnsupportedAppUsage;
 import android.content.Context;
 import android.content.pm.IPackageManager;
-import android.content.pm.PackageManager;
-import android.content.pm.PermissionInfo;
 import android.os.RemoteException;
 
 import com.android.internal.annotations.Immutable;
@@ -107,11 +104,11 @@
     /**
      * Get set of permissions that have been split into more granular or dependent permissions.
      *
-     * <p>E.g. before {@link android.os.Build.VERSION_CODES#P0} an app that was granted
+     * <p>E.g. before {@link android.os.Build.VERSION_CODES#Q} an app that was granted
      * {@link Manifest.permission#ACCESS_COARSE_LOCATION} could access he location while it was in
-     * foreground and background. On platforms after {@link android.os.Build.VERSION_CODES#P0}
+     * foreground and background. On platforms after {@link android.os.Build.VERSION_CODES#Q}
      * the location permission only grants location access while the app is in foreground. This
-     * would break apps that target before {@link android.os.Build.VERSION_CODES#P0}. Hence whenever
+     * would break apps that target before {@link android.os.Build.VERSION_CODES#Q}. Hence whenever
      * such an old app asks for a location permission (i.e. the
      * {@link SplitPermissionInfo#getSplitPermission()}), then the
      * {@link Manifest.permission#ACCESS_BACKGROUND_LOCATION} permission (inside
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 500d0d4..d587820 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -11956,6 +11956,36 @@
         public static final String JOB_SCHEDULER_CONSTANTS = "job_scheduler_constants";
 
         /**
+         * Job scheduler QuotaController specific settings.
+         * This is encoded as a key=value list, separated by commas. Ex:
+         *
+         * "max_job_count_working=5,max_job_count_rare=2"
+         *
+         * <p>
+         * Type: string
+         *
+         * @hide
+         * @see com.android.server.job.JobSchedulerService.Constants
+         */
+        public static final String JOB_SCHEDULER_QUOTA_CONTROLLER_CONSTANTS =
+                "job_scheduler_quota_controller_constants";
+
+        /**
+         * Job scheduler TimeController specific settings.
+         * This is encoded as a key=value list, separated by commas. Ex:
+         *
+         * "skip_not_ready_jobs=true5,other_key=2"
+         *
+         * <p>
+         * Type: string
+         *
+         * @hide
+         * @see com.android.server.job.JobSchedulerService.Constants
+         */
+        public static final String JOB_SCHEDULER_TIME_CONTROLLER_CONSTANTS =
+                "job_scheduler_time_controller_constants";
+
+        /**
          * ShortcutManager specific settings.
          * This is encoded as a key=value list, separated by commas. Ex:
          *
diff --git a/core/java/android/service/voice/VoiceInteractionSession.java b/core/java/android/service/voice/VoiceInteractionSession.java
index cd8c7ae..c9d37bf 100644
--- a/core/java/android/service/voice/VoiceInteractionSession.java
+++ b/core/java/android/service/voice/VoiceInteractionSession.java
@@ -1995,7 +1995,8 @@
         }
 
         /**
-         * @return the index of the activity that this state is for.
+         * @return the index of the activity that this state is for or -1
+         *     if there was no assist data captured.
          */
         public @IntRange(from = -1) int getIndex() {
             return mIndex;
@@ -2048,7 +2049,8 @@
     }
 
     /**
-     * Represents the id of an assist source activity.
+     * Represents the id of an assist source activity. You can use
+     * {@link #equals(Object)} to compare instances of this class.
      */
     public static class ActivityId {
         private final int mTaskId;
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index c2aec6a..a25f2ee 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -508,14 +508,25 @@
      *
      * @param displayId Display ID.
      * @param shouldShow Indicates that the display should show IME.
-     * @see KeyguardManager#isDeviceSecure()
-     * @see KeyguardManager#isDeviceLocked()
      * @hide
      */
     @TestApi
     default void setShouldShowIme(int displayId, boolean shouldShow) {
     }
 
+    /**
+     * Indicates that the display should show IME.
+     *
+     * @param displayId The id of the display.
+     * @return {@code true} if the display should show IME when an input field becomes
+     * focused on it.
+     * @hide
+     */
+    @TestApi
+    default boolean shouldShowIme(int displayId) {
+        return false;
+    }
+
     public static class LayoutParams extends ViewGroup.LayoutParams implements Parcelable {
         /**
          * X position for this window.  With the default gravity it is ignored.
diff --git a/core/java/android/view/WindowManagerImpl.java b/core/java/android/view/WindowManagerImpl.java
index 2e4db5c..c349443 100644
--- a/core/java/android/view/WindowManagerImpl.java
+++ b/core/java/android/view/WindowManagerImpl.java
@@ -192,4 +192,13 @@
         } catch (RemoteException e) {
         }
     }
+
+    @Override
+    public boolean shouldShowIme(int displayId) {
+        try {
+            return WindowManagerGlobal.getWindowManagerService().shouldShowIme(displayId);
+        } catch (RemoteException e) {
+        }
+        return false;
+    }
 }
diff --git a/core/java/android/view/textclassifier/TextClassificationManager.java b/core/java/android/view/textclassifier/TextClassificationManager.java
index 417c6e7..042b943 100644
--- a/core/java/android/view/textclassifier/TextClassificationManager.java
+++ b/core/java/android/view/textclassifier/TextClassificationManager.java
@@ -25,6 +25,7 @@
 import android.database.ContentObserver;
 import android.os.ServiceManager;
 import android.provider.DeviceConfig;
+import android.provider.DeviceConfig.Properties;
 import android.provider.Settings;
 import android.service.textclassifier.TextClassifierService;
 import android.view.textclassifier.TextClassifier.TextClassifierType;
@@ -199,7 +200,7 @@
                 getApplicationContext().getContentResolver()
                         .unregisterContentObserver(mSettingsObserver);
                 if (ConfigParser.ENABLE_DEVICE_CONFIG) {
-                    DeviceConfig.removeOnPropertyChangedListener(mSettingsObserver);
+                    DeviceConfig.removeOnPropertiesChangedListener(mSettingsObserver);
                 }
             }
         } finally {
@@ -286,7 +287,7 @@
     }
 
     private static final class SettingsObserver extends ContentObserver
-            implements DeviceConfig.OnPropertyChangedListener {
+            implements DeviceConfig.OnPropertiesChangedListener {
 
         private final WeakReference<TextClassificationManager> mTcm;
 
@@ -298,7 +299,7 @@
                     false /* notifyForDescendants */,
                     this);
             if (ConfigParser.ENABLE_DEVICE_CONFIG) {
-                DeviceConfig.addOnPropertyChangedListener(
+                DeviceConfig.addOnPropertiesChangedListener(
                         DeviceConfig.NAMESPACE_TEXTCLASSIFIER,
                         ActivityThread.currentApplication().getMainExecutor(),
                         this);
@@ -311,7 +312,7 @@
         }
 
         @Override
-        public void onPropertyChanged(String namespace, String name, String value) {
+        public void onPropertiesChanged(Properties properties) {
             invalidateSettings();
         }
 
diff --git a/core/java/com/android/internal/app/AbstractResolverComparator.java b/core/java/com/android/internal/app/AbstractResolverComparator.java
index b7276a0..87d80d4 100644
--- a/core/java/com/android/internal/app/AbstractResolverComparator.java
+++ b/core/java/com/android/internal/app/AbstractResolverComparator.java
@@ -22,9 +22,14 @@
 import android.content.Intent;
 import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
 import android.os.UserHandle;
 import android.util.Log;
+
 import com.android.internal.app.ResolverActivity.ResolvedComponentInfo;
+
 import java.util.ArrayList;
 import java.util.Comparator;
 import java.util.List;
@@ -35,6 +40,8 @@
 abstract class AbstractResolverComparator implements Comparator<ResolvedComponentInfo> {
 
     private static final int NUM_OF_TOP_ANNOTATIONS_TO_USE = 3;
+    private static final boolean DEBUG = false;
+    private static final String TAG = "AbstractResolverComp";
 
     private AfterCompute mAfterCompute;
     protected final PackageManager mPm;
@@ -47,6 +54,46 @@
     // can be null if mHttp == false or current user has no default browser package
     private final String mDefaultBrowserPackageName;
 
+    // message types
+    static final int RANKER_SERVICE_RESULT = 0;
+    static final int RANKER_RESULT_TIMEOUT = 1;
+
+    // timeout for establishing connections with a ResolverRankerService, collecting features and
+    // predicting ranking scores.
+    private static final int WATCHDOG_TIMEOUT_MILLIS = 500;
+
+    protected final Handler mHandler = new Handler(Looper.getMainLooper()) {
+        public void handleMessage(Message msg) {
+            switch (msg.what) {
+                case RANKER_SERVICE_RESULT:
+                    if (DEBUG) {
+                        Log.d(TAG, "RANKER_SERVICE_RESULT");
+                    }
+                    if (mHandler.hasMessages(RANKER_RESULT_TIMEOUT)) {
+                        if (msg.obj != null) {
+                            handleResultMessage(msg);
+                        } else {
+                            Log.e(TAG, "Receiving null prediction results.");
+                        }
+                        mHandler.removeMessages(RANKER_RESULT_TIMEOUT);
+                        afterCompute();
+                    }
+                    break;
+
+                case RANKER_RESULT_TIMEOUT:
+                    if (DEBUG) {
+                        Log.d(TAG, "RANKER_RESULT_TIMEOUT; unbinding services");
+                    }
+                    mHandler.removeMessages(RANKER_SERVICE_RESULT);
+                    afterCompute();
+                    break;
+
+                default:
+                    super.handleMessage(msg);
+            }
+        }
+    };
+
     AbstractResolverComparator(Context context, Intent intent) {
         String scheme = intent.getScheme();
         mHttp = "http".equals(scheme) || "https".equals(scheme);
@@ -142,9 +189,16 @@
      * #getScore(ComponentName)} or {@link #compare(Object, Object)}, in order to prepare the
      * comparator for those calls. Note that {@link #getScore(ComponentName)} uses {@link
      * ComponentName}, so the implementation will have to be prepared to identify a {@link
-     * ResolvedComponentInfo} by {@link ComponentName}.
+     * ResolvedComponentInfo} by {@link ComponentName}. {@link #beforeCompute()} will be called
+     * before doing any computing.
      */
-    abstract void compute(List<ResolvedComponentInfo> targets);
+    final void compute(List<ResolvedComponentInfo> targets) {
+        beforeCompute();
+        doCompute(targets);
+    }
+
+    /** Implementation of compute called after {@link #beforeCompute()}. */
+    abstract void doCompute(List<ResolvedComponentInfo> targets);
 
     /**
      * Returns the score that was calculated for the corresponding {@link ResolvedComponentInfo}
@@ -152,6 +206,9 @@
      */
     abstract float getScore(ComponentName name);
 
+    /** Handles result message sent to mHandler. */
+    abstract void handleResultMessage(Message message);
+
     /**
      * Reports to UsageStats what was chosen.
      */
@@ -172,10 +229,26 @@
     void updateModel(ComponentName componentName) {
     }
 
+    /** Called before {@link #doCompute(List)}. Sets up 500ms timeout. */
+    void beforeCompute() {
+        if (DEBUG) Log.d(TAG, "Setting watchdog timer for " + WATCHDOG_TIMEOUT_MILLIS + "ms");
+        if (mHandler == null) {
+            Log.d(TAG, "Error: Handler is Null; Needs to be initialized.");
+            return;
+        }
+        mHandler.sendEmptyMessageDelayed(RANKER_RESULT_TIMEOUT, WATCHDOG_TIMEOUT_MILLIS);
+    }
+
     /**
-     * Called when the {@link ResolverActivity} is destroyed.
+     * Called when the {@link ResolverActivity} is destroyed. This calls {@link #afterCompute()}. If
+     * this call needs to happen at a different time during destroy, the method should be
+     * overridden.
      */
-    abstract void destroy();
+    void destroy() {
+        mHandler.removeMessages(RANKER_SERVICE_RESULT);
+        mHandler.removeMessages(RANKER_RESULT_TIMEOUT);
+        afterCompute();
+    }
 
     private boolean isDefaultBrowser(ResolveInfo ri) {
         // It makes sense to prefer the default browser
diff --git a/core/java/com/android/internal/app/AppPredictionServiceResolverComparator.java b/core/java/com/android/internal/app/AppPredictionServiceResolverComparator.java
index cb44c67..3b4e1a0 100644
--- a/core/java/com/android/internal/app/AppPredictionServiceResolverComparator.java
+++ b/core/java/com/android/internal/app/AppPredictionServiceResolverComparator.java
@@ -26,6 +26,7 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.pm.ResolveInfo;
+import android.os.Message;
 import android.os.UserHandle;
 import android.view.textclassifier.Log;
 
@@ -73,7 +74,7 @@
     }
 
     @Override
-    void compute(List<ResolvedComponentInfo> targets) {
+    void doCompute(List<ResolvedComponentInfo> targets) {
         List<AppTarget> appTargets = new ArrayList<>();
         for (ResolvedComponentInfo target : targets) {
             appTargets.add(new AppTarget.Builder(new AppTargetId(target.name.flattenToString()))
@@ -82,15 +83,24 @@
         }
         mAppPredictor.sortTargets(appTargets, mContext.getMainExecutor(),
                 sortedAppTargets -> {
-                    for (int i = 0; i < sortedAppTargets.size(); i++) {
-                        mTargetRanks.put(new ComponentName(sortedAppTargets.get(i).getPackageName(),
-                                sortedAppTargets.get(i).getClassName()), i);
-                    }
-                    afterCompute();
+                    Message msg =
+                            Message.obtain(mHandler, RANKER_SERVICE_RESULT, sortedAppTargets);
+                    msg.sendToTarget();
                 });
     }
 
     @Override
+    void handleResultMessage(Message msg) {
+        if (msg.what == RANKER_SERVICE_RESULT) {
+            final List<AppTarget> sortedAppTargets = (List<AppTarget>) msg.obj;
+            for (int i = 0; i < sortedAppTargets.size(); i++) {
+                mTargetRanks.put(new ComponentName(sortedAppTargets.get(i).getPackageName(),
+                        sortedAppTargets.get(i).getClassName()), i);
+            }
+        }
+    }
+
+    @Override
     float getScore(ComponentName name) {
         Integer rank = mTargetRanks.get(name);
         if (rank == null) {
@@ -111,9 +121,4 @@
                         .setClassName(componentName.getClassName()).build(),
                     ACTION_LAUNCH).build());
     }
-
-    @Override
-    void destroy() {
-        // Do nothing. App Predictor destruction is handled by caller.
-    }
 }
diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java
index 1b805ac..1eabbd8 100644
--- a/core/java/com/android/internal/app/ChooserActivity.java
+++ b/core/java/com/android/internal/app/ChooserActivity.java
@@ -114,6 +114,7 @@
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 import com.android.internal.util.ImageUtils;
+import com.android.internal.widget.ResolverDrawerLayout;
 
 import com.google.android.collect.Lists;
 
@@ -143,6 +144,8 @@
     public static final String EXTRA_PRIVATE_RETAIN_IN_ON_STOP
             = "com.android.internal.app.ChooserActivity.EXTRA_PRIVATE_RETAIN_IN_ON_STOP";
 
+    private static final String PREF_NUM_SHEET_EXPANSIONS = "pref_num_sheet_expansions";
+
     private static final boolean DEBUG = false;
 
     /**
@@ -502,6 +505,21 @@
                     chooserHeader.setElevation(defaultElevation);
                 }
             });
+
+            mResolverDrawerLayout.setOnCollapsedChangedListener(
+                    new ResolverDrawerLayout.OnCollapsedChangedListener() {
+
+                        // Only consider one expansion per activity creation
+                        private boolean mWrittenOnce = false;
+
+                        @Override
+                        public void onCollapsedChanged(boolean isCollapsed) {
+                            if (!isCollapsed && !mWrittenOnce) {
+                                incrementNumSheetExpansions();
+                                mWrittenOnce = true;
+                            }
+                        }
+                    });
         }
 
         if (DEBUG) {
@@ -881,6 +899,15 @@
         return CONTENT_PREVIEW_TEXT;
     }
 
+    private int getNumSheetExpansions() {
+        return getPreferences(Context.MODE_PRIVATE).getInt(PREF_NUM_SHEET_EXPANSIONS, 0);
+    }
+
+    private void incrementNumSheetExpansions() {
+        getPreferences(Context.MODE_PRIVATE).edit().putInt(PREF_NUM_SHEET_EXPANSIONS,
+                getNumSheetExpansions() + 1).apply();
+    }
+
     @Override
     protected void onDestroy() {
         super.onDestroy();
@@ -2494,19 +2521,25 @@
 
         private DirectShareViewHolder mDirectShareViewHolder;
         private int mChooserTargetWidth = 0;
+        private boolean mShowAzLabelIfPoss;
 
         private static final int VIEW_TYPE_DIRECT_SHARE = 0;
         private static final int VIEW_TYPE_NORMAL = 1;
         private static final int VIEW_TYPE_CONTENT_PREVIEW = 2;
         private static final int VIEW_TYPE_PROFILE = 3;
+        private static final int VIEW_TYPE_AZ_LABEL = 4;
 
         private static final int MAX_TARGETS_PER_ROW_PORTRAIT = 4;
         private static final int MAX_TARGETS_PER_ROW_LANDSCAPE = 8;
 
+        private static final int NUM_EXPANSIONS_TO_HIDE_AZ_LABEL = 20;
+
         public ChooserRowAdapter(ChooserListAdapter wrappedAdapter) {
             mChooserListAdapter = wrappedAdapter;
             mLayoutInflater = LayoutInflater.from(ChooserActivity.this);
 
+            mShowAzLabelIfPoss = getNumSheetExpansions() < NUM_EXPANSIONS_TO_HIDE_AZ_LABEL;
+
             wrappedAdapter.registerDataSetObserver(new DataSetObserver() {
                 @Override
                 public void onChanged() {
@@ -2553,12 +2586,27 @@
         }
 
         @Override
+        public boolean areAllItemsEnabled() {
+            return false;
+        }
+
+        @Override
+        public boolean isEnabled(int position) {
+            int viewType = getItemViewType(position);
+            if (viewType == VIEW_TYPE_CONTENT_PREVIEW) {
+                return false;
+            }
+            return true;
+        }
+
+        @Override
         public int getCount() {
             return (int) (
                     getContentPreviewRowCount()
                             + getProfileRowCount()
                             + getServiceTargetRowCount()
                             + getCallerAndRankedTargetRowCount()
+                            + getAzLabelRowCount()
                             + Math.ceil(
                             (float) mChooserListAdapter.getAlphaTargetCount()
                                     / getMaxTargetsPerRow())
@@ -2596,6 +2644,11 @@
             return 0;
         }
 
+        public int getAzLabelRowCount() {
+            // Only show a label if the a-z list is showing
+            return (mShowAzLabelIfPoss && mChooserListAdapter.getAlphaTargetCount() > 0) ? 1 : 0;
+        }
+
         @Override
         public Object getItem(int position) {
             // We have nothing useful to return here.
@@ -2620,6 +2673,10 @@
                 return createProfileView(convertView, parent);
             }
 
+            if (viewType == VIEW_TYPE_AZ_LABEL) {
+                return createAzLabelView(parent);
+            }
+
             if (convertView == null) {
                 holder = createViewHolder(viewType, parent);
             } else {
@@ -2633,27 +2690,29 @@
 
         @Override
         public int getItemViewType(int position) {
-            if (position == 0 && getContentPreviewRowCount() == 1) {
-                return VIEW_TYPE_CONTENT_PREVIEW;
-            }
+            int count;
 
-            if (getProfileRowCount() == 1 && position == getContentPreviewRowCount()) {
-                return VIEW_TYPE_PROFILE;
-            }
+            int countSum = (count = getContentPreviewRowCount());
+            if (count > 0 && position < countSum) return VIEW_TYPE_CONTENT_PREVIEW;
 
-            final int start = getFirstRowPosition(position);
-            final int startType = mChooserListAdapter.getPositionTargetType(start);
+            countSum += (count = getProfileRowCount());
+            if (count > 0 && position < countSum) return VIEW_TYPE_PROFILE;
 
-            if (startType == ChooserListAdapter.TARGET_SERVICE) {
-                return VIEW_TYPE_DIRECT_SHARE;
-            }
+            countSum += (count = getServiceTargetRowCount());
+            if (count > 0 && position < countSum) return VIEW_TYPE_DIRECT_SHARE;
+
+            countSum += (count = getCallerAndRankedTargetRowCount());
+            if (count > 0 && position < countSum) return VIEW_TYPE_NORMAL;
+
+            countSum += (count = getAzLabelRowCount());
+            if (count > 0 && position < countSum) return VIEW_TYPE_AZ_LABEL;
 
             return VIEW_TYPE_NORMAL;
         }
 
         @Override
         public int getViewTypeCount() {
-            return 4;
+            return 5;
         }
 
         private ViewGroup createContentPreviewView(View convertView, ViewGroup parent) {
@@ -2680,6 +2739,10 @@
             return profileRow;
         }
 
+        private View createAzLabelView(ViewGroup parent) {
+            return mLayoutInflater.inflate(R.layout.chooser_az_label_row, parent, false);
+        }
+
         private RowViewHolder loadViewsIntoRow(RowViewHolder holder) {
             final int spec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
             final int exactSpec = MeasureSpec.makeMeasureSpec(mChooserTargetWidth,
@@ -2778,16 +2841,24 @@
         }
 
         /**
-         * Need to merge CALLER + ranked STANDARD into a single row. All other types
-         * are placed into their own row as determined by their target type, and dividers
-         * are added in the list to separate each type.
+         * Need to merge CALLER + ranked STANDARD into a single row and prevent a separator from
+         * showing on top of the AZ list if the AZ label is visible. All other types are placed into
+         * their own row as determined by their target type, and dividers are added in the list to
+         * separate each type.
          */
         int getRowType(int rowPosition) {
+            // Merge caller and ranked standard into a single row
             int positionType = mChooserListAdapter.getPositionTargetType(rowPosition);
             if (positionType == ChooserListAdapter.TARGET_CALLER) {
                 return ChooserListAdapter.TARGET_STANDARD;
             }
 
+            // If an the A-Z label is shown, prevent a separator from appearing by making the A-Z
+            // row type the same as the suggestion row type
+            if (getAzLabelRowCount() > 0 && positionType == ChooserListAdapter.TARGET_STANDARD_AZ) {
+                return ChooserListAdapter.TARGET_STANDARD;
+            }
+
             return positionType;
         }
 
@@ -2867,6 +2938,8 @@
                 return serviceCount + (row - serviceRows) * getMaxTargetsPerRow();
             }
 
+            row -= getAzLabelRowCount();
+
             return callerAndRankedCount + serviceCount
                     + (row - callerAndRankedRows - serviceRows) * getMaxTargetsPerRow();
         }
diff --git a/core/java/com/android/internal/app/ResolverRankerServiceResolverComparator.java b/core/java/com/android/internal/app/ResolverRankerServiceResolverComparator.java
index 726b186..a781907 100644
--- a/core/java/com/android/internal/app/ResolverRankerServiceResolverComparator.java
+++ b/core/java/com/android/internal/app/ResolverRankerServiceResolverComparator.java
@@ -18,7 +18,6 @@
 package com.android.internal.app;
 
 import android.app.usage.UsageStats;
-import android.app.usage.UsageStatsManager;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
@@ -28,9 +27,7 @@
 import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.pm.ResolveInfo;
 import android.metrics.LogMaker;
-import android.os.Handler;
 import android.os.IBinder;
-import android.os.Looper;
 import android.os.Message;
 import android.os.RemoteException;
 import android.os.UserHandle;
@@ -67,10 +64,6 @@
 
     private static final float RECENCY_MULTIPLIER = 2.f;
 
-    // message types
-    private static final int RESOLVER_RANKER_SERVICE_RESULT = 0;
-    private static final int RESOLVER_RANKER_RESULT_TIMEOUT = 1;
-
     // timeout for establishing connections with a ResolverRankerService.
     private static final int CONNECTION_COST_TIMEOUT_MILLIS = 200;
     // timeout for establishing connections with a ResolverRankerService, collecting features and
@@ -93,57 +86,6 @@
     private Context mContext;
     private CountDownLatch mConnectSignal;
 
-    private final Handler mHandler = new Handler(Looper.getMainLooper()) {
-        public void handleMessage(Message msg) {
-            switch (msg.what) {
-                case RESOLVER_RANKER_SERVICE_RESULT:
-                    if (DEBUG) {
-                        Log.d(TAG, "RESOLVER_RANKER_SERVICE_RESULT");
-                    }
-                    if (mHandler.hasMessages(RESOLVER_RANKER_RESULT_TIMEOUT)) {
-                        if (msg.obj != null) {
-                            final List<ResolverTarget> receivedTargets =
-                                    (List<ResolverTarget>) msg.obj;
-                            if (receivedTargets != null && mTargets != null
-                                    && receivedTargets.size() == mTargets.size()) {
-                                final int size = mTargets.size();
-                                boolean isUpdated = false;
-                                for (int i = 0; i < size; ++i) {
-                                    final float predictedProb =
-                                            receivedTargets.get(i).getSelectProbability();
-                                    if (predictedProb != mTargets.get(i).getSelectProbability()) {
-                                        mTargets.get(i).setSelectProbability(predictedProb);
-                                        isUpdated = true;
-                                    }
-                                }
-                                if (isUpdated) {
-                                    mRankerServiceName = mResolvedRankerName;
-                                }
-                            } else {
-                                Log.e(TAG, "Sizes of sent and received ResolverTargets diff.");
-                            }
-                        } else {
-                            Log.e(TAG, "Receiving null prediction results.");
-                        }
-                        mHandler.removeMessages(RESOLVER_RANKER_RESULT_TIMEOUT);
-                        afterCompute();
-                    }
-                    break;
-
-                case RESOLVER_RANKER_RESULT_TIMEOUT:
-                    if (DEBUG) {
-                        Log.d(TAG, "RESOLVER_RANKER_RESULT_TIMEOUT; unbinding services");
-                    }
-                    mHandler.removeMessages(RESOLVER_RANKER_SERVICE_RESULT);
-                    afterCompute();
-                    break;
-
-                default:
-                    super.handleMessage(msg);
-            }
-        }
-    };
-
     public ResolverRankerServiceResolverComparator(Context context, Intent intent,
                 String referrerPackage, AfterCompute afterCompute) {
         super(context, intent);
@@ -159,11 +101,35 @@
         setCallBack(afterCompute);
     }
 
+    @Override
+    public void handleResultMessage(Message msg) {
+        if (msg.what != RANKER_SERVICE_RESULT) {
+            return;
+        }
+        final List<ResolverTarget> receivedTargets = (List<ResolverTarget>) msg.obj;
+        if (receivedTargets != null && mTargets != null
+                    && receivedTargets.size() == mTargets.size()) {
+            final int size = mTargets.size();
+            boolean isUpdated = false;
+            for (int i = 0; i < size; ++i) {
+                final float predictedProb =
+                        receivedTargets.get(i).getSelectProbability();
+                if (predictedProb != mTargets.get(i).getSelectProbability()) {
+                    mTargets.get(i).setSelectProbability(predictedProb);
+                    isUpdated = true;
+                }
+            }
+            if (isUpdated) {
+                mRankerServiceName = mResolvedRankerName;
+            }
+        } else {
+            Log.e(TAG, "Sizes of sent and received ResolverTargets diff.");
+        }
+    }
+
     // compute features for each target according to usage stats of targets.
     @Override
-    public void compute(List<ResolvedComponentInfo> targets) {
-        reset();
-
+    public void doCompute(List<ResolvedComponentInfo> targets) {
         final long recentSinceTime = mCurrentTime - RECENCY_TIME_PERIOD;
 
         float mostRecencyScore = 1.0f;
@@ -322,8 +288,8 @@
     // unbind the service and clear unhandled messges.
     @Override
     public void destroy() {
-        mHandler.removeMessages(RESOLVER_RANKER_SERVICE_RESULT);
-        mHandler.removeMessages(RESOLVER_RANKER_RESULT_TIMEOUT);
+        mHandler.removeMessages(RANKER_SERVICE_RESULT);
+        mHandler.removeMessages(RANKER_RESULT_TIMEOUT);
         if (mConnection != null) {
             mContext.unbindService(mConnection);
             mConnection.destroy();
@@ -417,15 +383,6 @@
         return null;
     }
 
-    // set a watchdog, to avoid waiting for ranking service for too long.
-    private void startWatchDog(int timeOutLimit) {
-        if (DEBUG) Log.d(TAG, "Setting watchdog timer for " + timeOutLimit + "ms");
-        if (mHandler == null) {
-            Log.d(TAG, "Error: Handler is Null; Needs to be initialized.");
-        }
-        mHandler.sendEmptyMessageDelayed(RESOLVER_RANKER_RESULT_TIMEOUT, timeOutLimit);
-    }
-
     private class ResolverRankerServiceConnection implements ServiceConnection {
         private final CountDownLatch mConnectSignal;
 
@@ -442,7 +399,7 @@
                 }
                 synchronized (mLock) {
                     final Message msg = Message.obtain();
-                    msg.what = RESOLVER_RANKER_SERVICE_RESULT;
+                    msg.what = RANKER_SERVICE_RESULT;
                     msg.obj = targets;
                     mHandler.sendMessage(msg);
                 }
@@ -477,12 +434,13 @@
         }
     }
 
-    private void reset() {
+    @Override
+    void beforeCompute() {
+        super.beforeCompute();
         mTargetsDict.clear();
         mTargets = null;
         mRankerServiceName = new ComponentName(mContext, this.getClass());
         mResolvedRankerName = null;
-        startWatchDog(WATCHDOG_TIMEOUT_MILLIS);
         initRanker(mContext);
     }
 
diff --git a/core/java/com/android/internal/content/FileSystemProvider.java b/core/java/com/android/internal/content/FileSystemProvider.java
index 76826d3..9e8bd64 100644
--- a/core/java/com/android/internal/content/FileSystemProvider.java
+++ b/core/java/com/android/internal/content/FileSystemProvider.java
@@ -45,6 +45,7 @@
 import android.webkit.MimeTypeMap;
 
 import com.android.internal.annotations.GuardedBy;
+import com.android.internal.util.ArrayUtils;
 
 import libcore.io.IoUtils;
 
@@ -450,7 +451,11 @@
 
     @Override
     public String getDocumentType(String documentId) throws FileNotFoundException {
-        final File file = getFileForDocId(documentId);
+        return getDocumentType(documentId, getFileForDocId(documentId));
+    }
+
+    private String getDocumentType(final String documentId, final File file)
+            throws FileNotFoundException {
         if (file.isDirectory()) {
             return Document.MIME_TYPE_DIR;
         } else {
@@ -532,51 +537,63 @@
         return DocumentsContract.openImageThumbnail(file);
     }
 
-    protected RowBuilder includeFile(MatrixCursor result, String docId, File file)
+    protected RowBuilder includeFile(final MatrixCursor result, String docId, File file)
             throws FileNotFoundException {
+        final String[] columns = result.getColumnNames();
+        final RowBuilder row = result.newRow();
+
         if (docId == null) {
             docId = getDocIdForFile(file);
         } else {
             file = getFileForDocId(docId);
         }
+        final String mimeType = getDocumentType(docId, file);
+        row.add(Document.COLUMN_DOCUMENT_ID, docId);
+        row.add(Document.COLUMN_MIME_TYPE, mimeType);
 
-        int flags = 0;
+        final int flagIndex = ArrayUtils.indexOf(columns, Document.COLUMN_FLAGS);
+        if (flagIndex != -1) {
+            int flags = 0;
+            if (file.canWrite()) {
+                if (mimeType.equals(Document.MIME_TYPE_DIR)) {
+                    flags |= Document.FLAG_DIR_SUPPORTS_CREATE;
+                    flags |= Document.FLAG_SUPPORTS_DELETE;
+                    flags |= Document.FLAG_SUPPORTS_RENAME;
+                    flags |= Document.FLAG_SUPPORTS_MOVE;
+                } else {
+                    flags |= Document.FLAG_SUPPORTS_WRITE;
+                    flags |= Document.FLAG_SUPPORTS_DELETE;
+                    flags |= Document.FLAG_SUPPORTS_RENAME;
+                    flags |= Document.FLAG_SUPPORTS_MOVE;
+                }
+            }
 
-        if (file.canWrite()) {
-            if (file.isDirectory()) {
-                flags |= Document.FLAG_DIR_SUPPORTS_CREATE;
-                flags |= Document.FLAG_SUPPORTS_DELETE;
-                flags |= Document.FLAG_SUPPORTS_RENAME;
-                flags |= Document.FLAG_SUPPORTS_MOVE;
-            } else {
-                flags |= Document.FLAG_SUPPORTS_WRITE;
-                flags |= Document.FLAG_SUPPORTS_DELETE;
-                flags |= Document.FLAG_SUPPORTS_RENAME;
-                flags |= Document.FLAG_SUPPORTS_MOVE;
+            if (mimeType.startsWith("image/")) {
+                flags |= Document.FLAG_SUPPORTS_THUMBNAIL;
+            }
+
+            if (typeSupportsMetadata(mimeType)) {
+                flags |= Document.FLAG_SUPPORTS_METADATA;
+            }
+            row.add(flagIndex, flags);
+        }
+
+        final int displayNameIndex = ArrayUtils.indexOf(columns, Document.COLUMN_DISPLAY_NAME);
+        if (displayNameIndex != -1) {
+            row.add(displayNameIndex, file.getName());
+        }
+
+        final int lastModifiedIndex = ArrayUtils.indexOf(columns, Document.COLUMN_LAST_MODIFIED);
+        if (lastModifiedIndex != -1) {
+            final long lastModified = file.lastModified();
+            // Only publish dates reasonably after epoch
+            if (lastModified > 31536000000L) {
+                row.add(lastModifiedIndex, lastModified);
             }
         }
-
-        final String mimeType = getDocumentType(docId);
-        final String displayName = file.getName();
-        if (mimeType.startsWith("image/")) {
-            flags |= Document.FLAG_SUPPORTS_THUMBNAIL;
-        }
-
-        if (typeSupportsMetadata(mimeType)) {
-            flags |= Document.FLAG_SUPPORTS_METADATA;
-        }
-
-        final RowBuilder row = result.newRow();
-        row.add(Document.COLUMN_DOCUMENT_ID, docId);
-        row.add(Document.COLUMN_DISPLAY_NAME, displayName);
-        row.add(Document.COLUMN_SIZE, file.length());
-        row.add(Document.COLUMN_MIME_TYPE, mimeType);
-        row.add(Document.COLUMN_FLAGS, flags);
-
-        // Only publish dates reasonably after epoch
-        long lastModified = file.lastModified();
-        if (lastModified > 31536000000L) {
-            row.add(Document.COLUMN_LAST_MODIFIED, lastModified);
+        final int sizeIndex = ArrayUtils.indexOf(columns, Document.COLUMN_SIZE);
+        if (sizeIndex != -1) {
+            row.add(sizeIndex, file.length());
         }
 
         // Return the row builder just in case any subclass want to add more stuff to it.
diff --git a/core/java/com/android/internal/statusbar/IStatusBar.aidl b/core/java/com/android/internal/statusbar/IStatusBar.aidl
index cfc32cf..7e501d2 100644
--- a/core/java/com/android/internal/statusbar/IStatusBar.aidl
+++ b/core/java/com/android/internal/statusbar/IStatusBar.aidl
@@ -50,9 +50,11 @@
      * @param mask which flags to change
      * @param fullscreenBounds the current bounds of the fullscreen stack, in screen coordinates
      * @param dockedBounds the current bounds of the docked stack, in screen coordinates
+     * @param navbarColorManagedByIme {@code true} if navigation bar color is managed by IME.
      */
     void setSystemUiVisibility(int displayId, int vis, int fullscreenStackVis, int dockedStackVis,
-            int mask, in Rect fullscreenBounds, in Rect dockedBounds);
+            int mask, in Rect fullscreenBounds, in Rect dockedBounds,
+            boolean navbarColorManagedByIme);
 
     void topAppWindowChanged(int displayId, boolean menuVisible);
     void setImeWindowStatus(int displayId, in IBinder token, int vis, int backDisposition,
diff --git a/core/java/com/android/internal/statusbar/RegisterStatusBarResult.java b/core/java/com/android/internal/statusbar/RegisterStatusBarResult.java
index ff94264..6b0f8b2 100644
--- a/core/java/com/android/internal/statusbar/RegisterStatusBarResult.java
+++ b/core/java/com/android/internal/statusbar/RegisterStatusBarResult.java
@@ -39,12 +39,13 @@
     public final IBinder mImeToken;
     public final Rect mFullscreenStackBounds;
     public final Rect mDockedStackBounds;
+    public final boolean mNavbarColorManagedByIme;
 
     public RegisterStatusBarResult(ArrayMap<String, StatusBarIcon> icons, int disabledFlags1,
             int systemUiVisibility, boolean menuVisible, int imeWindowVis, int imeBackDisposition,
             boolean showImeSwitcher, int disabledFlags2, int fullscreenStackSysUiVisibility,
             int dockedStackSysUiVisibility, IBinder imeToken, Rect fullscreenStackBounds,
-            Rect dockedStackBounds) {
+            Rect dockedStackBounds, boolean navbarColorManagedByIme) {
         mIcons = new ArrayMap<>(icons);
         mDisabledFlags1 = disabledFlags1;
         mSystemUiVisibility = systemUiVisibility;
@@ -58,6 +59,7 @@
         mImeToken = imeToken;
         mFullscreenStackBounds = fullscreenStackBounds;
         mDockedStackBounds = dockedStackBounds;
+        mNavbarColorManagedByIme = navbarColorManagedByIme;
     }
 
     @Override
@@ -80,6 +82,7 @@
         dest.writeStrongBinder(mImeToken);
         dest.writeTypedObject(mFullscreenStackBounds, flags);
         dest.writeTypedObject(mDockedStackBounds, flags);
+        dest.writeBoolean(mNavbarColorManagedByIme);
     }
 
     /**
@@ -103,11 +106,12 @@
                     final IBinder imeToken = source.readStrongBinder();
                     final Rect fullscreenStackBounds = source.readTypedObject(Rect.CREATOR);
                     final Rect dockedStackBounds = source.readTypedObject(Rect.CREATOR);
+                    final boolean navbarColorManagedByIme = source.readBoolean();
                     return new RegisterStatusBarResult(icons, disabledFlags1, systemUiVisibility,
                             menuVisible, imeWindowVis, imeBackDisposition, showImeSwitcher,
                             disabledFlags2, fullscreenStackSysUiVisibility,
                             dockedStackSysUiVisibility, imeToken, fullscreenStackBounds,
-                            dockedStackBounds);
+                            dockedStackBounds, navbarColorManagedByIme);
                 }
 
                 @Override
diff --git a/core/java/com/android/internal/widget/ResolverDrawerLayout.java b/core/java/com/android/internal/widget/ResolverDrawerLayout.java
index 6b0d85e..3adb36f 100644
--- a/core/java/com/android/internal/widget/ResolverDrawerLayout.java
+++ b/core/java/com/android/internal/widget/ResolverDrawerLayout.java
@@ -104,6 +104,7 @@
 
     private OnDismissedListener mOnDismissedListener;
     private RunOnDismissedListener mRunOnDismissedListener;
+    private OnCollapsedChangedListener mOnCollapsedChangedListener;
 
     private boolean mDismissLocked;
 
@@ -267,6 +268,10 @@
         return mOnDismissedListener != null && !mDismissLocked;
     }
 
+    public void setOnCollapsedChangedListener(OnCollapsedChangedListener listener) {
+        mOnCollapsedChangedListener = listener;
+    }
+
     @Override
     public boolean onInterceptTouchEvent(MotionEvent ev) {
         final int action = ev.getActionMasked();
@@ -548,6 +553,10 @@
         if (mScrollIndicatorDrawable != null) {
             setWillNotDraw(!isCollapsed);
         }
+
+        if (mOnCollapsedChangedListener != null) {
+            mOnCollapsedChangedListener.onCollapsedChanged(isCollapsed);
+        }
     }
 
     void dispatchOnDismissed() {
@@ -1078,8 +1087,25 @@
         };
     }
 
+    /**
+     * Listener for sheet dismissed events.
+     */
     public interface OnDismissedListener {
-        public void onDismissed();
+        /**
+         * Callback when the sheet is dismissed by the user.
+         */
+        void onDismissed();
+    }
+
+    /**
+     * Listener for sheet collapsed / expanded events.
+     */
+    public interface OnCollapsedChangedListener {
+        /**
+         * Callback when the sheet is either fully expanded or collapsed.
+         * @param isCollapsed true when collapsed, false when expanded.
+         */
+        void onCollapsedChanged(boolean isCollapsed);
     }
 
     private class RunOnDismissedListener implements Runnable {
diff --git a/core/jni/android_net_NetUtils.cpp b/core/jni/android_net_NetUtils.cpp
index 28c59db..c5fc9b3 100644
--- a/core/jni/android_net_NetUtils.cpp
+++ b/core/jni/android_net_NetUtils.cpp
@@ -49,8 +49,8 @@
 namespace android {
 
 constexpr int MAXPACKETSIZE = 8 * 1024;
-// FrameworkListener limits the size of commands to 1024 bytes. TODO: fix this.
-constexpr int MAXCMDSIZE = 1024;
+// FrameworkListener limits the size of commands to 4096 bytes.
+constexpr int MAXCMDSIZE = 4096;
 
 static void throwErrnoException(JNIEnv* env, const char* functionName, int error) {
     ScopedLocalRef<jstring> detailMessage(env, env->NewStringUTF(functionName));
diff --git a/core/proto/android/app/settings_enums.proto b/core/proto/android/app/settings_enums.proto
index 1ff7418..9cffb2b 100644
--- a/core/proto/android/app/settings_enums.proto
+++ b/core/proto/android/app/settings_enums.proto
@@ -2354,4 +2354,11 @@
 
     // OPEN: Settings > Pick preferred SIM dialog
     DIALOG_PREFERRED_SIM_PICKER = 1709;
+
+    // OPEN: Settings > Network & internet > Mobile network > Delete sim
+    DIALOG_DELETE_SIM_CONFIRMATION = 1713;
+
+    // OPEN: Settings >  Network & internet > Mobile network > Delete sim > (answer yes to
+    //       confirmation)
+    DIALOG_DELETE_SIM_PROGRESS = 1714;
 }
diff --git a/core/proto/android/providers/settings/global.proto b/core/proto/android/providers/settings/global.proto
index b47097d..8f16b41 100644
--- a/core/proto/android/providers/settings/global.proto
+++ b/core/proto/android/providers/settings/global.proto
@@ -501,7 +501,10 @@
     }
     optional IntentFirewall intent_firewall = 65;
 
-    optional SettingProto job_scheduler_constants = 66;
+    optional SettingProto job_scheduler_constants = 66 [ (android.privacy).dest = DEST_AUTOMATIC ];
+    optional SettingProto job_scheduler_quota_controller_constants = 149 [ (android.privacy).dest = DEST_AUTOMATIC ];
+    optional SettingProto job_scheduler_time_controller_constants = 150 [ (android.privacy).dest = DEST_AUTOMATIC ];
+
     optional SettingProto keep_profile_in_background = 67 [ (android.privacy).dest = DEST_AUTOMATIC ];
 
     message LangId {
@@ -1049,5 +1052,5 @@
 
     // Please insert fields in alphabetical order and group them into messages
     // if possible (to avoid reaching the method limit).
-    // Next tag = 149;
+    // Next tag = 151;
 }
diff --git a/core/proto/android/stats/connectivity/resolv_stats.proto b/core/proto/android/stats/connectivity/resolv_stats.proto
deleted file mode 100644
index 43eb673..0000000
--- a/core/proto/android/stats/connectivity/resolv_stats.proto
+++ /dev/null
@@ -1,182 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-syntax = "proto2";
-package android.stats.connectivity;
-import "frameworks/base/core/proto/android/net/networkcapabilities.proto";
-
-enum EventType {
-    EVENT_UNKNOWN       = 0;
-    EVENT_GETADDRINFO   = 1;
-    EVENT_GETHOSTBYNAME = 2;
-    EVENT_GETHOSTBYADDR = 3;
-    EVENT_RES_NSEND     = 4;
-}
-
-enum PrivateDnsModes {
-    OFF           = 0;
-    OPPORTUNISTIC = 1;
-    STRICT        = 2;
-}
-// The return value of the DNS resolver for each DNS lookups.
-// bionic/libc/include/netdb.h
-// system/netd/resolv/include/netd_resolv/resolv.h
-enum ReturnCode {
-    RC_EAI_NO_ERROR   = 0;
-    RC_EAI_ADDRFAMILY = 1;
-    RC_EAI_AGAIN      = 2;
-    RC_EAI_BADFLAGS   = 3;
-    RC_EAI_FAIL       = 4;
-    RC_EAI_FAMILY     = 5;
-    RC_EAI_MEMORY     = 6;
-    RC_EAI_NODATA     = 7;
-    RC_EAI_NONAME     = 8;
-    RC_EAI_SERVICE    = 9;
-    RC_EAI_SOCKTYPE   = 10;
-    RC_EAI_SYSTEM     = 11;
-    RC_EAI_BADHINTS   = 12;
-    RC_EAI_PROTOCOL   = 13;
-    RC_EAI_OVERFLOW   = 14;
-    RC_RESOLV_TIMEOUT = 255;
-    RC_EAI_MAX        = 256;
-}
-
-
-enum NsRcode {
-    ns_r_noerror   = 0;    // No error occurred.
-    ns_r_formerr   = 1;    // Format error.
-    ns_r_servfail  = 2;   // Server failure.
-    ns_r_nxdomain  = 3;   // Name error.
-    ns_r_notimpl   = 4;    // Unimplemented.
-    ns_r_refused   = 5;    // Operation refused.
-    // these are for BIND_UPDATE
-    ns_r_yxdomain  = 6;   // Name exists
-    ns_r_yxrrset   = 7;    // RRset exists
-    ns_r_nxrrset   = 8;    // RRset does not exist
-    ns_r_notauth   = 9;    // Not authoritative for zone
-    ns_r_notzone   = 10;   // Zone of record different from zone section
-    ns_r_max       = 11;
-    // The following are EDNS extended rcodes
-    ns_r_badvers   = 16;
-    // The following are TSIG errors
-    //ns_r_badsig  = 16,
-    ns_r_badkey    = 17;
-    ns_r_badtime   = 18;
-}
-
-// Currently defined type values for resources and queries.
-enum NsType {
-    ns_t_invalid = 0;    // Cookie.
-    ns_t_a = 1;          // Host address.
-    ns_t_ns = 2;         // Authoritative server.
-    ns_t_md = 3;         // Mail destination.
-    ns_t_mf = 4;         // Mail forwarder.
-    ns_t_cname = 5;      // Canonical name.
-    ns_t_soa = 6;        // Start of authority zone.
-    ns_t_mb = 7;         // Mailbox domain name.
-    ns_t_mg = 8;         // Mail group member.
-    ns_t_mr = 9;         // Mail rename name.
-    ns_t_null = 10;      // Null resource record.
-    ns_t_wks = 11;       // Well known service.
-    ns_t_ptr = 12;       // Domain name pointer.
-    ns_t_hinfo = 13;     // Host information.
-    ns_t_minfo = 14;     // Mailbox information.
-    ns_t_mx = 15;        // Mail routing information.
-    ns_t_txt = 16;       // Text strings.
-    ns_t_rp = 17;        // Responsible person.
-    ns_t_afsdb = 18;     // AFS cell database.
-    ns_t_x25 = 19;       // X_25 calling address.
-    ns_t_isdn = 20;      // ISDN calling address.
-    ns_t_rt = 21;        // Router.
-    ns_t_nsap = 22;      // NSAP address.
-    ns_t_nsap_ptr = 23;  // Reverse NSAP lookup (deprecated).
-    ns_t_sig = 24;       // Security signature.
-    ns_t_key = 25;       // Security key.
-    ns_t_px = 26;        // X.400 mail mapping.
-    ns_t_gpos = 27;      // Geographical position (withdrawn).
-    ns_t_aaaa = 28;      // IPv6 Address.
-    ns_t_loc = 29;       // Location Information.
-    ns_t_nxt = 30;       // Next domain (security).
-    ns_t_eid = 31;       // Endpoint identifier.
-    ns_t_nimloc = 32;    // Nimrod Locator.
-    ns_t_srv = 33;       // Server Selection.
-    ns_t_atma = 34;      // ATM Address
-    ns_t_naptr = 35;     // Naming Authority PoinTeR
-    ns_t_kx = 36;        // Key Exchange
-    ns_t_cert = 37;      // Certification record
-    ns_t_a6 = 38;        // IPv6 address (experimental)
-    ns_t_dname = 39;     // Non-terminal DNAME
-    ns_t_sink = 40;      // Kitchen sink (experimentatl)
-    ns_t_opt = 41;       // EDNS0 option (meta-RR)
-    ns_t_apl = 42;       // Address prefix list (RFC 3123)
-    ns_t_ds = 43;        // Delegation Signer
-    ns_t_sshfp = 44;     // SSH Fingerprint
-    ns_t_ipseckey = 45;  // IPSEC Key
-    ns_t_rrsig = 46;     // RRset Signature
-    ns_t_nsec = 47;      // Negative security
-    ns_t_dnskey = 48;    // DNS Key
-    ns_t_dhcid = 49;     // Dynamic host configuratin identifier
-    ns_t_nsec3 = 50;     // Negative security type 3
-    ns_t_nsec3param = 51;// Negative security type 3 parameters
-    ns_t_hip = 55;       // Host Identity Protocol
-    ns_t_spf = 99;       // Sender Policy Framework
-    ns_t_tkey = 249;     // Transaction key
-    ns_t_tsig = 250;     // Transaction signature.
-    ns_t_ixfr = 251;     // Incremental zone transfer.
-    ns_t_axfr = 252;     // Transfer zone of authority.
-    ns_t_mailb = 253;    // Transfer mailbox records.
-    ns_t_maila = 254;    // Transfer mail agent records.
-    ns_t_any = 255;      // Wildcard match.
-    ns_t_zxfr = 256;     // BIND-specific, nonstandard.
-    ns_t_dlv = 32769;    // DNSSEC look-aside validatation.
-    ns_t_max = 65536;
-}
-
-enum IpVersion {
-   IPV4  = 0;
-   IPV6  = 1;
-   MIXED = 2;
-}
-
-enum TransportType {
-    UDP = 0;
-    TCP = 1;
-    DOT = 2;
-    DOT_UDP = 3;
-    DOT_TCP = 4;
-}
-
-message DnsQueryEvent {
-    optional NsRcode rrcode           = 1;
-    optional NsType rrtype            = 2;
-    optional bool cache_hit           = 3;
-    optional IpVersion ipversion      = 4;
-    optional TransportType transport  = 5;
-    optional int32 packet_retransmits = 6;  // Used only by the UDP transport
-    optional int32 reconnects         = 7;  // Used only by TCP and DOT
-    optional int32 latency_micros     = 8;
-    optional int32 active_experiments = 9;
-    optional android.net.NetworkCapabilitiesProto.Transport network_type = 10;
-}
-
-message DnsQueryEventRe {
-    repeated DnsQueryEvent dns_query_event = 1;
-}
-
-
-message DnsCallEvent {
-
-}
-
diff --git a/core/proto/android/stats/devicepolicy/device_policy_enums.proto b/core/proto/android/stats/devicepolicy/device_policy_enums.proto
index 0db7424..0821d14 100644
--- a/core/proto/android/stats/devicepolicy/device_policy_enums.proto
+++ b/core/proto/android/stats/devicepolicy/device_policy_enums.proto
@@ -149,4 +149,6 @@
   PROVISIONING_PREPARE_STARTED = 122;
   PROVISIONING_PREPARE_COMPLETED = 123;
   PROVISIONING_FLOW_TYPE = 124;
+  CROSS_PROFILE_APPS_GET_TARGET_USER_PROFILES = 125;
+  CROSS_PROFILE_APPS_START_ACTIVITY_AS_USER = 126;
 }
diff --git a/core/proto/android/stats/dnsresolver/Android.bp b/core/proto/android/stats/dnsresolver/Android.bp
new file mode 100644
index 0000000..0b5aa86
--- /dev/null
+++ b/core/proto/android/stats/dnsresolver/Android.bp
@@ -0,0 +1,25 @@
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+java_library_static {
+    name: "dnsresolverprotosnano",
+    proto: {
+        type: "nano",
+    },
+    srcs: [
+        "dns_resolver.proto",
+    ],
+    sdk_version: "system_current",
+    no_framework_libs: true,
+}
diff --git a/core/proto/android/stats/dnsresolver/dns_resolver.proto b/core/proto/android/stats/dnsresolver/dns_resolver.proto
new file mode 100644
index 0000000..af6fea0
--- /dev/null
+++ b/core/proto/android/stats/dnsresolver/dns_resolver.proto
@@ -0,0 +1,214 @@
+/*

+ * Copyright (C) 2019 The Android Open Source Project

+ *

+ * Licensed under the Apache License, Version 2.0 (the "License");

+ * you may not use this file except in compliance with the License.

+ * You may obtain a copy of the License at

+ *

+ *      http://www.apache.org/licenses/LICENSE-2.0

+ *

+ * Unless required by applicable law or agreed to in writing, software

+ * distributed under the License is distributed on an "AS IS" BASIS,

+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

+ * See the License for the specific language governing permissions and

+ * limitations under the License.

+ */

+syntax = "proto2";

+package android.stats.dnsresolver;

+

+enum EventType {

+    EVENT_UNKNOWN = 0;

+    EVENT_GETADDRINFO = 1;

+    EVENT_GETHOSTBYNAME = 2;

+    EVENT_GETHOSTBYADDR = 3;

+    EVENT_RES_NSEND = 4;

+}

+

+// The return value of the DNS resolver for each DNS lookups.

+// bionic/libc/include/netdb.h

+// system/netd/resolv/include/netd_resolv/resolv.h

+enum ReturnCode {

+    RC_EAI_NO_ERROR = 0;

+    RC_EAI_ADDRFAMILY = 1;

+    RC_EAI_AGAIN = 2;

+    RC_EAI_BADFLAGS = 3;

+    RC_EAI_FAIL = 4;

+    RC_EAI_FAMILY = 5;

+    RC_EAI_MEMORY = 6;

+    RC_EAI_NODATA = 7;

+    RC_EAI_NONAME = 8;

+    RC_EAI_SERVICE = 9;

+    RC_EAI_SOCKTYPE = 10;

+    RC_EAI_SYSTEM = 11;

+    RC_EAI_BADHINTS = 12;

+    RC_EAI_PROTOCOL = 13;

+    RC_EAI_OVERFLOW = 14;

+    RC_RESOLV_TIMEOUT = 255;

+    RC_EAI_MAX = 256;

+}

+

+enum NsRcode {

+    NS_R_NO_ERROR = 0;  // No error occurred.

+    NS_R_FORMERR = 1;   // Format error.

+    NS_R_SERVFAIL = 2;  // Server failure.

+    NS_R_NXDOMAIN = 3;  // Name error.

+    NS_R_NOTIMPL = 4;   // Unimplemented.

+    NS_R_REFUSED = 5;   // Operation refused.

+    // these are for BIND_UPDATE

+    NS_R_YXDOMAIN = 6;  // Name exists

+    NS_R_YXRRSET = 7;   // RRset exists

+    NS_R_NXRRSET = 8;   // RRset does not exist

+    NS_R_NOTAUTH = 9;   // Not authoritative for zone

+    NS_R_NOTZONE = 10;  // Zone of record different from zone section

+    NS_R_MAX = 11;

+    // The following are EDNS extended rcodes

+    NS_R_BADVERS = 16;

+    // The following are TSIG errors

+    // NS_R_BADSIG  = 16,

+    NS_R_BADKEY = 17;

+    NS_R_BADTIME = 18;

+}

+

+// Currently defined type values for resources and queries.

+enum NsType {

+    NS_T_INVALID = 0;      // Cookie.

+    NS_T_A = 1;            // Host address.

+    NS_T_NS = 2;           // Authoritative server.

+    NS_T_MD = 3;           // Mail destination.

+    NS_T_MF = 4;           // Mail forwarder.

+    NS_T_CNAME = 5;        // Canonical name.

+    NS_T_SOA = 6;          // Start of authority zone.

+    NS_T_MB = 7;           // Mailbox domain name.

+    NS_T_MG = 8;           // Mail group member.

+    NS_T_MR = 9;           // Mail rename name.

+    NS_T_NULL = 10;        // Null resource record.

+    NS_T_WKS = 11;         // Well known service.

+    NS_T_PTR = 12;         // Domain name pointer.

+    NS_T_HINFO = 13;       // Host information.

+    NS_T_MINFO = 14;       // Mailbox information.

+    NS_T_MX = 15;          // Mail routing information.

+    NS_T_TXT = 16;         // Text strings.

+    NS_T_RP = 17;          // Responsible person.

+    NS_T_AFSDB = 18;       // AFS cell database.

+    NS_T_X25 = 19;         // X_25 calling address.

+    NS_T_ISDN = 20;        // ISDN calling address.

+    NS_T_RT = 21;          // Router.

+    NS_T_NSAP = 22;        // NSAP address.

+    NS_T_NSAP_PTR = 23;    // Reverse NSAP lookup (deprecated).

+    NS_T_SIG = 24;         // Security signature.

+    NS_T_KEY = 25;         // Security key.

+    NS_T_PX = 26;          // X.400 mail mapping.

+    NS_T_GPOS = 27;        // Geographical position (withdrawn).

+    NS_T_AAAA = 28;        // IPv6 Address.

+    NS_T_LOC = 29;         // Location Information.

+    NS_T_NXT = 30;         // Next domain (security).

+    NS_T_EID = 31;         // Endpoint identifier.

+    NS_T_NIMLOC = 32;      // Nimrod Locator.

+    NS_T_SRV = 33;         // Server Selection.

+    NS_T_ATMA = 34;        // ATM Address

+    NS_T_NAPTR = 35;       // Naming Authority PoinTeR

+    NS_T_KX = 36;          // Key Exchange

+    NS_T_CERT = 37;        // Certification record

+    NS_T_A6 = 38;          // IPv6 address (experimental)

+    NS_T_DNAME = 39;       // Non-terminal DNAME

+    NS_T_SINK = 40;        // Kitchen sink (experimentatl)

+    NS_T_OPT = 41;         // EDNS0 option (meta-RR)

+    NS_T_APL = 42;         // Address prefix list (RFC 3123)

+    NS_T_DS = 43;          // Delegation Signer

+    NS_T_SSHFP = 44;       // SSH Fingerprint

+    NS_T_IPSECKEY = 45;    // IPSEC Key

+    NS_T_RRSIG = 46;       // RRset Signature

+    NS_T_NSEC = 47;        // Negative security

+    NS_T_DNSKEY = 48;      // DNS Key

+    NS_T_DHCID = 49;       // Dynamic host configuratin identifier

+    NS_T_NSEC3 = 50;       // Negative security type 3

+    NS_T_NSEC3PARAM = 51;  // Negative security type 3 parameters

+    NS_T_HIP = 55;         // Host Identity Protocol

+    NS_T_SPF = 99;         // Sender Policy Framework

+    NS_T_TKEY = 249;       // Transaction key

+    NS_T_TSIG = 250;       // Transaction signature.

+    NS_T_IXFR = 251;       // Incremental zone transfer.

+    NS_T_AXFR = 252;       // Transfer zone of authority.

+    NS_T_MAILB = 253;      // Transfer mailbox records.

+    NS_T_MAILA = 254;      // Transfer mail agent records.

+    NS_T_ANY = 255;        // Wildcard match.

+    NS_T_ZXFR = 256;       // BIND-specific, nonstandard.

+    NS_T_DLV = 32769;      // DNSSEC look-aside validatation.

+    NS_T_MAX = 65536;

+}

+

+enum IpVersion {

+    IV_UNKNOWN = 0;

+    IV_IPV4 = 1;

+    IV_IPV6 = 2;

+}

+

+enum TransportType {

+    TT_UNKNOWN = 0;

+    TT_UDP = 1;

+    TT_TCP = 2;

+    TT_DOT = 3;

+}

+

+enum PrivateDnsModes {

+    PDM_UNKNOWN = 0;

+    PDM_OFF = 1;

+    PDM_OPPORTUNISTIC = 2;

+    PDM_STRICT = 3;

+}

+

+enum Transport {

+    // Indicates this network uses a Cellular transport.

+    TRANSPORT_DEFAULT = 0;  // TRANSPORT_CELLULAR

+    // Indicates this network uses a Wi-Fi transport.

+    TRANSPORT_WIFI = 1;

+    // Indicates this network uses a Bluetooth transport.

+    TRANSPORT_BLUETOOTH = 2;

+    // Indicates this network uses an Ethernet transport.

+    TRANSPORT_ETHERNET = 3;

+    // Indicates this network uses a VPN transport.

+    TRANSPORT_VPN = 4;

+    // Indicates this network uses a Wi-Fi Aware transport.

+    TRANSPORT_WIFI_AWARE = 5;

+    // Indicates this network uses a LoWPAN transport.

+    TRANSPORT_LOWPAN = 6;

+}

+

+enum CacheStatus{

+    // the cache can't handle that kind of queries.

+    // or the answer buffer is too small.

+    CS_UNSUPPORTED = 0;

+    // the cache doesn't know about this query.

+    CS_NOTFOUND = 1;

+    // the cache found the answer.

+    CS_FOUND = 2;

+    // Don't do anything on cache.

+    CS_SKIP = 3;

+}

+

+message DnsQueryEvent {

+    optional android.stats.dnsresolver.NsRcode rcode = 1;

+

+    optional android.stats.dnsresolver.NsType type = 2;

+

+    optional android.stats.dnsresolver.CacheStatus cache_hit = 3;

+

+    optional android.stats.dnsresolver.IpVersion ip_version = 4;

+

+    optional android.stats.dnsresolver.TransportType transport = 5;

+

+    // Number of DNS query retry times

+    optional int32 retry_times = 6;

+

+    // Ordinal number of name server.

+    optional int32 dns_server_count = 7;

+

+    // Used only by TCP and DOT. True for new connections.

+    optional bool connected = 8;

+

+    optional int32 latency_micros = 9;

+}

+

+message DnsQueryEvents {

+    repeated DnsQueryEvent dns_query_event = 1;

+}

diff --git a/core/proto/android/stats/mediametrics/mediametrics.proto b/core/proto/android/stats/mediametrics/mediametrics.proto
new file mode 100644
index 0000000..34ed90a
--- /dev/null
+++ b/core/proto/android/stats/mediametrics/mediametrics.proto
@@ -0,0 +1,234 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+syntax = "proto2";
+
+package android.stats.mediametrics;
+
+/**
+ * Track how we arbitrate between microphone/input requests.
+ * Logged from
+ *   frameworks/av/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp
+ *   frameworks/av/services/mediaanalytics/statsd_audiopolicy.cpp
+ * Next Tag: 10
+ */
+message AudioPolicyData {
+    optional int32 status = 1;
+    optional string request_source = 2;
+    optional string request_package = 3;
+    optional int32 request_session = 4;
+    optional string request_device = 5;
+    optional string active_source = 6;
+    optional string active_package = 7;
+    optional int32 active_session = 8;
+    optional string active_device = 9;
+}
+
+/**
+ * Track properties of audio recording
+ * Logged from
+ *   frameworks/av/media/libaudioclient/AudioRecord.cpp
+ *   frameworks/av/services/mediaanalytics/statsd_audiorecord.cpp
+ * Next Tag: 16
+ */
+message AudioRecordData {
+    optional string encoding = 1;
+    optional string source = 2;
+    optional int32 latency = 3;
+    optional int32 samplerate = 4;
+    optional int32 channels = 5;
+    optional int64 created_millis = 6;
+    optional int64 duration_millis = 7;
+    optional int32 count = 8;
+    optional int32 error_code = 9;
+    optional string error_function = 10;
+    optional int32 port_id = 11;
+    optional int32 frame_count = 12;
+    optional string attributes = 13;
+    optional int64 channel_mask = 14;
+    optional int64 start_count = 15;
+
+}
+
+/**
+ * Track audio thread performance data
+ * Logged from
+ *   frameworks/av/media/libnblog/ReportPerformance.cpp
+ *   frameworks/av/services/mediaanalytics/statsd_audiothread.cpp
+ * Next Tag: 28
+ */
+message AudioThreadData {
+    optional string type = 1;
+    optional int32 framecount = 2;
+    optional int32 samplerate = 3;
+    optional string work_millis_hist = 4;
+    optional string latency_millis_hist = 5;
+    optional string warmup_millis_hist = 6;
+    optional int64 underruns = 7;
+    optional int64 overruns = 8;
+    optional int64 active_millis = 9;
+    optional int64 duration_millis = 10;
+
+    optional int32 id = 11;
+    optional int32 port_id = 12;
+    optional int32 sample_rate = 13;
+    optional int64 channel_mask = 14;
+    optional string encoding = 15;
+    optional int32 frame_count = 16;
+    optional string output_device = 17;
+    optional string input_device = 18;
+    optional double io_jitter_mean_millis = 19;
+    optional double io_jitter_stddev_millis = 20;
+    optional double process_time_mean_millis = 21;
+    optional double process_time_stddev_millis = 22;
+    optional double timestamp_jitter_mean_millis = 23;
+    optional double timestamp_jitter_stddev_millis = 24;
+    optional double latency_mean_millis = 25;
+    optional double latency_stddev_millis = 26;
+
+}
+
+/**
+ * Track audio track playback data
+ * Logged from
+ *   frameworks/av/media/libaudioclient/AudioTrack.cpp
+ *   frameworks/av/services/mediaanalytics/statsd_audiotrack.cpp
+ * Next Tag: 12
+ */
+message AudioTrackData {
+    optional string stream_type = 1;
+    optional string content_type = 2;
+    optional string track_usage = 3;
+    optional int32 sample_rate = 4;
+    optional int64 channel_mask = 5;
+
+    optional int32 underrun_frames = 6;
+    optional int32 startup_glitch = 7;
+
+    optional int32 port_id = 8;
+    optional string encoding = 9;
+    optional int32 frame_count = 10;
+    optional string attributes = 11;
+
+
+}
+
+/**
+ * Track Media Codec usage
+ * Logged from:
+ *   frameworks/av/media/libstagefright/MediaCodec.cpp
+ *   frameworks/av/services/mediaanalytics/statsd_codec.cpp
+ * Next Tag: 21
+ */
+message CodecData {
+    optional string codec = 1;
+    optional string mime = 2;
+    optional string mode = 3;
+    optional int32 encoder = 4;
+    optional int32 secure = 5;
+    optional int32 width = 6;
+    optional int32 height = 7;
+    optional int32 rotation = 8;
+    optional int32 crypto = 9;
+    optional int32 profile = 10;
+    optional int32 level = 11;
+    optional int32 max_width = 12;
+    optional int32 max_height = 13;
+    optional int32 error_code = 14;
+    optional string error_state = 15;
+    optional int64 latency_max = 16;
+    optional int64 latency_min = 17;
+    optional int64 latency_avg = 18;
+    optional int64 latency_count = 19;
+    optional int64 latency_unknown = 20;
+}
+
+/**
+ * Track Media Extractor (pulling video/audio streams out of containers) usage
+ * Logged from:
+ *   frameworks/av/media/libstagefright/RemoteMediaExtractor.cpp
+ *   frameworks/av/services/mediaanalytics/statsd_extractor.cpp
+ * Next Tag: 4
+ */
+message ExtractorData {
+    optional string format = 1;
+    optional string mime = 2;
+    optional int32 tracks = 3;
+}
+
+/**
+ * Track Media Player usage
+ * this handles both nuplayer and nuplayer2
+ * Logged from:
+ *   frameworks/av/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp
+ *   frameworks/av/media/libmediaplayer2/nuplayer2/NuPlayer2Driver.cpp
+ *   frameworks/av/services/mediaanalytics/statsd_nuplayer.cpp
+ * Next Tag: 21
+ */
+message NuPlayerData {
+    optional string whichPlayer = 1;
+
+    optional string video_mime = 2;
+    optional string video_codec = 3;
+    optional int32 width = 4;
+    optional int32 height = 5;
+    optional int64 frames = 6;
+    optional int64 frames_dropped = 7;
+    optional double framerate = 8;
+    optional string audio_mime = 9;
+    optional string audio_codec = 10;
+    optional int64 duration_millis = 11;
+    optional int64 playing_millis = 12;
+    optional int32 error = 13;
+    optional int32 error_code = 14;
+    optional string error_state = 15;
+    optional string data_source_type = 16;
+    optional int64 rebuffering_millis = 17;
+    optional int32 rebuffers = 18;
+    optional int32 rebuffer_at_exit = 19;
+    optional int64 frames_dropped_startup = 20;
+}
+
+/**
+ * Track information about recordings (e.g. camcorder)
+ * Logged from
+ *   frameworks/av/media/libmediaplayerservice/StagefrightRecorder.cpp
+ *   frameworks/av/services/mediaanalytics/if_statsd.cpp
+ * Next Tag: 22
+ */
+message RecorderData {
+    optional string audio_mime = 1;
+    optional string video_mime = 2;
+    optional int32 video_profile = 3;
+    optional int32 video_level = 4;
+    optional int32 width = 5;
+    optional int32 height = 6;
+    optional int32 rotation = 7;
+    optional int32 framerate = 8;
+    optional int32 capture_fps = 9;
+    optional double capture_fps_enable = 10;
+    optional int64 duration_millis = 11;
+    optional int64 paused_millis = 12;
+    optional int32 paused_count = 13;
+    optional int32 audio_bitrate = 14;
+    optional int32 audio_channels = 15;
+    optional int32 audio_samplerate = 16;
+    optional int32 movie_timescale = 17;
+    optional int32 audio_timescale = 18;
+    optional int32 video_timescale = 19;
+    optional int32 video_bitrate = 20;
+    optional int32 iframe_interval = 21;
+}
diff --git a/core/res/res/layout/chooser_az_label_row.xml b/core/res/res/layout/chooser_az_label_row.xml
new file mode 100644
index 0000000..1b733fc
--- /dev/null
+++ b/core/res/res/layout/chooser_az_label_row.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2019 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License
+  -->
+
+<!-- Separator applied as background -->
+<TextView xmlns:android="http://schemas.android.com/apk/res/android"
+          android:text="@string/chooser_all_apps_button_label"
+          android:contentDescription="@string/chooser_all_apps_button_label"
+          android:background="@drawable/chooser_row_layer_list"
+          android:textAppearance="?attr/textAppearanceSmall"
+          android:textColor="?attr/textColorSecondary"
+          android:textSize="14sp"
+          android:singleLine="true"
+          android:paddingTop="16dp"
+          android:layout_width="match_parent"
+          android:layout_height="wrap_content"
+          android:gravity="center"/>
+
diff --git a/core/res/res/layout/chooser_grid.xml b/core/res/res/layout/chooser_grid.xml
index 8727f4c..17b5f50 100644
--- a/core/res/res/layout/chooser_grid.xml
+++ b/core/res/res/layout/chooser_grid.xml
@@ -37,8 +37,8 @@
             android:layout_width="24dp"
             android:layout_height="4dp"
             android:src="@drawable/ic_drag_handle"
-            android:clickable="true"
             android:layout_marginTop="@dimen/chooser_edge_margin_thin"
+            android:layout_marginBottom="@dimen/chooser_edge_margin_thin"
             android:tint="@color/lighter_gray"
             android:layout_centerHorizontal="true"
             android:layout_alignParentTop="true" />
@@ -46,11 +46,8 @@
         <TextView android:id="@+id/title"
                   android:layout_height="wrap_content"
                   android:layout_width="wrap_content"
-                  android:textAppearance="?attr/textAppearanceMedium"
-                  android:textSize="20sp"
-                  android:textColor="?attr/textColorPrimary"
+                  android:textAppearance="@style/TextAppearance.DeviceDefault.WindowTitle"
                   android:gravity="center"
-                  android:paddingTop="@dimen/chooser_edge_margin_thin"
                   android:paddingBottom="@dimen/chooser_view_spacing"
                   android:paddingLeft="24dp"
                   android:paddingRight="24dp"
diff --git a/core/res/res/layout/chooser_grid_preview_text.xml b/core/res/res/layout/chooser_grid_preview_text.xml
index f3ca0af..e889e85 100644
--- a/core/res/res/layout/chooser_grid_preview_text.xml
+++ b/core/res/res/layout/chooser_grid_preview_text.xml
@@ -44,6 +44,7 @@
         android:layout_toStartOf="@id/copy_button"
         android:layout_centerVertical="true"
         android:ellipsize="end"
+        android:fontFamily="@android:string/config_headlineFontFamily"
         android:textColor="?android:attr/textColorPrimary"
         android:maxLines="2"/>
 
@@ -112,8 +113,8 @@
         android:layout_gravity="center_vertical"
         android:ellipsize="end"
         android:maxLines="2"
-        android:textSize="20sp"
-        android:textColor="?android:attr/textColorPrimary"/>
+        android:textAppearance="@style/TextAppearance.DeviceDefault.WindowTitle"
+        android:fontFamily="@android:string/config_headlineFontFamily"/>
   </LinearLayout>
 </LinearLayout>
 
diff --git a/core/res/res/layout/chooser_profile_row.xml b/core/res/res/layout/chooser_profile_row.xml
index 1a24a07..fe4f949 100644
--- a/core/res/res/layout/chooser_profile_row.xml
+++ b/core/res/res/layout/chooser_profile_row.xml
@@ -20,7 +20,7 @@
               android:layout_width="match_parent"
               android:layout_height="wrap_content"
               android:gravity="center">
-  <TextView
+  <Button
       android:id="@+id/profile_button"
       android:layout_width="wrap_content"
       android:layout_height="48dp"
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index ef834aa..75fd3a0 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -5373,4 +5373,7 @@
     <!-- ChooserActivity - No direct share targets are available. [CHAR LIMIT=NONE] -->
     <string name="chooser_no_direct_share_targets">Direct share not available</string>
 
+    <!-- ChooserActivity - Alphabetically sorted apps list label. [CHAR LIMIT=NONE] -->
+    <string name="chooser_all_apps_button_label">Apps list</string>
+
 </resources>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index bb560d3..fbe340e 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -3771,4 +3771,6 @@
   <java-symbol type="color" name="chooser_gradient_highlight" />
   <java-symbol type="drawable" name="chooser_direct_share_label_placeholder" />
   <java-symbol type="dimen" name="chooser_direct_share_label_placeholder_max_width" />
+  <java-symbol type="layout" name="chooser_az_label_row" />
+  <java-symbol type="string" name="chooser_all_apps_button_label" />
 </resources>
diff --git a/core/tests/coretests/src/android/provider/SettingsBackupTest.java b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
index f4d3c81..61fb811 100644
--- a/core/tests/coretests/src/android/provider/SettingsBackupTest.java
+++ b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
@@ -294,6 +294,8 @@
                     Settings.Global.INTENT_FIREWALL_UPDATE_CONTENT_URL,
                     Settings.Global.INTENT_FIREWALL_UPDATE_METADATA_URL,
                     Settings.Global.JOB_SCHEDULER_CONSTANTS,
+                    Settings.Global.JOB_SCHEDULER_QUOTA_CONTROLLER_CONSTANTS,
+                    Settings.Global.JOB_SCHEDULER_TIME_CONTROLLER_CONSTANTS,
                     Settings.Global.KEEP_PROFILE_IN_BACKGROUND,
                     Settings.Global.KERNEL_CPU_THREAD_READER,
                     Settings.Global.LANG_ID_UPDATE_CONTENT_URL,
diff --git a/core/tests/coretests/src/com/android/internal/statusbar/RegisterStatusBarResultTest.java b/core/tests/coretests/src/com/android/internal/statusbar/RegisterStatusBarResultTest.java
index 3cb1e18..b93c3a7 100644
--- a/core/tests/coretests/src/com/android/internal/statusbar/RegisterStatusBarResultTest.java
+++ b/core/tests/coretests/src/com/android/internal/statusbar/RegisterStatusBarResultTest.java
@@ -57,7 +57,8 @@
                 0x80 /* dockedStackSysUiVisibility */,
                 new Binder() /* imeToken */,
                 new Rect(0x100, 0x200, 0x400, 0x800) /* fullscreenStackBounds */,
-                new Rect(0x1000, 0x2000, 0x4000, 0x8000) /* dockedStackBounds */);
+                new Rect(0x1000, 0x2000, 0x4000, 0x8000) /* dockedStackBounds */,
+                true /* navbarColorManagedByIme */);
 
         final RegisterStatusBarResult copy = clone(original);
 
@@ -80,6 +81,7 @@
         assertThat(copy.mImeToken).isSameAs(original.mImeToken);
         assertThat(copy.mFullscreenStackBounds).isEqualTo(original.mFullscreenStackBounds);
         assertThat(copy.mDockedStackBounds).isEqualTo(original.mDockedStackBounds);
+        assertThat(copy.mNavbarColorManagedByIme).isEqualTo(original.mNavbarColorManagedByIme);
     }
 
     private RegisterStatusBarResult clone(RegisterStatusBarResult original) {
diff --git a/libs/hwui/hwui/Canvas.cpp b/libs/hwui/hwui/Canvas.cpp
index 68aa737..a48c860 100644
--- a/libs/hwui/hwui/Canvas.cpp
+++ b/libs/hwui/hwui/Canvas.cpp
@@ -154,6 +154,11 @@
     // minikin may modify the original paint
     Paint paint(origPaint);
 
+    // interpret 'linear metrics' flag as 'linear', forcing no-hinting when drawing
+    if (paint.getSkFont().isLinearMetrics()) {
+        paint.getSkFont().setHinting(SkFontHinting::kNone);
+    }
+
     minikin::Layout layout = MinikinUtils::doLayout(&paint, bidiFlags, typeface, text, textSize,
                                                     start, count, contextStart, contextCount, mt);
 
@@ -234,23 +239,30 @@
 };
 
 void Canvas::drawTextOnPath(const uint16_t* text, int count, minikin::Bidi bidiFlags,
-                            const SkPath& path, float hOffset, float vOffset, const Paint& paint,
-                            const Typeface* typeface) {
-    Paint paintCopy(paint);
+                            const SkPath& path, float hOffset, float vOffset,
+                            const Paint& origPaint, const Typeface* typeface) {
+    // minikin may modify the original paint
+    Paint paint(origPaint);
+
+    // interpret 'linear metrics' flag as 'linear', forcing no-hinting when drawing
+    if (paint.getSkFont().isLinearMetrics()) {
+        paint.getSkFont().setHinting(SkFontHinting::kNone);
+    }
+
     minikin::Layout layout =
-            MinikinUtils::doLayout(&paintCopy, bidiFlags, typeface, text, count,  // text buffer
-                                   0, count,                                      // draw range
-                                   0, count,                                      // context range
+            MinikinUtils::doLayout(&paint, bidiFlags, typeface, text, count,  // text buffer
+                                   0, count,                                  // draw range
+                                   0, count,                                  // context range
                                    nullptr);
-    hOffset += MinikinUtils::hOffsetForTextAlign(&paintCopy, layout, path);
+    hOffset += MinikinUtils::hOffsetForTextAlign(&paint, layout, path);
 
     // Set align to left for drawing, as we don't want individual
     // glyphs centered or right-aligned; the offset above takes
     // care of all alignment.
-    paintCopy.setTextAlign(Paint::kLeft_Align);
+    paint.setTextAlign(Paint::kLeft_Align);
 
-    DrawTextOnPathFunctor f(layout, this, hOffset, vOffset, paintCopy, path);
-    MinikinUtils::forFontRun(layout, &paintCopy, f);
+    DrawTextOnPathFunctor f(layout, this, hOffset, vOffset, paint, path);
+    MinikinUtils::forFontRun(layout, &paint, f);
 }
 
 int Canvas::sApiLevel = 1;
diff --git a/media/java/android/media/MediaCodecInfo.java b/media/java/android/media/MediaCodecInfo.java
index a56e7f5..f304f7c 100644
--- a/media/java/android/media/MediaCodecInfo.java
+++ b/media/java/android/media/MediaCodecInfo.java
@@ -24,6 +24,7 @@
 import android.annotation.TestApi;
 import android.annotation.UnsupportedAppUsage;
 import android.os.Build;
+import android.os.SystemProperties;
 import android.util.Log;
 import android.util.Pair;
 import android.util.Range;
@@ -1105,7 +1106,11 @@
             mBitrateRange = Range.create(0, Integer.MAX_VALUE);
             mMaxInputChannelCount = MAX_INPUT_CHANNEL_COUNT;
             // mBitrateRange = Range.create(1, 320000);
-            mSampleRateRanges = new Range[] { Range.create(8000, 96000) };
+            final int minSampleRate = SystemProperties.
+                getInt("ro.mediacodec.min_sample_rate", 7350);
+            final int maxSampleRate = SystemProperties.
+                getInt("ro.mediacodec.max_sample_rate", 192000);
+            mSampleRateRanges = new Range[] { Range.create(minSampleRate, maxSampleRate) };
             mSampleRates = null;
         }
 
@@ -1677,6 +1682,13 @@
                 return "PerformancePoint(" + info + ")";
             }
 
+            @Override
+            public int hashCode() {
+                // only max frame rate must equal between performance points that equal to one
+                // another
+                return mMaxFrameRate;
+            }
+
             /**
              * Create a detailed performance point with custom max frame rate and macroblock size.
              *
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/Camera2SurfaceViewTestCase.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/Camera2SurfaceViewTestCase.java
index a8b782e8..dc43b9a 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/Camera2SurfaceViewTestCase.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/Camera2SurfaceViewTestCase.java
@@ -16,15 +16,17 @@
 
 package com.android.mediaframeworktest;
 
-import com.android.ex.camera2.blocking.BlockingSessionCallback;
-import com.android.ex.camera2.blocking.BlockingStateCallback;
-import com.android.ex.camera2.exceptions.TimeoutRuntimeException;
-import com.android.mediaframeworktest.helpers.CameraErrorCollector;
-import com.android.mediaframeworktest.helpers.CameraTestResultPrinter;
-import com.android.mediaframeworktest.helpers.CameraTestUtils;
-import com.android.mediaframeworktest.helpers.CameraTestUtils.SimpleCaptureCallback;
-import com.android.mediaframeworktest.helpers.StaticMetadata;
-import com.android.mediaframeworktest.helpers.StaticMetadata.CheckLevel;
+import static com.android.ex.camera2.blocking.BlockingStateCallback.STATE_CLOSED;
+import static com.android.mediaframeworktest.helpers.CameraTestUtils.CAMERA_CLOSE_TIMEOUT_MS;
+import static com.android.mediaframeworktest.helpers.CameraTestUtils.MAX_READER_IMAGES;
+import static com.android.mediaframeworktest.helpers.CameraTestUtils.PREVIEW_SIZE_BOUND;
+import static com.android.mediaframeworktest.helpers.CameraTestUtils.configureCameraSession;
+import static com.android.mediaframeworktest.helpers.CameraTestUtils.getPreviewSizeBound;
+import static com.android.mediaframeworktest.helpers.CameraTestUtils.getSortedSizesForFormat;
+import static com.android.mediaframeworktest.helpers.CameraTestUtils.getSupportedPreviewSizes;
+import static com.android.mediaframeworktest.helpers.CameraTestUtils.getSupportedStillSizes;
+import static com.android.mediaframeworktest.helpers.CameraTestUtils.getSupportedVideoSizes;
+import static com.android.mediaframeworktest.helpers.CameraTestUtils.makeImageReader;
 
 import android.content.Context;
 import android.graphics.ImageFormat;
@@ -39,14 +41,11 @@
 import android.hardware.camera2.CaptureResult;
 import android.hardware.camera2.params.StreamConfigurationMap;
 import android.media.ImageReader;
-import android.graphics.SurfaceTexture;
 import android.os.Bundle;
-import android.os.Environment;
 import android.os.Handler;
 import android.os.HandlerThread;
 import android.os.Looper;
 import android.test.ActivityInstrumentationTestCase2;
-import android.test.InstrumentationTestRunner;
 import android.util.Log;
 import android.util.Range;
 import android.util.Size;
@@ -54,24 +53,24 @@
 import android.view.SurfaceHolder;
 import android.view.WindowManager;
 
+import androidx.test.InstrumentationRegistry;
+
+import com.android.ex.camera2.blocking.BlockingSessionCallback;
+import com.android.ex.camera2.blocking.BlockingStateCallback;
+import com.android.ex.camera2.exceptions.TimeoutRuntimeException;
+import com.android.mediaframeworktest.helpers.CameraErrorCollector;
+import com.android.mediaframeworktest.helpers.CameraTestResultPrinter;
+import com.android.mediaframeworktest.helpers.CameraTestUtils;
+import com.android.mediaframeworktest.helpers.CameraTestUtils.SimpleCaptureCallback;
+import com.android.mediaframeworktest.helpers.StaticMetadata;
+import com.android.mediaframeworktest.helpers.StaticMetadata.CheckLevel;
+
 import java.text.NumberFormat;
 import java.text.ParseException;
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.List;
 
-import static com.android.ex.camera2.blocking.BlockingStateCallback.STATE_CLOSED;
-import static com.android.mediaframeworktest.helpers.CameraTestUtils.CAMERA_CLOSE_TIMEOUT_MS;
-import static com.android.mediaframeworktest.helpers.CameraTestUtils.MAX_READER_IMAGES;
-import static com.android.mediaframeworktest.helpers.CameraTestUtils.PREVIEW_SIZE_BOUND;
-import static com.android.mediaframeworktest.helpers.CameraTestUtils.configureCameraSession;
-import static com.android.mediaframeworktest.helpers.CameraTestUtils.getPreviewSizeBound;
-import static com.android.mediaframeworktest.helpers.CameraTestUtils.getSupportedPreviewSizes;
-import static com.android.mediaframeworktest.helpers.CameraTestUtils.getSupportedStillSizes;
-import static com.android.mediaframeworktest.helpers.CameraTestUtils.getSupportedVideoSizes;
-import static com.android.mediaframeworktest.helpers.CameraTestUtils.getSortedSizesForFormat;
-import static com.android.mediaframeworktest.helpers.CameraTestUtils.makeImageReader;
-
 /**
  * Camera2 Preview test case base class by using SurfaceView as rendering target.
  *
@@ -98,8 +97,9 @@
     protected static final String ARG_KEY_RESULT_TO_FILE = "resultToFile";
 
     // TODO: Use internal storage for this to make sure the file is only visible to test.
-    protected static final String DEBUG_FILE_NAME_BASE =
-            Environment.getExternalStorageDirectory().getPath();
+    protected static final String DEBUG_FILE_NAME_BASE = InstrumentationRegistry
+            .getInstrumentation().getTargetContext()
+            .getExternalFilesDir(null).getPath();
     protected static final int WAIT_FOR_RESULT_TIMEOUT_MS = 3000;
     protected static final float FRAME_DURATION_ERROR_MARGIN = 0.005f; // 0.5 percent error margin.
     protected static final int NUM_RESULTS_WAIT_TIMEOUT = 100;
@@ -779,7 +779,7 @@
     //--------------------------------------------------------------------------------
 
     protected Bundle getArguments() {
-        return ((InstrumentationTestRunner)getInstrumentation()).getArguments();
+        return InstrumentationRegistry.getArguments();
     }
 
     protected <E extends Number> Number getArgumentsAsNumber(String key, E defaultValue) {
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/helpers/CameraTestResultPrinter.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/helpers/CameraTestResultPrinter.java
index ed2eebf..a1fc6c2 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/helpers/CameraTestResultPrinter.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/helpers/CameraTestResultPrinter.java
@@ -18,9 +18,10 @@
 
 import android.app.Instrumentation;
 import android.os.Bundle;
-import android.os.Environment;
 import android.util.Log;
 
+import androidx.test.InstrumentationRegistry;
+
 import java.io.BufferedWriter;
 import java.io.File;
 import java.io.FileWriter;
@@ -28,8 +29,9 @@
 public class CameraTestResultPrinter {
 
     private static final String TAG = CameraTestResultPrinter.class.getSimpleName();
-    private static final String RESULT_DIR = Environment.getExternalStorageDirectory() +
-            "/camera-out/";
+    private static final String RESULT_DIR = InstrumentationRegistry
+            .getInstrumentation().getTargetContext()
+            .getExternalFilesDir(null).getPath() + "/camera-out/";
     private static final String RESULT_FILE_FORMAT = "fwk-stress_camera_%s.txt";
     private static final String RESULT_SWAP_FILE = "fwk-stress.swp";
     private static final String KEY_NUM_ATTEMPTS = "numAttempts";   // Total number of iterations
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/stress/Camera2CaptureRequestTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/stress/Camera2CaptureRequestTest.java
index ebfd92e..31b7967 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/stress/Camera2CaptureRequestTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/stress/Camera2CaptureRequestTest.java
@@ -16,8 +16,13 @@
 
 package com.android.mediaframeworktest.stress;
 
-import com.android.mediaframeworktest.Camera2SurfaceViewTestCase;
-import com.android.mediaframeworktest.helpers.CameraTestUtils.SimpleCaptureCallback;
+import static android.hardware.camera2.CameraCharacteristics.CONTROL_AE_MODE_OFF;
+import static android.hardware.camera2.CameraCharacteristics.CONTROL_AE_MODE_ON;
+import static android.hardware.camera2.CameraCharacteristics.CONTROL_AE_MODE_ON_ALWAYS_FLASH;
+import static android.hardware.camera2.CameraCharacteristics.CONTROL_AE_MODE_ON_AUTO_FLASH;
+import static android.hardware.camera2.CameraCharacteristics.CONTROL_AE_MODE_ON_AUTO_FLASH_REDEYE;
+
+import static com.android.mediaframeworktest.helpers.CameraTestUtils.getValueNotNull;
 
 import android.hardware.camera2.CameraCharacteristics;
 import android.hardware.camera2.CameraDevice;
@@ -26,14 +31,10 @@
 import android.util.Log;
 import android.util.Size;
 
-import java.util.Arrays;
+import com.android.mediaframeworktest.Camera2SurfaceViewTestCase;
+import com.android.mediaframeworktest.helpers.CameraTestUtils.SimpleCaptureCallback;
 
-import static android.hardware.camera2.CameraCharacteristics.CONTROL_AE_MODE_OFF;
-import static android.hardware.camera2.CameraCharacteristics.CONTROL_AE_MODE_ON;
-import static android.hardware.camera2.CameraCharacteristics.CONTROL_AE_MODE_ON_ALWAYS_FLASH;
-import static android.hardware.camera2.CameraCharacteristics.CONTROL_AE_MODE_ON_AUTO_FLASH;
-import static android.hardware.camera2.CameraCharacteristics.CONTROL_AE_MODE_ON_AUTO_FLASH_REDEYE;
-import static com.android.mediaframeworktest.helpers.CameraTestUtils.getValueNotNull;
+import java.util.Arrays;
 
 /**
  * <p>
@@ -49,7 +50,7 @@
  *    -e iterations 10 \
  *    -e waitIntervalMs 1000 \
  *    -e resultToFile false \
- *    -r -w com.android.mediaframeworktest/.Camera2InstrumentationTestRunner
+ *    -r -w com.android.mediaframeworktest/androidx.test.runner.AndroidJUnitRunner
  */
 public class Camera2CaptureRequestTest extends Camera2SurfaceViewTestCase {
     private static final String TAG = "CaptureRequestTest";
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/stress/Camera2RecordingTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/stress/Camera2RecordingTest.java
index 8f27fef..6a4db57 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/stress/Camera2RecordingTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/stress/Camera2RecordingTest.java
@@ -16,11 +16,17 @@
 
 package com.android.mediaframeworktest.stress;
 
-import com.android.ex.camera2.blocking.BlockingSessionCallback;
-import com.android.mediaframeworktest.Camera2SurfaceViewTestCase;
-import com.android.mediaframeworktest.helpers.CameraTestUtils;
-
-import junit.framework.AssertionFailedError;
+import static com.android.ex.camera2.blocking.BlockingSessionCallback.SESSION_CLOSED;
+import static com.android.mediaframeworktest.helpers.CameraTestUtils.CAPTURE_IMAGE_TIMEOUT_MS;
+import static com.android.mediaframeworktest.helpers.CameraTestUtils.SESSION_CLOSE_TIMEOUT_MS;
+import static com.android.mediaframeworktest.helpers.CameraTestUtils.SIZE_BOUND_1080P;
+import static com.android.mediaframeworktest.helpers.CameraTestUtils.SIZE_BOUND_2160P;
+import static com.android.mediaframeworktest.helpers.CameraTestUtils.SimpleCaptureCallback;
+import static com.android.mediaframeworktest.helpers.CameraTestUtils.SimpleImageReaderListener;
+import static com.android.mediaframeworktest.helpers.CameraTestUtils.configureCameraSession;
+import static com.android.mediaframeworktest.helpers.CameraTestUtils.configureCameraSessionWithParameters;
+import static com.android.mediaframeworktest.helpers.CameraTestUtils.getSupportedVideoSizes;
+import static com.android.mediaframeworktest.helpers.CameraTestUtils.getValueNotNull;
 
 import android.graphics.ImageFormat;
 import android.hardware.camera2.CameraCaptureSession;
@@ -36,7 +42,6 @@
 import android.media.MediaExtractor;
 import android.media.MediaFormat;
 import android.media.MediaRecorder;
-import android.os.Environment;
 import android.os.SystemClock;
 import android.test.suitebuilder.annotation.LargeTest;
 import android.util.Log;
@@ -44,24 +49,20 @@
 import android.util.Size;
 import android.view.Surface;
 
+import androidx.test.InstrumentationRegistry;
+
+import com.android.ex.camera2.blocking.BlockingSessionCallback;
+import com.android.mediaframeworktest.Camera2SurfaceViewTestCase;
+import com.android.mediaframeworktest.helpers.CameraTestUtils;
+
+import junit.framework.AssertionFailedError;
+
 import java.io.File;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.HashMap;
 import java.util.List;
 
-import static com.android.ex.camera2.blocking.BlockingSessionCallback.SESSION_CLOSED;
-import static com.android.mediaframeworktest.helpers.CameraTestUtils.CAPTURE_IMAGE_TIMEOUT_MS;
-import static com.android.mediaframeworktest.helpers.CameraTestUtils.SESSION_CLOSE_TIMEOUT_MS;
-import static com.android.mediaframeworktest.helpers.CameraTestUtils.SIZE_BOUND_1080P;
-import static com.android.mediaframeworktest.helpers.CameraTestUtils.SIZE_BOUND_2160P;
-import static com.android.mediaframeworktest.helpers.CameraTestUtils.SimpleCaptureCallback;
-import static com.android.mediaframeworktest.helpers.CameraTestUtils.SimpleImageReaderListener;
-import static com.android.mediaframeworktest.helpers.CameraTestUtils.configureCameraSession;
-import static com.android.mediaframeworktest.helpers.CameraTestUtils.configureCameraSessionWithParameters;
-import static com.android.mediaframeworktest.helpers.CameraTestUtils.getSupportedVideoSizes;
-import static com.android.mediaframeworktest.helpers.CameraTestUtils.getValueNotNull;
-
 /**
  * CameraDevice video recording use case tests by using MediaRecorder and
  * MediaCodec.
@@ -71,7 +72,7 @@
  *    -e iterations 10 \
  *    -e waitIntervalMs 1000 \
  *    -e resultToFile false \
- *    -r -w com.android.mediaframeworktest/.Camera2InstrumentationTestRunner
+ *    -r -w com.android.mediaframeworktest/androidx.test.runner.AndroidJUnitRunner
  */
 @LargeTest
 public class Camera2RecordingTest extends Camera2SurfaceViewTestCase {
@@ -85,7 +86,9 @@
     private static final int BIT_RATE_MIN = 64000;
     private static final int BIT_RATE_MAX = 40000000;
     private static final int VIDEO_FRAME_RATE = 30;
-    private final String VIDEO_FILE_PATH = Environment.getExternalStorageDirectory().getPath();
+    private static final String VIDEO_FILE_PATH = InstrumentationRegistry
+            .getInstrumentation().getTargetContext()
+            .getExternalFilesDir(null).getPath();
     private static final int[] mCamcorderProfileList = {
             CamcorderProfile.QUALITY_HIGH,
             CamcorderProfile.QUALITY_2160P,
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/stress/Camera2StillCaptureTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/stress/Camera2StillCaptureTest.java
index 812543b..f7373f7 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/stress/Camera2StillCaptureTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/stress/Camera2StillCaptureTest.java
@@ -16,12 +16,15 @@
 
 package com.android.mediaframeworktest.stress;
 
-import com.android.ex.camera2.blocking.BlockingSessionCallback;
-import com.android.ex.camera2.exceptions.TimeoutRuntimeException;
-import com.android.mediaframeworktest.Camera2SurfaceViewTestCase;
-import com.android.mediaframeworktest.helpers.Camera2Focuser;
-import com.android.mediaframeworktest.helpers.CameraTestUtils;
-import com.android.mediaframeworktest.helpers.CameraTestUtils.SimpleCaptureCallback;
+import static com.android.mediaframeworktest.helpers.CameraTestUtils.CAPTURE_IMAGE_TIMEOUT_MS;
+import static com.android.mediaframeworktest.helpers.CameraTestUtils.MAX_READER_IMAGES;
+import static com.android.mediaframeworktest.helpers.CameraTestUtils.SimpleImageReaderListener;
+import static com.android.mediaframeworktest.helpers.CameraTestUtils.basicValidateJpegImage;
+import static com.android.mediaframeworktest.helpers.CameraTestUtils.configureCameraSession;
+import static com.android.mediaframeworktest.helpers.CameraTestUtils.dumpFile;
+import static com.android.mediaframeworktest.helpers.CameraTestUtils.getDataFromImage;
+import static com.android.mediaframeworktest.helpers.CameraTestUtils.getValueNotNull;
+import static com.android.mediaframeworktest.helpers.CameraTestUtils.makeImageReader;
 
 import android.graphics.ImageFormat;
 import android.graphics.Point;
@@ -40,20 +43,17 @@
 import android.util.Size;
 import android.view.Surface;
 
+import com.android.ex.camera2.blocking.BlockingSessionCallback;
+import com.android.ex.camera2.exceptions.TimeoutRuntimeException;
+import com.android.mediaframeworktest.Camera2SurfaceViewTestCase;
+import com.android.mediaframeworktest.helpers.Camera2Focuser;
+import com.android.mediaframeworktest.helpers.CameraTestUtils;
+import com.android.mediaframeworktest.helpers.CameraTestUtils.SimpleCaptureCallback;
+
 import java.io.ByteArrayOutputStream;
 import java.util.ArrayList;
 import java.util.List;
 
-import static com.android.mediaframeworktest.helpers.CameraTestUtils.CAPTURE_IMAGE_TIMEOUT_MS;
-import static com.android.mediaframeworktest.helpers.CameraTestUtils.MAX_READER_IMAGES;
-import static com.android.mediaframeworktest.helpers.CameraTestUtils.SimpleImageReaderListener;
-import static com.android.mediaframeworktest.helpers.CameraTestUtils.basicValidateJpegImage;
-import static com.android.mediaframeworktest.helpers.CameraTestUtils.configureCameraSession;
-import static com.android.mediaframeworktest.helpers.CameraTestUtils.dumpFile;
-import static com.android.mediaframeworktest.helpers.CameraTestUtils.getDataFromImage;
-import static com.android.mediaframeworktest.helpers.CameraTestUtils.getValueNotNull;
-import static com.android.mediaframeworktest.helpers.CameraTestUtils.makeImageReader;
-
 /**
  * <p>Tests for still capture API.</p>
  *
@@ -62,7 +62,7 @@
  *    -e iterations 200 \
  *    -e waitIntervalMs 1000 \
  *    -e resultToFile false \
- *    -r -w com.android.mediaframeworktest/.Camera2InstrumentationTestRunner
+ *    -r -w com.android.mediaframeworktest/androidx.test.runner.AndroidJUnitRunner
  */
 public class Camera2StillCaptureTest extends Camera2SurfaceViewTestCase {
     private static final String TAG = "StillCaptureTest";
diff --git a/packages/ExtServices/src/android/ext/services/notification/AssistantSettings.java b/packages/ExtServices/src/android/ext/services/notification/AssistantSettings.java
index 46288bb..296db46 100644
--- a/packages/ExtServices/src/android/ext/services/notification/AssistantSettings.java
+++ b/packages/ExtServices/src/android/ext/services/notification/AssistantSettings.java
@@ -102,10 +102,10 @@
     }
 
     private void registerDeviceConfigs() {
-        DeviceConfig.addOnPropertyChangedListener(
+        DeviceConfig.addOnPropertiesChangedListener(
                 DeviceConfig.NAMESPACE_SYSTEMUI,
                 this::postToHandler,
-                this::onDeviceConfigPropertyChanged);
+                (properties) -> onDeviceConfigPropertiesChanged(properties.getNamespace()));
 
         // Update the fields in this class from the current state of the device config.
         updateFromDeviceConfigFlags();
@@ -116,10 +116,10 @@
     }
 
     @VisibleForTesting
-    void onDeviceConfigPropertyChanged(String namespace, String name, String value) {
+    void onDeviceConfigPropertiesChanged(String namespace) {
         if (!DeviceConfig.NAMESPACE_SYSTEMUI.equals(namespace)) {
             Log.e(LOG_TAG, "Received update from DeviceConfig for unrelated namespace: "
-                    + namespace + " " + name + "=" + value);
+                    + namespace);
             return;
         }
 
diff --git a/packages/ExtServices/tests/src/android/ext/services/notification/AssistantSettingsTest.java b/packages/ExtServices/tests/src/android/ext/services/notification/AssistantSettingsTest.java
index ad52e2b..5c877de 100644
--- a/packages/ExtServices/tests/src/android/ext/services/notification/AssistantSettingsTest.java
+++ b/packages/ExtServices/tests/src/android/ext/services/notification/AssistantSettingsTest.java
@@ -96,10 +96,7 @@
                 SystemUiDeviceConfigFlags.NAS_GENERATE_REPLIES,
                 "false",
                 false /* makeDefault */));
-        mAssistantSettings.onDeviceConfigPropertyChanged(
-                DeviceConfig.NAMESPACE_SYSTEMUI,
-                SystemUiDeviceConfigFlags.NAS_GENERATE_REPLIES,
-                "false");
+        mAssistantSettings.onDeviceConfigPropertiesChanged(DeviceConfig.NAMESPACE_SYSTEMUI);
 
         assertFalse(mAssistantSettings.mGenerateReplies);
     }
@@ -111,10 +108,7 @@
                 SystemUiDeviceConfigFlags.NAS_GENERATE_REPLIES,
                 "true",
                 false /* makeDefault */));
-        mAssistantSettings.onDeviceConfigPropertyChanged(
-                DeviceConfig.NAMESPACE_SYSTEMUI,
-                SystemUiDeviceConfigFlags.NAS_GENERATE_REPLIES,
-                "true");
+        mAssistantSettings.onDeviceConfigPropertiesChanged(DeviceConfig.NAMESPACE_SYSTEMUI);
 
         assertTrue(mAssistantSettings.mGenerateReplies);
     }
@@ -126,10 +120,7 @@
                 SystemUiDeviceConfigFlags.NAS_GENERATE_REPLIES,
                 "false",
                 false /* makeDefault */));
-        mAssistantSettings.onDeviceConfigPropertyChanged(
-                DeviceConfig.NAMESPACE_SYSTEMUI,
-                SystemUiDeviceConfigFlags.NAS_GENERATE_REPLIES,
-                "false");
+        mAssistantSettings.onDeviceConfigPropertiesChanged(DeviceConfig.NAMESPACE_SYSTEMUI);
 
         assertFalse(mAssistantSettings.mGenerateReplies);
 
@@ -138,10 +129,7 @@
                 SystemUiDeviceConfigFlags.NAS_GENERATE_REPLIES,
                 null,
                 false /* makeDefault */));
-        mAssistantSettings.onDeviceConfigPropertyChanged(
-                DeviceConfig.NAMESPACE_SYSTEMUI,
-                SystemUiDeviceConfigFlags.NAS_GENERATE_REPLIES,
-                null);
+        mAssistantSettings.onDeviceConfigPropertiesChanged(DeviceConfig.NAMESPACE_SYSTEMUI);
 
         // Go back to the default value.
         assertTrue(mAssistantSettings.mGenerateReplies);
@@ -154,10 +142,7 @@
                 SystemUiDeviceConfigFlags.NAS_GENERATE_ACTIONS,
                 "false",
                 false /* makeDefault */));
-        mAssistantSettings.onDeviceConfigPropertyChanged(
-                DeviceConfig.NAMESPACE_SYSTEMUI,
-                SystemUiDeviceConfigFlags.NAS_GENERATE_ACTIONS,
-                "false");
+        mAssistantSettings.onDeviceConfigPropertiesChanged(DeviceConfig.NAMESPACE_SYSTEMUI);
 
         assertFalse(mAssistantSettings.mGenerateActions);
     }
@@ -169,10 +154,7 @@
                 SystemUiDeviceConfigFlags.NAS_GENERATE_ACTIONS,
                 "true",
                 false /* makeDefault */));
-        mAssistantSettings.onDeviceConfigPropertyChanged(
-                DeviceConfig.NAMESPACE_SYSTEMUI,
-                SystemUiDeviceConfigFlags.NAS_GENERATE_ACTIONS,
-                "true");
+        mAssistantSettings.onDeviceConfigPropertiesChanged(DeviceConfig.NAMESPACE_SYSTEMUI);
 
         assertTrue(mAssistantSettings.mGenerateActions);
     }
@@ -184,10 +166,7 @@
                 SystemUiDeviceConfigFlags.NAS_GENERATE_ACTIONS,
                 "false",
                 false /* makeDefault */));
-        mAssistantSettings.onDeviceConfigPropertyChanged(
-                DeviceConfig.NAMESPACE_SYSTEMUI,
-                SystemUiDeviceConfigFlags.NAS_GENERATE_ACTIONS,
-                "false");
+        mAssistantSettings.onDeviceConfigPropertiesChanged(DeviceConfig.NAMESPACE_SYSTEMUI);
 
         assertFalse(mAssistantSettings.mGenerateActions);
 
@@ -196,10 +175,7 @@
                 SystemUiDeviceConfigFlags.NAS_GENERATE_ACTIONS,
                 null,
                 false /* makeDefault */));
-        mAssistantSettings.onDeviceConfigPropertyChanged(
-                DeviceConfig.NAMESPACE_SYSTEMUI,
-                SystemUiDeviceConfigFlags.NAS_GENERATE_ACTIONS,
-                null);
+        mAssistantSettings.onDeviceConfigPropertiesChanged(DeviceConfig.NAMESPACE_SYSTEMUI);
 
         // Go back to the default value.
         assertTrue(mAssistantSettings.mGenerateActions);
@@ -212,10 +188,7 @@
                 SystemUiDeviceConfigFlags.NAS_MAX_MESSAGES_TO_EXTRACT,
                 "10",
                 false /* makeDefault */));
-        mAssistantSettings.onDeviceConfigPropertyChanged(
-                DeviceConfig.NAMESPACE_SYSTEMUI,
-                SystemUiDeviceConfigFlags.NAS_MAX_MESSAGES_TO_EXTRACT,
-                "10");
+        mAssistantSettings.onDeviceConfigPropertiesChanged(DeviceConfig.NAMESPACE_SYSTEMUI);
 
         assertEquals(10, mAssistantSettings.mMaxMessagesToExtract);
     }
@@ -227,20 +200,14 @@
                 SystemUiDeviceConfigFlags.NAS_MAX_SUGGESTIONS,
                 "5",
                 false /* makeDefault */));
-        mAssistantSettings.onDeviceConfigPropertyChanged(
-                DeviceConfig.NAMESPACE_SYSTEMUI,
-                SystemUiDeviceConfigFlags.NAS_MAX_SUGGESTIONS,
-                "5");
+        mAssistantSettings.onDeviceConfigPropertiesChanged(DeviceConfig.NAMESPACE_SYSTEMUI);
 
         assertEquals(5, mAssistantSettings.mMaxSuggestions);
     }
 
     @Test
     public void testMaxSuggestionsEmpty() {
-        mAssistantSettings.onDeviceConfigPropertyChanged(
-                DeviceConfig.NAMESPACE_SYSTEMUI,
-                SystemUiDeviceConfigFlags.NAS_MAX_SUGGESTIONS,
-                "");
+        mAssistantSettings.onDeviceConfigPropertiesChanged(DeviceConfig.NAMESPACE_SYSTEMUI);
 
         assertEquals(DEFAULT_MAX_SUGGESTIONS, mAssistantSettings.mMaxSuggestions);
     }
diff --git a/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/Tile.java b/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/Tile.java
index 0ffd471..05c2f24 100644
--- a/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/Tile.java
+++ b/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/Tile.java
@@ -174,7 +174,7 @@
                             packageManager.getResourcesForApplication(mActivityPackage);
                     title = res.getString(mMetaData.getInt(META_DATA_PREFERENCE_TITLE));
                 } catch (PackageManager.NameNotFoundException | Resources.NotFoundException e) {
-                    Log.d(TAG, "Couldn't find info", e);
+                    Log.w(TAG, "Couldn't find info", e);
                 }
             } else {
                 title = mMetaData.getString(META_DATA_PREFERENCE_TITLE);
@@ -183,26 +183,16 @@
         // Set the preference title to the activity's label if no
         // meta-data is found
         if (title == null) {
-            title = getActivityInfo(context).loadLabel(packageManager);
+            final ActivityInfo activityInfo = getActivityInfo(context);
+            if (activityInfo == null) {
+                return null;
+            }
+            title = activityInfo.loadLabel(packageManager);
         }
         return title;
     }
 
     /**
-     * Returns the raw metadata for summary, this is used for comparing 2 summary text without
-     * loading the real string.
-     */
-    public String getSummaryReference() {
-        if (mSummaryOverride != null) {
-            return mSummaryOverride.toString();
-        }
-        if (mMetaData != null && mMetaData.containsKey(META_DATA_PREFERENCE_SUMMARY)) {
-            return mMetaData.get(META_DATA_PREFERENCE_SUMMARY).toString();
-        }
-        return null;
-    }
-
-    /**
      * Overrides the summary. This can happen when injected tile wants to provide dynamic summary.
      */
     public void overrideSummary(CharSequence summaryOverride) {
@@ -302,7 +292,7 @@
         if (iconResId != 0) {
             final Icon icon = Icon.createWithResource(activityInfo.packageName, iconResId);
             if (isIconTintable(context)) {
-                final TypedArray a = context.obtainStyledAttributes(new int[] {
+                final TypedArray a = context.obtainStyledAttributes(new int[]{
                         android.R.attr.colorControlNormal});
                 final int tintColor = a.getColor(0, 0);
                 a.recycle();
@@ -357,6 +347,9 @@
             if (infoList != null && !infoList.isEmpty()) {
                 mActivityInfo = infoList.get(0).activityInfo;
                 mMetaData = mActivityInfo.metaData;
+            } else {
+                Log.e(TAG, "Cannot find package info for "
+                        + intent.getComponent().flattenToString());
             }
         }
         return mActivityInfo;
diff --git a/packages/SettingsLib/res/color/dark_mode_icon_color_dual_tone_background.xml b/packages/SettingsLib/res/color/dark_mode_icon_color_dual_tone_background.xml
index 6b45a47..c8a80ac 100644
--- a/packages/SettingsLib/res/color/dark_mode_icon_color_dual_tone_background.xml
+++ b/packages/SettingsLib/res/color/dark_mode_icon_color_dual_tone_background.xml
@@ -14,5 +14,5 @@
      limitations under the License.
 -->
 <selector xmlns:android="http://schemas.android.com/apk/res/android">
-    <item android:alpha="0.24" android:color="@android:color/black" />
+    <item android:alpha="0.24" android:color="?android:attr/colorBackground" />
 </selector>
\ No newline at end of file
diff --git a/packages/SettingsLib/res/color/dark_mode_icon_color_dual_tone_fill.xml b/packages/SettingsLib/res/color/dark_mode_icon_color_dual_tone_fill.xml
index 5ef085b..8dcfdbb 100644
--- a/packages/SettingsLib/res/color/dark_mode_icon_color_dual_tone_fill.xml
+++ b/packages/SettingsLib/res/color/dark_mode_icon_color_dual_tone_fill.xml
@@ -14,5 +14,5 @@
      limitations under the License.
 -->
 <selector xmlns:android="http://schemas.android.com/apk/res/android">
-    <item android:alpha="0.47" android:color="@android:color/black" />
+    <item android:alpha="0.47" android:color="?android:attr/colorBackground" />
 </selector>
\ No newline at end of file
diff --git a/packages/SettingsLib/res/color/light_mode_icon_color_dual_tone_background.xml b/packages/SettingsLib/res/color/light_mode_icon_color_dual_tone_background.xml
index 81f63a4..34de548 100644
--- a/packages/SettingsLib/res/color/light_mode_icon_color_dual_tone_background.xml
+++ b/packages/SettingsLib/res/color/light_mode_icon_color_dual_tone_background.xml
@@ -14,5 +14,5 @@
      limitations under the License.
 -->
 <selector xmlns:android="http://schemas.android.com/apk/res/android">
-    <item android:alpha="0.3" android:color="@android:color/white" />
+    <item android:alpha="0.3" android:color="?android:attr/colorForeground" />
 </selector>
\ No newline at end of file
diff --git a/packages/SettingsLib/res/color/light_mode_icon_color_dual_tone_fill.xml b/packages/SettingsLib/res/color/light_mode_icon_color_dual_tone_fill.xml
index 61f4e26..15944c3 100644
--- a/packages/SettingsLib/res/color/light_mode_icon_color_dual_tone_fill.xml
+++ b/packages/SettingsLib/res/color/light_mode_icon_color_dual_tone_fill.xml
@@ -14,5 +14,5 @@
      limitations under the License.
 -->
 <selector xmlns:android="http://schemas.android.com/apk/res/android">
-    <item android:color="@android:color/white" />
+    <item android:color="?android:attr/colorForeground" />
 </selector>
\ No newline at end of file
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/TileTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/TileTest.java
index d0d1e58..5da6205 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/TileTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/TileTest.java
@@ -23,6 +23,7 @@
 import org.robolectric.RuntimeEnvironment;
 import org.robolectric.shadow.api.Shadow;
 import org.robolectric.shadows.ShadowPackageManager;
+import org.robolectric.util.ReflectionHelpers;
 
 @RunWith(RobolectricTestRunner.class)
 public class TileTest {
@@ -164,4 +165,17 @@
 
         assertThat(tile.mLastUpdateTime).isNotEqualTo(staleTimeStamp);
     }
+
+    @Test
+    public void getTitle_noActivity_returnNull() {
+        final ResolveInfo info = new ResolveInfo();
+        info.activityInfo = mActivityInfo;
+        final ShadowPackageManager spm = Shadow.extract(mContext.getPackageManager());
+        spm.removePackage(mActivityInfo.packageName);
+
+        final Tile tile = new Tile(mActivityInfo, "category");
+        ReflectionHelpers.setField(tile, "mActivityInfo", null);
+
+        assertThat(tile.getTitle(RuntimeEnvironment.application)).isNull();
+    }
 }
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
index 3195471..033708a 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
@@ -797,6 +797,12 @@
                 Settings.Global.JOB_SCHEDULER_CONSTANTS,
                 GlobalSettingsProto.JOB_SCHEDULER_CONSTANTS);
         dumpSetting(s, p,
+                Settings.Global.JOB_SCHEDULER_QUOTA_CONTROLLER_CONSTANTS,
+                GlobalSettingsProto.JOB_SCHEDULER_QUOTA_CONTROLLER_CONSTANTS);
+        dumpSetting(s, p,
+                Settings.Global.JOB_SCHEDULER_TIME_CONTROLLER_CONSTANTS,
+                GlobalSettingsProto.JOB_SCHEDULER_TIME_CONTROLLER_CONSTANTS);
+        dumpSetting(s, p,
                 Settings.Global.KEEP_PROFILE_IN_BACKGROUND,
                 GlobalSettingsProto.KEEP_PROFILE_IN_BACKGROUND);
 
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/ActivityStarter.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/ActivityStarter.java
index 66d5d11..25a3fa2 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/ActivityStarter.java
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/ActivityStarter.java
@@ -17,6 +17,7 @@
 import android.annotation.Nullable;
 import android.app.PendingIntent;
 import android.content.Intent;
+import android.view.View;
 
 import com.android.systemui.plugins.annotations.ProvidesInterface;
 
@@ -32,13 +33,20 @@
     void startPendingIntentDismissingKeyguard(PendingIntent intent);
 
     /**
-     * Similar to {@link #startPendingIntentDismissingKeyguard(PendingIntent, Runnable)}, but allows
+     * Similar to {@link #startPendingIntentDismissingKeyguard(PendingIntent)}, but allows
      * you to specify the callback that is executed on the UI thread after the intent is sent.
      */
     void startPendingIntentDismissingKeyguard(PendingIntent intent,
             Runnable intentSentUiThreadCallback);
 
     /**
+     * Similar to {@link #startPendingIntentDismissingKeyguard(PendingIntent, Runnable)}, but also
+     * specifies an associated view that should be used for the activity launch animation.
+     */
+    void startPendingIntentDismissingKeyguard(PendingIntent intent,
+            Runnable intentSentUiThreadCallback, View associatedView);
+
+    /**
      * The intent flag can be specified in startActivity().
      */
     void startActivity(Intent intent, boolean onlyProvisioned, boolean dismissShade, int flags);
diff --git a/packages/SystemUI/res-keyguard/values/dimens.xml b/packages/SystemUI/res-keyguard/values/dimens.xml
index d8f543f..112dde6 100644
--- a/packages/SystemUI/res-keyguard/values/dimens.xml
+++ b/packages/SystemUI/res-keyguard/values/dimens.xml
@@ -53,7 +53,7 @@
     <!-- Clock with header -->
     <dimen name="widget_small_font_size">22dp</dimen>
     <dimen name="widget_vertical_padding">32dp</dimen>
-    <dimen name="widget_vertical_padding_clock">30dp</dimen>
+    <dimen name="widget_vertical_padding_clock">12dp</dimen>
     <!-- Subtitle paddings -->
     <dimen name="widget_horizontal_padding">8dp</dimen>
     <dimen name="widget_icon_size">16dp</dimen>
diff --git a/packages/SystemUI/res/layout-land/global_actions_column.xml b/packages/SystemUI/res/layout-land/global_actions_column.xml
new file mode 100644
index 0000000..99a4e13
--- /dev/null
+++ b/packages/SystemUI/res/layout-land/global_actions_column.xml
@@ -0,0 +1,68 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2019 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License
+  -->
+
+<com.android.systemui.globalactions.GlobalActionsColumnLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@id/global_actions_view"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:orientation="horizontal"
+    android:clipToPadding="false"
+    android:theme="@style/qs_theme"
+    android:gravity="center_horizontal | top"
+    android:clipChildren="false"
+>
+    <LinearLayout
+        android:layout_height="wrap_content"
+        android:layout_width="wrap_content"
+        android:padding="0dp"
+        android:orientation="horizontal"
+        android:clipChildren="false"
+        android:clipToPadding="false"
+    >
+        <!-- Grid of action items -->
+        <LinearLayout
+            android:id="@android:id/list"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:orientation="horizontal"
+            android:layout_marginTop="@dimen/global_actions_grid_side_margin"
+            android:translationZ="@dimen/global_actions_translate"
+            android:paddingLeft="@dimen/global_actions_grid_horizontal_padding"
+            android:paddingRight="@dimen/global_actions_grid_horizontal_padding"
+            android:paddingTop="@dimen/global_actions_grid_vertical_padding"
+            android:paddingBottom="@dimen/global_actions_grid_vertical_padding"
+            android:background="?android:attr/colorBackgroundFloating"
+        />
+        <!-- For separated items-->
+        <LinearLayout
+            android:id="@+id/separated_button"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_marginLeft="@dimen/global_actions_grid_side_margin"
+            android:layout_marginTop="@dimen/global_actions_grid_side_margin"
+            android:paddingLeft="@dimen/global_actions_grid_horizontal_padding"
+            android:paddingRight="@dimen/global_actions_grid_horizontal_padding"
+            android:paddingTop="@dimen/global_actions_grid_vertical_padding"
+            android:paddingBottom="@dimen/global_actions_grid_vertical_padding"
+            android:orientation="horizontal"
+            android:background="?android:attr/colorBackgroundFloating"
+            android:translationZ="@dimen/global_actions_translate"
+        />
+    </LinearLayout>
+
+</com.android.systemui.globalactions.GlobalActionsColumnLayout>
diff --git a/packages/SystemUI/res/layout-land/global_actions_column_seascape.xml b/packages/SystemUI/res/layout-land/global_actions_column_seascape.xml
new file mode 100644
index 0000000..0f86131
--- /dev/null
+++ b/packages/SystemUI/res/layout-land/global_actions_column_seascape.xml
@@ -0,0 +1,68 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2019 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License
+  -->
+
+<com.android.systemui.globalactions.GlobalActionsColumnLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@id/global_actions_view"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:orientation="horizontal"
+    android:clipToPadding="false"
+    android:theme="@style/qs_theme"
+    android:gravity="center_horizontal | bottom"
+    android:clipChildren="false"
+>
+    <LinearLayout
+        android:layout_height="wrap_content"
+        android:layout_width="wrap_content"
+        android:padding="0dp"
+        android:orientation="horizontal"
+    >
+        <!-- Grid of action items -->
+        <com.android.systemui.globalactions.ListGridLayout
+            android:id="@android:id/list"
+            android:layout_gravity="bottom|left"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:orientation="horizontal"
+            android:layout_marginBottom="@dimen/global_actions_grid_side_margin"
+            android:translationZ="@dimen/global_actions_translate"
+            android:paddingLeft="@dimen/global_actions_grid_horizontal_padding"
+            android:paddingRight="@dimen/global_actions_grid_horizontal_padding"
+            android:paddingTop="@dimen/global_actions_grid_vertical_padding"
+            android:paddingBottom="@dimen/global_actions_grid_vertical_padding"
+            android:background="?android:attr/colorBackgroundFloating"
+        />
+        <!-- For separated items-->
+        <LinearLayout
+            android:id="@+id/separated_button"
+            android:layout_gravity="top|left"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_marginRight="@dimen/global_actions_grid_side_margin"
+            android:layout_marginBottom="@dimen/global_actions_grid_side_margin"
+            android:paddingLeft="@dimen/global_actions_grid_horizontal_padding"
+            android:paddingRight="@dimen/global_actions_grid_horizontal_padding"
+            android:paddingTop="@dimen/global_actions_grid_vertical_padding"
+            android:paddingBottom="@dimen/global_actions_grid_vertical_padding"
+            android:orientation="horizontal"
+            android:background="?android:attr/colorBackgroundFloating"
+            android:translationZ="@dimen/global_actions_translate"
+        />
+    </LinearLayout>
+
+</com.android.systemui.globalactions.GlobalActionsColumnLayout>
diff --git a/packages/SystemUI/res/layout/global_actions_column.xml b/packages/SystemUI/res/layout/global_actions_column.xml
new file mode 100644
index 0000000..b58146b
--- /dev/null
+++ b/packages/SystemUI/res/layout/global_actions_column.xml
@@ -0,0 +1,70 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2019 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License
+  -->
+
+<com.android.systemui.globalactions.GlobalActionsColumnLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@id/global_actions_view"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:orientation="vertical"
+    android:clipToPadding="false"
+    android:theme="@style/qs_theme"
+    android:gravity="center_vertical | right"
+    android:clipChildren="false"
+>
+    <LinearLayout
+        android:layout_height="wrap_content"
+        android:layout_width="wrap_content"
+        android:gravity="top | right"
+        android:orientation="vertical"
+        android:padding="0dp"
+        android:layout_marginTop="@dimen/global_actions_grid_container_bottom_margin"
+        android:layout_marginBottom="@dimen/global_actions_grid_container_bottom_margin"
+    >
+        <!-- Global actions is right-aligned to be physically near power button -->
+        <LinearLayout
+            android:id="@android:id/list"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:orientation="vertical"
+            android:gravity="right"
+            android:translationZ="@dimen/global_actions_translate"
+            android:layout_marginRight="@dimen/global_actions_grid_side_margin"
+            android:paddingLeft="@dimen/global_actions_grid_horizontal_padding"
+            android:paddingRight="@dimen/global_actions_grid_horizontal_padding"
+            android:paddingTop="@dimen/global_actions_grid_vertical_padding"
+            android:paddingBottom="@dimen/global_actions_grid_vertical_padding"
+        />
+
+        <!-- For separated items-->
+        <LinearLayout
+            android:id="@+id/separated_button"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_marginTop="@dimen/global_actions_grid_side_margin"
+            android:layout_marginRight="@dimen/global_actions_grid_side_margin"
+            android:paddingLeft="@dimen/global_actions_grid_horizontal_padding"
+            android:paddingRight="@dimen/global_actions_grid_horizontal_padding"
+            android:paddingTop="@dimen/global_actions_grid_vertical_padding"
+            android:paddingBottom="@dimen/global_actions_grid_vertical_padding"
+            android:orientation="vertical"
+            android:gravity="center"
+            android:translationZ="@dimen/global_actions_translate"
+        />
+    </LinearLayout>
+
+</com.android.systemui.globalactions.GlobalActionsColumnLayout>
diff --git a/packages/SystemUI/res/layout/qs_carrier.xml b/packages/SystemUI/res/layout/qs_carrier.xml
index 8f74806..8dd06f0 100644
--- a/packages/SystemUI/res/layout/qs_carrier.xml
+++ b/packages/SystemUI/res/layout/qs_carrier.xml
@@ -42,6 +42,7 @@
         android:layout_weight="1"
         android:textAppearance="@style/TextAppearance.QS.Status"
         android:textDirection="locale"
+        android:marqueeRepeatLimit="marquee_forever"
         android:singleLine="true"
         android:maxEms="7"/>
 
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl
index 2c5fa60..b826b30 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl
@@ -38,11 +38,6 @@
     void startScreenPinning(int taskId) = 1;
 
     /**
-     * Enables/disables launcher/overview interaction features {@link InteractionType}.
-     */
-    void setInteractionState(int flags) = 4;
-
-    /**
      * Notifies SystemUI that split screen has been invoked.
      */
     void onSplitScreenInvoked() = 5;
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/NavigationBarCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/NavigationBarCompat.java
deleted file mode 100644
index 8d6f830..0000000
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/NavigationBarCompat.java
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package com.android.systemui.shared.system;
-
-import android.annotation.IntDef;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-
-public class NavigationBarCompat extends QuickStepContract {
-
-    @Retention(RetentionPolicy.SOURCE)
-    @IntDef({HIT_TARGET_NONE, HIT_TARGET_BACK, HIT_TARGET_HOME, HIT_TARGET_OVERVIEW})
-    public @interface HitTarget{}
-
-    public static final int HIT_TARGET_NONE = 0;
-    public static final int HIT_TARGET_BACK = 1;
-    public static final int HIT_TARGET_HOME = 2;
-    public static final int HIT_TARGET_OVERVIEW = 3;
-    public static final int HIT_TARGET_ROTATION = 4;
-    public static final int HIT_TARGET_DEAD_ZONE = 5;
-
-    @Retention(RetentionPolicy.SOURCE)
-    @IntDef({FLAG_DISABLE_SWIPE_UP,
-            FLAG_DISABLE_QUICK_SCRUB,
-            FLAG_SHOW_OVERVIEW_BUTTON
-    })
-    public @interface InteractionType {}
-
-    /**
-     * Interaction type: whether the gesture to swipe up from the navigation bar will trigger
-     * launcher to show overview
-     */
-    public static final int FLAG_DISABLE_SWIPE_UP = 0x1;
-    /**
-     * Interaction type: enable quick scrub interaction on the home button
-     */
-    public static final int FLAG_DISABLE_QUICK_SCRUB = 0x2;
-
-    /**
-     * Interaction type: show/hide the overview button while this service is connected to launcher
-     */
-    public static final int FLAG_SHOW_OVERVIEW_BUTTON = 0x4;
-
-
-}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardMessageArea.java b/packages/SystemUI/src/com/android/keyguard/KeyguardMessageArea.java
index 0a6885c..037a8d3 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardMessageArea.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardMessageArea.java
@@ -59,14 +59,22 @@
     private ColorStateList mDefaultColorState;
     private CharSequence mMessage;
     private ColorStateList mNextMessageColorState = ColorStateList.valueOf(DEFAULT_COLOR);
+    private boolean mBouncerVisible;
 
     private KeyguardUpdateMonitorCallback mInfoCallback = new KeyguardUpdateMonitorCallback() {
         public void onFinishedGoingToSleep(int why) {
             setSelected(false);
-        };
+        }
+
         public void onStartedWakingUp() {
             setSelected(true);
-        };
+        }
+
+        @Override
+        public void onKeyguardBouncerChanged(boolean bouncer) {
+            mBouncerVisible = bouncer;
+            update();
+        }
     };
 
     public KeyguardMessageArea(Context context) {
@@ -188,7 +196,7 @@
 
     private void update() {
         CharSequence status = mMessage;
-        setVisibility(TextUtils.isEmpty(status) ? INVISIBLE : VISIBLE);
+        setVisibility(TextUtils.isEmpty(status) || !mBouncerVisible ? INVISIBLE : VISIBLE);
         setText(status);
         ColorStateList colorState = mDefaultColorState;
         if (mNextMessageColorState.getDefaultColor() != DEFAULT_COLOR) {
diff --git a/packages/SystemUI/src/com/android/keyguard/clock/AnalogClockController.java b/packages/SystemUI/src/com/android/keyguard/clock/AnalogClockController.java
index 123e138..eec1d70 100644
--- a/packages/SystemUI/src/com/android/keyguard/clock/AnalogClockController.java
+++ b/packages/SystemUI/src/com/android/keyguard/clock/AnalogClockController.java
@@ -189,6 +189,7 @@
     @Override
     public void setDarkAmount(float darkAmount) {
         mClockPosition.setDarkAmount(darkAmount);
+        mBigClockView.setDarkAmount(darkAmount);
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/keyguard/clock/BubbleClockController.java b/packages/SystemUI/src/com/android/keyguard/clock/BubbleClockController.java
index 8db61b8..5fec61b 100644
--- a/packages/SystemUI/src/com/android/keyguard/clock/BubbleClockController.java
+++ b/packages/SystemUI/src/com/android/keyguard/clock/BubbleClockController.java
@@ -181,6 +181,7 @@
     @Override
     public void setDarkAmount(float darkAmount) {
         mClockPosition.setDarkAmount(darkAmount);
+        mView.setDarkAmount(darkAmount);
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/keyguard/clock/ClockLayout.java b/packages/SystemUI/src/com/android/keyguard/clock/ClockLayout.java
index 962b8f8..1908345 100644
--- a/packages/SystemUI/src/com/android/keyguard/clock/ClockLayout.java
+++ b/packages/SystemUI/src/com/android/keyguard/clock/ClockLayout.java
@@ -20,6 +20,7 @@
 import android.content.Context;
 import android.content.res.Resources;
 import android.util.AttributeSet;
+import android.util.MathUtils;
 import android.view.View;
 import android.widget.FrameLayout;
 
@@ -43,6 +44,8 @@
     private int mBurnInPreventionOffsetX;
     private int mBurnInPreventionOffsetY;
 
+    private float mDarkAmount;
+
     public ClockLayout(Context context) {
         this(context, null);
     }
@@ -78,11 +81,21 @@
         positionChildren();
     }
 
+    /**
+     * See {@link com.android.systemui.plugins.ClockPlugin#setDarkAmount(float)}.
+     */
+    void setDarkAmount(float darkAmount) {
+        mDarkAmount = darkAmount;
+        positionChildren();
+    }
+
     private void positionChildren() {
-        final float offsetX = getBurnInOffset(mBurnInPreventionOffsetX * 2, true)
-                - mBurnInPreventionOffsetX;
-        final float offsetY = getBurnInOffset(mBurnInPreventionOffsetY * 2, false)
-                - mBurnInPreventionOffsetY;
+        final float offsetX = MathUtils.lerp(0f,
+                getBurnInOffset(mBurnInPreventionOffsetX * 2, true) - mBurnInPreventionOffsetX,
+                mDarkAmount);
+        final float offsetY = MathUtils.lerp(0f,
+                getBurnInOffset(mBurnInPreventionOffsetY * 2, false) - mBurnInPreventionOffsetY,
+                mDarkAmount);
 
         // Put the analog clock in the middle of the screen.
         if (mAnalogClock != null) {
diff --git a/packages/SystemUI/src/com/android/systemui/ActivityStarterDelegate.java b/packages/SystemUI/src/com/android/systemui/ActivityStarterDelegate.java
index 04f887b..41a7bc4 100644
--- a/packages/SystemUI/src/com/android/systemui/ActivityStarterDelegate.java
+++ b/packages/SystemUI/src/com/android/systemui/ActivityStarterDelegate.java
@@ -16,6 +16,7 @@
 
 import android.app.PendingIntent;
 import android.content.Intent;
+import android.view.View;
 
 import com.android.systemui.plugins.ActivityStarter;
 
@@ -53,6 +54,16 @@
     }
 
     @Override
+    public void startPendingIntentDismissingKeyguard(PendingIntent intent,
+            Runnable intentSentCallback, View associatedView) {
+        if (mActualStarter == null) {
+            return;
+        }
+        mActualStarter.startPendingIntentDismissingKeyguard(intent, intentSentCallback,
+                associatedView);
+    }
+
+    @Override
     public void startActivity(Intent intent, boolean onlyProvisioned, boolean dismissShade,
             int flags) {
         if (mActualStarter == null) {
diff --git a/packages/SystemUI/src/com/android/systemui/HardwareUiLayout.java b/packages/SystemUI/src/com/android/systemui/HardwareUiLayout.java
index 802903d..ad2e002 100644
--- a/packages/SystemUI/src/com/android/systemui/HardwareUiLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/HardwareUiLayout.java
@@ -80,16 +80,6 @@
     }
 
     @Override
-    public void removeAllItems() {
-        if (mList != null) {
-            mList.removeAllViews();
-        }
-        if (mSeparatedView != null) {
-            mSeparatedView.removeAllViews();
-        }
-    }
-
-    @Override
     protected void onAttachedToWindow() {
         super.onAttachedToWindow();
         updateSettings();
diff --git a/packages/SystemUI/src/com/android/systemui/MultiListLayout.java b/packages/SystemUI/src/com/android/systemui/MultiListLayout.java
index f8287a4..d153fb0 100644
--- a/packages/SystemUI/src/com/android/systemui/MultiListLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/MultiListLayout.java
@@ -45,11 +45,6 @@
     protected abstract ViewGroup getListView();
 
     /**
-     * Removes all child items from the separated and list views, if they exist.
-     */
-    protected abstract void removeAllItems();
-
-    /**
      * Sets the divided view, which may have a differently-colored background.
      */
     public abstract void setDivisionView(View v);
@@ -110,6 +105,25 @@
         onUpdateList();
     }
 
+    protected void removeAllSeparatedViews() {
+        ViewGroup separated = getSeparatedView();
+        if (separated != null) {
+            separated.removeAllViews();
+        }
+    }
+
+    protected void removeAllListViews() {
+        ViewGroup list = getListView();
+        if (list != null) {
+            list.removeAllViews();
+        }
+    }
+
+    protected void removeAllItems() {
+        removeAllListViews();
+        removeAllSeparatedViews();
+    }
+
     protected void onUpdateList() {
         removeAllItems();
         setSeparatedViewVisibility(mAdapter.hasSeparatedItems());
diff --git a/packages/SystemUI/src/com/android/systemui/Prefs.java b/packages/SystemUI/src/com/android/systemui/Prefs.java
index 2797570..10ae343 100644
--- a/packages/SystemUI/src/com/android/systemui/Prefs.java
+++ b/packages/SystemUI/src/com/android/systemui/Prefs.java
@@ -57,7 +57,6 @@
             Key.SEEN_RINGER_GUIDANCE_COUNT,
             Key.QS_HAS_TURNED_OFF_MOBILE_DATA,
             Key.TOUCHED_RINGER_TOGGLE,
-            Key.QUICK_STEP_INTERACTION_FLAGS,
             Key.HAS_SEEN_ODI_CAPTIONS_TOOLTIP
     })
     public @interface Key {
@@ -103,7 +102,6 @@
         String QS_TILE_SPECS_REVEALED = "QsTileSpecsRevealed";
         String QS_HAS_TURNED_OFF_MOBILE_DATA = "QsHasTurnedOffMobileData";
         String TOUCHED_RINGER_TOGGLE = "TouchedRingerToggle";
-        String QUICK_STEP_INTERACTION_FLAGS = "QuickStepInteractionFlags";
         String HAS_SEEN_ODI_CAPTIONS_TOOLTIP = "HasSeenODICaptionsTooltip";
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsColumnLayout.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsColumnLayout.java
new file mode 100644
index 0000000..5907028
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsColumnLayout.java
@@ -0,0 +1,195 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.globalactions;
+
+import static com.android.systemui.util.leak.RotationUtils.ROTATION_LANDSCAPE;
+import static com.android.systemui.util.leak.RotationUtils.ROTATION_NONE;
+import static com.android.systemui.util.leak.RotationUtils.ROTATION_SEASCAPE;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.Gravity;
+import android.view.View;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.systemui.R;
+
+/**
+ * Grid-based implementation of the button layout created by the global actions dialog.
+ */
+public class GlobalActionsColumnLayout extends GlobalActionsLayout {
+    private boolean mLastSnap;
+
+    public GlobalActionsColumnLayout(Context context, AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    @Override
+    protected void onLayout(boolean changed, int l, int t, int r, int b) {
+        super.onLayout(changed, l, t, r, b);
+
+        post(() -> updateSnap());
+    }
+
+    @Override
+    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+    }
+
+    @VisibleForTesting
+    protected boolean shouldReverseListItems() {
+        int rotation = getCurrentRotation();
+        if (rotation == ROTATION_NONE) {
+            return false;
+        }
+        if (getCurrentLayoutDirection() == View.LAYOUT_DIRECTION_RTL) {
+            return rotation == ROTATION_LANDSCAPE;
+        }
+        return rotation == ROTATION_SEASCAPE;
+    }
+
+    @Override
+    public void onUpdateList() {
+        super.onUpdateList();
+        updateChildOrdering();
+    }
+
+    private void updateChildOrdering() {
+        if (shouldReverseListItems()) {
+            getListView().bringToFront();
+        } else {
+            getSeparatedView().bringToFront();
+        }
+    }
+
+    /**
+     *  Snap this layout to align with the power button.
+     */
+    @VisibleForTesting
+    protected void snapToPowerButton() {
+        int offset = getPowerButtonOffsetDistance();
+        switch (getCurrentRotation()) {
+            case (ROTATION_LANDSCAPE):
+                setPadding(offset, 0, 0, 0);
+                setGravity(Gravity.LEFT | Gravity.TOP);
+                break;
+            case (ROTATION_SEASCAPE):
+                setPadding(0, 0, offset, 0);
+                setGravity(Gravity.RIGHT | Gravity.BOTTOM);
+                break;
+            default:
+                setPadding(0, offset, 0, 0);
+                setGravity(Gravity.TOP | Gravity.RIGHT);
+                break;
+        }
+    }
+
+    /**
+     *  Detach this layout from snapping to the power button and instead center along that edge.
+     */
+    @VisibleForTesting
+    protected void centerAlongEdge() {
+        switch (getCurrentRotation()) {
+            case (ROTATION_LANDSCAPE):
+                setPadding(0, 0, 0, 0);
+                setGravity(Gravity.CENTER_HORIZONTAL | Gravity.TOP);
+                break;
+            case (ROTATION_SEASCAPE):
+                setPadding(0, 0, 0, 0);
+                setGravity(Gravity.CENTER_HORIZONTAL | Gravity.BOTTOM);
+                break;
+            default:
+                setPadding(0, 0, 0, 0);
+                setGravity(Gravity.CENTER_VERTICAL | Gravity.RIGHT);
+                break;
+        }
+    }
+
+    /**
+     * Determines the distance from the top of the screen to the power button.
+     */
+    @VisibleForTesting
+    protected int getPowerButtonOffsetDistance() {
+        return Math.round(getContext().getResources().getDimension(
+                R.dimen.global_actions_top_padding));
+    }
+
+    /**
+     * Check whether there is enough extra space below the dialog such that we can offset the top
+     * of the dialog from the top of the phone to line it up with the power button, then either
+     * snap the dialog to the power button or center it along the edge with snapToPowerButton.
+     */
+    @VisibleForTesting
+    protected boolean shouldSnapToPowerButton() {
+        int offsetSize = getPowerButtonOffsetDistance();
+        int dialogSize;
+        int screenSize;
+        View wrapper = getWrapper();
+        int rotation = getCurrentRotation();
+        if (rotation == ROTATION_NONE) {
+            dialogSize = wrapper.getMeasuredHeight();
+            screenSize = getMeasuredHeight();
+        } else {
+            dialogSize = wrapper.getMeasuredWidth();
+            screenSize = getMeasuredWidth();
+        }
+        return dialogSize + offsetSize < screenSize;
+    }
+
+    @VisibleForTesting
+    protected void updateSnap() {
+        boolean snap = shouldSnapToPowerButton();
+        if (snap != mLastSnap) {
+            if (snap) {
+                snapToPowerButton();
+            } else {
+                centerAlongEdge();
+            }
+        }
+        mLastSnap = snap;
+    }
+
+    @VisibleForTesting
+    protected float getGridItemSize() {
+        return getContext().getResources().getDimension(R.dimen.global_actions_grid_item_height);
+    }
+
+    @VisibleForTesting
+    protected float getAnimationDistance() {
+        return getGridItemSize() / 2;
+    }
+
+    @Override
+    public float getAnimationOffsetX() {
+        if (getCurrentRotation() == ROTATION_NONE) {
+            return getAnimationDistance();
+        }
+        return 0;
+    }
+
+    @Override
+    public float getAnimationOffsetY() {
+        switch (getCurrentRotation()) {
+            case ROTATION_LANDSCAPE:
+                return -getAnimationDistance();
+            case ROTATION_SEASCAPE:
+                return getAnimationDistance();
+            default: // Portrait
+                return 0;
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
index 64511a0..afe905e 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
@@ -1583,13 +1583,20 @@
         }
 
         private int getGlobalActionsLayoutId(Context context) {
-            if (isForceGridEnabled(context) || shouldUsePanel()) {
-                if (RotationUtils.getRotation(context) == RotationUtils.ROTATION_SEASCAPE) {
+            boolean useGridLayout = isForceGridEnabled(context) || shouldUsePanel();
+            if (RotationUtils.getRotation(context) == RotationUtils.ROTATION_SEASCAPE) {
+                if (useGridLayout) {
                     return com.android.systemui.R.layout.global_actions_grid_seascape;
+                } else {
+                    return com.android.systemui.R.layout.global_actions_column_seascape;
                 }
-                return com.android.systemui.R.layout.global_actions_grid;
+            } else {
+                if (useGridLayout) {
+                    return com.android.systemui.R.layout.global_actions_grid;
+                } else {
+                    return com.android.systemui.R.layout.global_actions_column;
+                }
             }
-            return com.android.systemui.R.layout.global_actions_wrapped;
         }
 
         @Override
@@ -1712,7 +1719,7 @@
         }
 
         public void onRotate(int from, int to) {
-            if (mShowing && (shouldUsePanel() || isForceGridEnabled(mContext))) {
+            if (mShowing) {
                 refreshDialog();
             }
         }
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsGridLayout.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsGridLayout.java
index 554ed73..e1462d1 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsGridLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsGridLayout.java
@@ -22,58 +22,23 @@
 
 import android.content.Context;
 import android.util.AttributeSet;
-import android.util.Log;
 import android.view.View;
 import android.view.ViewGroup;
 
 import com.android.internal.annotations.VisibleForTesting;
-import com.android.systemui.HardwareBgDrawable;
-import com.android.systemui.MultiListLayout;
-import com.android.systemui.util.leak.RotationUtils;
 
 /**
  * Grid-based implementation of the button layout created by the global actions dialog.
  */
-public class GlobalActionsGridLayout extends MultiListLayout {
-
-    boolean mBackgroundsSet;
-
+public class GlobalActionsGridLayout extends GlobalActionsLayout {
     public GlobalActionsGridLayout(Context context, AttributeSet attrs) {
         super(context, attrs);
     }
 
-    private void setBackgrounds() {
-        int gridBackgroundColor = getResources().getColor(
-                com.android.systemui.R.color.global_actions_grid_background, null);
-        int separatedBackgroundColor = getResources().getColor(
-                com.android.systemui.R.color.global_actions_separated_background, null);
-        HardwareBgDrawable listBackground  = new HardwareBgDrawable(true, true, getContext());
-        HardwareBgDrawable separatedBackground = new HardwareBgDrawable(true, true, getContext());
-        listBackground.setTint(gridBackgroundColor);
-        separatedBackground.setTint(separatedBackgroundColor);
-        getListView().setBackground(listBackground);
-        getSeparatedView().setBackground(separatedBackground);
-    }
-
-    @Override
-    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
-        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
-
-        // backgrounds set only once, the first time onMeasure is called after inflation
-        if (getListView() != null && !mBackgroundsSet) {
-            setBackgrounds();
-            mBackgroundsSet = true;
-        }
-    }
-
     @VisibleForTesting
-    protected int getCurrentRotation() {
-        return RotationUtils.getRotation(mContext);
-    }
-
-    @VisibleForTesting
-    protected void setupListView(ListGridLayout listView, int itemCount) {
-        listView.setExpectedCount(itemCount);
+    protected void setupListView() {
+        ListGridLayout listView = getListView();
+        listView.setExpectedCount(mAdapter.countListItems());
         listView.setReverseSublists(shouldReverseSublists());
         listView.setReverseItems(shouldReverseListItems());
         listView.setSwapRowsAndColumns(shouldSwapRowsAndColumns());
@@ -81,29 +46,8 @@
 
     @Override
     public void onUpdateList() {
+        setupListView();
         super.onUpdateList();
-
-        ViewGroup separatedView = getSeparatedView();
-        ListGridLayout listView = getListView();
-        setupListView(listView, mAdapter.countListItems());
-
-        for (int i = 0; i < mAdapter.getCount(); i++) {
-            // generate the view item
-            View v;
-            boolean separated = mAdapter.shouldBeSeparated(i);
-            if (separated) {
-                v = mAdapter.getView(i, null, separatedView);
-            } else {
-                v = mAdapter.getView(i, null, listView);
-            }
-            Log.d("GlobalActionsGridLayout", "View: " + v);
-
-            if (separated) {
-                separatedView.addView(v);
-            } else {
-                listView.addItem(v);
-            }
-        }
         updateSeparatedItemSize();
     }
 
@@ -111,7 +55,8 @@
      * If the separated view contains only one item, expand the bounds of that item to take up the
      * entire view, so that the whole thing is touch-able.
      */
-    private void updateSeparatedItemSize() {
+    @VisibleForTesting
+    protected void updateSeparatedItemSize() {
         ViewGroup separated = getSeparatedView();
         if (separated.getChildCount() == 0) {
             return;
@@ -129,13 +74,24 @@
     }
 
     @Override
-    protected ViewGroup getSeparatedView() {
-        return findViewById(com.android.systemui.R.id.separated_button);
+    protected ListGridLayout getListView() {
+        return (ListGridLayout) super.getListView();
     }
 
     @Override
-    protected ListGridLayout getListView() {
-        return findViewById(android.R.id.list);
+    protected void removeAllListViews() {
+        ListGridLayout list = getListView();
+        if (list != null) {
+            list.removeAllItems();
+        }
+    }
+
+    @Override
+    protected void addToListView(View v, boolean reverse) {
+        ListGridLayout list = getListView();
+        if (list != null) {
+            list.addItem(v);
+        }
     }
 
     @Override
@@ -174,12 +130,7 @@
         return true;
     }
 
-    /**
-     * Determines whether the ListGridLayout should reverse the ordering of items within sublists.
-     * Used for RTL languages to ensure that items appear in the same positions, without having to
-     * override layoutDirection, which breaks Talkback ordering.
-     */
-    @VisibleForTesting
+    @Override
     protected boolean shouldReverseListItems() {
         int rotation = getCurrentRotation();
         boolean reverse = false; // should we add items to parents in the reverse order?
@@ -187,20 +138,13 @@
                 || rotation == ROTATION_SEASCAPE) {
             reverse = !reverse; // if we're in portrait or seascape, reverse items
         }
-        if (getLayoutDirection() == View.LAYOUT_DIRECTION_RTL) {
+        if (getCurrentLayoutDirection() == View.LAYOUT_DIRECTION_RTL) {
             reverse = !reverse; // if we're in an RTL language, reverse items (again)
         }
         return reverse;
     }
 
-    /**
-     * Not ued in this implementation of the Global Actions Menu, but necessary for some others.
-     */
-    @Override
-    public void setDivisionView(View v) {
-        // do nothing
-    }
-
+    @VisibleForTesting
     protected float getAnimationDistance() {
         int rows = getListView().getRowCount();
         float gridItemSize = getContext().getResources().getDimension(
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsLayout.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsLayout.java
new file mode 100644
index 0000000..f755a93
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsLayout.java
@@ -0,0 +1,146 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.globalactions;
+
+import android.content.Context;
+import android.text.TextUtils;
+import android.util.AttributeSet;
+import android.view.View;
+import android.view.ViewGroup;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.systemui.HardwareBgDrawable;
+import com.android.systemui.MultiListLayout;
+import com.android.systemui.R;
+import com.android.systemui.util.leak.RotationUtils;
+
+import java.util.Locale;
+
+/**
+ * Grid-based implementation of the button layout created by the global actions dialog.
+ */
+public abstract class GlobalActionsLayout extends MultiListLayout {
+
+    boolean mBackgroundsSet;
+
+    public GlobalActionsLayout(Context context, AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    private void setBackgrounds() {
+        int gridBackgroundColor = getResources().getColor(
+                R.color.global_actions_grid_background, null);
+        int separatedBackgroundColor = getResources().getColor(
+                R.color.global_actions_separated_background, null);
+        HardwareBgDrawable listBackground  = new HardwareBgDrawable(true, true, getContext());
+        HardwareBgDrawable separatedBackground = new HardwareBgDrawable(true, true, getContext());
+        listBackground.setTint(gridBackgroundColor);
+        separatedBackground.setTint(separatedBackgroundColor);
+        getListView().setBackground(listBackground);
+        getSeparatedView().setBackground(separatedBackground);
+    }
+
+    @Override
+    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+
+        // backgrounds set only once, the first time onMeasure is called after inflation
+        if (getListView() != null && !mBackgroundsSet) {
+            setBackgrounds();
+            mBackgroundsSet = true;
+        }
+    }
+
+    protected void addToListView(View v, boolean reverse) {
+        if (reverse) {
+            getListView().addView(v, 0);
+        } else {
+            getListView().addView(v);
+        }
+    }
+
+    protected void addToSeparatedView(View v, boolean reverse) {
+        if (reverse) {
+            getSeparatedView().addView(v, 0);
+        } else {
+            getSeparatedView().addView(v);
+        }
+    }
+
+    @VisibleForTesting
+    protected int getCurrentLayoutDirection() {
+        return TextUtils.getLayoutDirectionFromLocale(Locale.getDefault());
+    }
+
+    @VisibleForTesting
+    protected int getCurrentRotation() {
+        return RotationUtils.getRotation(mContext);
+    }
+
+    /**
+     * Determines whether the ListGridLayout should reverse the ordering of items within sublists.
+     * Used for RTL languages to ensure that items appear in the same positions, without having to
+     * override layoutDirection, which breaks Talkback ordering.
+     */
+    protected abstract boolean shouldReverseListItems();
+
+    @Override
+    public void onUpdateList() {
+        super.onUpdateList();
+
+        ViewGroup separatedView = getSeparatedView();
+        ViewGroup listView = getListView();
+
+        for (int i = 0; i < mAdapter.getCount(); i++) {
+            // generate the view item
+            View v;
+            boolean separated = mAdapter.shouldBeSeparated(i);
+            if (separated) {
+                v = mAdapter.getView(i, null, separatedView);
+            } else {
+                v = mAdapter.getView(i, null, listView);
+            }
+            if (separated) {
+                addToSeparatedView(v, false);
+            } else {
+                addToListView(v, shouldReverseListItems());
+            }
+        }
+    }
+
+    @Override
+    protected ViewGroup getSeparatedView() {
+        return findViewById(R.id.separated_button);
+    }
+
+    @Override
+    protected ViewGroup getListView() {
+        return findViewById(android.R.id.list);
+    }
+
+    protected View getWrapper() {
+        return getChildAt(0);
+    }
+
+    /**
+     * Not used in this implementation of the Global Actions Menu, but necessary for some others.
+     */
+    @Override
+    public void setDivisionView(View v) {
+        // do nothing
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/pip/tv/PipNotification.java b/packages/SystemUI/src/com/android/systemui/pip/tv/PipNotification.java
index 89ecc6a..d50f294e4 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/tv/PipNotification.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/tv/PipNotification.java
@@ -43,7 +43,7 @@
  */
 public class PipNotification {
     private static final String TAG = "PipNotification";
-    private static final String NOTIFICATION_TAG = PipNotification.class.getName();
+    private static final String NOTIFICATION_TAG = PipNotification.class.getSimpleName();
     private static final boolean DEBUG = PipManager.DEBUG;
 
     private static final String ACTION_MENU = "PipNotification.menu";
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
index 6adce83..9431f20 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
@@ -38,6 +38,7 @@
 import android.service.notification.ZenModeConfig;
 import android.text.format.DateUtils;
 import android.util.AttributeSet;
+import android.util.Log;
 import android.util.Pair;
 import android.util.StatsLog;
 import android.view.ContextThemeWrapper;
@@ -524,11 +525,11 @@
             mActivityStarter.postStartActivityDismissingKeyguard(new Intent(
                     AlarmClock.ACTION_SHOW_ALARMS), 0);
         } else if (v == mNextAlarmContainer) {
-            if (mNextAlarm.getShowIntent() != null
-                    && mNextAlarm.getShowIntent().getIntent() != null) {
+            if (mNextAlarm.getShowIntent() != null) {
                 mActivityStarter.postStartActivityDismissingKeyguard(
-                        mNextAlarm.getShowIntent().getIntent(), 0);
+                        mNextAlarm.getShowIntent());
             } else {
+                Log.d(TAG, "No PendingIntent for next alarm. Using default intent");
                 mActivityStarter.postStartActivityDismissingKeyguard(new Intent(
                         AlarmClock.ACTION_SHOW_ALARMS), 0);
             }
diff --git a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
index 5ed6a42..78c7cd4 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
@@ -17,14 +17,12 @@
 package com.android.systemui.recents;
 
 import static android.content.pm.PackageManager.MATCH_SYSTEM_ONLY;
+import static android.view.Display.DEFAULT_DISPLAY;
 import static android.view.MotionEvent.ACTION_CANCEL;
 import static android.view.MotionEvent.ACTION_DOWN;
 import static android.view.MotionEvent.ACTION_UP;
 import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_3BUTTON;
 
-import static com.android.systemui.shared.system.NavigationBarCompat.FLAG_DISABLE_SWIPE_UP;
-import static com.android.systemui.shared.system.NavigationBarCompat.FLAG_SHOW_OVERVIEW_BUTTON;
-import static com.android.systemui.shared.system.NavigationBarCompat.InteractionType;
 import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_INPUT_MONITOR;
 import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SUPPORTS_WINDOW_CORNERS;
 import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SYSUI_PROXY;
@@ -32,7 +30,6 @@
 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_BOUNCER_SHOWING;
 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_NAV_BAR_HIDDEN;
 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED;
-import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_SCREEN_PINNING;
 
 import android.annotation.FloatRange;
 import android.app.ActivityTaskManager;
@@ -61,7 +58,6 @@
 import com.android.internal.policy.ScreenDecorationsUtils;
 import com.android.systemui.Dependency;
 import com.android.systemui.Dumpable;
-import com.android.systemui.Prefs;
 import com.android.systemui.SysUiServiceProvider;
 import com.android.systemui.recents.OverviewProxyService.OverviewProxyListener;
 import com.android.systemui.shared.recents.IOverviewProxy;
@@ -103,10 +99,6 @@
     // Max backoff caps at 5 mins
     private static final long MAX_BACKOFF_MILLIS = 10 * 60 * 1000;
 
-    // Default interaction flags if swipe up is disabled before connecting to launcher
-    private static final int DEFAULT_DISABLE_SWIPE_UP_STATE = FLAG_DISABLE_SWIPE_UP
-            | FLAG_SHOW_OVERVIEW_BUTTON;
-
     private final Context mContext;
     private final Handler mHandler;
     private final Runnable mConnectionRunnable = this::internalConnectToCurrentUser;
@@ -119,7 +111,6 @@
 
     private IOverviewProxy mOverviewProxy;
     private int mConnectionBackoffAttempts;
-    private @InteractionType int mInteractionFlags;
     private @SystemUiStateFlags int mSysUiStateFlags;
     private boolean mBound;
     private boolean mIsEnabled;
@@ -233,27 +224,6 @@
         }
 
         @Override
-        public void setInteractionState(@InteractionType int flags) {
-            if (!verifyCaller("setInteractionState")) {
-                return;
-            }
-            long token = Binder.clearCallingIdentity();
-            try {
-                if (mInteractionFlags != flags) {
-                    mInteractionFlags = flags;
-                    mHandler.post(() -> {
-                        for (int i = mConnectionCallbacks.size() - 1; i >= 0; --i) {
-                            mConnectionCallbacks.get(i).onInteractionFlagsChanged(flags);
-                        }
-                    });
-                }
-            } finally {
-                Prefs.putInt(mContext, Prefs.Key.QUICK_STEP_INTERACTION_FLAGS, mInteractionFlags);
-                Binder.restoreCallingIdentity(token);
-            }
-        }
-
-        @Override
         public Rect getNonMinimizedSplitScreenSecondaryBounds() {
             if (!verifyCaller("getNonMinimizedSplitScreenSecondaryBounds")) {
                 return null;
@@ -380,12 +350,6 @@
         public void onReceive(Context context, Intent intent) {
             updateEnabledState();
 
-            // When launcher service is disabled, reset interaction flags because it is inactive
-            if (!isEnabled()) {
-                mInteractionFlags = getDefaultInteractionFlags();
-                Prefs.remove(mContext, Prefs.Key.QUICK_STEP_INTERACTION_FLAGS);
-            }
-
             // Reconnect immediately, instead of waiting for resume to arrive.
             startConnectionToCurrentUser();
         }
@@ -479,8 +443,6 @@
                 com.android.internal.R.string.config_recentsComponentName));
         mQuickStepIntent = new Intent(ACTION_QUICKSTEP)
                 .setPackage(mRecentsComponentName.getPackageName());
-        mInteractionFlags = Prefs.getInt(mContext, Prefs.Key.QUICK_STEP_INTERACTION_FLAGS,
-                getDefaultInteractionFlags());
         mWindowCornerRadius = ScreenDecorationsUtils.getWindowCornerRadius(mContext.getResources());
         mSupportsRoundedCornersOnWindows = ScreenDecorationsUtils
                 .supportsRoundedCornersOnWindows(mContext.getResources());
@@ -515,7 +477,12 @@
         }
     }
 
-    public void setSystemUiStateFlag(int flag, boolean enabled) {
+    public void setSystemUiStateFlag(int flag, boolean enabled, int displayId) {
+        if (displayId != DEFAULT_DISPLAY) {
+            // Ignore non-default displays for now
+            return;
+        }
+
         int newState = mSysUiStateFlags;
         if (enabled) {
             newState |= flag;
@@ -540,8 +507,6 @@
                 && statusBar.getPanel().isFullyExpanded();
         final boolean bouncerShowing = statusBar != null && statusBar.isBouncerShowing();
         mSysUiStateFlags = 0;
-        mSysUiStateFlags |= ActivityManagerWrapper.getInstance().isScreenPinningActive()
-                ? SYSUI_STATE_SCREEN_PINNING : 0;
         mSysUiStateFlags |= (navBarFragment != null && !navBarFragment.isNavBarWindowVisible())
                 ? SYSUI_STATE_NAV_BAR_HIDDEN : 0;
         mSysUiStateFlags |= panelExpanded
@@ -653,7 +618,6 @@
     public void addCallback(OverviewProxyListener listener) {
         mConnectionCallbacks.add(listener);
         listener.onConnectionChanged(mOverviewProxy != null);
-        listener.onInteractionFlagsChanged(mInteractionFlags);
         listener.onBackButtonAlphaChanged(mBackButtonAlpha, false);
     }
 
@@ -663,7 +627,7 @@
     }
 
     public boolean shouldShowSwipeUpUI() {
-        return isEnabled() && ((mInteractionFlags & FLAG_DISABLE_SWIPE_UP) == 0);
+        return isEnabled() && !QuickStepContract.isLegacyMode(mNavBarMode);
     }
 
     public boolean isEnabled() {
@@ -674,10 +638,6 @@
         return mOverviewProxy;
     }
 
-    public int getInteractionFlags() {
-        return mInteractionFlags;
-    }
-
     private void disconnectFromLauncherService() {
         if (mBound) {
             // Always unbind the service (ie. if called through onNullBinding or onBindingDied)
@@ -693,13 +653,6 @@
         }
     }
 
-    private int getDefaultInteractionFlags() {
-        // If there is no settings available use device default or get it from settings
-        return QuickStepContract.isLegacyMode(mNavBarMode)
-                ? DEFAULT_DISABLE_SWIPE_UP_STATE
-                : 0;
-    }
-
     private void notifyBackButtonAlphaChanged(float alpha, boolean animate) {
         for (int i = mConnectionCallbacks.size() - 1; i >= 0; --i) {
             mConnectionCallbacks.get(i).onBackButtonAlphaChanged(alpha, animate);
@@ -765,7 +718,6 @@
         pw.print("  isCurrentUserSetup="); pw.println(mDeviceProvisionedController
                 .isCurrentUserSetup());
         pw.print("  connectionBackoffAttempts="); pw.println(mConnectionBackoffAttempts);
-        pw.print("  interactionFlags="); pw.println(mInteractionFlags);
 
         pw.print("  quickStepIntent="); pw.println(mQuickStepIntent);
         pw.print("  quickStepIntentResolved="); pw.println(isEnabled());
@@ -775,7 +727,6 @@
     public interface OverviewProxyListener {
         default void onConnectionChanged(boolean isConnected) {}
         default void onQuickStepStarted() {}
-        default void onInteractionFlagsChanged(@InteractionType int flags) {}
         default void onOverviewShown(boolean fromHome) {}
         default void onQuickScrubStarted() {}
         default void onBackButtonAlphaChanged(float alpha, boolean animate) {}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java b/packages/SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java
index ade903d..c3c0d63 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java
@@ -16,7 +16,6 @@
 
 package com.android.systemui.recents;
 
-import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_SCREEN_PINNING;
 import static com.android.systemui.util.leak.RotationUtils.ROTATION_LANDSCAPE;
 import static com.android.systemui.util.leak.RotationUtils.ROTATION_SEASCAPE;
 
@@ -139,7 +138,6 @@
         if (v.getId() == R.id.screen_pinning_ok_button || mRequestWindow == v) {
             try {
                 ActivityTaskManager.getService().startSystemLockTaskMode(taskId);
-                mOverviewProxyService.setSystemUiStateFlag(SYSUI_STATE_SCREEN_PINNING, true);
             } catch (RemoteException e) {}
         }
         clearPrompt();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
index a688f36..9f8ab61 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
@@ -175,9 +175,11 @@
          * @param fullscreenStackBounds The current bounds of the fullscreen stack, in screen
          *                              coordinates.
          * @param dockedStackBounds The current bounds of the docked stack, in screen coordinates.
+         * @param navbarColorManagedByIme {@code true} if navigation bar color is managed by IME.
          */
         default void setSystemUiVisibility(int displayId, int vis, int fullscreenStackVis,
-                int dockedStackVis, int mask, Rect fullscreenStackBounds, Rect dockedStackBounds) {
+                int dockedStackVis, int mask, Rect fullscreenStackBounds, Rect dockedStackBounds,
+                boolean navbarColorManagedByIme) {
         }
 
         /**
@@ -459,7 +461,8 @@
 
     @Override
     public void setSystemUiVisibility(int displayId, int vis, int fullscreenStackVis,
-            int dockedStackVis, int mask, Rect fullscreenStackBounds, Rect dockedStackBounds) {
+            int dockedStackVis, int mask, Rect fullscreenStackBounds, Rect dockedStackBounds,
+            boolean navbarColorManagedByIme) {
         synchronized (mLock) {
             // Don't coalesce these, since it might have one time flags set such as
             // STATUS_BAR_UNHIDE which might get lost.
@@ -469,6 +472,7 @@
             args.argi3 = fullscreenStackVis;
             args.argi4 = dockedStackVis;
             args.argi5 = mask;
+            args.argi6 = navbarColorManagedByIme ? 1 : 0;
             args.arg1 = fullscreenStackBounds;
             args.arg2 = dockedStackBounds;
             mHandler.obtainMessage(MSG_SET_SYSTEMUI_VISIBILITY, args).sendToTarget();
@@ -879,7 +883,8 @@
                     args = (SomeArgs) msg.obj;
                     for (int i = 0; i < mCallbacks.size(); i++) {
                         mCallbacks.get(i).setSystemUiVisibility(args.argi1, args.argi2, args.argi3,
-                                args.argi4, args.argi5, (Rect) args.arg1, (Rect) args.arg2);
+                                args.argi4, args.argi5, (Rect) args.arg1, (Rect) args.arg2,
+                                args.argi6 == 1);
                     }
                     args.recycle();
                     break;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NavigationBarController.java b/packages/SystemUI/src/com/android/systemui/statusbar/NavigationBarController.java
index 85848ca..bd25209 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NavigationBarController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NavigationBarController.java
@@ -53,7 +53,7 @@
 @Singleton
 public class NavigationBarController implements Callbacks {
 
-    private static final String TAG = NavigationBarController.class.getName();
+    private static final String TAG = NavigationBarController.class.getSimpleName();
 
     private final Context mContext;
     private final Handler mHandler;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
index fa22367..1615e65 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
@@ -39,6 +39,7 @@
 import android.os.Trace;
 import android.os.UserHandle;
 import android.provider.DeviceConfig;
+import android.provider.DeviceConfig.Properties;
 import android.util.Log;
 import android.view.View;
 import android.widget.ImageView;
@@ -118,15 +119,18 @@
     private ImageView mBackdropBack;
 
     private boolean mShowCompactMediaSeekbar;
-    private final DeviceConfig.OnPropertyChangedListener mPropertyChangedListener =
-            new DeviceConfig.OnPropertyChangedListener() {
+    private final DeviceConfig.OnPropertiesChangedListener mPropertiesChangedListener =
+            new DeviceConfig.OnPropertiesChangedListener() {
         @Override
-        public void onPropertyChanged(String namespace, String name, String value) {
-            if (SystemUiDeviceConfigFlags.COMPACT_MEDIA_SEEKBAR_ENABLED.equals(name)) {
-                if (DEBUG_MEDIA) {
-                    Log.v(TAG, "DEBUG_MEDIA: compact media seekbar flag updated: " + value);
+        public void onPropertiesChanged(Properties properties) {
+            for (String name : properties.getKeyset()) {
+                if (SystemUiDeviceConfigFlags.COMPACT_MEDIA_SEEKBAR_ENABLED.equals(name)) {
+                    String value = properties.getString(name, null);
+                    if (DEBUG_MEDIA) {
+                        Log.v(TAG, "DEBUG_MEDIA: compact media seekbar flag updated: " + value);
+                    }
+                    mShowCompactMediaSeekbar = "true".equals(value);
                 }
-                mShowCompactMediaSeekbar = "true".equals(value);
             }
         }
     };
@@ -189,9 +193,9 @@
                 DeviceConfig.getProperty(DeviceConfig.NAMESPACE_SYSTEMUI,
                     SystemUiDeviceConfigFlags.COMPACT_MEDIA_SEEKBAR_ENABLED));
 
-        DeviceConfig.addOnPropertyChangedListener(DeviceConfig.NAMESPACE_SYSTEMUI,
+        DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_SYSTEMUI,
                 mContext.getMainExecutor(),
-                mPropertyChangedListener);
+                mPropertiesChangedListener);
     }
 
     public void setUpWithPresenter(NotificationPresenter presenter) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimator.java
index 333239e..0d9f4e7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimator.java
@@ -30,6 +30,7 @@
 import android.view.RemoteAnimationTarget;
 import android.view.SyncRtSurfaceTransactionApplier;
 import android.view.SyncRtSurfaceTransactionApplier.SurfaceParams;
+import android.view.View;
 
 import com.android.internal.policy.ScreenDecorationsUtils;
 import com.android.systemui.Interpolators;
@@ -79,11 +80,12 @@
     }
 
     public RemoteAnimationAdapter getLaunchAnimation(
-            ExpandableNotificationRow sourceNotification, boolean occluded) {
-        if (!mCallback.areLaunchAnimationsEnabled() || occluded) {
+            View sourceView, boolean occluded) {
+        if (!(sourceView instanceof ExpandableNotificationRow) || !mCallback.areLaunchAnimationsEnabled() || occluded) {
             return null;
         }
-        AnimationRunner animationRunner = new AnimationRunner(sourceNotification);
+        AnimationRunner animationRunner = new AnimationRunner(
+                (ExpandableNotificationRow) sourceView);
         return new RemoteAnimationAdapter(animationRunner, ANIMATION_DURATION,
                 ANIMATION_DURATION - 150 /* statusBarTransitionDelay */);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationMenuRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationMenuRow.java
index ef7d20c..73f7732 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationMenuRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationMenuRow.java
@@ -42,7 +42,6 @@
 import com.android.systemui.R;
 import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
 import com.android.systemui.statusbar.AlphaOptimizedImageView;
-import com.android.systemui.statusbar.notification.NotificationUtils;
 import com.android.systemui.statusbar.notification.row.NotificationGuts.GutsContent;
 import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
 
@@ -80,7 +79,7 @@
     private OnMenuEventListener mMenuListener;
     private boolean mDismissRtl;
     private boolean mIsForeground;
-    private final boolean mIsUsingNewInterruptionModel;
+    private final boolean mIsUsingBidirectionalSwipe;
 
     private ValueAnimator mFadeAnimator;
     private boolean mAnimating;
@@ -113,12 +112,19 @@
     private boolean mIsUserTouching;
 
     public NotificationMenuRow(Context context) {
+        //TODO: (b/131242807) not using bidirectional swipe for now
+        this(context, false);
+    }
+
+    // Only needed for testing until we want to turn bidirectional swipe back on
+    @VisibleForTesting
+    NotificationMenuRow(Context context, boolean isUsingBidirectionalSwipe) {
         mContext = context;
         mShouldShowMenu = context.getResources().getBoolean(R.bool.config_showNotificationGear);
         mHandler = new Handler(Looper.getMainLooper());
         mLeftMenuItems = new ArrayList<>();
         mRightMenuItems = new ArrayList<>();
-        mIsUsingNewInterruptionModel = NotificationUtils.useNewInterruptionModel(mContext);
+        mIsUsingBidirectionalSwipe = isUsingBidirectionalSwipe;
     }
 
     @Override
@@ -255,13 +261,13 @@
             mSnoozeItem = createSnoozeItem(mContext);
         }
         mAppOpsItem = createAppOpsItem(mContext);
-        if (mIsUsingNewInterruptionModel) {
+        if (mIsUsingBidirectionalSwipe) {
             mInfoItem = createInfoItem(mContext, !mParent.getEntry().isHighPriority());
         } else {
             mInfoItem = createInfoItem(mContext);
         }
 
-        if (!mIsUsingNewInterruptionModel) {
+        if (!mIsUsingBidirectionalSwipe) {
             if (!isForeground) {
                 mRightMenuItems.add(mSnoozeItem);
             }
@@ -618,12 +624,12 @@
 
     @Override
     public boolean shouldShowGutsOnSnapOpen() {
-        return mIsUsingNewInterruptionModel;
+        return mIsUsingBidirectionalSwipe;
     }
 
     @Override
     public MenuItem menuItemToExposeOnSnap() {
-        return mIsUsingNewInterruptionModel ? mInfoItem : null;
+        return mIsUsingBidirectionalSwipe ? mInfoItem : null;
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
index ebda585..7a9da6b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
@@ -6355,13 +6355,8 @@
 
         @Override
         public boolean canChildBeDismissedInDirection(View v, boolean isRightOrDown) {
-            boolean isValidDirection;
-            if (NotificationUtils.useNewInterruptionModel(mContext)) {
-                isValidDirection = mDismissRtl ? !isRightOrDown : isRightOrDown;
-            } else {
-                isValidDirection = true;
-            }
-            return isValidDirection && canChildBeDismissed(v);
+            //TODO: b/131242807 for why this doesn't do anything with direction
+            return canChildBeDismissed(v);
         }
     };
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoHideController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoHideController.java
index fdf8cce..5912cd7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoHideController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoHideController.java
@@ -95,7 +95,8 @@
 
     @Override
     public void setSystemUiVisibility(int displayId, int vis, int fullscreenStackVis,
-            int dockedStackVis, int mask, Rect fullscreenStackBounds, Rect dockedStackBounds) {
+            int dockedStackVis, int mask, Rect fullscreenStackBounds, Rect dockedStackBounds,
+            boolean navbarColorManagedByIme) {
         if (displayId != mDisplayId) {
             return;
         }
@@ -119,7 +120,7 @@
             if (mSystemUiVisibility != newVal) {
                 mCommandQueue.setSystemUiVisibility(mDisplayId, mSystemUiVisibility,
                         fullscreenStackVis, dockedStackVis, mask, fullscreenStackBounds,
-                        dockedStackBounds);
+                        dockedStackBounds, navbarColorManagedByIme);
             }
 
             notifySystemUiVisibilityChanged(mSystemUiVisibility);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarController.java
index b590ca7..b0b656a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarController.java
@@ -79,6 +79,9 @@
     private final Rect mLastDockedBounds = new Rect();
     private boolean mQsCustomizing;
 
+    private boolean mDirectReplying;
+    private boolean mNavbarColorManagedByIme;
+
     @Inject
     public LightBarController(Context ctx, DarkIconDispatcher darkIconDispatcher,
             BatteryController batteryController) {
@@ -100,7 +103,7 @@
 
     public void onSystemUiVisibilityChanged(int fullscreenStackVis, int dockedStackVis,
             int mask, Rect fullscreenStackBounds, Rect dockedStackBounds, boolean sbModeChanged,
-            int statusBarMode) {
+            int statusBarMode, boolean navbarColorManagedByIme) {
         int oldFullscreen = mFullscreenStackVisibility;
         int newFullscreen = (oldFullscreen & ~mask) | (fullscreenStackVis & mask);
         int diffFullscreen = newFullscreen ^ oldFullscreen;
@@ -122,12 +125,13 @@
         mFullscreenStackVisibility = newFullscreen;
         mDockedStackVisibility = newDocked;
         mLastStatusBarMode = statusBarMode;
+        mNavbarColorManagedByIme = navbarColorManagedByIme;
         mLastFullscreenBounds.set(fullscreenStackBounds);
         mLastDockedBounds.set(dockedStackBounds);
     }
 
     public void onNavigationVisibilityChanged(int vis, int mask, boolean nbModeChanged,
-            int navigationBarMode) {
+            int navigationBarMode, boolean navbarColorManagedByIme) {
         int oldVis = mSystemUiVisibility;
         int newVis = (oldVis & ~mask) | (vis & mask);
         int diffVis = newVis ^ oldVis;
@@ -136,21 +140,24 @@
             boolean last = mNavigationLight;
             mHasLightNavigationBar = isLight(vis, navigationBarMode,
                     View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR);
-            mNavigationLight = mHasLightNavigationBar && !mForceDarkForScrim && !mQsCustomizing;
+            mNavigationLight = mHasLightNavigationBar
+                    && (mDirectReplying && mNavbarColorManagedByIme || !mForceDarkForScrim)
+                    && !mQsCustomizing;
             if (mNavigationLight != last) {
                 updateNavigation();
             }
         }
         mSystemUiVisibility = newVis;
         mLastNavigationBarMode = navigationBarMode;
+        mNavbarColorManagedByIme = navbarColorManagedByIme;
     }
 
     private void reevaluate() {
         onSystemUiVisibilityChanged(mFullscreenStackVisibility,
                 mDockedStackVisibility, 0 /* mask */, mLastFullscreenBounds, mLastDockedBounds,
-                true /* sbModeChange*/, mLastStatusBarMode);
+                true /* sbModeChange*/, mLastStatusBarMode, mNavbarColorManagedByIme);
         onNavigationVisibilityChanged(mSystemUiVisibility, 0 /* mask */, true /* nbModeChanged */,
-                mLastNavigationBarMode);
+                mLastNavigationBarMode, mNavbarColorManagedByIme);
     }
 
     public void setQsCustomizing(boolean customizing) {
@@ -159,6 +166,16 @@
         reevaluate();
     }
 
+    /**
+     * Sets whether the direct-reply is in use or not.
+     * @param directReplying {@code true} when the direct-reply is in-use.
+     */
+    public void setDirectReplying(boolean directReplying) {
+        if (mDirectReplying == directReplying) return;
+        mDirectReplying = directReplying;
+        reevaluate();
+    }
+
     public void setScrimState(ScrimState scrimState, float scrimBehindAlpha,
             GradientColors scrimInFrontColor) {
         boolean forceDarkForScrimLast = mForceDarkForScrim;
@@ -260,7 +277,9 @@
         pw.print(" mLastNavigationBarMode="); pw.println(mLastNavigationBarMode);
 
         pw.print(" mForceDarkForScrim="); pw.print(mForceDarkForScrim);
-        pw.print(" mQsCustomizing="); pw.println(mQsCustomizing);
+        pw.print(" mQsCustomizing="); pw.print(mQsCustomizing);
+        pw.print(" mDirectReplying="); pw.println(mDirectReplying);
+        pw.print(" mNavbarColorManagedByIme="); pw.println(mNavbarColorManagedByIme);
 
         pw.println();
 
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 de57066..38eed16 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
@@ -24,11 +24,9 @@
 import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_3BUTTON;
 
 import static com.android.systemui.recents.OverviewProxyService.OverviewProxyListener;
-import static com.android.systemui.shared.system.NavigationBarCompat.InteractionType;
 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_A11Y_BUTTON_CLICKABLE;
 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE;
 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_NAV_BAR_HIDDEN;
-import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_SCREEN_PINNING;
 import static com.android.systemui.statusbar.phone.BarTransitions.MODE_LIGHTS_OUT;
 import static com.android.systemui.statusbar.phone.BarTransitions.MODE_LIGHTS_OUT_TRANSPARENT;
 import static com.android.systemui.statusbar.phone.BarTransitions.MODE_OPAQUE;
@@ -197,12 +195,6 @@
         }
 
         @Override
-        public void onInteractionFlagsChanged(@InteractionType int flags) {
-            mNavigationBarView.updateStates();
-            updateScreenPinningGestures();
-        }
-
-        @Override
         public void startAssistant(Bundle bundle) {
             mAssistManager.startAssist(bundle);
         }
@@ -335,7 +327,10 @@
         filter.addAction(Intent.ACTION_USER_SWITCHED);
         getContext().registerReceiverAsUser(mBroadcastReceiver, UserHandle.ALL, filter, null, null);
         notifyNavigationBarScreenOn();
+
         mOverviewProxyService.addCallback(mOverviewProxyListener);
+        mOverviewProxyService.setSystemUiStateFlag(SYSUI_STATE_NAV_BAR_HIDDEN,
+                !isNavBarWindowVisible(), mDisplayId);
 
         // Currently there is no accelerometer sensor on non-default display.
         if (mIsOnDefaultDisplay) {
@@ -471,7 +466,7 @@
             if (DEBUG_WINDOW_STATE) Log.d(TAG, "Navigation bar " + windowStateToString(state));
 
             mOverviewProxyService.setSystemUiStateFlag(SYSUI_STATE_NAV_BAR_HIDDEN,
-                    !isNavBarWindowVisible());
+                    !isNavBarWindowVisible(), mDisplayId);
             mNavigationBarView.getRotateSuggestionButton()
                     .onNavigationBarWindowVisibilityChange(isNavBarWindowVisible());
         }
@@ -512,12 +507,13 @@
         mAutoHideController.touchAutoHide();
 
         mLightBarController.onNavigationVisibilityChanged(mSystemUiVisibility, 0 /* mask */,
-                true /* nbModeChanged */, mNavigationBarMode);
+                true /* nbModeChanged */, mNavigationBarMode, false /* navbarColorManagedByIme */);
     }
 
     @Override
     public void setSystemUiVisibility(int displayId, int vis, int fullscreenStackVis,
-            int dockedStackVis, int mask, Rect fullscreenStackBounds, Rect dockedStackBounds) {
+            int dockedStackVis, int mask, Rect fullscreenStackBounds, Rect dockedStackBounds,
+            boolean navbarColorManagedByIme) {
         if (displayId != mDisplayId) {
             return;
         }
@@ -545,7 +541,7 @@
             }
         }
         mLightBarController.onNavigationVisibilityChanged(
-                vis, mask, nbModeChanged, mNavigationBarMode);
+                vis, mask, nbModeChanged, mNavigationBarMode, navbarColorManagedByIme);
     }
 
     private @TransitionMode int computeBarMode(int oldVis, int newVis) {
@@ -831,7 +827,6 @@
                     activityManager.stopSystemLockTaskMode();
                     // When exiting refresh disabled flags.
                     mNavigationBarView.updateNavButtonIcons();
-                    mOverviewProxyService.setSystemUiStateFlag(SYSUI_STATE_SCREEN_PINNING, false);
                 }
             }
 
@@ -883,9 +878,10 @@
         boolean clickable = (flags & SYSUI_STATE_A11Y_BUTTON_CLICKABLE) != 0;
         boolean longClickable = (flags & SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE) != 0;
         mNavigationBarView.setAccessibilityButtonState(clickable, longClickable);
-        mOverviewProxyService.setSystemUiStateFlag(SYSUI_STATE_A11Y_BUTTON_CLICKABLE, clickable);
         mOverviewProxyService.setSystemUiStateFlag(
-                SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE, longClickable);
+                SYSUI_STATE_A11Y_BUTTON_CLICKABLE, clickable, mDisplayId);
+        mOverviewProxyService.setSystemUiStateFlag(
+                SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE, longClickable, mDisplayId);
     }
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
index 4333200..1c03844 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
@@ -19,7 +19,6 @@
 import static android.view.WindowManagerPolicyConstants.NAV_BAR_INVALID;
 import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_3BUTTON;
 
-import static com.android.systemui.shared.system.NavigationBarCompat.FLAG_SHOW_OVERVIEW_BUTTON;
 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED;
 import static com.android.systemui.statusbar.phone.BarTransitions.MODE_OPAQUE;
 
@@ -637,10 +636,6 @@
         // as they are used for exiting.
         final boolean pinningActive = ActivityManagerWrapper.getInstance().isScreenPinningActive();
         if (mOverviewProxyService.isEnabled()) {
-            // Use interaction flags to show/hide navigation buttons but will be shown if required
-            // to exit screen pinning.
-            final int flags = mOverviewProxyService.getInteractionFlags();
-            disableRecent |= (flags & FLAG_SHOW_OVERVIEW_BUTTON) == 0;
             if (pinningActive) {
                 disableBack = disableHome = false;
             }
@@ -730,7 +725,7 @@
     public void onPanelExpandedChange(boolean expanded) {
         updateSlippery();
         mOverviewProxyService.setSystemUiStateFlag(SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED,
-                expanded);
+                expanded, getContext().getDisplayId());
     }
 
     public void updateStates() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationModeController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationModeController.java
index a00feeb..fa2263f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationModeController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationModeController.java
@@ -17,8 +17,10 @@
 package com.android.systemui.statusbar.phone;
 
 import static android.content.Intent.ACTION_OVERLAY_CHANGED;
-import static android.content.Intent.ACTION_USER_SWITCHED;
+import static android.os.UserHandle.USER_CURRENT;
 import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_3BUTTON;
+import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_3BUTTON_OVERLAY;
+import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL_OVERLAY;
 
 import android.content.BroadcastReceiver;
 import android.content.Context;
@@ -28,16 +30,21 @@
 import android.content.pm.PackageManager;
 import android.content.res.ApkAssets;
 import android.os.PatternMatcher;
+import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.os.UserHandle;
 import android.util.Log;
+import android.util.SparseBooleanArray;
 
 import com.android.systemui.Dumpable;
+import com.android.systemui.UiOffloadThread;
 import com.android.systemui.shared.system.ActivityManagerWrapper;
+import com.android.systemui.statusbar.policy.DeviceProvisionedController;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
 import java.util.ArrayList;
+import java.util.Arrays;
 
 import javax.inject.Inject;
 import javax.inject.Singleton;
@@ -48,7 +55,7 @@
 @Singleton
 public class NavigationModeController implements Dumpable {
 
-    private static final String TAG = NavigationModeController.class.getName();
+    private static final String TAG = NavigationModeController.class.getSimpleName();
     private static final boolean DEBUG = true;
 
     public interface ModeChangedListener {
@@ -57,6 +64,10 @@
 
     private final Context mContext;
     private final IOverlayManager mOverlayManager;
+    private final DeviceProvisionedController mDeviceProvisionedController;
+    private final UiOffloadThread mUiOffloadThread;
+
+    private SparseBooleanArray mRestoreGesturalNavBarMode = new SparseBooleanArray();
 
     private int mMode = NAV_BAR_MODE_3BUTTON;
     private ArrayList<ModeChangedListener> mListeners = new ArrayList<>();
@@ -64,36 +75,90 @@
     private BroadcastReceiver mReceiver = new BroadcastReceiver() {
         @Override
         public void onReceive(Context context, Intent intent) {
-            context = getCurrentUserContext();
-            int mode = getCurrentInteractionMode(context);
-            mMode = mode;
-            if (DEBUG) {
-                Log.e(TAG, "ACTION_OVERLAY_CHANGED: mode=" + mMode
-                        + " contextUser=" + context.getUserId());
-                dumpAssetPaths(context);
-            }
-
-            for (int i = 0; i < mListeners.size(); i++) {
-                mListeners.get(i).onNavigationModeChanged(mode);
+            if (intent.getAction().equals(ACTION_OVERLAY_CHANGED)) {
+                if (DEBUG) {
+                    Log.d(TAG, "ACTION_OVERLAY_CHANGED");
+                }
+                updateCurrentInteractionMode(true /* notify */);
             }
         }
     };
 
+    private final DeviceProvisionedController.DeviceProvisionedListener mDeviceProvisionedCallback =
+            new DeviceProvisionedController.DeviceProvisionedListener() {
+                @Override
+                public void onDeviceProvisionedChanged() {
+                    if (DEBUG) {
+                        Log.d(TAG, "onDeviceProvisionedChanged: "
+                                + mDeviceProvisionedController.isDeviceProvisioned());
+                    }
+                    // Once the device has been provisioned, check if we can restore gestural nav
+                    restoreGesturalNavOverlayIfNecessary();
+                }
+
+                @Override
+                public void onUserSetupChanged() {
+                    if (DEBUG) {
+                        Log.d(TAG, "onUserSetupChanged: "
+                                + mDeviceProvisionedController.isCurrentUserSetup());
+                    }
+                    // Once the user has been setup, check if we can restore gestural nav
+                    restoreGesturalNavOverlayIfNecessary();
+                }
+
+                @Override
+                public void onUserSwitched() {
+                    if (DEBUG) {
+                        Log.d(TAG, "onUserSwitched: "
+                                + ActivityManagerWrapper.getInstance().getCurrentUserId());
+                    }
+
+                    // Update the nav mode for the current user
+                    updateCurrentInteractionMode(true /* notify */);
+
+                    // When switching users, defer enabling the gestural nav overlay until the user
+                    // is all set up
+                    deferGesturalNavOverlayIfNecessary();
+                }
+            };
+
     @Inject
-    public NavigationModeController(Context context) {
+    public NavigationModeController(Context context,
+            DeviceProvisionedController deviceProvisionedController,
+            UiOffloadThread uiOffloadThread) {
         mContext = context;
         mOverlayManager = IOverlayManager.Stub.asInterface(
                 ServiceManager.getService(Context.OVERLAY_SERVICE));
+        mUiOffloadThread = uiOffloadThread;
+        mDeviceProvisionedController = deviceProvisionedController;
+        mDeviceProvisionedController.addCallback(mDeviceProvisionedCallback);
 
         IntentFilter overlayFilter = new IntentFilter(ACTION_OVERLAY_CHANGED);
         overlayFilter.addDataScheme("package");
         overlayFilter.addDataSchemeSpecificPart("android", PatternMatcher.PATTERN_LITERAL);
         mContext.registerReceiverAsUser(mReceiver, UserHandle.ALL, overlayFilter, null, null);
 
-        IntentFilter userFilter = new IntentFilter(ACTION_USER_SWITCHED);
-        mContext.registerReceiverAsUser(mReceiver, UserHandle.ALL, userFilter, null, null);
+        updateCurrentInteractionMode(false /* notify */);
 
-        mMode = getCurrentInteractionMode(getCurrentUserContext());
+        // Check if we need to defer enabling gestural nav
+        deferGesturalNavOverlayIfNecessary();
+    }
+
+    public void updateCurrentInteractionMode(boolean notify) {
+        Context context = getCurrentUserContext();
+        int mode = getCurrentInteractionMode(context);
+        mMode = mode;
+        if (DEBUG) {
+            Log.e(TAG, "updateCurrentInteractionMode: mode=" + mMode
+                    + " contextUser=" + context.getUserId());
+            dumpAssetPaths(context);
+        }
+
+        if (notify) {
+            for (int i = 0; i < mListeners.size(); i++) {
+                mListeners.get(i).onNavigationModeChanged(mode);
+            }
+        }
     }
 
     public int addListener(ModeChangedListener listener) {
@@ -129,10 +194,77 @@
         }
     }
 
+    private void deferGesturalNavOverlayIfNecessary() {
+        final int userId = mDeviceProvisionedController.getCurrentUser();
+        mRestoreGesturalNavBarMode.put(userId, false);
+        if (mDeviceProvisionedController.isDeviceProvisioned()
+                && mDeviceProvisionedController.isCurrentUserSetup()) {
+            // User is already setup and device is provisioned, nothing to do
+            if (DEBUG) {
+                Log.d(TAG, "deferGesturalNavOverlayIfNecessary: device is provisioned and user is "
+                        + "setup");
+            }
+            return;
+        }
+
+        ArrayList<String> defaultOverlays = new ArrayList<>();
+        try {
+            defaultOverlays.addAll(Arrays.asList(mOverlayManager.getDefaultOverlayPackages()));
+        } catch (RemoteException e) {
+            Log.e(TAG, "deferGesturalNavOverlayIfNecessary: failed to fetch default overlays");
+        }
+        if (!defaultOverlays.contains(NAV_BAR_MODE_GESTURAL_OVERLAY)) {
+            // No default gesture nav overlay
+            if (DEBUG) {
+                Log.d(TAG, "deferGesturalNavOverlayIfNecessary: no default gestural overlay, "
+                        + "default=" + defaultOverlays);
+            }
+            return;
+        }
+
+        // If the default is gestural, force-enable three button mode until the device is
+        // provisioned
+        setModeOverlay(NAV_BAR_MODE_3BUTTON_OVERLAY, USER_CURRENT);
+        mRestoreGesturalNavBarMode.put(userId, true);
+        if (DEBUG) {
+            Log.d(TAG, "deferGesturalNavOverlayIfNecessary: setting to 3 button mode");
+        }
+    }
+
+    private void restoreGesturalNavOverlayIfNecessary() {
+        if (DEBUG) {
+            Log.d(TAG, "restoreGesturalNavOverlayIfNecessary: needs restore="
+                    + mRestoreGesturalNavBarMode);
+        }
+        final int userId = mDeviceProvisionedController.getCurrentUser();
+        if (mRestoreGesturalNavBarMode.get(userId)) {
+            // Restore the gestural state if necessary
+            setModeOverlay(NAV_BAR_MODE_GESTURAL_OVERLAY, USER_CURRENT);
+            mRestoreGesturalNavBarMode.put(userId, false);
+        }
+    }
+
+    public void setModeOverlay(String overlayPkg, int userId) {
+        mUiOffloadThread.submit(() -> {
+            try {
+                mOverlayManager.setEnabledExclusiveInCategory(overlayPkg, userId);
+            } catch (RemoteException e) {
+                Log.e(TAG, "Failed to enable overlay " + overlayPkg + " for user " + userId);
+            }
+        });
+    }
+
     @Override
     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
         pw.println("NavigationModeController:");
         pw.println("  mode=" + mMode);
+        String defaultOverlays = "";
+        try {
+            defaultOverlays = String.join(", ", mOverlayManager.getDefaultOverlayPackages());
+        } catch (RemoteException e) {
+            defaultOverlays = "failed_to_fetch";
+        }
+        pw.println("  defaultOverlays=" + defaultOverlays);
         dumpAssetPaths(getCurrentUserContext());
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
index d5706e3..27368de 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
@@ -44,6 +44,7 @@
 import com.android.systemui.UiOffloadThread;
 import com.android.systemui.privacy.PrivacyItem;
 import com.android.systemui.privacy.PrivacyItemController;
+import com.android.systemui.privacy.PrivacyType;
 import com.android.systemui.qs.tiles.DndTile;
 import com.android.systemui.qs.tiles.RotationLockTile;
 import com.android.systemui.statusbar.CommandQueue;
@@ -229,9 +230,11 @@
         mIconController.setIconVisibility(mSlotDataSaver, false);
 
         // privacy items
-        mIconController.setIcon(mSlotMicrophone, R.drawable.stat_sys_mic_none, null);
+        mIconController.setIcon(mSlotMicrophone, R.drawable.stat_sys_mic_none,
+                PrivacyType.TYPE_MICROPHONE.getName(mContext));
         mIconController.setIconVisibility(mSlotMicrophone, false);
-        mIconController.setIcon(mSlotCamera, R.drawable.stat_sys_camera, null);
+        mIconController.setIcon(mSlotCamera, R.drawable.stat_sys_camera,
+                PrivacyType.TYPE_CAMERA.getName(mContext));
         mIconController.setIconVisibility(mSlotCamera, false);
         mIconController.setIcon(mSlotLocation, LOCATION_STATUS_ICON_ID,
                 mContext.getString(R.string.accessibility_location_active));
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
index 93db82d..5c16972 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -593,6 +593,7 @@
                 updateScrimController();
             };
     private ActivityIntentHelper mActivityIntentHelper;
+    private ShadeController mShadeController;
 
     @Override
     public void onActiveStateChanged(int code, int uid, String packageName, boolean active) {
@@ -710,7 +711,8 @@
 
         setSystemUiVisibility(mDisplayId, result.mSystemUiVisibility,
                 result.mFullscreenStackSysUiVisibility, result.mDockedStackSysUiVisibility,
-                0xffffffff, result.mFullscreenStackBounds, result.mDockedStackBounds);
+                0xffffffff, result.mFullscreenStackBounds, result.mDockedStackBounds,
+                result.mNavbarColorManagedByIme);
         topAppWindowChanged(mDisplayId, result.mMenuVisible);
         // StatusBarManagerService has a back up of IME token and it's restored here.
         setImeWindowStatus(mDisplayId, result.mImeToken, result.mImeWindowVis,
@@ -1062,7 +1064,7 @@
         final StatusBarRemoteInputCallback mStatusBarRemoteInputCallback =
                 (StatusBarRemoteInputCallback) Dependency.get(
                         NotificationRemoteInputManager.Callback.class);
-        final ShadeController shadeController = Dependency.get(ShadeController.class);
+        mShadeController = Dependency.get(ShadeController.class);
         final ActivityStarter activityStarter = Dependency.get(ActivityStarter.class);
 
         mNotificationActivityStarter = new StatusBarNotificationActivityStarter(mContext,
@@ -1070,7 +1072,7 @@
                 mHeadsUpManager, activityStarter, mActivityLaunchAnimator,
                 mBarService, mStatusBarStateController, mKeyguardManager, mDreamManager,
                 mRemoteInputManager, mStatusBarRemoteInputCallback, mGroupManager,
-                mLockscreenUserManager, shadeController, mKeyguardMonitor,
+                mLockscreenUserManager, mShadeController, mKeyguardMonitor,
                 mNotificationInterruptionStateProvider, mMetricsLogger,
                 new LockPatternUtils(mContext), Dependency.get(MAIN_HANDLER),
                 Dependency.get(BG_HANDLER), mActivityIntentHelper, mBubbleController);
@@ -2077,7 +2079,8 @@
 
     @Override // CommandQueue
     public void setSystemUiVisibility(int displayId, int vis, int fullscreenStackVis,
-            int dockedStackVis, int mask, Rect fullscreenStackBounds, Rect dockedStackBounds) {
+            int dockedStackVis, int mask, Rect fullscreenStackBounds, Rect dockedStackBounds,
+            boolean navbarColorManagedByIme) {
         if (displayId != mDisplayId) {
             return;
         }
@@ -2114,7 +2117,8 @@
             }
         }
         mLightBarController.onSystemUiVisibilityChanged(fullscreenStackVis, dockedStackVis,
-                mask, fullscreenStackBounds, dockedStackBounds, sbModeChanged, mStatusBarMode);
+                mask, fullscreenStackBounds, dockedStackBounds, sbModeChanged, mStatusBarMode,
+                navbarColorManagedByIme);
     }
 
     @Override
@@ -2235,11 +2239,12 @@
         Log.v(TAG, "setLightsOn(" + on + ")");
         if (on) {
             setSystemUiVisibility(mDisplayId, 0, 0, 0, View.SYSTEM_UI_FLAG_LOW_PROFILE,
-                    mLastFullscreenStackBounds, mLastDockedStackBounds);
+                    mLastFullscreenStackBounds, mLastDockedStackBounds,
+                    false /* navbarColorManagedByIme */);
         } else {
             setSystemUiVisibility(mDisplayId, View.SYSTEM_UI_FLAG_LOW_PROFILE, 0, 0,
                     View.SYSTEM_UI_FLAG_LOW_PROFILE, mLastFullscreenStackBounds,
-                    mLastDockedStackBounds);
+                    mLastDockedStackBounds, false /* navbarColorManagedByIme */);
         }
     }
 
@@ -3584,7 +3589,7 @@
 
         // Notify overview proxy service of the new states
         Dependency.get(OverviewProxyService.class).setSystemUiStateFlag(SYSUI_STATE_BOUNCER_SHOWING,
-                isBouncerShowing());
+                isBouncerShowing(), mContext.getDisplayId());
     }
 
     /**
@@ -4344,6 +4349,13 @@
     @Override
     public void startPendingIntentDismissingKeyguard(
             final PendingIntent intent, @Nullable final Runnable intentSentUiThreadCallback) {
+        startPendingIntentDismissingKeyguard(intent, intentSentUiThreadCallback, null /* row */);
+    }
+
+    @Override
+    public void startPendingIntentDismissingKeyguard(
+            final PendingIntent intent, @Nullable final Runnable intentSentUiThreadCallback,
+            View associatedView) {
         final boolean afterKeyguardGone = intent.isActivity()
                 && mActivityIntentHelper.wouldLaunchResolverActivity(intent.getIntent(),
                 mLockscreenUserManager.getCurrentUserId());
@@ -4351,7 +4363,8 @@
         executeActionDismissingKeyguard(() -> {
             try {
                 intent.send(null, 0, null, null, null, null, getActivityOptions(
-                        null /* animationAdapter */));
+                        mActivityLaunchAnimator.getLaunchAnimation(associatedView,
+                                mShadeController.isOccluded())));
             } catch (PendingIntent.CanceledException e) {
                 // the stack trace isn't very helpful here.
                 // Just log the exception message.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java
index 9923ec9..41fa346 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java
@@ -56,7 +56,7 @@
 import com.android.systemui.bubbles.BubbleController;
 import com.android.systemui.plugins.statusbar.phone.NavBarButtonProvider.ButtonInterface;
 import com.android.systemui.recents.OverviewProxyService;
-import com.android.systemui.shared.system.NavigationBarCompat;
+import com.android.systemui.shared.system.QuickStepContract;
 
 public class KeyButtonView extends ImageView implements ButtonInterface {
     private static final String TAG = KeyButtonView.class.getSimpleName();
@@ -244,11 +244,11 @@
                 y = (int)ev.getRawY();
 
                 boolean exceededTouchSlopX = Math.abs(x - mTouchDownX) > (mIsVertical
-                        ? NavigationBarCompat.getQuickScrubTouchSlopPx()
-                        : NavigationBarCompat.getQuickStepTouchSlopPx());
+                        ? QuickStepContract.getQuickScrubTouchSlopPx()
+                        : QuickStepContract.getQuickStepTouchSlopPx());
                 boolean exceededTouchSlopY = Math.abs(y - mTouchDownY) > (mIsVertical
-                        ? NavigationBarCompat.getQuickStepTouchSlopPx()
-                        : NavigationBarCompat.getQuickScrubTouchSlopPx());
+                        ? QuickStepContract.getQuickStepTouchSlopPx()
+                        : QuickStepContract.getQuickScrubTouchSlopPx());
                 if (exceededTouchSlopX || exceededTouchSlopY) {
                     // When quick step is enabled, prevent animating the ripple triggered by
                     // setPressed and decide to run it on touch up
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java
index 11e5625..c398ad8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java
@@ -94,8 +94,7 @@
         mPhone = phone;
         mDefaults = defaults;
         mSubscriptionInfo = info;
-        mPhoneStateListener = new MobilePhoneStateListener(info.getSubscriptionId(),
-                receiverLooper);
+        mPhoneStateListener = new MobilePhoneStateListener(receiverLooper);
         mNetworkNameSeparator = getStringIfExists(R.string.status_bar_network_name_separator);
         mNetworkNameDefault = getStringIfExists(
                 com.android.internal.R.string.lockscreen_carrier_default);
@@ -574,8 +573,8 @@
     }
 
     class MobilePhoneStateListener extends PhoneStateListener {
-        public MobilePhoneStateListener(int subId, Looper looper) {
-            super(subId, looper);
+        public MobilePhoneStateListener(Looper looper) {
+            super(looper);
         }
 
         @Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
index faf63c8..950f07d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
@@ -662,8 +662,9 @@
                 cachedControllers.remove(subId);
             } else {
                 MobileSignalController controller = new MobileSignalController(mContext, mConfig,
-                        mHasMobileDataFeature, mPhone, mCallbackHandler,
-                        this, subscriptions.get(i), mSubDefaults, mReceiverHandler.getLooper());
+                        mHasMobileDataFeature, mPhone.createForSubscriptionId(subId),
+                        mCallbackHandler, this, subscriptions.get(i),
+                        mSubDefaults, mReceiverHandler.getLooper());
                 controller.setUserSetupComplete(mUserSetup);
                 mMobileSignalControllers.put(subId, controller);
                 if (subscriptions.get(i).getSimSlotIndex() == 0) {
@@ -1049,7 +1050,8 @@
         SubscriptionInfo info = new SubscriptionInfo(id, "", simSlotIndex, "", "", 0, 0, "", 0,
                 null, null, null, "", false, null, null);
         MobileSignalController controller = new MobileSignalController(mContext,
-                mConfig, mHasMobileDataFeature, mPhone, mCallbackHandler, this, info,
+                mConfig, mHasMobileDataFeature,
+                mPhone.createForSubscriptionId(info.getSubscriptionId()), mCallbackHandler, this, info,
                 mSubDefaults, mReceiverHandler.getLooper());
         mMobileSignalControllers.put(id, controller);
         controller.getState().userSetup = true;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
index c1950a2..43795dc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
@@ -63,6 +63,7 @@
 import com.android.systemui.statusbar.notification.collection.NotificationEntry.EditedSuggestionInfo;
 import com.android.systemui.statusbar.notification.row.wrapper.NotificationViewWrapper;
 import com.android.systemui.statusbar.notification.stack.StackStateAnimator;
+import com.android.systemui.statusbar.phone.LightBarController;
 
 import java.util.function.Consumer;
 
@@ -516,10 +517,12 @@
         private final Drawable mBackground;
         private RemoteInputView mRemoteInputView;
         boolean mShowImeOnInputConnection;
+        private LightBarController mLightBarController;
 
         public RemoteEditText(Context context, AttributeSet attrs) {
             super(context, attrs);
             mBackground = getBackground();
+            mLightBarController = Dependency.get(LightBarController.class);
         }
 
         private void defocusIfNeeded(boolean animate) {
@@ -558,6 +561,9 @@
             if (!focused) {
                 defocusIfNeeded(true /* animate */);
             }
+            if (!mRemoteInputView.mRemoved) {
+                mLightBarController.setDirectReplying(focused);
+            }
         }
 
         @Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyConstants.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyConstants.java
index f02b544..655c29c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyConstants.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyConstants.java
@@ -93,10 +93,10 @@
     }
 
     private void registerDeviceConfigListener() {
-        DeviceConfig.addOnPropertyChangedListener(
+        DeviceConfig.addOnPropertiesChangedListener(
                 DeviceConfig.NAMESPACE_SYSTEMUI,
                 this::postToHandler,
-                this::onDeviceConfigPropertyChanged);
+                (properties) -> onDeviceConfigPropertiesChanged(properties.getNamespace()));
     }
 
     private void postToHandler(Runnable r) {
@@ -104,10 +104,10 @@
     }
 
     @VisibleForTesting
-    void onDeviceConfigPropertyChanged(String namespace, String name, String value) {
+    void onDeviceConfigPropertiesChanged(String namespace) {
         if (!DeviceConfig.NAMESPACE_SYSTEMUI.equals(namespace)) {
             Log.e(TAG, "Received update from DeviceConfig for unrelated namespace: "
-                    + namespace + " " + name + "=" + value);
+                    + namespace);
             return;
         }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyView.java
index 0f7a0f0..640f0f0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyView.java
@@ -349,7 +349,7 @@
                             smartReplyController.smartActionClicked(
                                     entry, actionIndex, action, smartActions.fromAssistant);
                             headsUpManager.removeNotification(entry.key, true);
-                        });
+                        }, entry.getRow());
         if (useDelayedOnClickListener) {
             onClickListener = new DelayedOnClickListener(onClickListener,
                     smartReplyView.mConstants.getOnClickInitDelay());
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/CarrierTextControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/CarrierTextControllerTest.java
index 212c8f5..e312990 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/CarrierTextControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/CarrierTextControllerTest.java
@@ -189,10 +189,6 @@
         when(mKeyguardUpdateMonitor.getSimState(anyInt())).thenReturn(IccCardConstants.State.READY);
         when(mKeyguardUpdateMonitor.getSubscriptionInfo(anyBoolean())).thenReturn(list);
 
-        // STOPSHIP(b/130246708) This line makes sure that SubscriptionManager provides the
-        // same answer as KeyguardUpdateMonitor. Remove when this is addressed
-        when(mSubscriptionManager.getActiveSubscriptionInfoList(anyBoolean())).thenReturn(list);
-
         mKeyguardUpdateMonitor.mServiceStates = new HashMap<>();
 
         ArgumentCaptor<CarrierTextController.CarrierTextCallbackInfo> captor =
@@ -217,10 +213,6 @@
         when(mKeyguardUpdateMonitor.getSimState(anyInt())).thenReturn(IccCardConstants.State.READY);
         when(mKeyguardUpdateMonitor.getSubscriptionInfo(anyBoolean())).thenReturn(list);
 
-        // STOPSHIP(b/130246708) This line makes sure that SubscriptionManager provides the
-        // same answer as KeyguardUpdateMonitor. Remove when this is addressed
-        when(mSubscriptionManager.getActiveSubscriptionInfoList(anyBoolean())).thenReturn(list);
-
         mKeyguardUpdateMonitor.mServiceStates = new HashMap<>();
 
         ArgumentCaptor<CarrierTextController.CarrierTextCallbackInfo> captor =
@@ -243,11 +235,6 @@
         when(mKeyguardUpdateMonitor.getSubscriptionInfo(anyBoolean())).thenReturn(
                 new ArrayList<>());
 
-        // STOPSHIP(b/130246708) This line makes sure that SubscriptionManager provides the
-        // same answer as KeyguardUpdateMonitor. Remove when this is addressed
-        when(mSubscriptionManager.getActiveSubscriptionInfoList(anyBoolean())).thenReturn(
-                new ArrayList<>());
-
         ArgumentCaptor<CarrierTextController.CarrierTextCallbackInfo> captor =
                 ArgumentCaptor.forClass(
                         CarrierTextController.CarrierTextCallbackInfo.class);
@@ -271,10 +258,6 @@
         when(mKeyguardUpdateMonitor.getSimState(anyInt())).thenReturn(IccCardConstants.State.READY);
         when(mKeyguardUpdateMonitor.getSubscriptionInfo(anyBoolean())).thenReturn(list);
 
-        // STOPSHIP(b/130246708) This line makes sure that SubscriptionManager provides the
-        // same answer as KeyguardUpdateMonitor. Remove when this is addressed
-        when(mSubscriptionManager.getActiveSubscriptionInfoList(anyBoolean())).thenReturn(list);
-
         mKeyguardUpdateMonitor.mServiceStates = new HashMap<>();
 
         ArgumentCaptor<CarrierTextController.CarrierTextCallbackInfo> captor =
@@ -300,10 +283,6 @@
                 .thenReturn(IccCardConstants.State.NOT_READY);
         when(mKeyguardUpdateMonitor.getSubscriptionInfo(anyBoolean())).thenReturn(list);
 
-        // STOPSHIP(b/130246708) This line makes sure that SubscriptionManager provides the
-        // same answer as KeyguardUpdateMonitor. Remove when this is addressed
-        when(mSubscriptionManager.getActiveSubscriptionInfoList(anyBoolean())).thenReturn(list);
-
         mKeyguardUpdateMonitor.mServiceStates = new HashMap<>();
 
         ArgumentCaptor<CarrierTextController.CarrierTextCallbackInfo> captor =
@@ -329,10 +308,6 @@
                 .thenReturn(IccCardConstants.State.READY);
         when(mKeyguardUpdateMonitor.getSubscriptionInfo(anyBoolean())).thenReturn(list);
 
-        // STOPSHIP(b/130246708) This line makes sure that SubscriptionManager provides the
-        // same answer as KeyguardUpdateMonitor. Remove when this is addressed
-        when(mSubscriptionManager.getActiveSubscriptionInfoList(anyBoolean())).thenReturn(list);
-
         mKeyguardUpdateMonitor.mServiceStates = new HashMap<>();
 
         ArgumentCaptor<CarrierTextController.CarrierTextCallbackInfo> captor =
@@ -361,10 +336,6 @@
         when(mKeyguardUpdateMonitor.getSubscriptionInfo(anyBoolean())).thenReturn(list);
         mKeyguardUpdateMonitor.mServiceStates = new HashMap<>();
 
-        // STOPSHIP(b/130246708) This line makes sure that SubscriptionManager provides the
-        // same answer as KeyguardUpdateMonitor. Remove when this is addressed
-        when(mSubscriptionManager.getActiveSubscriptionInfoList(anyBoolean())).thenReturn(list);
-
         ArgumentCaptor<CarrierTextController.CarrierTextCallbackInfo> captor =
                 ArgumentCaptor.forClass(
                         CarrierTextController.CarrierTextCallbackInfo.class);
@@ -385,12 +356,9 @@
         list.add(TEST_SUBSCRIPTION_2);
         when(mKeyguardUpdateMonitor.getSimState(anyInt()))
             .thenReturn(IccCardConstants.State.READY);
-        when(mKeyguardUpdateMonitor.getSubscriptionInfo(anyBoolean())).thenReturn(list);
+
         mKeyguardUpdateMonitor.mServiceStates = new HashMap<>();
         mCarrierTextController.updateDisplayOpportunisticSubscriptionCarrierText();
-
-        // STOPSHIP(b/130246708) This line makes sure that SubscriptionManager provides the
-        // same answer as KeyguardUpdateMonitor. Remove when this is addressed
         when(mSubscriptionManager.getActiveSubscriptionInfoList(anyBoolean())).thenReturn(list);
 
         ArgumentCaptor<CarrierTextController.CarrierTextCallbackInfo> captor =
diff --git a/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsColumnLayoutTest.java b/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsColumnLayoutTest.java
new file mode 100644
index 0000000..16d665c
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsColumnLayoutTest.java
@@ -0,0 +1,201 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.globalactions;
+
+import static junit.framework.Assert.assertEquals;
+
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import android.testing.AndroidTestingRunner;
+import android.view.Gravity;
+import android.view.LayoutInflater;
+import android.view.View;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.systemui.R;
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.util.leak.RotationUtils;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Tests for {@link ListGridLayout}.
+ */
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+public class GlobalActionsColumnLayoutTest extends SysuiTestCase {
+
+    private GlobalActionsColumnLayout mColumnLayout;
+
+    @Before
+    public void setUp() throws Exception {
+        mColumnLayout = spy((GlobalActionsColumnLayout)
+                LayoutInflater.from(mContext).inflate(R.layout.global_actions_column, null));
+    }
+
+    @Test
+    public void testShouldReverseListItems() {
+        doReturn(View.LAYOUT_DIRECTION_LTR).when(mColumnLayout).getCurrentLayoutDirection();
+
+        doReturn(RotationUtils.ROTATION_LANDSCAPE).when(mColumnLayout).getCurrentRotation();
+        assertEquals(false, mColumnLayout.shouldReverseListItems());
+
+        doReturn(RotationUtils.ROTATION_NONE).when(mColumnLayout).getCurrentRotation();
+        assertEquals(false, mColumnLayout.shouldReverseListItems());
+
+        doReturn(RotationUtils.ROTATION_SEASCAPE).when(mColumnLayout).getCurrentRotation();
+        assertEquals(true, mColumnLayout.shouldReverseListItems());
+
+        doReturn(View.LAYOUT_DIRECTION_RTL).when(mColumnLayout).getCurrentLayoutDirection();
+
+        doReturn(RotationUtils.ROTATION_LANDSCAPE).when(mColumnLayout).getCurrentRotation();
+        assertEquals(true, mColumnLayout.shouldReverseListItems());
+
+        doReturn(RotationUtils.ROTATION_NONE).when(mColumnLayout).getCurrentRotation();
+        assertEquals(false, mColumnLayout.shouldReverseListItems());
+
+        doReturn(RotationUtils.ROTATION_SEASCAPE).when(mColumnLayout).getCurrentRotation();
+        assertEquals(false, mColumnLayout.shouldReverseListItems());
+    }
+
+    @Test
+    public void testGetAnimationOffsetX() {
+        doReturn(50f).when(mColumnLayout).getAnimationDistance();
+
+        doReturn(RotationUtils.ROTATION_NONE).when(mColumnLayout).getCurrentRotation();
+        assertEquals(50f, mColumnLayout.getAnimationOffsetX(), .01);
+
+        doReturn(RotationUtils.ROTATION_LANDSCAPE).when(mColumnLayout).getCurrentRotation();
+        assertEquals(0, mColumnLayout.getAnimationOffsetX(), .01);
+
+        doReturn(RotationUtils.ROTATION_SEASCAPE).when(mColumnLayout).getCurrentRotation();
+        assertEquals(0, mColumnLayout.getAnimationOffsetX(), .01);
+    }
+
+    @Test
+    public void testGetAnimationOffsetY() {
+        doReturn(50f).when(mColumnLayout).getAnimationDistance();
+
+        doReturn(RotationUtils.ROTATION_NONE).when(mColumnLayout).getCurrentRotation();
+        assertEquals(0, mColumnLayout.getAnimationOffsetY(), .01);
+
+        doReturn(RotationUtils.ROTATION_LANDSCAPE).when(mColumnLayout).getCurrentRotation();
+        assertEquals(-50f, mColumnLayout.getAnimationOffsetY(), .01);
+
+        doReturn(RotationUtils.ROTATION_SEASCAPE).when(mColumnLayout).getCurrentRotation();
+        assertEquals(50f, mColumnLayout.getAnimationOffsetY(), .01);
+    }
+
+    @Test
+    public void testSnapToPowerButton_portrait() {
+        doReturn(RotationUtils.ROTATION_NONE).when(mColumnLayout).getCurrentRotation();
+        doReturn(50).when(mColumnLayout).getPowerButtonOffsetDistance();
+
+        mColumnLayout.snapToPowerButton();
+        assertEquals(Gravity.TOP | Gravity.RIGHT, mColumnLayout.getGravity());
+        assertEquals(50, mColumnLayout.getPaddingTop(), .01);
+    }
+
+    @Test
+    public void testCenterAlongEdge_portrait() {
+        doReturn(RotationUtils.ROTATION_NONE).when(mColumnLayout).getCurrentRotation();
+
+        mColumnLayout.centerAlongEdge();
+        assertEquals(Gravity.CENTER_VERTICAL | Gravity.RIGHT, mColumnLayout.getGravity());
+        assertEquals(0, mColumnLayout.getPaddingTop(), .01);
+    }
+
+    @Test
+    public void testUpdateSnap_initialState() {
+        doReturn(false).when(mColumnLayout).shouldSnapToPowerButton();
+
+        mColumnLayout.updateSnap(); // should do nothing, since snap has not changed from init state
+
+        verify(mColumnLayout, times(0)).snapToPowerButton();
+        verify(mColumnLayout, times(0)).centerAlongEdge();
+    }
+
+    @Test
+    public void testUpdateSnap_snapThenSnap() {
+        doReturn(true).when(mColumnLayout).shouldSnapToPowerButton();
+
+        mColumnLayout.updateSnap(); // should snap to power button
+
+        verify(mColumnLayout, times(1)).snapToPowerButton();
+        verify(mColumnLayout, times(0)).centerAlongEdge();
+
+        mColumnLayout.updateSnap(); // should do nothing, since this is the same state as last time
+
+        verify(mColumnLayout, times(1)).snapToPowerButton();
+        verify(mColumnLayout, times(0)).centerAlongEdge();
+    }
+
+    @Test
+    public void testUpdateSnap_snapThenCenter() {
+        doReturn(true).when(mColumnLayout).shouldSnapToPowerButton();
+
+        mColumnLayout.updateSnap(); // should snap to power button
+
+        verify(mColumnLayout, times(1)).snapToPowerButton();
+        verify(mColumnLayout, times(0)).centerAlongEdge();
+
+        doReturn(false).when(mColumnLayout).shouldSnapToPowerButton();
+
+        mColumnLayout.updateSnap(); // should center to edge
+
+        verify(mColumnLayout, times(1)).snapToPowerButton();
+        verify(mColumnLayout, times(1)).centerAlongEdge();
+    }
+
+    @Test
+    public void testShouldSnapToPowerButton_vertical() {
+        doReturn(RotationUtils.ROTATION_NONE).when(mColumnLayout).getCurrentRotation();
+        doReturn(300).when(mColumnLayout).getPowerButtonOffsetDistance();
+        doReturn(1000).when(mColumnLayout).getMeasuredHeight();
+        View wrapper = spy(new View(mContext, null));
+        doReturn(wrapper).when(mColumnLayout).getWrapper();
+        doReturn(500).when(wrapper).getMeasuredHeight();
+
+        assertEquals(true, mColumnLayout.shouldSnapToPowerButton());
+
+        doReturn(600).when(mColumnLayout).getMeasuredHeight();
+
+        assertEquals(false, mColumnLayout.shouldSnapToPowerButton());
+    }
+
+    @Test
+    public void testShouldSnapToPowerButton_horizontal() {
+        doReturn(RotationUtils.ROTATION_LANDSCAPE).when(mColumnLayout).getCurrentRotation();
+        doReturn(300).when(mColumnLayout).getPowerButtonOffsetDistance();
+        doReturn(1000).when(mColumnLayout).getMeasuredWidth();
+        View wrapper = spy(new View(mContext, null));
+        doReturn(wrapper).when(mColumnLayout).getWrapper();
+        doReturn(500).when(wrapper).getMeasuredWidth();
+
+        assertEquals(true, mColumnLayout.shouldSnapToPowerButton());
+
+        doReturn(600).when(mColumnLayout).getMeasuredWidth();
+
+        assertEquals(false, mColumnLayout.shouldSnapToPowerButton());
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsGridLayoutTest.java b/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsGridLayoutTest.java
index 3c52e9d..a396f3e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsGridLayoutTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsGridLayoutTest.java
@@ -18,12 +18,8 @@
 
 import static junit.framework.Assert.assertEquals;
 
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.spy;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
 
 import android.testing.AndroidTestingRunner;
 import android.view.LayoutInflater;
@@ -32,7 +28,6 @@
 
 import androidx.test.filters.SmallTest;
 
-import com.android.systemui.MultiListLayout;
 import com.android.systemui.R;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.util.leak.RotationUtils;
@@ -49,61 +44,12 @@
 public class GlobalActionsGridLayoutTest extends SysuiTestCase {
 
     private GlobalActionsGridLayout mGridLayout;
-    private TestAdapter mAdapter;
     private ListGridLayout mListGrid;
 
-    private class TestAdapter extends MultiListLayout.MultiListAdapter {
-        @Override
-        public void onClickItem(int index) { }
-
-        @Override
-        public boolean onLongClickItem(int index) {
-            return true;
-        }
-
-        @Override
-        public int countSeparatedItems() {
-            return -1;
-        }
-
-        @Override
-        public int countListItems() {
-            return -1;
-        }
-
-        @Override
-        public boolean shouldBeSeparated(int position) {
-            return false;
-        }
-
-        @Override
-        public int getCount() {
-            return countSeparatedItems() + countListItems();
-        }
-
-        @Override
-        public Object getItem(int position) {
-            return null;
-        }
-
-        @Override
-        public long getItemId(int position) {
-            return -1;
-        }
-
-        @Override
-        public View getView(int position, View convertView, ViewGroup parent) {
-            return null;
-        }
-    }
-
-
     @Before
     public void setUp() throws Exception {
         mGridLayout = spy((GlobalActionsGridLayout)
                 LayoutInflater.from(mContext).inflate(R.layout.global_actions_grid, null));
-        mAdapter = spy(new TestAdapter());
-        mGridLayout.setAdapter(mAdapter);
         mListGrid = spy(mGridLayout.getListView());
         doReturn(mListGrid).when(mGridLayout).getListView();
     }
@@ -122,7 +68,7 @@
 
     @Test
     public void testShouldReverseListItems() {
-        doReturn(View.LAYOUT_DIRECTION_LTR).when(mGridLayout).getLayoutDirection();
+        doReturn(View.LAYOUT_DIRECTION_LTR).when(mGridLayout).getCurrentLayoutDirection();
 
         doReturn(RotationUtils.ROTATION_LANDSCAPE).when(mGridLayout).getCurrentRotation();
         assertEquals(false, mGridLayout.shouldReverseListItems());
@@ -133,7 +79,7 @@
         doReturn(RotationUtils.ROTATION_SEASCAPE).when(mGridLayout).getCurrentRotation();
         assertEquals(true, mGridLayout.shouldReverseListItems());
 
-        doReturn(View.LAYOUT_DIRECTION_RTL).when(mGridLayout).getLayoutDirection();
+        doReturn(View.LAYOUT_DIRECTION_RTL).when(mGridLayout).getCurrentLayoutDirection();
 
         doReturn(RotationUtils.ROTATION_LANDSCAPE).when(mGridLayout).getCurrentRotation();
         assertEquals(true, mGridLayout.shouldReverseListItems());
@@ -185,123 +131,26 @@
         assertEquals(0f, mGridLayout.getAnimationOffsetY(), .01);
     }
 
-    @Test(expected = IllegalStateException.class)
-    public void testOnUpdateList_noAdapter() {
-        mGridLayout.setAdapter(null);
-        mGridLayout.updateList();
-    }
-
     @Test
-    public void testOnUpdateList_noItems() {
-        doReturn(0).when(mAdapter).countSeparatedItems();
-        doReturn(0).when(mAdapter).countListItems();
-        mGridLayout.updateList();
-
-        ViewGroup separatedView = mGridLayout.getSeparatedView();
-        ListGridLayout listView = mGridLayout.getListView();
-
-        assertEquals(0, separatedView.getChildCount());
-        assertEquals(View.GONE, separatedView.getVisibility());
-
-        verify(mListGrid, times(0)).addItem(any());
-    }
-
-    @Test
-    public void testOnUpdateList_resizesFirstSeparatedItem() {
-        doReturn(1).when(mAdapter).countSeparatedItems();
-        doReturn(0).when(mAdapter).countListItems();
+    public void testUpdateSeparatedItemSize() {
         View firstView = new View(mContext, null);
         View secondView = new View(mContext, null);
 
-        doReturn(firstView).when(mAdapter).getView(eq(0), any(), any());
-        doReturn(true).when(mAdapter).shouldBeSeparated(0);
+        ViewGroup separatedView = mGridLayout.getSeparatedView();
+        separatedView.addView(firstView);
 
-        mGridLayout.updateList();
+        mGridLayout.updateSeparatedItemSize();
 
         ViewGroup.LayoutParams childParams = firstView.getLayoutParams();
         assertEquals(ViewGroup.LayoutParams.MATCH_PARENT, childParams.width);
         assertEquals(ViewGroup.LayoutParams.MATCH_PARENT, childParams.height);
 
-        doReturn(2).when(mAdapter).countSeparatedItems();
-        doReturn(secondView).when(mAdapter).getView(eq(1), any(), any());
-        doReturn(true).when(mAdapter).shouldBeSeparated(1);
+        separatedView.addView(secondView);
 
-        mGridLayout.updateList();
+        mGridLayout.updateSeparatedItemSize();
 
         childParams = firstView.getLayoutParams();
         assertEquals(ViewGroup.LayoutParams.WRAP_CONTENT, childParams.width);
         assertEquals(ViewGroup.LayoutParams.WRAP_CONTENT, childParams.height);
-
-
-    }
-
-    @Test
-    public void testOnUpdateList_onlySeparatedItems() {
-        doReturn(1).when(mAdapter).countSeparatedItems();
-        doReturn(0).when(mAdapter).countListItems();
-        View testView = new View(mContext, null);
-        doReturn(testView).when(mAdapter).getView(eq(0), any(), any());
-        doReturn(true).when(mAdapter).shouldBeSeparated(0);
-
-        mGridLayout.updateList();
-
-        verify(mListGrid, times(0)).addItem(any());
-    }
-
-    @Test
-    public void testOnUpdateList_oneSeparatedOneList() {
-        doReturn(1).when(mAdapter).countSeparatedItems();
-        doReturn(1).when(mAdapter).countListItems();
-        View view1 = new View(mContext, null);
-        View view2 = new View(mContext, null);
-
-        doReturn(view1).when(mAdapter).getView(eq(0), any(), any());
-        doReturn(true).when(mAdapter).shouldBeSeparated(0);
-
-        doReturn(view2).when(mAdapter).getView(eq(1), any(), any());
-        doReturn(false).when(mAdapter).shouldBeSeparated(1);
-
-        mGridLayout.updateList();
-
-        ViewGroup separatedView = mGridLayout.getSeparatedView();
-
-        assertEquals(1, separatedView.getChildCount());
-        assertEquals(View.VISIBLE, separatedView.getVisibility());
-        assertEquals(view1, separatedView.getChildAt(0));
-
-        verify(mListGrid, times(1)).addItem(view2);
-    }
-
-    @Test
-    public void testOnUpdateList_fourInList() {
-        doReturn(0).when(mAdapter).countSeparatedItems();
-        doReturn(4).when(mAdapter).countListItems();
-        View view1 = new View(mContext, null);
-        View view2 = new View(mContext, null);
-        View view3 = new View(mContext, null);
-        View view4 = new View(mContext, null);
-
-        doReturn(view1).when(mAdapter).getView(eq(0), any(), any());
-        doReturn(false).when(mAdapter).shouldBeSeparated(0);
-
-        doReturn(view2).when(mAdapter).getView(eq(1), any(), any());
-        doReturn(false).when(mAdapter).shouldBeSeparated(1);
-
-        doReturn(view3).when(mAdapter).getView(eq(2), any(), any());
-        doReturn(false).when(mAdapter).shouldBeSeparated(2);
-
-        doReturn(view4).when(mAdapter).getView(eq(3), any(), any());
-        doReturn(false).when(mAdapter).shouldBeSeparated(3);
-
-        mGridLayout.updateList();
-
-        ViewGroup separatedView = mGridLayout.getSeparatedView();
-        assertEquals(0, separatedView.getChildCount());
-        assertEquals(View.GONE, separatedView.getVisibility());
-
-        verify(mListGrid, times(1)).addItem(view1);
-        verify(mListGrid, times(1)).addItem(view2);
-        verify(mListGrid, times(1)).addItem(view3);
-        verify(mListGrid, times(1)).addItem(view4);
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsLayoutTest.java b/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsLayoutTest.java
new file mode 100644
index 0000000..16dcd65
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsLayoutTest.java
@@ -0,0 +1,303 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.globalactions;
+
+import static junit.framework.Assert.assertEquals;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.spy;
+
+import android.content.Context;
+import android.testing.AndroidTestingRunner;
+import android.util.AttributeSet;
+import android.view.View;
+import android.view.ViewGroup;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.systemui.MultiListLayout;
+import com.android.systemui.SysuiTestCase;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.ArrayList;
+
+/**
+ * Tests for {@link ListGridLayout}.
+ */
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+public class GlobalActionsLayoutTest extends SysuiTestCase {
+
+    private TestLayout mLayout;
+    private TestAdapter mAdapter;
+
+    private class TestAdapter extends MultiListLayout.MultiListAdapter {
+        @Override
+        public void onClickItem(int index) { }
+
+        @Override
+        public boolean onLongClickItem(int index) {
+            return true;
+        }
+
+        @Override
+        public int countSeparatedItems() {
+            return -1;
+        }
+
+        @Override
+        public int countListItems() {
+            return -1;
+        }
+
+        @Override
+        public boolean shouldBeSeparated(int position) {
+            return false;
+        }
+
+        @Override
+        public int getCount() {
+            return countSeparatedItems() + countListItems();
+        }
+
+        @Override
+        public Object getItem(int position) {
+            return null;
+        }
+
+        @Override
+        public long getItemId(int position) {
+            return -1;
+        }
+
+        @Override
+        public View getView(int position, View convertView, ViewGroup parent) {
+            return null;
+        }
+    }
+
+    private class TestLayout extends GlobalActionsLayout {
+        ArrayList<View> mSeparatedViews = new ArrayList<>();
+        ArrayList<View> mListViews = new ArrayList<>();
+        boolean mSeparatedViewVisible = false;
+
+        TestLayout(Context context, AttributeSet attrs) {
+            super(context, attrs);
+        }
+
+        @Override
+        protected boolean shouldReverseListItems() {
+            return false;
+        }
+
+        @Override
+        public float getAnimationOffsetX() {
+            return 0;
+        }
+
+        @Override
+        public float getAnimationOffsetY() {
+            return 0;
+        }
+
+        @Override
+        protected void addToListView(View v, boolean reverse) {
+            if (reverse) {
+                mListViews.add(0, v);
+            } else {
+                mListViews.add(v);
+            }
+        }
+
+        @Override
+        protected void addToSeparatedView(View v, boolean reverse) {
+            if (reverse) {
+                mSeparatedViews.add(0, v);
+            } else {
+                mSeparatedViews.add(v);
+            }
+        }
+
+        @Override
+        protected void setSeparatedViewVisibility(boolean visible) {
+            mSeparatedViewVisible = visible;
+        }
+    }
+
+    @Before
+    public void setUp() throws Exception {
+        mLayout = spy(new TestLayout(mContext, null));
+        mAdapter = spy(new TestAdapter());
+        mLayout.setAdapter(mAdapter);
+    }
+
+    @Test(expected = IllegalStateException.class)
+    public void testOnUpdateList_noAdapter() {
+        mLayout.setAdapter(null);
+        mLayout.updateList();
+    }
+
+    @Test
+    public void testOnUpdateList_noItems() {
+        doReturn(0).when(mAdapter).countSeparatedItems();
+        doReturn(0).when(mAdapter).countListItems();
+        mLayout.updateList();
+
+        assertEquals(0, mLayout.mSeparatedViews.size());
+        assertEquals(0, mLayout.mListViews.size());
+
+        assertEquals(false, mLayout.mSeparatedViewVisible);
+    }
+
+    @Test
+    public void testOnUpdateList_oneSeparatedOneList() {
+        doReturn(1).when(mAdapter).countSeparatedItems();
+        doReturn(1).when(mAdapter).countListItems();
+        View view1 = new View(mContext, null);
+        View view2 = new View(mContext, null);
+
+        doReturn(view1).when(mAdapter).getView(eq(0), any(), any());
+        doReturn(true).when(mAdapter).shouldBeSeparated(0);
+
+        doReturn(view2).when(mAdapter).getView(eq(1), any(), any());
+        doReturn(false).when(mAdapter).shouldBeSeparated(1);
+
+        mLayout.updateList();
+
+        assertEquals(1, mLayout.mSeparatedViews.size());
+        assertEquals(1, mLayout.mListViews.size());
+        assertEquals(view1, mLayout.mSeparatedViews.get(0));
+        assertEquals(view2, mLayout.mListViews.get(0));
+    }
+
+
+    @Test
+    public void testOnUpdateList_twoSeparatedItems() {
+        doReturn(2).when(mAdapter).countSeparatedItems();
+        doReturn(0).when(mAdapter).countListItems();
+        View view1 = new View(mContext, null);
+        View view2 = new View(mContext, null);
+
+        doReturn(view1).when(mAdapter).getView(eq(0), any(), any());
+        doReturn(true).when(mAdapter).shouldBeSeparated(0);
+        doReturn(view2).when(mAdapter).getView(eq(1), any(), any());
+        doReturn(true).when(mAdapter).shouldBeSeparated(1);
+
+        mLayout.updateList();
+
+        assertEquals(2, mLayout.mSeparatedViews.size());
+        assertEquals(0, mLayout.mListViews.size());
+
+        assertEquals(view1, mLayout.mSeparatedViews.get(0));
+        assertEquals(view2, mLayout.mSeparatedViews.get(1));
+
+        // if separated view has items in it, should be made visible
+        assertEquals(true, mLayout.mSeparatedViewVisible);
+    }
+
+    @Test
+    public void testOnUpdateList_twoSeparatedItems_reverse() {
+        doReturn(2).when(mAdapter).countSeparatedItems();
+        doReturn(0).when(mAdapter).countListItems();
+        doReturn(true).when(mLayout).shouldReverseListItems();
+        View view1 = new View(mContext, null);
+        View view2 = new View(mContext, null);
+
+        doReturn(view1).when(mAdapter).getView(eq(0), any(), any());
+        doReturn(true).when(mAdapter).shouldBeSeparated(0);
+
+        doReturn(view2).when(mAdapter).getView(eq(1), any(), any());
+        doReturn(true).when(mAdapter).shouldBeSeparated(1);
+
+        mLayout.updateList();
+
+        assertEquals(2, mLayout.mSeparatedViews.size());
+        assertEquals(0, mLayout.mListViews.size());
+
+        // separated view items are not reversed in current implementation, and this is intentional!
+        assertEquals(view1, mLayout.mSeparatedViews.get(0));
+        assertEquals(view2, mLayout.mSeparatedViews.get(1));
+    }
+
+    @Test
+    public void testOnUpdateList_fourInList() {
+        doReturn(0).when(mAdapter).countSeparatedItems();
+        doReturn(4).when(mAdapter).countListItems();
+        View view1 = new View(mContext, null);
+        View view2 = new View(mContext, null);
+        View view3 = new View(mContext, null);
+        View view4 = new View(mContext, null);
+
+        doReturn(view1).when(mAdapter).getView(eq(0), any(), any());
+        doReturn(false).when(mAdapter).shouldBeSeparated(0);
+
+        doReturn(view2).when(mAdapter).getView(eq(1), any(), any());
+        doReturn(false).when(mAdapter).shouldBeSeparated(1);
+
+        doReturn(view3).when(mAdapter).getView(eq(2), any(), any());
+        doReturn(false).when(mAdapter).shouldBeSeparated(2);
+
+        doReturn(view4).when(mAdapter).getView(eq(3), any(), any());
+        doReturn(false).when(mAdapter).shouldBeSeparated(3);
+
+        mLayout.updateList();
+
+        assertEquals(0, mLayout.mSeparatedViews.size());
+        assertEquals(4, mLayout.mListViews.size());
+        assertEquals(view1, mLayout.mListViews.get(0));
+        assertEquals(view2, mLayout.mListViews.get(1));
+        assertEquals(view3, mLayout.mListViews.get(2));
+        assertEquals(view4, mLayout.mListViews.get(3));
+    }
+
+    @Test
+    public void testOnUpdateList_fourInList_reverse() {
+        doReturn(0).when(mAdapter).countSeparatedItems();
+        doReturn(4).when(mAdapter).countListItems();
+        doReturn(true).when(mLayout).shouldReverseListItems();
+        View view1 = new View(mContext, null);
+        View view2 = new View(mContext, null);
+        View view3 = new View(mContext, null);
+        View view4 = new View(mContext, null);
+
+        doReturn(view1).when(mAdapter).getView(eq(0), any(), any());
+        doReturn(false).when(mAdapter).shouldBeSeparated(0);
+
+        doReturn(view2).when(mAdapter).getView(eq(1), any(), any());
+        doReturn(false).when(mAdapter).shouldBeSeparated(1);
+
+        doReturn(view3).when(mAdapter).getView(eq(2), any(), any());
+        doReturn(false).when(mAdapter).shouldBeSeparated(2);
+
+        doReturn(view4).when(mAdapter).getView(eq(3), any(), any());
+        doReturn(false).when(mAdapter).shouldBeSeparated(3);
+
+        mLayout.updateList();
+
+        assertEquals(0, mLayout.mSeparatedViews.size());
+        assertEquals(4, mLayout.mListViews.size());
+        assertEquals(view1, mLayout.mListViews.get(3));
+        assertEquals(view2, mLayout.mListViews.get(2));
+        assertEquals(view3, mLayout.mListViews.get(1));
+        assertEquals(view4, mLayout.mListViews.get(0));
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java
index c2f55e2..2bde5f6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java
@@ -112,19 +112,19 @@
     @Test
     public void testSetSystemUiVisibility() {
         Rect r = new Rect();
-        mCommandQueue.setSystemUiVisibility(DEFAULT_DISPLAY, 1, 2, 3, 4, null, r);
+        mCommandQueue.setSystemUiVisibility(DEFAULT_DISPLAY, 1, 2, 3, 4, null, r, false);
         waitForIdleSync();
         verify(mCallbacks).setSystemUiVisibility(eq(DEFAULT_DISPLAY), eq(1), eq(2), eq(3), eq(4),
-                eq(null), eq(r));
+                eq(null), eq(r), eq(false));
     }
 
     @Test
     public void testSetSystemUiVisibilityForSecondaryDisplay() {
         Rect r = new Rect();
-        mCommandQueue.setSystemUiVisibility(SECONDARY_DISPLAY, 1, 2, 3, 4, null, r);
+        mCommandQueue.setSystemUiVisibility(SECONDARY_DISPLAY, 1, 2, 3, 4, null, r, false);
         waitForIdleSync();
         verify(mCallbacks).setSystemUiVisibility(eq(SECONDARY_DISPLAY), eq(1), eq(2), eq(3), eq(4),
-                eq(null), eq(r));
+                eq(null), eq(r), eq(false));
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationMenuRowTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationMenuRowTest.java
index c62a802..43ea92f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationMenuRowTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationMenuRowTest.java
@@ -112,11 +112,8 @@
     }
 
     @Test
-    public void testNoAppOpsInSlowSwipe_newInterruptionModel() {
-        Settings.Secure.putInt(mContext.getContentResolver(),
-                NOTIFICATION_NEW_INTERRUPTION_MODEL, 1);
-
-        NotificationMenuRow row = new NotificationMenuRow(mContext);
+    public void testNoAppOpsInSlowSwipe_biDirectionalSwipe() {
+        NotificationMenuRow row = new NotificationMenuRow(mContext, true);
         row.createMenu(mRow, null);
 
         ViewGroup container = (ViewGroup) row.getMenuView();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/AutoHideControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/AutoHideControllerTest.java
index 1b34a75..f614354 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/AutoHideControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/AutoHideControllerTest.java
@@ -70,7 +70,7 @@
 
     @Test
     public void testSetSystemUiVisibilityEarlyReturnWithDifferentDisplay() {
-        mAutoHideController.setSystemUiVisibility(1, 1, 2, 3, 4, null, new Rect());
+        mAutoHideController.setSystemUiVisibility(1, 1, 2, 3, 4, null, new Rect(), false);
 
         verify(mAutoHideController, never()).notifySystemUiVisibilityChanged(anyInt());
     }
@@ -78,7 +78,8 @@
     @Test
     public void testSetSystemUiVisibilityEarlyReturnWithSameVisibility() {
         mAutoHideController
-                .setSystemUiVisibility(DEFAULT_DISPLAY, View.VISIBLE, 2, 3, 4, null, new Rect());
+                .setSystemUiVisibility(
+                        DEFAULT_DISPLAY, View.VISIBLE, 2, 3, 4, null, new Rect(), false);
 
         verify(mAutoHideController, never()).notifySystemUiVisibilityChanged(anyInt());
     }
@@ -92,7 +93,7 @@
                 View.SYSTEM_UI_FLAG_FULLSCREEN | View.STATUS_BAR_UNHIDE;
 
         mAutoHideController.setSystemUiVisibility(
-                DEFAULT_DISPLAY, expectedStatus, 2, 3, FULL_MASK, null, new Rect());
+                DEFAULT_DISPLAY, expectedStatus, 2, 3, FULL_MASK, null, new Rect(), false);
 
         assertEquals("System UI visibility should not be changed",
                 expectedStatus, mAutoHideController.mSystemUiVisibility);
@@ -109,7 +110,7 @@
 
         mAutoHideController.setSystemUiVisibility(
                 DEFAULT_DISPLAY, View.STATUS_BAR_UNHIDE | View.NAVIGATION_BAR_UNHIDE,
-                2, 3, FULL_MASK, null, new Rect());
+                2, 3, FULL_MASK, null, new Rect(), false);
 
         int expectedStatus = View.VISIBLE;
         assertEquals(expectedStatus, mAutoHideController.mSystemUiVisibility);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java
index ce5bfce..616b46a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java
@@ -130,6 +130,7 @@
         when(mMockCm.isNetworkSupported(ConnectivityManager.TYPE_MOBILE)).thenReturn(true);
         when(mMockCm.getDefaultNetworkCapabilitiesForUser(0)).thenReturn(
                 new NetworkCapabilities[] { mNetCapabilities });
+        when(mMockTm.createForSubscriptionId(anyInt())).thenReturn(mMockTm);
 
         mSignalStrength = mock(SignalStrength.class);
         mServiceState = mock(ServiceState.class);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RemoteInputViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RemoteInputViewTest.java
index 8bf1606..bc468bf 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RemoteInputViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RemoteInputViewTest.java
@@ -42,6 +42,7 @@
 import com.android.systemui.statusbar.NotificationTestHelper;
 import com.android.systemui.statusbar.RemoteInputController;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
+import com.android.systemui.statusbar.phone.LightBarController;
 import com.android.systemui.util.Assert;
 
 import org.junit.After;
@@ -67,6 +68,7 @@
     @Mock private RemoteInputController mController;
     @Mock private ShortcutManager mShortcutManager;
     @Mock private RemoteInputQuickSettingsDisabler mRemoteInputQuickSettingsDisabler;
+    @Mock private LightBarController mLightBarController;
     private BlockingQueueIntentReceiver mReceiver;
     private RemoteInputView mView;
 
@@ -77,6 +79,8 @@
 
         mDependency.injectTestDependency(RemoteInputQuickSettingsDisabler.class,
                 mRemoteInputQuickSettingsDisabler);
+        mDependency.injectTestDependency(LightBarController.class,
+                mLightBarController);
 
         mReceiver = new BlockingQueueIntentReceiver();
         mContext.registerReceiver(mReceiver, new IntentFilter(TEST_ACTION), null,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SmartReplyConstantsTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SmartReplyConstantsTest.java
index fb2b7dc..c761a44 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SmartReplyConstantsTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SmartReplyConstantsTest.java
@@ -229,8 +229,7 @@
     }
 
     private void triggerConstantsOnChange() {
-        mConstants.onDeviceConfigPropertyChanged(DeviceConfig.NAMESPACE_SYSTEMUI,
-                "" /* name */, "" /* value */);
+        mConstants.onDeviceConfigPropertiesChanged(DeviceConfig.NAMESPACE_SYSTEMUI);
     }
 
     private void resetAllDeviceConfigFlags() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SmartReplyViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SmartReplyViewTest.java
index 01f3c92..8c5fac4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SmartReplyViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SmartReplyViewTest.java
@@ -634,7 +634,8 @@
 
         mView.getChildAt(2).performClick();
 
-        verify(mActivityStarter, times(1)).startPendingIntentDismissingKeyguard(any(), any());
+        verify(mActivityStarter, times(1)).startPendingIntentDismissingKeyguard(any(), any(),
+                any());
     }
 
     @Test
@@ -645,7 +646,7 @@
 
         mView.getChildAt(2).performClick();
 
-        verify(mActivityStarter, never()).startPendingIntentDismissingKeyguard(any(), any());
+        verify(mActivityStarter, never()).startPendingIntentDismissingKeyguard(any(), any(), any());
     }
 
     @Test
@@ -657,7 +658,8 @@
         Thread.sleep(delayMs);
         mView.getChildAt(2).performClick();
 
-        verify(mActivityStarter, times(1)).startPendingIntentDismissingKeyguard(any(), any());
+        verify(mActivityStarter, times(1)).startPendingIntentDismissingKeyguard(any(), any(),
+                any());
     }
 
     @Test
@@ -668,7 +670,8 @@
 
         mView.getChildAt(2).performClick();
 
-        verify(mActivityStarter, times(1)).startPendingIntentDismissingKeyguard(any(), any());
+        verify(mActivityStarter, times(1)).startPendingIntentDismissingKeyguard(any(), any(),
+                any());
     }
 
     @Test
diff --git a/proto/src/metrics_constants/metrics_constants.proto b/proto/src/metrics_constants/metrics_constants.proto
index 21c6035..fe92d45 100644
--- a/proto/src/metrics_constants/metrics_constants.proto
+++ b/proto/src/metrics_constants/metrics_constants.proto
@@ -7206,6 +7206,13 @@
     // ACTION: Share Wi-Fi hotspot by generating a QR code
     ACTION_SETTINGS_SHARE_WIFI_HOTSPOT_QR_CODE = 1712;
 
+    // OPEN: Settings > Network & internet > Mobile network > Delete sim
+    DIALOG_DELETE_SIM_CONFIRMATION = 1713;
+
+    // OPEN: Settings >  Network & internet > Mobile network > Delete sim > (answer yes to
+    //       confirmation)
+    DIALOG_DELETE_SIM_PROGRESS = 1714;
+
     // ---- End Q Constants, all Q constants go above this line ----
     // Add new aosp constants above this line.
     // END OF AOSP CONSTANTS
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
index a94d1dc..a64f4e4 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
@@ -94,6 +94,7 @@
 import java.util.List;
 import java.util.Map;
 import java.util.Objects;
+import java.util.Set;
 
 /**
  * Entry point service for autofill management.
@@ -192,9 +193,9 @@
         mUi = new AutoFillUI(ActivityThread.currentActivityThread().getSystemUiContext());
         mAm = LocalServices.getService(ActivityManagerInternal.class);
 
-        DeviceConfig.addOnPropertyChangedListener(DeviceConfig.NAMESPACE_AUTOFILL,
+        DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_AUTOFILL,
                 ActivityThread.currentApplication().getMainExecutor(),
-                (namespace, key, value) -> onDeviceConfigChange(key));
+                (properties) -> onDeviceConfigChange(properties.getKeyset()));
 
         setLogLevelFromSettings();
         setMaxPartitionsFromSettings();
@@ -270,15 +271,17 @@
         }
     }
 
-    private void onDeviceConfigChange(@NonNull String key) {
-        switch (key) {
-            case AutofillManager.DEVICE_CONFIG_AUTOFILL_SMART_SUGGESTION_SUPPORTED_MODES:
-            case AutofillManager.DEVICE_CONFIG_AUGMENTED_SERVICE_IDLE_UNBIND_TIMEOUT:
-            case AutofillManager.DEVICE_CONFIG_AUGMENTED_SERVICE_REQUEST_TIMEOUT:
-                setDeviceConfigProperties();
-                break;
-            default:
-                Slog.i(mTag, "Ignoring change on " + key);
+    private void onDeviceConfigChange(@NonNull Set<String> keys) {
+        for (String key : keys) {
+            switch (key) {
+                case AutofillManager.DEVICE_CONFIG_AUTOFILL_SMART_SUGGESTION_SUPPORTED_MODES:
+                case AutofillManager.DEVICE_CONFIG_AUGMENTED_SERVICE_IDLE_UNBIND_TIMEOUT:
+                case AutofillManager.DEVICE_CONFIG_AUGMENTED_SERVICE_REQUEST_TIMEOUT:
+                    setDeviceConfigProperties();
+                    break;
+                default:
+                    Slog.i(mTag, "Ignoring change on " + key);
+            }
         }
     }
 
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
index e3cbcc3..a3231f9 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
@@ -57,6 +57,7 @@
 import android.service.autofill.FillEventHistory.Event;
 import android.service.autofill.FillResponse;
 import android.service.autofill.IAutoFillService;
+import android.service.autofill.SaveInfo;
 import android.service.autofill.UserData;
 import android.util.ArrayMap;
 import android.util.ArraySet;
@@ -595,8 +596,8 @@
         ArrayList<Session> previousSessions = null;
         for (int i = 0; i < size; i++) {
             final Session previousSession = mSessions.valueAt(i);
-            // TODO(b/113281366): only return sessions asked to be kept alive / add CTS test
-            if (previousSession.taskId == session.taskId && previousSession.id != session.id) {
+            if (previousSession.taskId == session.taskId && previousSession.id != session.id
+                    && (previousSession.getSaveInfoFlagsLocked() & SaveInfo.FLAG_DELAY_SAVE) != 0) {
                 if (previousSessions == null) {
                     previousSessions = new ArrayList<>(size);
                 }
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index 9eda9db..7b97353 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -1203,9 +1203,12 @@
 
     @GuardedBy("mLock")
     @Nullable
-    private FillResponse getLastResponseLocked(@Nullable String logPrefix) {
+    private FillResponse getLastResponseLocked(@Nullable String logPrefixFmt) {
+        final String logPrefix = sDebug && logPrefixFmt != null
+                ? String.format(logPrefixFmt, this.id)
+                : null;
         if (mContexts == null) {
-            if (sDebug && logPrefix != null) Slog.d(TAG, logPrefix + ": no contexts");
+            if (logPrefix != null) Slog.d(TAG, logPrefix + ": no contexts");
             return null;
         }
         if (mResponses == null) {
@@ -1241,6 +1244,12 @@
         return response == null ? null : response.getSaveInfo();
     }
 
+    @GuardedBy("mLock")
+    int getSaveInfoFlagsLocked() {
+        final SaveInfo saveInfo = getSaveInfoLocked();
+        return saveInfo == null ? 0 : saveInfo.getFlags();
+    }
+
     /**
      * Generates a {@link android.service.autofill.FillEventHistory.Event#TYPE_CONTEXT_COMMITTED}
      * when necessary.
@@ -1252,7 +1261,7 @@
     private void handleLogContextCommitted() {
         final FillResponse lastResponse;
         synchronized (mLock) {
-            lastResponse = getLastResponseLocked("logContextCommited()");
+            lastResponse = getLastResponseLocked("logContextCommited(%s)");
         }
 
         if (lastResponse == null) {
@@ -1295,7 +1304,7 @@
     @GuardedBy("mLock")
     private void logContextCommittedLocked(@Nullable ArrayList<AutofillId> detectedFieldIds,
             @Nullable ArrayList<FieldClassification> detectedFieldClassifications) {
-        final FillResponse lastResponse = getLastResponseLocked("logContextCommited()");
+        final FillResponse lastResponse = getLastResponseLocked("logContextCommited(%s)");
         if (lastResponse == null) return;
 
         final int flags = lastResponse.getFlags();
@@ -1610,7 +1619,7 @@
                     + id + " destroyed");
             return false;
         }
-        final FillResponse response = getLastResponseLocked("showSaveLocked()");
+        final FillResponse response = getLastResponseLocked("showSaveLocked(%s)");
         final SaveInfo saveInfo = response == null ? null : response.getSaveInfo();
 
         /*
@@ -1624,13 +1633,13 @@
          * - server didn't ask to keep session alive
          */
         if (saveInfo == null) {
-            if (sVerbose) Slog.v(TAG, "showSaveLocked(): no saveInfo from service");
+            if (sVerbose) Slog.v(TAG, "showSaveLocked(" + this.id + "): no saveInfo from service");
             return true;
         }
 
         if ((saveInfo.getFlags() & SaveInfo.FLAG_DELAY_SAVE) != 0) {
             // TODO(b/113281366): log metrics
-            if (sDebug) Slog.v(TAG, "showSaveLocked(): service asked to delay save");
+            if (sDebug) Slog.v(TAG, "showSaveLocked(" + this.id + "): service asked to delay save");
             return false;
         }
 
@@ -1962,7 +1971,8 @@
             if (node != null) {
                 final AutofillValue value = node.getAutofillValue();
                 if (sDebug) {
-                    Slog.d(TAG, "getValueFromContexts(" + autofillId + ") at " + i + ": " + value);
+                    Slog.d(TAG, "getValueFromContexts(" + this.id + "/" + autofillId + ") at "
+                            + i + ": " + value);
                 }
                 if (value != null && !value.isEmpty()) {
                     return value;
@@ -2066,7 +2076,7 @@
             return;
         }
 
-        if (sVerbose) Slog.v(TAG, "callSaveLocked(): mViewStates=" + mViewStates);
+        if (sVerbose) Slog.v(TAG, "callSaveLocked(" + this.id + "): mViewStates=" + mViewStates);
 
         if (mContexts == null) {
             Slog.w(TAG, "callSaveLocked(): no contexts");
@@ -2109,15 +2119,15 @@
         final ArrayList<FillContext> contexts;
         if (previousSessions != null) {
             if (sDebug) {
-                Slog.d(TAG, "mergeSessions(): Merging the content of " + previousSessions.size()
-                        + " sessions for task " + taskId);
+                Slog.d(TAG, "mergeSessions(" + this.id + "): Merging the content of "
+                        + previousSessions.size() + " sessions for task " + taskId);
             }
             contexts = new ArrayList<>();
             for (int i = 0; i < previousSessions.size(); i++) {
                 final Session previousSession = previousSessions.get(i);
                 final ArrayList<FillContext> previousContexts = previousSession.mContexts;
                 if (previousContexts == null) {
-                    Slog.w(TAG, "mergeSessions(): Not merging null contexts from "
+                    Slog.w(TAG, "mergeSessions(" + this.id + "): Not merging null contexts from "
                             + previousSession.id);
                     continue;
                 }
@@ -2125,14 +2135,14 @@
                     previousSession.updateValuesForSaveLocked();
                 }
                 if (sDebug) {
-                    Slog.d(TAG, "mergeSessions(): adding " + previousContexts.size()
+                    Slog.d(TAG, "mergeSessions(" + this.id + "): adding " + previousContexts.size()
                             + " context from previous session #" + previousSession.id);
                 }
                 contexts.addAll(previousContexts);
                 if (mClientState == null && previousSession.mClientState != null) {
                     if (sDebug) {
-                        Slog.d(TAG, "mergeSessions(): setting client state from previous session"
-                                + previousSession.id);
+                        Slog.d(TAG, "mergeSessions(" + this.id + "): setting client state from "
+                                + "previous session" + previousSession.id);
                     }
                     mClientState = previousSession.mClientState;
                 }
@@ -2250,8 +2260,8 @@
             return;
         }
         if (sVerbose) {
-            Slog.v(TAG, "updateLocked(): id=" + id + ", action=" + actionAsString(action)
-                    + ", flags=" + flags);
+            Slog.v(TAG, "updateLocked(" + this.id + "): id=" + id + ", action="
+                    + actionAsString(action) + ", flags=" + flags);
         }
         ViewState viewState = mViewStates.get(id);
 
@@ -3291,7 +3301,7 @@
      */
     @GuardedBy("mLock")
     void removeSelfLocked() {
-        if (sVerbose) Slog.v(TAG, "removeSelfLocked(): " + mPendingSaveUi);
+        if (sVerbose) Slog.v(TAG, "removeSelfLocked(" + this.id + "): " + mPendingSaveUi);
         if (mDestroyed) {
             Slog.w(TAG, "Call to Session#removeSelfLocked() rejected - session: "
                     + id + " destroyed");
diff --git a/services/autofill/java/com/android/server/autofill/ViewState.java b/services/autofill/java/com/android/server/autofill/ViewState.java
index 33a2e50..e1b089c 100644
--- a/services/autofill/java/com/android/server/autofill/ViewState.java
+++ b/services/autofill/java/com/android/server/autofill/ViewState.java
@@ -214,7 +214,7 @@
         if (mDatasetId != null) {
             builder.append(", datasetId:" ).append(mDatasetId);
         }
-        builder.append("state:" ).append(getStateAsString());
+        builder.append(", state:").append(getStateAsString());
         if (mCurrentValue != null) {
             builder.append(", currentValue:" ).append(mCurrentValue);
         }
diff --git a/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java b/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java
index 94e617d..5b4694d 100644
--- a/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java
+++ b/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java
@@ -50,6 +50,7 @@
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.provider.DeviceConfig;
+import android.provider.DeviceConfig.Properties;
 import android.provider.Settings;
 import android.service.contentcapture.ActivityEvent.ActivityEventType;
 import android.util.ArraySet;
@@ -131,9 +132,9 @@
                 com.android.internal.R.string.config_defaultContentCaptureService),
                 UserManager.DISALLOW_CONTENT_CAPTURE,
                 /*packageUpdatePolicy=*/ PACKAGE_UPDATE_POLICY_NO_REFRESH);
-        DeviceConfig.addOnPropertyChangedListener(DeviceConfig.NAMESPACE_CONTENT_CAPTURE,
+        DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_CONTENT_CAPTURE,
                 ActivityThread.currentApplication().getMainExecutor(),
-                (namespace, key, value) -> onDeviceConfigChange(key, value));
+                (properties) -> onDeviceConfigChange(properties));
         setDeviceConfigProperties();
 
         if (mDevCfgLogHistorySize > 0) {
@@ -255,23 +256,25 @@
         return enabled;
     }
 
-    private void onDeviceConfigChange(@NonNull String key, @Nullable String value) {
-        switch (key) {
-            case ContentCaptureManager.DEVICE_CONFIG_PROPERTY_SERVICE_EXPLICITLY_ENABLED:
-                setDisabledByDeviceConfig(value);
-                return;
-            case ContentCaptureManager.DEVICE_CONFIG_PROPERTY_LOGGING_LEVEL:
-                setLoggingLevelFromDeviceConfig();
-                return;
-            case ContentCaptureManager.DEVICE_CONFIG_PROPERTY_MAX_BUFFER_SIZE:
-            case ContentCaptureManager.DEVICE_CONFIG_PROPERTY_IDLE_FLUSH_FREQUENCY:
-            case ContentCaptureManager.DEVICE_CONFIG_PROPERTY_LOG_HISTORY_SIZE:
-            case ContentCaptureManager.DEVICE_CONFIG_PROPERTY_TEXT_CHANGE_FLUSH_FREQUENCY:
-            case ContentCaptureManager.DEVICE_CONFIG_PROPERTY_IDLE_UNBIND_TIMEOUT:
-                setFineTuneParamsFromDeviceConfig();
-                return;
-            default:
-                Slog.i(mTag, "Ignoring change on " + key);
+    private void onDeviceConfigChange(@NonNull Properties properties) {
+        for (String key : properties.getKeyset()) {
+            switch (key) {
+                case ContentCaptureManager.DEVICE_CONFIG_PROPERTY_SERVICE_EXPLICITLY_ENABLED:
+                    setDisabledByDeviceConfig(properties.getString(key, null));
+                    return;
+                case ContentCaptureManager.DEVICE_CONFIG_PROPERTY_LOGGING_LEVEL:
+                    setLoggingLevelFromDeviceConfig();
+                    return;
+                case ContentCaptureManager.DEVICE_CONFIG_PROPERTY_MAX_BUFFER_SIZE:
+                case ContentCaptureManager.DEVICE_CONFIG_PROPERTY_IDLE_FLUSH_FREQUENCY:
+                case ContentCaptureManager.DEVICE_CONFIG_PROPERTY_LOG_HISTORY_SIZE:
+                case ContentCaptureManager.DEVICE_CONFIG_PROPERTY_TEXT_CHANGE_FLUSH_FREQUENCY:
+                case ContentCaptureManager.DEVICE_CONFIG_PROPERTY_IDLE_UNBIND_TIMEOUT:
+                    setFineTuneParamsFromDeviceConfig();
+                    return;
+                default:
+                    Slog.i(mTag, "Ignoring change on " + key);
+            }
         }
     }
 
diff --git a/services/core/java/com/android/server/RescueParty.java b/services/core/java/com/android/server/RescueParty.java
index 1fe0271..fb1a962 100644
--- a/services/core/java/com/android/server/RescueParty.java
+++ b/services/core/java/com/android/server/RescueParty.java
@@ -72,8 +72,12 @@
     static final int LEVEL_FACTORY_RESET = 4;
     @VisibleForTesting
     static final String PROP_RESCUE_BOOT_COUNT = "sys.rescue_boot_count";
+    /**
+     * The boot trigger window size must always be greater than Watchdog's deadlock timeout
+     * {@link Watchdog#DEFAULT_TIMEOUT}.
+     */
     @VisibleForTesting
-    static final long BOOT_TRIGGER_WINDOW_MILLIS = 300 * DateUtils.SECOND_IN_MILLIS;
+    static final long BOOT_TRIGGER_WINDOW_MILLIS = 600 * DateUtils.SECOND_IN_MILLIS;
     @VisibleForTesting
     static final long PERSISTENT_APP_CRASH_TRIGGER_WINDOW_MILLIS = 30 * DateUtils.SECOND_IN_MILLIS;
     @VisibleForTesting
diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java
index 410d436..baec3cc 100644
--- a/services/core/java/com/android/server/StorageManagerService.java
+++ b/services/core/java/com/android/server/StorageManagerService.java
@@ -802,8 +802,8 @@
                 }
             });
         // For now, simply clone property when it changes
-        DeviceConfig.addOnPropertyChangedListener(DeviceConfig.NAMESPACE_STORAGE,
-                mContext.getMainExecutor(), (namespace, name, value) -> {
+        DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_STORAGE,
+                mContext.getMainExecutor(), (properties) -> {
                     refreshIsolatedStorageSettings();
                 });
         refreshIsolatedStorageSettings();
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index 76136df..df92106 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -634,7 +634,7 @@
         }
 
         if (allowBackgroundActivityStarts) {
-            r.hasStartedWhitelistingBgActivityStarts = true;
+            r.setHasStartedWhitelistingBgActivityStarts(true);
             scheduleCleanUpHasStartedWhitelistingBgActivityStartsLocked(r);
         }
 
@@ -761,11 +761,6 @@
         }
         service.callStart = false;
 
-        // the service will not necessarily be brought down, so only clear the whitelisting state
-        // for start-based bg activity starts now, and drop any existing future cleanup callback
-        service.setHasStartedWhitelistingBgActivityStarts(false);
-        mAm.mHandler.removeCallbacks(service.startedWhitelistingBgActivityStartsCleanUp);
-
         bringDownServiceIfNeededLocked(service, false, false);
     }
 
@@ -1768,12 +1763,7 @@
                     callerApp.uid, callerApp.processName, callingPackage);
 
             IBinder binder = connection.asBinder();
-            ArrayList<ConnectionRecord> clist = s.getConnections().get(binder);
-            if (clist == null) {
-                clist = new ArrayList<ConnectionRecord>();
-                s.putConnection(binder, clist);
-            }
-            clist.add(c);
+            s.addConnection(binder, c);
             b.connections.add(c);
             if (activity != null) {
                 activity.addConnection(c);
@@ -1792,9 +1782,9 @@
             if (s.app != null) {
                 updateServiceClientActivitiesLocked(s.app, c, true);
             }
-            clist = mServiceConnections.get(binder);
+            ArrayList<ConnectionRecord> clist = mServiceConnections.get(binder);
             if (clist == null) {
-                clist = new ArrayList<ConnectionRecord>();
+                clist = new ArrayList<>();
                 mServiceConnections.put(binder, clist);
             }
             clist.add(c);
@@ -3828,8 +3818,8 @@
     public PendingIntent getRunningServiceControlPanelLocked(ComponentName name) {
         int userId = UserHandle.getUserId(Binder.getCallingUid());
         ServiceRecord r = getServiceByNameLocked(name, userId);
-        ArrayMap<IBinder, ArrayList<ConnectionRecord>> connections = r.getConnections();
         if (r != null) {
+            ArrayMap<IBinder, ArrayList<ConnectionRecord>> connections = r.getConnections();
             for (int conni = connections.size() - 1; conni >= 0; conni--) {
                 ArrayList<ConnectionRecord> conn = connections.valueAt(conni);
                 for (int i=0; i<conn.size(); i++) {
diff --git a/services/core/java/com/android/server/am/ActivityManagerConstants.java b/services/core/java/com/android/server/am/ActivityManagerConstants.java
index d7decb4..0da39e7 100644
--- a/services/core/java/com/android/server/am/ActivityManagerConstants.java
+++ b/services/core/java/com/android/server/am/ActivityManagerConstants.java
@@ -26,7 +26,8 @@
 import android.os.Build;
 import android.os.Handler;
 import android.provider.DeviceConfig;
-import android.provider.DeviceConfig.OnPropertyChangedListener;
+import android.provider.DeviceConfig.OnPropertiesChangedListener;
+import android.provider.DeviceConfig.Properties;
 import android.provider.Settings;
 import android.text.TextUtils;
 import android.text.TextUtils.SimpleStringSplitter;
@@ -315,23 +316,25 @@
     private static final Uri ENABLE_AUTOMATIC_SYSTEM_SERVER_HEAP_DUMPS_URI =
             Settings.Global.getUriFor(Settings.Global.ENABLE_AUTOMATIC_SYSTEM_SERVER_HEAP_DUMPS);
 
-    private final OnPropertyChangedListener mOnDeviceConfigChangedListener =
-            new OnPropertyChangedListener() {
+    private final OnPropertiesChangedListener mOnDeviceConfigChangedListener =
+            new OnPropertiesChangedListener() {
                 @Override
-                public void onPropertyChanged(String namespace, String name, String value) {
-                    if (name == null) {
-                        return;
-                    }
-                    switch (name) {
-                        case KEY_MAX_CACHED_PROCESSES:
-                            updateMaxCachedProcesses();
-                            break;
-                        case KEY_DEFAULT_BACKGROUND_ACTIVITY_STARTS_ENABLED:
-                        case KEY_BACKGROUND_ACTIVITY_STARTS_PACKAGE_NAMES_WHITELIST:
-                            updateBackgroundActivityStarts();
-                            break;
-                        default:
-                            break;
+                public void onPropertiesChanged(Properties properties) {
+                    for (String name : properties.getKeyset()) {
+                        if (name == null) {
+                            return;
+                        }
+                        switch (name) {
+                            case KEY_MAX_CACHED_PROCESSES:
+                                updateMaxCachedProcesses();
+                                break;
+                            case KEY_DEFAULT_BACKGROUND_ACTIVITY_STARTS_ENABLED:
+                            case KEY_BACKGROUND_ACTIVITY_STARTS_PACKAGE_NAMES_WHITELIST:
+                                updateBackgroundActivityStarts();
+                                break;
+                            default:
+                                break;
+                        }
                     }
                 }
             };
@@ -362,7 +365,7 @@
         if (mSystemServerAutomaticHeapDumpEnabled) {
             updateEnableAutomaticSystemServerHeapDumps();
         }
-        DeviceConfig.addOnPropertyChangedListener(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+        DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
                 ActivityThread.currentApplication().getMainExecutor(),
                 mOnDeviceConfigChangedListener);
         updateMaxCachedProcesses();
diff --git a/services/core/java/com/android/server/am/AppCompactor.java b/services/core/java/com/android/server/am/AppCompactor.java
index 13b55db..b0f8f86 100644
--- a/services/core/java/com/android/server/am/AppCompactor.java
+++ b/services/core/java/com/android/server/am/AppCompactor.java
@@ -30,7 +30,8 @@
 import android.os.SystemClock;
 import android.os.Trace;
 import android.provider.DeviceConfig;
-import android.provider.DeviceConfig.OnPropertyChangedListener;
+import android.provider.DeviceConfig.OnPropertiesChangedListener;
+import android.provider.DeviceConfig.Properties;
 import android.text.TextUtils;
 import android.util.EventLog;
 import android.util.Slog;
@@ -126,29 +127,31 @@
     private final ArrayList<ProcessRecord> mPendingCompactionProcesses =
             new ArrayList<ProcessRecord>();
     private final ActivityManagerService mAm;
-    private final OnPropertyChangedListener mOnFlagsChangedListener =
-            new OnPropertyChangedListener() {
+    private final OnPropertiesChangedListener mOnFlagsChangedListener =
+            new OnPropertiesChangedListener() {
                 @Override
-                public void onPropertyChanged(String namespace, String name, String value) {
+                public void onPropertiesChanged(Properties properties) {
                     synchronized (mPhenotypeFlagLock) {
-                        if (KEY_USE_COMPACTION.equals(name)) {
-                            updateUseCompaction();
-                        } else if (KEY_COMPACT_ACTION_1.equals(name)
-                                || KEY_COMPACT_ACTION_2.equals(name)) {
-                            updateCompactionActions();
-                        } else if (KEY_COMPACT_THROTTLE_1.equals(name)
-                                || KEY_COMPACT_THROTTLE_2.equals(name)
-                                || KEY_COMPACT_THROTTLE_3.equals(name)
-                                || KEY_COMPACT_THROTTLE_4.equals(name)) {
-                            updateCompactionThrottles();
-                        } else if (KEY_COMPACT_STATSD_SAMPLE_RATE.equals(name)) {
-                            updateStatsdSampleRate();
-                        } else if (KEY_COMPACT_FULL_RSS_THROTTLE_KB.equals(name)) {
-                            updateFullRssThrottle();
-                        } else if (KEY_COMPACT_FULL_DELTA_RSS_THROTTLE_KB.equals(name)) {
-                            updateFullDeltaRssThrottle();
-                        } else if (KEY_COMPACT_PROC_STATE_THROTTLE.equals(name)) {
-                            updateProcStateThrottle();
+                        for (String name : properties.getKeyset()) {
+                            if (KEY_USE_COMPACTION.equals(name)) {
+                                updateUseCompaction();
+                            } else if (KEY_COMPACT_ACTION_1.equals(name)
+                                    || KEY_COMPACT_ACTION_2.equals(name)) {
+                                updateCompactionActions();
+                            } else if (KEY_COMPACT_THROTTLE_1.equals(name)
+                                    || KEY_COMPACT_THROTTLE_2.equals(name)
+                                    || KEY_COMPACT_THROTTLE_3.equals(name)
+                                    || KEY_COMPACT_THROTTLE_4.equals(name)) {
+                                updateCompactionThrottles();
+                            } else if (KEY_COMPACT_STATSD_SAMPLE_RATE.equals(name)) {
+                                updateStatsdSampleRate();
+                            } else if (KEY_COMPACT_FULL_RSS_THROTTLE_KB.equals(name)) {
+                                updateFullRssThrottle();
+                            } else if (KEY_COMPACT_FULL_DELTA_RSS_THROTTLE_KB.equals(name)) {
+                                updateFullDeltaRssThrottle();
+                            } else if (KEY_COMPACT_PROC_STATE_THROTTLE.equals(name)) {
+                                updateProcStateThrottle();
+                            }
                         }
                     }
                     if (mTestCallback != null) {
@@ -229,7 +232,7 @@
      * starts the background thread if necessary.
      */
     public void init() {
-        DeviceConfig.addOnPropertyChangedListener(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+        DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
                 ActivityThread.currentApplication().getMainExecutor(), mOnFlagsChangedListener);
         synchronized (mPhenotypeFlagLock) {
             updateUseCompaction();
diff --git a/services/core/java/com/android/server/am/ProcessRecord.java b/services/core/java/com/android/server/am/ProcessRecord.java
index e891e6e..563b2f3 100644
--- a/services/core/java/com/android/server/am/ProcessRecord.java
+++ b/services/core/java/com/android/server/am/ProcessRecord.java
@@ -1186,8 +1186,8 @@
                 !mAllowBackgroundActivityStartsTokens.isEmpty());
     }
 
-    void addBoundClientUids(ArraySet<Integer> clientUids) {
-        mBoundClientUids.addAll(clientUids);
+    void addBoundClientUid(int clientUid) {
+        mBoundClientUids.add(clientUid);
         mWindowProcessController.setBoundClientUids(mBoundClientUids);
     }
 
diff --git a/services/core/java/com/android/server/am/ServiceRecord.java b/services/core/java/com/android/server/am/ServiceRecord.java
index 27c62d0..0426ec1 100644
--- a/services/core/java/com/android/server/am/ServiceRecord.java
+++ b/services/core/java/com/android/server/am/ServiceRecord.java
@@ -38,7 +38,6 @@
 import android.os.UserHandle;
 import android.provider.Settings;
 import android.util.ArrayMap;
-import android.util.ArraySet;
 import android.util.Slog;
 import android.util.TimeUtils;
 import android.util.proto.ProtoOutputStream;
@@ -131,10 +130,10 @@
     int pendingConnectionImportance;   // To be filled in to ProcessRecord once it connects
 
     // any current binding to this service has BIND_ALLOW_BACKGROUND_ACTIVITY_STARTS flag?
-    private boolean hasBindingWhitelistingBgActivityStarts;
+    private boolean mHasBindingWhitelistingBgActivityStarts;
     // is this service currently whitelisted to start activities from background by providing
     // allowBackgroundActivityStarts=true to startServiceLocked()?
-    boolean hasStartedWhitelistingBgActivityStarts;
+    private boolean mHasStartedWhitelistingBgActivityStarts;
     // used to clean up the state of hasStartedWhitelistingBgActivityStarts after a timeout
     Runnable startedWhitelistingBgActivityStartsCleanUp;
 
@@ -384,13 +383,13 @@
         if (whitelistManager) {
             pw.print(prefix); pw.print("whitelistManager="); pw.println(whitelistManager);
         }
-        if (hasBindingWhitelistingBgActivityStarts) {
+        if (mHasBindingWhitelistingBgActivityStarts) {
             pw.print(prefix); pw.print("hasBindingWhitelistingBgActivityStarts=");
-            pw.println(hasBindingWhitelistingBgActivityStarts);
+            pw.println(mHasBindingWhitelistingBgActivityStarts);
         }
-        if (hasStartedWhitelistingBgActivityStarts) {
+        if (mHasStartedWhitelistingBgActivityStarts) {
             pw.print(prefix); pw.print("hasStartedWhitelistingBgActivityStarts=");
-            pw.println(hasStartedWhitelistingBgActivityStarts);
+            pw.println(mHasStartedWhitelistingBgActivityStarts);
         }
         if (delayed) {
             pw.print(prefix); pw.print("delayed="); pw.println(delayed);
@@ -542,7 +541,8 @@
 
     public void setProcess(ProcessRecord _proc) {
         if (_proc != null) {
-            if (hasStartedWhitelistingBgActivityStarts || hasBindingWhitelistingBgActivityStarts) {
+            if (mHasStartedWhitelistingBgActivityStarts
+                    || mHasBindingWhitelistingBgActivityStarts) {
                 _proc.addAllowBackgroundActivityStartsToken(this);
             } else {
                 _proc.removeAllowBackgroundActivityStartsToken(this);
@@ -580,15 +580,17 @@
         return connections;
     }
 
-    void putConnection(IBinder binder, ArrayList<ConnectionRecord> clist) {
-        connections.put(binder, clist);
-        // if we have a process attached, add bound client uids of this connection to it
+    void addConnection(IBinder binder, ConnectionRecord c) {
+        ArrayList<ConnectionRecord> clist = connections.get(binder);
+        if (clist == null) {
+            clist = new ArrayList<>();
+            connections.put(binder, clist);
+        }
+        clist.add(c);
+
+        // if we have a process attached, add bound client uid of this connection to it
         if (app != null) {
-            ArraySet<Integer> boundClientUids = new ArraySet<>();
-            for (int i = 0; i < clist.size(); i++) {
-                boundClientUids.add(clist.get(i).clientUid);
-            }
-            app.addBoundClientUids(boundClientUids);
+            app.addBoundClientUid(c.clientUid);
         }
     }
 
@@ -614,22 +616,22 @@
                 break;
             }
         }
-        if (hasBindingWhitelistingBgActivityStarts != hasWhitelistingBinding) {
-            hasBindingWhitelistingBgActivityStarts = hasWhitelistingBinding;
+        if (mHasBindingWhitelistingBgActivityStarts != hasWhitelistingBinding) {
+            mHasBindingWhitelistingBgActivityStarts = hasWhitelistingBinding;
             updateParentProcessBgActivityStartsWhitelistingToken();
         }
     }
 
     void setHasBindingWhitelistingBgActivityStarts(boolean newValue) {
-        if (hasBindingWhitelistingBgActivityStarts != newValue) {
-            hasBindingWhitelistingBgActivityStarts = newValue;
+        if (mHasBindingWhitelistingBgActivityStarts != newValue) {
+            mHasBindingWhitelistingBgActivityStarts = newValue;
             updateParentProcessBgActivityStartsWhitelistingToken();
         }
     }
 
     void setHasStartedWhitelistingBgActivityStarts(boolean newValue) {
-        if (hasStartedWhitelistingBgActivityStarts != newValue) {
-            hasStartedWhitelistingBgActivityStarts = newValue;
+        if (mHasStartedWhitelistingBgActivityStarts != newValue) {
+            mHasStartedWhitelistingBgActivityStarts = newValue;
             updateParentProcessBgActivityStartsWhitelistingToken();
         }
     }
@@ -647,7 +649,7 @@
         if (app == null) {
             return;
         }
-        if (hasStartedWhitelistingBgActivityStarts || hasBindingWhitelistingBgActivityStarts) {
+        if (mHasStartedWhitelistingBgActivityStarts || mHasBindingWhitelistingBgActivityStarts) {
             // if the token is already there it's safe to "re-add it" - we're deadling with
             // a set of Binder objects
             app.addAllowBackgroundActivityStartsToken(this);
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 507f398..d510912 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -1825,14 +1825,28 @@
                             && streamTypeAlias == AudioSystem.STREAM_MUSIC
                             // vol change on a full volume device
                             && ((device & mFullVolumeDevices) != 0)) {
-                        int keyCode = (direction == -1) ? KeyEvent.KEYCODE_VOLUME_DOWN :
-                                KeyEvent.KEYCODE_VOLUME_UP;
-                        final long ident = Binder.clearCallingIdentity();
-                        try {
-                            mHdmiPlaybackClient.sendKeyEvent(keyCode, true);
-                            mHdmiPlaybackClient.sendKeyEvent(keyCode, false);
-                        } finally {
-                            Binder.restoreCallingIdentity(ident);
+                        int keyCode = KeyEvent.KEYCODE_UNKNOWN;
+                        switch (direction) {
+                            case AudioManager.ADJUST_RAISE:
+                                keyCode = KeyEvent.KEYCODE_VOLUME_UP;
+                                break;
+                            case AudioManager.ADJUST_LOWER:
+                                keyCode = KeyEvent.KEYCODE_VOLUME_DOWN;
+                                break;
+                            case AudioManager.ADJUST_TOGGLE_MUTE:
+                                keyCode = KeyEvent.KEYCODE_VOLUME_MUTE;
+                                break;
+                            default:
+                                break;
+                        }
+                        if (keyCode != KeyEvent.KEYCODE_UNKNOWN) {
+                            final long ident = Binder.clearCallingIdentity();
+                            try {
+                                mHdmiPlaybackClient.sendKeyEvent(keyCode, true);
+                                mHdmiPlaybackClient.sendKeyEvent(keyCode, false);
+                            } finally {
+                                Binder.restoreCallingIdentity(ident);
+                            }
                         }
                     }
 
diff --git a/services/core/java/com/android/server/connectivity/tethering/IPv6TetheringCoordinator.java b/services/core/java/com/android/server/connectivity/tethering/IPv6TetheringCoordinator.java
index 38eb0bc..8186343 100644
--- a/services/core/java/com/android/server/connectivity/tethering/IPv6TetheringCoordinator.java
+++ b/services/core/java/com/android/server/connectivity/tethering/IPv6TetheringCoordinator.java
@@ -16,7 +16,6 @@
 
 package com.android.server.connectivity.tethering;
 
-import android.net.ConnectivityManager;
 import android.net.IpPrefix;
 import android.net.LinkAddress;
 import android.net.LinkProperties;
@@ -164,11 +163,6 @@
     }
 
     private LinkProperties getInterfaceIPv6LinkProperties(IpServer ipServer) {
-        if (ipServer.interfaceType() == ConnectivityManager.TETHERING_BLUETOOTH) {
-            // TODO: Figure out IPv6 support on PAN interfaces.
-            return null;
-        }
-
         final Downstream ds = findDownstream(ipServer);
         if (ds == null) return null;
 
diff --git a/services/core/java/com/android/server/gpu/GpuService.java b/services/core/java/com/android/server/gpu/GpuService.java
index 0f73f37..d439653 100644
--- a/services/core/java/com/android/server/gpu/GpuService.java
+++ b/services/core/java/com/android/server/gpu/GpuService.java
@@ -37,6 +37,7 @@
 import android.os.SystemProperties;
 import android.os.UserHandle;
 import android.provider.DeviceConfig;
+import android.provider.DeviceConfig.Properties;
 import android.provider.Settings;
 import android.util.Base64;
 import android.util.Slog;
@@ -138,18 +139,19 @@
         }
     }
 
-    private final class DeviceConfigListener implements DeviceConfig.OnPropertyChangedListener {
+    private final class DeviceConfigListener implements DeviceConfig.OnPropertiesChangedListener {
 
         DeviceConfigListener() {
             super();
-            DeviceConfig.addOnPropertyChangedListener(DeviceConfig.NAMESPACE_GAME_DRIVER,
+            DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_GAME_DRIVER,
                     mContext.getMainExecutor(), this);
         }
         @Override
-        public void onPropertyChanged(String namespace, String name, String value) {
+        public void onPropertiesChanged(Properties properties) {
             synchronized (mDeviceConfigLock) {
-                if (Settings.Global.GAME_DRIVER_BLACKLISTS.equals(name)) {
-                    parseBlacklists(value != null ? value : "");
+                if (properties.getKeyset().contains(Settings.Global.GAME_DRIVER_BLACKLISTS)) {
+                    parseBlacklists(
+                            properties.getString(Settings.Global.GAME_DRIVER_BLACKLISTS, ""));
                     setBlacklist();
                 }
             }
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index 651ce7d..30d244f 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -1409,7 +1409,7 @@
         mIWindowManager = IWindowManager.Stub.asInterface(
                 ServiceManager.getService(Context.WINDOW_SERVICE));
         mWindowManagerInternal = LocalServices.getService(WindowManagerInternal.class);
-        mImeDisplayValidator = mWindowManagerInternal::shouldShowSystemDecorOnDisplay;
+        mImeDisplayValidator = displayId -> mWindowManagerInternal.shouldShowIme(displayId);
         mCaller = new HandlerCaller(context, null, new HandlerCaller.Callback() {
             @Override
             public void executeMessage(Message msg) {
@@ -2139,7 +2139,9 @@
         if (displayId == DEFAULT_DISPLAY || displayId == INVALID_DISPLAY) {
             return FALLBACK_DISPLAY_ID;
         }
-        // Show IME window on fallback display when the display is not allowed.
+
+        // Show IME window on fallback display when the display doesn't support system decorations
+        // or the display is virtual and isn't owned by system for security concern.
         return checker.displayCanShowIme(displayId) ? displayId : FALLBACK_DISPLAY_ID;
     }
 
diff --git a/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java
index 580150e..e0b8e71 100644
--- a/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java
@@ -79,6 +79,9 @@
 import com.android.internal.inputmethod.UnbindReason;
 import com.android.internal.messages.nano.SystemMessageProto;
 import com.android.internal.notification.SystemNotificationChannels;
+import com.android.internal.os.TransferPipe;
+import com.android.internal.util.DumpUtils;
+import com.android.internal.util.IndentingPrintWriter;
 import com.android.internal.util.function.pooled.PooledLambda;
 import com.android.internal.view.IInputContext;
 import com.android.internal.view.IInputMethodClient;
@@ -90,6 +93,8 @@
 import com.android.server.wm.WindowManagerInternal;
 
 import java.io.FileDescriptor;
+import java.io.IOException;
+import java.io.PrintWriter;
 import java.lang.annotation.Retention;
 import java.util.Collections;
 import java.util.List;
@@ -645,6 +650,14 @@
             mSelfReportedDisplayId = selfReportedDisplayId;
             mClientId = InputMethodClientIdSource.getNext();
         }
+
+        @GuardedBy("PerUserData.mLock")
+        void dumpLocked(FileDescriptor fd, IndentingPrintWriter ipw, String[] args) {
+            ipw.println("mState=" + mState + ",mBindingSequence=" + mBindingSequence
+                    + ",mWriteChannel=" + mWriteChannel
+                    + ",mInputMethodSession=" + mInputMethodSession
+                    + ",mMSInputMethodSession=" + mMSInputMethodSession);
+        }
     }
 
     private static final class UserDataMap {
@@ -673,6 +686,22 @@
                 return mMap.removeReturnOld(userId);
             }
         }
+
+        @AnyThread
+        void dump(FileDescriptor fd, IndentingPrintWriter ipw, String[] args) {
+            synchronized (mMap) {
+                for (int i = 0; i < mMap.size(); i++) {
+                    int userId = mMap.keyAt(i);
+                    PerUserData data = mMap.valueAt(i);
+                    ipw.println("userId=" + userId + ", data=");
+                    if (data != null) {
+                        ipw.increaseIndent();
+                        data.dump(fd, ipw, args);
+                        ipw.decreaseIndent();
+                    }
+                }
+            }
+        }
     }
 
     private static final class TokenInfo {
@@ -967,6 +996,71 @@
             }
         }
 
+        @AnyThread
+        void dump(FileDescriptor fd, IndentingPrintWriter ipw, String[] args) {
+            synchronized (mLock) {
+                ipw.println("mState=" + mState
+                        + ",mCurrentInputMethod=" + mCurrentInputMethod
+                        + ",mCurrentInputMethodInfo=" + mCurrentInputMethodInfo);
+
+                if (mCurrentInputMethod != null) {
+                    // indentation will not be kept. So add visual separator here.
+                    ipw.println(">>Dump CurrentInputMethod>>");
+                    ipw.flush();
+                    try {
+                        TransferPipe.dumpAsync(mCurrentInputMethod.asBinder(), fd, args);
+                    } catch (IOException | RemoteException e) {
+                        ipw.println("Failed to dump input method service: " + e);
+                    }
+                    ipw.println("<<Dump CurrentInputMethod<<");
+                }
+
+                ipw.println("mDisplayIdToImeWindowTokenMap=");
+                for (TokenInfo info : mDisplayIdToImeWindowTokenMap) {
+                    ipw.println(" display=" + info.mDisplayId + ",token="
+                            + info.mToken);
+                }
+                ipw.println("mClientMap=");
+                ipw.increaseIndent();
+                for (int i = 0; i < mClientMap.size(); i++) {
+
+                    ipw.println("binder=" + mClientMap.keyAt(i));
+                    ipw.println(" InputMethodClientInfo=");
+                    InputMethodClientInfo info = mClientMap.valueAt(i);
+                    if (info != null) {
+                        ipw.increaseIndent();
+                        info.dumpLocked(fd, ipw, args);
+                        ipw.decreaseIndent();
+                    }
+                }
+                ipw.decreaseIndent();
+                ipw.println("mClientIdToClientMap=");
+                ipw.increaseIndent();
+                for (int i = 0; i < mClientIdToClientMap.size(); i++) {
+                    ipw.println("clientId=" + mClientIdToClientMap.keyAt(i));
+                    ipw.println(" InputMethodClientInfo=");
+                    InputMethodClientInfo info = mClientIdToClientMap.valueAt(i);
+                    if (info != null) {
+                        ipw.increaseIndent();
+                        info.dumpLocked(fd, ipw, args);
+                        ipw.decreaseIndent();
+                    }
+                    if (info.mClient != null) {
+                        // indentation will not be kept. So add visual separator here.
+                        ipw.println(">>DumpClientStart>>");
+                        ipw.flush(); // all writes should be flushed to guarantee order.
+                        try {
+                            TransferPipe.dumpAsync(info.mClient.asBinder(), fd, args);
+                        } catch (IOException | RemoteException e) {
+                            ipw.println(" Failed to dump client:" + e);
+                        }
+                        ipw.println("<<DumpClientEnd<<");
+                    }
+                }
+                ipw.decreaseIndent();
+            }
+        }
+
         private static final class ClientDeathRecipient implements IBinder.DeathRecipient {
             private final PerUserData mPerUserData;
             private final IInputMethodClient mClient;
@@ -1106,6 +1200,16 @@
             }
             return Collections.singletonList(info);
         }
+
+        @AnyThread
+        void dump(FileDescriptor fd, IndentingPrintWriter ipw, String[] args) {
+            synchronized (mArray) {
+                for (int i = 0; i < mArray.size(); i++) {
+                    ipw.println("userId=" + mArray.keyAt(i));
+                    ipw.println(" InputMethodInfo=" + mArray.valueAt(i));
+                }
+            }
+        }
     }
 
     /**
@@ -1601,5 +1705,19 @@
                 @Nullable FileDescriptor err, String[] args, @Nullable ShellCallback callback,
                 ResultReceiver resultReceiver) {
         }
+
+        @BinderThread
+        @Override
+        protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+            if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
+            final String prefixChild = "  ";
+            pw.println("Current Multi Client Input Method Manager state:");
+            IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " ");
+            ipw.println("mUserDataMap=");
+            if (mUserDataMap != null) {
+                ipw.increaseIndent();
+                mUserDataMap.dump(fd, ipw, args);
+            }
+        }
     }
 }
diff --git a/services/core/java/com/android/server/job/JobSchedulerService.java b/services/core/java/com/android/server/job/JobSchedulerService.java
index d5883bb..b676618 100644
--- a/services/core/java/com/android/server/job/JobSchedulerService.java
+++ b/services/core/java/com/android/server/job/JobSchedulerService.java
@@ -18,6 +18,7 @@
 
 import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED;
 import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER;
+import static android.text.format.DateUtils.MINUTE_IN_MILLIS;
 
 import android.annotation.NonNull;
 import android.annotation.UserIdInt;
@@ -463,32 +464,6 @@
         private static final String KEY_CONN_CONGESTION_DELAY_FRAC = "conn_congestion_delay_frac";
         private static final String KEY_CONN_PREFETCH_RELAX_FRAC = "conn_prefetch_relax_frac";
         private static final String KEY_USE_HEARTBEATS = "use_heartbeats";
-        private static final String KEY_TIME_CONTROLLER_SKIP_NOT_READY_JOBS =
-                "tc_skip_not_ready_jobs";
-        private static final String KEY_QUOTA_CONTROLLER_ALLOWED_TIME_PER_PERIOD_MS =
-                "qc_allowed_time_per_period_ms";
-        private static final String KEY_QUOTA_CONTROLLER_IN_QUOTA_BUFFER_MS =
-                "qc_in_quota_buffer_ms";
-        private static final String KEY_QUOTA_CONTROLLER_WINDOW_SIZE_ACTIVE_MS =
-                "qc_window_size_active_ms";
-        private static final String KEY_QUOTA_CONTROLLER_WINDOW_SIZE_WORKING_MS =
-                "qc_window_size_working_ms";
-        private static final String KEY_QUOTA_CONTROLLER_WINDOW_SIZE_FREQUENT_MS =
-                "qc_window_size_frequent_ms";
-        private static final String KEY_QUOTA_CONTROLLER_WINDOW_SIZE_RARE_MS =
-                "qc_window_size_rare_ms";
-        private static final String KEY_QUOTA_CONTROLLER_MAX_EXECUTION_TIME_MS =
-                "qc_max_execution_time_ms";
-        private static final String KEY_QUOTA_CONTROLLER_MAX_JOB_COUNT_ACTIVE =
-                "qc_max_job_count_active";
-        private static final String KEY_QUOTA_CONTROLLER_MAX_JOB_COUNT_WORKING =
-                "qc_max_job_count_working";
-        private static final String KEY_QUOTA_CONTROLLER_MAX_JOB_COUNT_FREQUENT =
-                "qc_max_job_count_frequent";
-        private static final String KEY_QUOTA_CONTROLLER_MAX_JOB_COUNT_RARE =
-                "qc_max_job_count_rare";
-        private static final String KEY_QUOTA_CONTROLLER_MAX_JOB_COUNT_PER_ALLOWED_TIME =
-                "qc_max_count_per_allowed_time";
 
         private static final int DEFAULT_MIN_IDLE_COUNT = 1;
         private static final int DEFAULT_MIN_CHARGING_COUNT = 1;
@@ -510,30 +485,6 @@
         private static final float DEFAULT_CONN_CONGESTION_DELAY_FRAC = 0.5f;
         private static final float DEFAULT_CONN_PREFETCH_RELAX_FRAC = 0.5f;
         private static final boolean DEFAULT_USE_HEARTBEATS = false;
-        private static final boolean DEFAULT_TIME_CONTROLLER_SKIP_NOT_READY_JOBS = true;
-        private static final long DEFAULT_QUOTA_CONTROLLER_ALLOWED_TIME_PER_PERIOD_MS =
-                10 * 60 * 1000L; // 10 minutes
-        private static final long DEFAULT_QUOTA_CONTROLLER_IN_QUOTA_BUFFER_MS =
-                30 * 1000L; // 30 seconds
-        private static final long DEFAULT_QUOTA_CONTROLLER_WINDOW_SIZE_ACTIVE_MS =
-                10 * 60 * 1000L; // 10 minutes for ACTIVE -- ACTIVE apps can run jobs at any time
-        private static final long DEFAULT_QUOTA_CONTROLLER_WINDOW_SIZE_WORKING_MS =
-                2 * 60 * 60 * 1000L; // 2 hours
-        private static final long DEFAULT_QUOTA_CONTROLLER_WINDOW_SIZE_FREQUENT_MS =
-                8 * 60 * 60 * 1000L; // 8 hours
-        private static final long DEFAULT_QUOTA_CONTROLLER_WINDOW_SIZE_RARE_MS =
-                24 * 60 * 60 * 1000L; // 24 hours
-        private static final long DEFAULT_QUOTA_CONTROLLER_MAX_EXECUTION_TIME_MS =
-                4 * 60 * 60 * 1000L; // 4 hours
-        private static final int DEFAULT_QUOTA_CONTROLLER_MAX_JOB_COUNT_ACTIVE =
-                200; // 1200/hr
-        private static final int DEFAULT_QUOTA_CONTROLLER_MAX_JOB_COUNT_WORKING =
-                1200; // 600/hr
-        private static final int DEFAULT_QUOTA_CONTROLLER_MAX_JOB_COUNT_FREQUENT =
-                1800; // 225/hr
-        private static final int DEFAULT_QUOTA_CONTROLLER_MAX_JOB_COUNT_RARE =
-                2400; // 100/hr
-        private static final int DEFAULT_QUOTA_CONTROLLER_MAX_JOB_COUNT_PER_ALLOWED_TIME = 20;
 
         /**
          * Minimum # of idle jobs that must be ready in order to force the JMS to schedule things
@@ -680,97 +631,6 @@
          */
         public boolean USE_HEARTBEATS = DEFAULT_USE_HEARTBEATS;
 
-        /**
-         * Whether or not TimeController should skip setting wakeup alarms for jobs that aren't
-         * ready now.
-         */
-        public boolean TIME_CONTROLLER_SKIP_NOT_READY_JOBS =
-                DEFAULT_TIME_CONTROLLER_SKIP_NOT_READY_JOBS;
-
-        /** How much time each app will have to run jobs within their standby bucket window. */
-        public long QUOTA_CONTROLLER_ALLOWED_TIME_PER_PERIOD_MS =
-                DEFAULT_QUOTA_CONTROLLER_ALLOWED_TIME_PER_PERIOD_MS;
-
-        /**
-         * How much time the package should have before transitioning from out-of-quota to in-quota.
-         * This should not affect processing if the package is already in-quota.
-         */
-        public long QUOTA_CONTROLLER_IN_QUOTA_BUFFER_MS =
-                DEFAULT_QUOTA_CONTROLLER_IN_QUOTA_BUFFER_MS;
-
-        /**
-         * The quota window size of the particular standby bucket. Apps in this standby bucket are
-         * expected to run only {@link #QUOTA_CONTROLLER_ALLOWED_TIME_PER_PERIOD_MS} within the past
-         * WINDOW_SIZE_MS.
-         */
-        public long QUOTA_CONTROLLER_WINDOW_SIZE_ACTIVE_MS =
-                DEFAULT_QUOTA_CONTROLLER_WINDOW_SIZE_ACTIVE_MS;
-
-        /**
-         * The quota window size of the particular standby bucket. Apps in this standby bucket are
-         * expected to run only {@link #QUOTA_CONTROLLER_ALLOWED_TIME_PER_PERIOD_MS} within the past
-         * WINDOW_SIZE_MS.
-         */
-        public long QUOTA_CONTROLLER_WINDOW_SIZE_WORKING_MS =
-                DEFAULT_QUOTA_CONTROLLER_WINDOW_SIZE_WORKING_MS;
-
-        /**
-         * The quota window size of the particular standby bucket. Apps in this standby bucket are
-         * expected to run only {@link #QUOTA_CONTROLLER_ALLOWED_TIME_PER_PERIOD_MS} within the past
-         * WINDOW_SIZE_MS.
-         */
-        public long QUOTA_CONTROLLER_WINDOW_SIZE_FREQUENT_MS =
-                DEFAULT_QUOTA_CONTROLLER_WINDOW_SIZE_FREQUENT_MS;
-
-        /**
-         * The quota window size of the particular standby bucket. Apps in this standby bucket are
-         * expected to run only {@link #QUOTA_CONTROLLER_ALLOWED_TIME_PER_PERIOD_MS} within the past
-         * WINDOW_SIZE_MS.
-         */
-        public long QUOTA_CONTROLLER_WINDOW_SIZE_RARE_MS =
-                DEFAULT_QUOTA_CONTROLLER_WINDOW_SIZE_RARE_MS;
-
-        /**
-         * The maximum amount of time an app can have its jobs running within a 24 hour window.
-         */
-        public long QUOTA_CONTROLLER_MAX_EXECUTION_TIME_MS =
-                DEFAULT_QUOTA_CONTROLLER_MAX_EXECUTION_TIME_MS;
-
-        /**
-         * The maximum number of jobs an app can run within this particular standby bucket's
-         * window size.
-         */
-        public int QUOTA_CONTROLLER_MAX_JOB_COUNT_ACTIVE =
-                DEFAULT_QUOTA_CONTROLLER_MAX_JOB_COUNT_ACTIVE;
-
-        /**
-         * The maximum number of jobs an app can run within this particular standby bucket's
-         * window size.
-         */
-        public int QUOTA_CONTROLLER_MAX_JOB_COUNT_WORKING =
-                DEFAULT_QUOTA_CONTROLLER_MAX_JOB_COUNT_WORKING;
-
-        /**
-         * The maximum number of jobs an app can run within this particular standby bucket's
-         * window size.
-         */
-        public int QUOTA_CONTROLLER_MAX_JOB_COUNT_FREQUENT =
-                DEFAULT_QUOTA_CONTROLLER_MAX_JOB_COUNT_FREQUENT;
-
-        /**
-         * The maximum number of jobs an app can run within this particular standby bucket's
-         * window size.
-         */
-        public int QUOTA_CONTROLLER_MAX_JOB_COUNT_RARE =
-                DEFAULT_QUOTA_CONTROLLER_MAX_JOB_COUNT_RARE;
-
-        /**
-         * The maximum number of jobs that can run within the past
-         * {@link #QUOTA_CONTROLLER_ALLOWED_TIME_PER_PERIOD_MS}.
-         */
-        public int QUOTA_CONTROLLER_MAX_JOB_COUNT_PER_ALLOWED_TIME =
-                DEFAULT_QUOTA_CONTROLLER_MAX_JOB_COUNT_PER_ALLOWED_TIME;
-
         private final KeyValueListParser mParser = new KeyValueListParser(',');
 
         void updateConstantsLocked(String value) {
@@ -834,45 +694,6 @@
             CONN_PREFETCH_RELAX_FRAC = mParser.getFloat(KEY_CONN_PREFETCH_RELAX_FRAC,
                     DEFAULT_CONN_PREFETCH_RELAX_FRAC);
             USE_HEARTBEATS = mParser.getBoolean(KEY_USE_HEARTBEATS, DEFAULT_USE_HEARTBEATS);
-            TIME_CONTROLLER_SKIP_NOT_READY_JOBS = mParser.getBoolean(
-                    KEY_TIME_CONTROLLER_SKIP_NOT_READY_JOBS,
-                    DEFAULT_TIME_CONTROLLER_SKIP_NOT_READY_JOBS);
-            QUOTA_CONTROLLER_ALLOWED_TIME_PER_PERIOD_MS = mParser.getDurationMillis(
-                    KEY_QUOTA_CONTROLLER_ALLOWED_TIME_PER_PERIOD_MS,
-                    DEFAULT_QUOTA_CONTROLLER_ALLOWED_TIME_PER_PERIOD_MS);
-            QUOTA_CONTROLLER_IN_QUOTA_BUFFER_MS = mParser.getDurationMillis(
-                    KEY_QUOTA_CONTROLLER_IN_QUOTA_BUFFER_MS,
-                    DEFAULT_QUOTA_CONTROLLER_IN_QUOTA_BUFFER_MS);
-            QUOTA_CONTROLLER_WINDOW_SIZE_ACTIVE_MS = mParser.getDurationMillis(
-                    KEY_QUOTA_CONTROLLER_WINDOW_SIZE_ACTIVE_MS,
-                    DEFAULT_QUOTA_CONTROLLER_WINDOW_SIZE_ACTIVE_MS);
-            QUOTA_CONTROLLER_WINDOW_SIZE_WORKING_MS = mParser.getDurationMillis(
-                    KEY_QUOTA_CONTROLLER_WINDOW_SIZE_WORKING_MS,
-                    DEFAULT_QUOTA_CONTROLLER_WINDOW_SIZE_WORKING_MS);
-            QUOTA_CONTROLLER_WINDOW_SIZE_FREQUENT_MS = mParser.getDurationMillis(
-                    KEY_QUOTA_CONTROLLER_WINDOW_SIZE_FREQUENT_MS,
-                    DEFAULT_QUOTA_CONTROLLER_WINDOW_SIZE_FREQUENT_MS);
-            QUOTA_CONTROLLER_WINDOW_SIZE_RARE_MS = mParser.getDurationMillis(
-                    KEY_QUOTA_CONTROLLER_WINDOW_SIZE_RARE_MS,
-                    DEFAULT_QUOTA_CONTROLLER_WINDOW_SIZE_RARE_MS);
-            QUOTA_CONTROLLER_MAX_EXECUTION_TIME_MS = mParser.getDurationMillis(
-                    KEY_QUOTA_CONTROLLER_MAX_EXECUTION_TIME_MS,
-                    DEFAULT_QUOTA_CONTROLLER_MAX_EXECUTION_TIME_MS);
-            QUOTA_CONTROLLER_MAX_JOB_COUNT_ACTIVE = mParser.getInt(
-                    KEY_QUOTA_CONTROLLER_MAX_JOB_COUNT_ACTIVE,
-                    DEFAULT_QUOTA_CONTROLLER_MAX_JOB_COUNT_ACTIVE);
-            QUOTA_CONTROLLER_MAX_JOB_COUNT_WORKING = mParser.getInt(
-                    KEY_QUOTA_CONTROLLER_MAX_JOB_COUNT_WORKING,
-                    DEFAULT_QUOTA_CONTROLLER_MAX_JOB_COUNT_WORKING);
-            QUOTA_CONTROLLER_MAX_JOB_COUNT_FREQUENT = mParser.getInt(
-                    KEY_QUOTA_CONTROLLER_MAX_JOB_COUNT_FREQUENT,
-                    DEFAULT_QUOTA_CONTROLLER_MAX_JOB_COUNT_FREQUENT);
-            QUOTA_CONTROLLER_MAX_JOB_COUNT_RARE = mParser.getInt(
-                    KEY_QUOTA_CONTROLLER_MAX_JOB_COUNT_RARE,
-                    DEFAULT_QUOTA_CONTROLLER_MAX_JOB_COUNT_RARE);
-            QUOTA_CONTROLLER_MAX_JOB_COUNT_PER_ALLOWED_TIME = mParser.getInt(
-                    KEY_QUOTA_CONTROLLER_MAX_JOB_COUNT_PER_ALLOWED_TIME,
-                    DEFAULT_QUOTA_CONTROLLER_MAX_JOB_COUNT_PER_ALLOWED_TIME);
         }
 
         void dump(IndentingPrintWriter pw) {
@@ -915,37 +736,11 @@
             pw.printPair(KEY_CONN_CONGESTION_DELAY_FRAC, CONN_CONGESTION_DELAY_FRAC).println();
             pw.printPair(KEY_CONN_PREFETCH_RELAX_FRAC, CONN_PREFETCH_RELAX_FRAC).println();
             pw.printPair(KEY_USE_HEARTBEATS, USE_HEARTBEATS).println();
-            pw.printPair(KEY_TIME_CONTROLLER_SKIP_NOT_READY_JOBS,
-                    TIME_CONTROLLER_SKIP_NOT_READY_JOBS).println();
-            pw.printPair(KEY_QUOTA_CONTROLLER_ALLOWED_TIME_PER_PERIOD_MS,
-                    QUOTA_CONTROLLER_ALLOWED_TIME_PER_PERIOD_MS).println();
-            pw.printPair(KEY_QUOTA_CONTROLLER_IN_QUOTA_BUFFER_MS,
-                    QUOTA_CONTROLLER_IN_QUOTA_BUFFER_MS).println();
-            pw.printPair(KEY_QUOTA_CONTROLLER_WINDOW_SIZE_ACTIVE_MS,
-                    QUOTA_CONTROLLER_WINDOW_SIZE_ACTIVE_MS).println();
-            pw.printPair(KEY_QUOTA_CONTROLLER_WINDOW_SIZE_WORKING_MS,
-                    QUOTA_CONTROLLER_WINDOW_SIZE_WORKING_MS).println();
-            pw.printPair(KEY_QUOTA_CONTROLLER_WINDOW_SIZE_FREQUENT_MS,
-                    QUOTA_CONTROLLER_WINDOW_SIZE_FREQUENT_MS).println();
-            pw.printPair(KEY_QUOTA_CONTROLLER_WINDOW_SIZE_RARE_MS,
-                    QUOTA_CONTROLLER_WINDOW_SIZE_RARE_MS).println();
-            pw.printPair(KEY_QUOTA_CONTROLLER_MAX_EXECUTION_TIME_MS,
-                    QUOTA_CONTROLLER_MAX_EXECUTION_TIME_MS).println();
-            pw.printPair(KEY_QUOTA_CONTROLLER_MAX_JOB_COUNT_ACTIVE,
-                    QUOTA_CONTROLLER_MAX_JOB_COUNT_ACTIVE).println();
-            pw.printPair(KEY_QUOTA_CONTROLLER_MAX_JOB_COUNT_WORKING,
-                    QUOTA_CONTROLLER_MAX_JOB_COUNT_WORKING).println();
-            pw.printPair(KEY_QUOTA_CONTROLLER_MAX_JOB_COUNT_FREQUENT,
-                    QUOTA_CONTROLLER_MAX_JOB_COUNT_FREQUENT).println();
-            pw.printPair(KEY_QUOTA_CONTROLLER_MAX_JOB_COUNT_RARE,
-                    QUOTA_CONTROLLER_MAX_JOB_COUNT_RARE).println();
-            pw.printPair(KEY_QUOTA_CONTROLLER_MAX_JOB_COUNT_PER_ALLOWED_TIME,
-                    QUOTA_CONTROLLER_MAX_JOB_COUNT_PER_ALLOWED_TIME).println();
+
             pw.decreaseIndent();
         }
 
-        void dump(ProtoOutputStream proto, long fieldId) {
-            final long token = proto.start(fieldId);
+        void dump(ProtoOutputStream proto) {
             proto.write(ConstantsProto.MIN_IDLE_COUNT, MIN_IDLE_COUNT);
             proto.write(ConstantsProto.MIN_CHARGING_COUNT, MIN_CHARGING_COUNT);
             proto.write(ConstantsProto.MIN_BATTERY_NOT_LOW_COUNT, MIN_BATTERY_NOT_LOW_COUNT);
@@ -973,40 +768,6 @@
             proto.write(ConstantsProto.CONN_CONGESTION_DELAY_FRAC, CONN_CONGESTION_DELAY_FRAC);
             proto.write(ConstantsProto.CONN_PREFETCH_RELAX_FRAC, CONN_PREFETCH_RELAX_FRAC);
             proto.write(ConstantsProto.USE_HEARTBEATS, USE_HEARTBEATS);
-
-            final long tcToken = proto.start(ConstantsProto.TIME_CONTROLLER);
-            proto.write(ConstantsProto.TimeController.SKIP_NOT_READY_JOBS,
-                    TIME_CONTROLLER_SKIP_NOT_READY_JOBS);
-            proto.end(tcToken);
-
-            final long qcToken = proto.start(ConstantsProto.QUOTA_CONTROLLER);
-            proto.write(ConstantsProto.QuotaController.ALLOWED_TIME_PER_PERIOD_MS,
-                    QUOTA_CONTROLLER_ALLOWED_TIME_PER_PERIOD_MS);
-            proto.write(ConstantsProto.QuotaController.IN_QUOTA_BUFFER_MS,
-                    QUOTA_CONTROLLER_IN_QUOTA_BUFFER_MS);
-            proto.write(ConstantsProto.QuotaController.ACTIVE_WINDOW_SIZE_MS,
-                    QUOTA_CONTROLLER_WINDOW_SIZE_ACTIVE_MS);
-            proto.write(ConstantsProto.QuotaController.WORKING_WINDOW_SIZE_MS,
-                    QUOTA_CONTROLLER_WINDOW_SIZE_WORKING_MS);
-            proto.write(ConstantsProto.QuotaController.FREQUENT_WINDOW_SIZE_MS,
-                    QUOTA_CONTROLLER_WINDOW_SIZE_FREQUENT_MS);
-            proto.write(ConstantsProto.QuotaController.RARE_WINDOW_SIZE_MS,
-                    QUOTA_CONTROLLER_WINDOW_SIZE_RARE_MS);
-            proto.write(ConstantsProto.QuotaController.MAX_EXECUTION_TIME_MS,
-                    QUOTA_CONTROLLER_MAX_EXECUTION_TIME_MS);
-            proto.write(ConstantsProto.QuotaController.MAX_JOB_COUNT_ACTIVE,
-                    QUOTA_CONTROLLER_MAX_JOB_COUNT_ACTIVE);
-            proto.write(ConstantsProto.QuotaController.MAX_JOB_COUNT_WORKING,
-                    QUOTA_CONTROLLER_MAX_JOB_COUNT_WORKING);
-            proto.write(ConstantsProto.QuotaController.MAX_JOB_COUNT_FREQUENT,
-                    QUOTA_CONTROLLER_MAX_JOB_COUNT_FREQUENT);
-            proto.write(ConstantsProto.QuotaController.MAX_JOB_COUNT_RARE,
-                    QUOTA_CONTROLLER_MAX_JOB_COUNT_RARE);
-            proto.write(ConstantsProto.QuotaController.MAX_JOB_COUNT_PER_ALLOWED_TIME,
-                    QUOTA_CONTROLLER_MAX_JOB_COUNT_PER_ALLOWED_TIME);
-            proto.end(qcToken);
-
-            proto.end(token);
         }
     }
 
@@ -1636,6 +1397,9 @@
     public void onBootPhase(int phase) {
         if (PHASE_SYSTEM_SERVICES_READY == phase) {
             mConstantsObserver.start(getContext().getContentResolver());
+            for (StateController controller : mControllers) {
+                controller.onSystemServicesReady();
+            }
 
             mAppStateTracker = Preconditions.checkNotNull(
                     LocalServices.getService(AppStateTracker.class));
@@ -1801,7 +1565,8 @@
      *
      * @see #maybeQueueReadyJobsForExecutionLocked
      */
-    private JobStatus getRescheduleJobForFailureLocked(JobStatus failureToReschedule) {
+    @VisibleForTesting
+    JobStatus getRescheduleJobForFailureLocked(JobStatus failureToReschedule) {
         final long elapsedNowMillis = sElapsedRealtimeClock.millis();
         final JobInfo job = failureToReschedule.getJob();
 
@@ -1848,6 +1613,10 @@
                 elapsedNowMillis + delayMillis,
                 JobStatus.NO_LATEST_RUNTIME, backoffAttempts,
                 failureToReschedule.getLastSuccessfulRunTime(), sSystemClock.millis());
+        if (job.isPeriodic()) {
+            newJob.setOriginalLatestRunTimeElapsed(
+                    failureToReschedule.getOriginalLatestRunTimeElapsed());
+        }
         for (int ic=0; ic<mControllers.size(); ic++) {
             StateController controller = mControllers.get(ic);
             controller.rescheduleForFailureLocked(newJob, failureToReschedule);
@@ -1868,23 +1637,41 @@
      * @return A new job representing the execution criteria for this instantiation of the
      * recurring job.
      */
-    private JobStatus getRescheduleJobForPeriodic(JobStatus periodicToReschedule) {
+    @VisibleForTesting
+    JobStatus getRescheduleJobForPeriodic(JobStatus periodicToReschedule) {
         final long elapsedNow = sElapsedRealtimeClock.millis();
-        // Compute how much of the period is remaining.
-        long runEarly = 0L;
+        final long newLatestRuntimeElapsed;
+        final long period = periodicToReschedule.getJob().getIntervalMillis();
+        final long latestRunTimeElapsed = periodicToReschedule.getOriginalLatestRunTimeElapsed();
+        final long flex = periodicToReschedule.getJob().getFlexMillis();
 
-        // If this periodic was rescheduled it won't have a deadline.
-        if (periodicToReschedule.hasDeadlineConstraint()) {
-            runEarly = Math.max(periodicToReschedule.getLatestRunTimeElapsed() - elapsedNow, 0L);
+        if (elapsedNow > latestRunTimeElapsed) {
+            // The job ran past its expected run window. Have it count towards the current window
+            // and schedule a new job for the next window.
+            if (DEBUG) {
+                Slog.i(TAG, "Periodic job ran after its intended window.");
+            }
+            final long diffMs = (elapsedNow - latestRunTimeElapsed);
+            int numSkippedWindows = (int) (diffMs / period) + 1; // +1 to include original window
+            if (period != flex && diffMs > Math.min(30 * MINUTE_IN_MILLIS, (period - flex) / 2)) {
+                if (DEBUG) {
+                    Slog.d(TAG, "Custom flex job ran too close to next window.");
+                }
+                // For custom flex periods, if the job was run too close to the next window,
+                // skip the next window and schedule for the following one.
+                numSkippedWindows += 1;
+            }
+            newLatestRuntimeElapsed = latestRunTimeElapsed + (period * numSkippedWindows);
+        } else {
+            newLatestRuntimeElapsed = latestRunTimeElapsed + period;
         }
-        long flex = periodicToReschedule.getJob().getFlexMillis();
-        long period = periodicToReschedule.getJob().getIntervalMillis();
-        long newLatestRuntimeElapsed = elapsedNow + runEarly + period;
-        long newEarliestRunTimeElapsed = newLatestRuntimeElapsed - flex;
+
+        final long newEarliestRunTimeElapsed = newLatestRuntimeElapsed - flex;
 
         if (DEBUG) {
             Slog.v(TAG, "Rescheduling executed periodic. New execution window [" +
-                    newEarliestRunTimeElapsed/1000 + ", " + newLatestRuntimeElapsed/1000 + "]s");
+                    newEarliestRunTimeElapsed / 1000 + ", " + newLatestRuntimeElapsed / 1000
+                    + "]s");
         }
         return new JobStatus(periodicToReschedule, getCurrentHeartbeat(),
                 newEarliestRunTimeElapsed, newLatestRuntimeElapsed,
@@ -3424,6 +3211,11 @@
         };
         synchronized (mLock) {
             mConstants.dump(pw);
+            for (StateController controller : mControllers) {
+                pw.increaseIndent();
+                controller.dumpConstants(pw);
+                pw.decreaseIndent();
+            }
             pw.println();
 
             pw.println("  Heartbeat:");
@@ -3614,7 +3406,13 @@
         };
 
         synchronized (mLock) {
-            mConstants.dump(proto, JobSchedulerServiceDumpProto.SETTINGS);
+            final long settingsToken = proto.start(JobSchedulerServiceDumpProto.SETTINGS);
+            mConstants.dump(proto);
+            for (StateController controller : mControllers) {
+                controller.dumpConstants(proto);
+            }
+            proto.end(settingsToken);
+
             proto.write(JobSchedulerServiceDumpProto.CURRENT_HEARTBEAT, mHeartbeat);
             proto.write(JobSchedulerServiceDumpProto.NEXT_HEARTBEAT, mNextBucketHeartbeat[0]);
             proto.write(JobSchedulerServiceDumpProto.NEXT_HEARTBEAT, mNextBucketHeartbeat[1]);
diff --git a/services/core/java/com/android/server/job/controllers/JobStatus.java b/services/core/java/com/android/server/job/controllers/JobStatus.java
index 48f21e4..fd20e11 100644
--- a/services/core/java/com/android/server/job/controllers/JobStatus.java
+++ b/services/core/java/com/android/server/job/controllers/JobStatus.java
@@ -161,6 +161,12 @@
      */
     private final long latestRunTimeElapsedMillis;
 
+    /**
+     * Valid only for periodic jobs. The original latest point in the future at which this
+     * job was expected to run.
+     */
+    private long mOriginalLatestRunTimeElapsedMillis;
+
     /** How many times this job has failed, used to compute back-off. */
     private final int numFailures;
 
@@ -394,6 +400,7 @@
 
         this.earliestRunTimeElapsedMillis = earliestRunTimeElapsedMillis;
         this.latestRunTimeElapsedMillis = latestRunTimeElapsedMillis;
+        this.mOriginalLatestRunTimeElapsedMillis = latestRunTimeElapsedMillis;
         this.numFailures = numFailures;
 
         int requiredConstraints = job.getConstraintFlags();
@@ -871,6 +878,14 @@
         return latestRunTimeElapsedMillis;
     }
 
+    public long getOriginalLatestRunTimeElapsed() {
+        return mOriginalLatestRunTimeElapsedMillis;
+    }
+
+    public void setOriginalLatestRunTimeElapsed(long latestRunTimeElapsed) {
+        mOriginalLatestRunTimeElapsedMillis = latestRunTimeElapsed;
+    }
+
     /**
      * Return the fractional position of "now" within the "run time" window of
      * this job.
diff --git a/services/core/java/com/android/server/job/controllers/QuotaController.java b/services/core/java/com/android/server/job/controllers/QuotaController.java
index 11f0939..2a9d3f3 100644
--- a/services/core/java/com/android/server/job/controllers/QuotaController.java
+++ b/services/core/java/com/android/server/job/controllers/QuotaController.java
@@ -34,9 +34,12 @@
 import android.app.usage.UsageStatsManagerInternal;
 import android.app.usage.UsageStatsManagerInternal.AppIdleStateChangeListener;
 import android.content.BroadcastReceiver;
+import android.content.ContentResolver;
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
+import android.database.ContentObserver;
+import android.net.Uri;
 import android.os.BatteryManager;
 import android.os.BatteryManagerInternal;
 import android.os.Handler;
@@ -44,8 +47,10 @@
 import android.os.Message;
 import android.os.RemoteException;
 import android.os.UserHandle;
+import android.provider.Settings;
 import android.util.ArrayMap;
 import android.util.ArraySet;
+import android.util.KeyValueListParser;
 import android.util.Log;
 import android.util.Slog;
 import android.util.SparseArray;
@@ -57,6 +62,7 @@
 import com.android.internal.os.BackgroundThread;
 import com.android.internal.util.IndentingPrintWriter;
 import com.android.server.LocalServices;
+import com.android.server.job.ConstantsProto;
 import com.android.server.job.JobSchedulerService;
 import com.android.server.job.StateControllerProto;
 
@@ -354,6 +360,7 @@
     private final AlarmManager mAlarmManager;
     private final ChargingTracker mChargeTracker;
     private final Handler mHandler;
+    private final QcConstants mQcConstants;
 
     private volatile boolean mInParole;
 
@@ -489,6 +496,7 @@
         mChargeTracker.startTracking();
         mActivityManagerInternal = LocalServices.getService(ActivityManagerInternal.class);
         mAlarmManager = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE);
+        mQcConstants = new QcConstants(mHandler);
 
         final IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_ADDED);
         mContext.registerReceiverAsUser(mPackageAddedReceiver, UserHandle.ALL, filter, null, null);
@@ -506,7 +514,12 @@
             // ignored; both services live in system_server
         }
 
-        onConstantsUpdatedLocked();
+        mShouldThrottle = !mConstants.USE_HEARTBEATS;
+    }
+
+    @Override
+    public void onSystemServicesReady() {
+        mQcConstants.start(mContext.getContentResolver());
     }
 
     @Override
@@ -581,89 +594,9 @@
 
     @Override
     public void onConstantsUpdatedLocked() {
-        boolean changed = false;
         if (mShouldThrottle == mConstants.USE_HEARTBEATS) {
             mShouldThrottle = !mConstants.USE_HEARTBEATS;
-            changed = true;
-        }
-        long newAllowedTimeMs = Math.min(MAX_PERIOD_MS,
-                Math.max(MINUTE_IN_MILLIS, mConstants.QUOTA_CONTROLLER_ALLOWED_TIME_PER_PERIOD_MS));
-        if (mAllowedTimePerPeriodMs != newAllowedTimeMs) {
-            mAllowedTimePerPeriodMs = newAllowedTimeMs;
-            mAllowedTimeIntoQuotaMs = mAllowedTimePerPeriodMs - mQuotaBufferMs;
-            changed = true;
-        }
-        long newQuotaBufferMs = Math.max(0,
-                Math.min(5 * MINUTE_IN_MILLIS, mConstants.QUOTA_CONTROLLER_IN_QUOTA_BUFFER_MS));
-        if (mQuotaBufferMs != newQuotaBufferMs) {
-            mQuotaBufferMs = newQuotaBufferMs;
-            mAllowedTimeIntoQuotaMs = mAllowedTimePerPeriodMs - mQuotaBufferMs;
-            mMaxExecutionTimeIntoQuotaMs = mMaxExecutionTimeMs - mQuotaBufferMs;
-            changed = true;
-        }
-        long newActivePeriodMs = Math.max(mAllowedTimePerPeriodMs,
-                Math.min(MAX_PERIOD_MS, mConstants.QUOTA_CONTROLLER_WINDOW_SIZE_ACTIVE_MS));
-        if (mBucketPeriodsMs[ACTIVE_INDEX] != newActivePeriodMs) {
-            mBucketPeriodsMs[ACTIVE_INDEX] = newActivePeriodMs;
-            changed = true;
-        }
-        long newWorkingPeriodMs = Math.max(mAllowedTimePerPeriodMs,
-                Math.min(MAX_PERIOD_MS, mConstants.QUOTA_CONTROLLER_WINDOW_SIZE_WORKING_MS));
-        if (mBucketPeriodsMs[WORKING_INDEX] != newWorkingPeriodMs) {
-            mBucketPeriodsMs[WORKING_INDEX] = newWorkingPeriodMs;
-            changed = true;
-        }
-        long newFrequentPeriodMs = Math.max(mAllowedTimePerPeriodMs,
-                Math.min(MAX_PERIOD_MS, mConstants.QUOTA_CONTROLLER_WINDOW_SIZE_FREQUENT_MS));
-        if (mBucketPeriodsMs[FREQUENT_INDEX] != newFrequentPeriodMs) {
-            mBucketPeriodsMs[FREQUENT_INDEX] = newFrequentPeriodMs;
-            changed = true;
-        }
-        long newRarePeriodMs = Math.max(mAllowedTimePerPeriodMs,
-                Math.min(MAX_PERIOD_MS, mConstants.QUOTA_CONTROLLER_WINDOW_SIZE_RARE_MS));
-        if (mBucketPeriodsMs[RARE_INDEX] != newRarePeriodMs) {
-            mBucketPeriodsMs[RARE_INDEX] = newRarePeriodMs;
-            changed = true;
-        }
-        long newMaxExecutionTimeMs = Math.max(60 * MINUTE_IN_MILLIS,
-                Math.min(MAX_PERIOD_MS, mConstants.QUOTA_CONTROLLER_MAX_EXECUTION_TIME_MS));
-        if (mMaxExecutionTimeMs != newMaxExecutionTimeMs) {
-            mMaxExecutionTimeMs = newMaxExecutionTimeMs;
-            mMaxExecutionTimeIntoQuotaMs = mMaxExecutionTimeMs - mQuotaBufferMs;
-            changed = true;
-        }
-        int newMaxCountPerAllowedPeriod = Math.max(10,
-                mConstants.QUOTA_CONTROLLER_MAX_JOB_COUNT_PER_ALLOWED_TIME);
-        if (mMaxJobCountPerAllowedTime != newMaxCountPerAllowedPeriod) {
-            mMaxJobCountPerAllowedTime = newMaxCountPerAllowedPeriod;
-            changed = true;
-        }
-        int newActiveMaxJobCount = Math.max(mMaxJobCountPerAllowedTime,
-                Math.max(MIN_BUCKET_JOB_COUNT, mConstants.QUOTA_CONTROLLER_MAX_JOB_COUNT_ACTIVE));
-        if (mMaxBucketJobCounts[ACTIVE_INDEX] != newActiveMaxJobCount) {
-            mMaxBucketJobCounts[ACTIVE_INDEX] = newActiveMaxJobCount;
-            changed = true;
-        }
-        int newWorkingMaxJobCount = Math.max(mMaxJobCountPerAllowedTime,
-                Math.max(MIN_BUCKET_JOB_COUNT, mConstants.QUOTA_CONTROLLER_MAX_JOB_COUNT_WORKING));
-        if (mMaxBucketJobCounts[WORKING_INDEX] != newWorkingMaxJobCount) {
-            mMaxBucketJobCounts[WORKING_INDEX] = newWorkingMaxJobCount;
-            changed = true;
-        }
-        int newFrequentMaxJobCount = Math.max(mMaxJobCountPerAllowedTime,
-                Math.max(MIN_BUCKET_JOB_COUNT, mConstants.QUOTA_CONTROLLER_MAX_JOB_COUNT_FREQUENT));
-        if (mMaxBucketJobCounts[FREQUENT_INDEX] != newFrequentMaxJobCount) {
-            mMaxBucketJobCounts[FREQUENT_INDEX] = newFrequentMaxJobCount;
-            changed = true;
-        }
-        int newRareMaxJobCount = Math.max(mMaxJobCountPerAllowedTime,
-                Math.max(MIN_BUCKET_JOB_COUNT, mConstants.QUOTA_CONTROLLER_MAX_JOB_COUNT_RARE));
-        if (mMaxBucketJobCounts[RARE_INDEX] != newRareMaxJobCount) {
-            mMaxBucketJobCounts[RARE_INDEX] = newRareMaxJobCount;
-            changed = true;
-        }
 
-        if (changed) {
             // Update job bookkeeping out of band.
             BackgroundThread.getHandler().post(() -> {
                 synchronized (mLock) {
@@ -1891,6 +1824,311 @@
         }
     }
 
+    @VisibleForTesting
+    class QcConstants extends ContentObserver {
+        private ContentResolver mResolver;
+        private final KeyValueListParser mParser = new KeyValueListParser(',');
+
+        private static final String KEY_ALLOWED_TIME_PER_PERIOD_MS = "allowed_time_per_period_ms";
+        private static final String KEY_IN_QUOTA_BUFFER_MS = "in_quota_buffer_ms";
+        private static final String KEY_WINDOW_SIZE_ACTIVE_MS = "window_size_active_ms";
+        private static final String KEY_WINDOW_SIZE_WORKING_MS = "window_size_working_ms";
+        private static final String KEY_WINDOW_SIZE_FREQUENT_MS = "window_size_frequent_ms";
+        private static final String KEY_WINDOW_SIZE_RARE_MS = "window_size_rare_ms";
+        private static final String KEY_MAX_EXECUTION_TIME_MS = "max_execution_time_ms";
+        private static final String KEY_MAX_JOB_COUNT_ACTIVE = "max_job_count_active";
+        private static final String KEY_MAX_JOB_COUNT_WORKING = "max_job_count_working";
+        private static final String KEY_MAX_JOB_COUNT_FREQUENT = "max_job_count_frequent";
+        private static final String KEY_MAX_JOB_COUNT_RARE = "max_job_count_rare";
+        private static final String KEY_MAX_JOB_COUNT_PER_ALLOWED_TIME =
+                "max_count_per_allowed_time";
+
+        private static final long DEFAULT_ALLOWED_TIME_PER_PERIOD_MS =
+                10 * 60 * 1000L; // 10 minutes
+        private static final long DEFAULT_IN_QUOTA_BUFFER_MS =
+                30 * 1000L; // 30 seconds
+        private static final long DEFAULT_WINDOW_SIZE_ACTIVE_MS =
+                10 * 60 * 1000L; // 10 minutes for ACTIVE -- ACTIVE apps can run jobs at any time
+        private static final long DEFAULT_WINDOW_SIZE_WORKING_MS =
+                2 * 60 * 60 * 1000L; // 2 hours
+        private static final long DEFAULT_WINDOW_SIZE_FREQUENT_MS =
+                8 * 60 * 60 * 1000L; // 8 hours
+        private static final long DEFAULT_WINDOW_SIZE_RARE_MS =
+                24 * 60 * 60 * 1000L; // 24 hours
+        private static final long DEFAULT_MAX_EXECUTION_TIME_MS =
+                4 * 60 * 60 * 1000L; // 4 hours
+        private static final int DEFAULT_MAX_JOB_COUNT_ACTIVE =
+                200; // 1200/hr
+        private static final int DEFAULT_MAX_JOB_COUNT_WORKING =
+                1200; // 600/hr
+        private static final int DEFAULT_MAX_JOB_COUNT_FREQUENT =
+                1800; // 225/hr
+        private static final int DEFAULT_MAX_JOB_COUNT_RARE =
+                2400; // 100/hr
+        private static final int DEFAULT_MAX_JOB_COUNT_PER_ALLOWED_TIME = 20;
+
+        /** How much time each app will have to run jobs within their standby bucket window. */
+        public long ALLOWED_TIME_PER_PERIOD_MS = DEFAULT_ALLOWED_TIME_PER_PERIOD_MS;
+
+        /**
+         * How much time the package should have before transitioning from out-of-quota to in-quota.
+         * This should not affect processing if the package is already in-quota.
+         */
+        public long IN_QUOTA_BUFFER_MS = DEFAULT_IN_QUOTA_BUFFER_MS;
+
+        /**
+         * The quota window size of the particular standby bucket. Apps in this standby bucket are
+         * expected to run only {@link #ALLOWED_TIME_PER_PERIOD_MS} within the past
+         * WINDOW_SIZE_MS.
+         */
+        public long WINDOW_SIZE_ACTIVE_MS = DEFAULT_WINDOW_SIZE_ACTIVE_MS;
+
+        /**
+         * The quota window size of the particular standby bucket. Apps in this standby bucket are
+         * expected to run only {@link #ALLOWED_TIME_PER_PERIOD_MS} within the past
+         * WINDOW_SIZE_MS.
+         */
+        public long WINDOW_SIZE_WORKING_MS = DEFAULT_WINDOW_SIZE_WORKING_MS;
+
+        /**
+         * The quota window size of the particular standby bucket. Apps in this standby bucket are
+         * expected to run only {@link #ALLOWED_TIME_PER_PERIOD_MS} within the past
+         * WINDOW_SIZE_MS.
+         */
+        public long WINDOW_SIZE_FREQUENT_MS = DEFAULT_WINDOW_SIZE_FREQUENT_MS;
+
+        /**
+         * The quota window size of the particular standby bucket. Apps in this standby bucket are
+         * expected to run only {@link #ALLOWED_TIME_PER_PERIOD_MS} within the past
+         * WINDOW_SIZE_MS.
+         */
+        public long WINDOW_SIZE_RARE_MS = DEFAULT_WINDOW_SIZE_RARE_MS;
+
+        /**
+         * The maximum amount of time an app can have its jobs running within a 24 hour window.
+         */
+        public long MAX_EXECUTION_TIME_MS = DEFAULT_MAX_EXECUTION_TIME_MS;
+
+        /**
+         * The maximum number of jobs an app can run within this particular standby bucket's
+         * window size.
+         */
+        public int MAX_JOB_COUNT_ACTIVE = DEFAULT_MAX_JOB_COUNT_ACTIVE;
+
+        /**
+         * The maximum number of jobs an app can run within this particular standby bucket's
+         * window size.
+         */
+        public int MAX_JOB_COUNT_WORKING = DEFAULT_MAX_JOB_COUNT_WORKING;
+
+        /**
+         * The maximum number of jobs an app can run within this particular standby bucket's
+         * window size.
+         */
+        public int MAX_JOB_COUNT_FREQUENT = DEFAULT_MAX_JOB_COUNT_FREQUENT;
+
+        /**
+         * The maximum number of jobs an app can run within this particular standby bucket's
+         * window size.
+         */
+        public int MAX_JOB_COUNT_RARE = DEFAULT_MAX_JOB_COUNT_RARE;
+
+        /**
+         * The maximum number of jobs that can run within the past
+         * {@link #ALLOWED_TIME_PER_PERIOD_MS}.
+         */
+        public int MAX_JOB_COUNT_PER_ALLOWED_TIME = DEFAULT_MAX_JOB_COUNT_PER_ALLOWED_TIME;
+
+        QcConstants(Handler handler) {
+            super(handler);
+        }
+
+        private void start(ContentResolver resolver) {
+            mResolver = resolver;
+            mResolver.registerContentObserver(Settings.Global.getUriFor(
+                    Settings.Global.JOB_SCHEDULER_QUOTA_CONTROLLER_CONSTANTS), false, this);
+            updateConstants();
+        }
+
+        @Override
+        public void onChange(boolean selfChange, Uri uri) {
+            final String constants = Settings.Global.getString(
+                    mResolver, Settings.Global.JOB_SCHEDULER_QUOTA_CONTROLLER_CONSTANTS);
+
+            try {
+                mParser.setString(constants);
+            } catch (Exception e) {
+                // Failed to parse the settings string, log this and move on with defaults.
+                Slog.e(TAG, "Bad jobscheduler quota controller settings", e);
+            }
+
+            ALLOWED_TIME_PER_PERIOD_MS = mParser.getDurationMillis(
+                    KEY_ALLOWED_TIME_PER_PERIOD_MS, DEFAULT_ALLOWED_TIME_PER_PERIOD_MS);
+            IN_QUOTA_BUFFER_MS = mParser.getDurationMillis(
+                    KEY_IN_QUOTA_BUFFER_MS, DEFAULT_IN_QUOTA_BUFFER_MS);
+            WINDOW_SIZE_ACTIVE_MS = mParser.getDurationMillis(
+                    KEY_WINDOW_SIZE_ACTIVE_MS, DEFAULT_WINDOW_SIZE_ACTIVE_MS);
+            WINDOW_SIZE_WORKING_MS = mParser.getDurationMillis(
+                    KEY_WINDOW_SIZE_WORKING_MS, DEFAULT_WINDOW_SIZE_WORKING_MS);
+            WINDOW_SIZE_FREQUENT_MS = mParser.getDurationMillis(
+                    KEY_WINDOW_SIZE_FREQUENT_MS, DEFAULT_WINDOW_SIZE_FREQUENT_MS);
+            WINDOW_SIZE_RARE_MS = mParser.getDurationMillis(
+                    KEY_WINDOW_SIZE_RARE_MS, DEFAULT_WINDOW_SIZE_RARE_MS);
+            MAX_EXECUTION_TIME_MS = mParser.getDurationMillis(
+                    KEY_MAX_EXECUTION_TIME_MS, DEFAULT_MAX_EXECUTION_TIME_MS);
+            MAX_JOB_COUNT_ACTIVE = mParser.getInt(
+                    KEY_MAX_JOB_COUNT_ACTIVE, DEFAULT_MAX_JOB_COUNT_ACTIVE);
+            MAX_JOB_COUNT_WORKING = mParser.getInt(
+                    KEY_MAX_JOB_COUNT_WORKING, DEFAULT_MAX_JOB_COUNT_WORKING);
+            MAX_JOB_COUNT_FREQUENT = mParser.getInt(
+                    KEY_MAX_JOB_COUNT_FREQUENT, DEFAULT_MAX_JOB_COUNT_FREQUENT);
+            MAX_JOB_COUNT_RARE = mParser.getInt(
+                    KEY_MAX_JOB_COUNT_RARE, DEFAULT_MAX_JOB_COUNT_RARE);
+            MAX_JOB_COUNT_PER_ALLOWED_TIME = mParser.getInt(
+                    KEY_MAX_JOB_COUNT_PER_ALLOWED_TIME, DEFAULT_MAX_JOB_COUNT_PER_ALLOWED_TIME);
+
+            updateConstants();
+        }
+
+        @VisibleForTesting
+        void updateConstants() {
+            synchronized (mLock) {
+                boolean changed = false;
+
+                long newAllowedTimeMs = Math.min(MAX_PERIOD_MS,
+                        Math.max(MINUTE_IN_MILLIS, ALLOWED_TIME_PER_PERIOD_MS));
+                if (mAllowedTimePerPeriodMs != newAllowedTimeMs) {
+                    mAllowedTimePerPeriodMs = newAllowedTimeMs;
+                    mAllowedTimeIntoQuotaMs = mAllowedTimePerPeriodMs - mQuotaBufferMs;
+                    changed = true;
+                }
+                long newQuotaBufferMs = Math.max(0,
+                        Math.min(5 * MINUTE_IN_MILLIS, IN_QUOTA_BUFFER_MS));
+                if (mQuotaBufferMs != newQuotaBufferMs) {
+                    mQuotaBufferMs = newQuotaBufferMs;
+                    mAllowedTimeIntoQuotaMs = mAllowedTimePerPeriodMs - mQuotaBufferMs;
+                    mMaxExecutionTimeIntoQuotaMs = mMaxExecutionTimeMs - mQuotaBufferMs;
+                    changed = true;
+                }
+                long newActivePeriodMs = Math.max(mAllowedTimePerPeriodMs,
+                        Math.min(MAX_PERIOD_MS, WINDOW_SIZE_ACTIVE_MS));
+                if (mBucketPeriodsMs[ACTIVE_INDEX] != newActivePeriodMs) {
+                    mBucketPeriodsMs[ACTIVE_INDEX] = newActivePeriodMs;
+                    changed = true;
+                }
+                long newWorkingPeriodMs = Math.max(mAllowedTimePerPeriodMs,
+                        Math.min(MAX_PERIOD_MS, WINDOW_SIZE_WORKING_MS));
+                if (mBucketPeriodsMs[WORKING_INDEX] != newWorkingPeriodMs) {
+                    mBucketPeriodsMs[WORKING_INDEX] = newWorkingPeriodMs;
+                    changed = true;
+                }
+                long newFrequentPeriodMs = Math.max(mAllowedTimePerPeriodMs,
+                        Math.min(MAX_PERIOD_MS, WINDOW_SIZE_FREQUENT_MS));
+                if (mBucketPeriodsMs[FREQUENT_INDEX] != newFrequentPeriodMs) {
+                    mBucketPeriodsMs[FREQUENT_INDEX] = newFrequentPeriodMs;
+                    changed = true;
+                }
+                long newRarePeriodMs = Math.max(mAllowedTimePerPeriodMs,
+                        Math.min(MAX_PERIOD_MS, WINDOW_SIZE_RARE_MS));
+                if (mBucketPeriodsMs[RARE_INDEX] != newRarePeriodMs) {
+                    mBucketPeriodsMs[RARE_INDEX] = newRarePeriodMs;
+                    changed = true;
+                }
+                long newMaxExecutionTimeMs = Math.max(60 * MINUTE_IN_MILLIS,
+                        Math.min(MAX_PERIOD_MS, MAX_EXECUTION_TIME_MS));
+                if (mMaxExecutionTimeMs != newMaxExecutionTimeMs) {
+                    mMaxExecutionTimeMs = newMaxExecutionTimeMs;
+                    mMaxExecutionTimeIntoQuotaMs = mMaxExecutionTimeMs - mQuotaBufferMs;
+                    changed = true;
+                }
+                int newMaxCountPerAllowedPeriod = Math.max(10,
+                        MAX_JOB_COUNT_PER_ALLOWED_TIME);
+                if (mMaxJobCountPerAllowedTime != newMaxCountPerAllowedPeriod) {
+                    mMaxJobCountPerAllowedTime = newMaxCountPerAllowedPeriod;
+                    changed = true;
+                }
+                int newActiveMaxJobCount = Math.max(mMaxJobCountPerAllowedTime,
+                        Math.max(MIN_BUCKET_JOB_COUNT, MAX_JOB_COUNT_ACTIVE));
+                if (mMaxBucketJobCounts[ACTIVE_INDEX] != newActiveMaxJobCount) {
+                    mMaxBucketJobCounts[ACTIVE_INDEX] = newActiveMaxJobCount;
+                    changed = true;
+                }
+                int newWorkingMaxJobCount = Math.max(mMaxJobCountPerAllowedTime,
+                        Math.max(MIN_BUCKET_JOB_COUNT, MAX_JOB_COUNT_WORKING));
+                if (mMaxBucketJobCounts[WORKING_INDEX] != newWorkingMaxJobCount) {
+                    mMaxBucketJobCounts[WORKING_INDEX] = newWorkingMaxJobCount;
+                    changed = true;
+                }
+                int newFrequentMaxJobCount = Math.max(mMaxJobCountPerAllowedTime,
+                        Math.max(MIN_BUCKET_JOB_COUNT, MAX_JOB_COUNT_FREQUENT));
+                if (mMaxBucketJobCounts[FREQUENT_INDEX] != newFrequentMaxJobCount) {
+                    mMaxBucketJobCounts[FREQUENT_INDEX] = newFrequentMaxJobCount;
+                    changed = true;
+                }
+                int newRareMaxJobCount = Math.max(mMaxJobCountPerAllowedTime,
+                        Math.max(MIN_BUCKET_JOB_COUNT, MAX_JOB_COUNT_RARE));
+                if (mMaxBucketJobCounts[RARE_INDEX] != newRareMaxJobCount) {
+                    mMaxBucketJobCounts[RARE_INDEX] = newRareMaxJobCount;
+                    changed = true;
+                }
+
+                if (changed && mShouldThrottle) {
+                    // Update job bookkeeping out of band.
+                    BackgroundThread.getHandler().post(() -> {
+                        synchronized (mLock) {
+                            maybeUpdateAllConstraintsLocked();
+                        }
+                    });
+                }
+            }
+        }
+
+        private void dump(IndentingPrintWriter pw) {
+            pw.println();
+            pw.println("QuotaController:");
+            pw.increaseIndent();
+            pw.printPair(KEY_ALLOWED_TIME_PER_PERIOD_MS, ALLOWED_TIME_PER_PERIOD_MS).println();
+            pw.printPair(KEY_IN_QUOTA_BUFFER_MS, IN_QUOTA_BUFFER_MS).println();
+            pw.printPair(KEY_WINDOW_SIZE_ACTIVE_MS, WINDOW_SIZE_ACTIVE_MS).println();
+            pw.printPair(KEY_WINDOW_SIZE_WORKING_MS, WINDOW_SIZE_WORKING_MS).println();
+            pw.printPair(KEY_WINDOW_SIZE_FREQUENT_MS, WINDOW_SIZE_FREQUENT_MS).println();
+            pw.printPair(KEY_WINDOW_SIZE_RARE_MS, WINDOW_SIZE_RARE_MS).println();
+            pw.printPair(KEY_MAX_EXECUTION_TIME_MS, MAX_EXECUTION_TIME_MS).println();
+            pw.printPair(KEY_MAX_JOB_COUNT_ACTIVE, MAX_JOB_COUNT_ACTIVE).println();
+            pw.printPair(KEY_MAX_JOB_COUNT_WORKING, MAX_JOB_COUNT_WORKING).println();
+            pw.printPair(KEY_MAX_JOB_COUNT_FREQUENT, MAX_JOB_COUNT_FREQUENT).println();
+            pw.printPair(KEY_MAX_JOB_COUNT_RARE, MAX_JOB_COUNT_RARE).println();
+            pw.printPair(KEY_MAX_JOB_COUNT_PER_ALLOWED_TIME, MAX_JOB_COUNT_PER_ALLOWED_TIME)
+                    .println();
+            pw.decreaseIndent();
+        }
+
+        private void dump(ProtoOutputStream proto) {
+            final long qcToken = proto.start(ConstantsProto.QUOTA_CONTROLLER);
+            proto.write(ConstantsProto.QuotaController.ALLOWED_TIME_PER_PERIOD_MS,
+                    ALLOWED_TIME_PER_PERIOD_MS);
+            proto.write(ConstantsProto.QuotaController.IN_QUOTA_BUFFER_MS, IN_QUOTA_BUFFER_MS);
+            proto.write(ConstantsProto.QuotaController.ACTIVE_WINDOW_SIZE_MS,
+                    WINDOW_SIZE_ACTIVE_MS);
+            proto.write(ConstantsProto.QuotaController.WORKING_WINDOW_SIZE_MS,
+                    WINDOW_SIZE_WORKING_MS);
+            proto.write(ConstantsProto.QuotaController.FREQUENT_WINDOW_SIZE_MS,
+                    WINDOW_SIZE_FREQUENT_MS);
+            proto.write(ConstantsProto.QuotaController.RARE_WINDOW_SIZE_MS, WINDOW_SIZE_RARE_MS);
+            proto.write(ConstantsProto.QuotaController.MAX_EXECUTION_TIME_MS,
+                    MAX_EXECUTION_TIME_MS);
+            proto.write(ConstantsProto.QuotaController.MAX_JOB_COUNT_ACTIVE, MAX_JOB_COUNT_ACTIVE);
+            proto.write(ConstantsProto.QuotaController.MAX_JOB_COUNT_WORKING,
+                    MAX_JOB_COUNT_WORKING);
+            proto.write(ConstantsProto.QuotaController.MAX_JOB_COUNT_FREQUENT,
+                    MAX_JOB_COUNT_FREQUENT);
+            proto.write(ConstantsProto.QuotaController.MAX_JOB_COUNT_RARE, MAX_JOB_COUNT_RARE);
+            proto.write(ConstantsProto.QuotaController.MAX_JOB_COUNT_PER_ALLOWED_TIME,
+                    MAX_JOB_COUNT_PER_ALLOWED_TIME);
+            proto.end(qcToken);
+        }
+    }
+
     //////////////////////// TESTING HELPERS /////////////////////////////
 
     @VisibleForTesting
@@ -1943,6 +2181,12 @@
         return mTimingSessions.get(userId, packageName);
     }
 
+    @VisibleForTesting
+    @NonNull
+    QcConstants getQcConstants() {
+        return mQcConstants;
+    }
+
     //////////////////////////// DATA DUMP //////////////////////////////
 
     @Override
@@ -2188,4 +2432,14 @@
         proto.end(mToken);
         proto.end(token);
     }
+
+    @Override
+    public void dumpConstants(IndentingPrintWriter pw) {
+        mQcConstants.dump(pw);
+    }
+
+    @Override
+    public void dumpConstants(ProtoOutputStream proto) {
+        mQcConstants.dump(proto);
+    }
 }
diff --git a/services/core/java/com/android/server/job/controllers/StateController.java b/services/core/java/com/android/server/job/controllers/StateController.java
index 74628fb..51be38b 100644
--- a/services/core/java/com/android/server/job/controllers/StateController.java
+++ b/services/core/java/com/android/server/job/controllers/StateController.java
@@ -52,6 +52,13 @@
     }
 
     /**
+     * Called when the system boot phase has reached
+     * {@link com.android.server.SystemService#PHASE_SYSTEM_SERVICES_READY}.
+     */
+    public void onSystemServicesReady() {
+    }
+
+    /**
      * Implement the logic here to decide whether a job should be tracked by this controller.
      * This logic is put here so the JobManager can be completely agnostic of Controller logic.
      * Also called when updating a task, so implementing controllers have to be aware of
@@ -127,4 +134,12 @@
             Predicate<JobStatus> predicate);
     public abstract void dumpControllerStateLocked(ProtoOutputStream proto, long fieldId,
             Predicate<JobStatus> predicate);
+
+    /** Dump any internal constants the Controller may have. */
+    public void dumpConstants(IndentingPrintWriter pw) {
+    }
+
+    /** Dump any internal constants the Controller may have. */
+    public void dumpConstants(ProtoOutputStream proto) {
+    }
 }
diff --git a/services/core/java/com/android/server/job/controllers/TimeController.java b/services/core/java/com/android/server/job/controllers/TimeController.java
index 70deb38..ababad9 100644
--- a/services/core/java/com/android/server/job/controllers/TimeController.java
+++ b/services/core/java/com/android/server/job/controllers/TimeController.java
@@ -18,13 +18,20 @@
 
 import static com.android.server.job.JobSchedulerService.sElapsedRealtimeClock;
 
+import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.AlarmManager;
 import android.app.AlarmManager.OnAlarmListener;
+import android.content.ContentResolver;
 import android.content.Context;
+import android.database.ContentObserver;
+import android.net.Uri;
+import android.os.Handler;
 import android.os.Process;
 import android.os.UserHandle;
 import android.os.WorkSource;
+import android.provider.Settings;
+import android.util.KeyValueListParser;
 import android.util.Log;
 import android.util.Slog;
 import android.util.TimeUtils;
@@ -32,6 +39,7 @@
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.IndentingPrintWriter;
+import com.android.server.job.ConstantsProto;
 import com.android.server.job.JobSchedulerService;
 import com.android.server.job.StateControllerProto;
 
@@ -55,6 +63,9 @@
     /** Delay alarm tag for logging purposes */
     private final String DELAY_TAG = "*job.delay*";
 
+    private final Handler mHandler;
+    private final TcConstants mTcConstants;
+
     private long mNextJobExpiredElapsedMillis;
     private long mNextDelayExpiredElapsedMillis;
 
@@ -70,6 +81,14 @@
         mNextJobExpiredElapsedMillis = Long.MAX_VALUE;
         mNextDelayExpiredElapsedMillis = Long.MAX_VALUE;
         mChainedAttributionEnabled = mService.isChainedAttributionEnabled();
+
+        mHandler = new Handler(mContext.getMainLooper());
+        mTcConstants = new TcConstants(mHandler);
+    }
+
+    @Override
+    public void onSystemServicesReady() {
+        mTcConstants.start(mContext.getContentResolver());
     }
 
     /**
@@ -118,7 +137,7 @@
                     job.hasDeadlineConstraint() ? job.getLatestRunTimeElapsed() : Long.MAX_VALUE;
             final long delayExpiredElapsed =
                     job.hasTimingDelayConstraint() ? job.getEarliestRunTime() : Long.MAX_VALUE;
-            if (mConstants.TIME_CONTROLLER_SKIP_NOT_READY_JOBS) {
+            if (mTcConstants.SKIP_NOT_READY_JOBS) {
                 if (wouldBeReadyWithConstraintLocked(job, JobStatus.CONSTRAINT_TIMING_DELAY)) {
                     maybeUpdateDelayAlarmLocked(delayExpiredElapsed, ws);
                 }
@@ -148,14 +167,8 @@
     }
 
     @Override
-    public void onConstantsUpdatedLocked() {
-        checkExpiredDeadlinesAndResetAlarm();
-        checkExpiredDelaysAndResetAlarm();
-    }
-
-    @Override
     public void evaluateStateLocked(JobStatus job) {
-        if (!mConstants.TIME_CONTROLLER_SKIP_NOT_READY_JOBS) {
+        if (!mTcConstants.SKIP_NOT_READY_JOBS) {
             return;
         }
 
@@ -248,7 +261,7 @@
                     }
                     it.remove();
                 } else {  // Sorted by expiry time, so take the next one and stop.
-                    if (mConstants.TIME_CONTROLLER_SKIP_NOT_READY_JOBS
+                    if (mTcConstants.SKIP_NOT_READY_JOBS
                             && !wouldBeReadyWithConstraintLocked(
                             job, JobStatus.CONSTRAINT_DEADLINE)) {
                         if (DEBUG) {
@@ -308,7 +321,7 @@
                         ready = true;
                     }
                 } else {
-                    if (mConstants.TIME_CONTROLLER_SKIP_NOT_READY_JOBS
+                    if (mTcConstants.SKIP_NOT_READY_JOBS
                             && !wouldBeReadyWithConstraintLocked(
                             job, JobStatus.CONSTRAINT_TIMING_DELAY)) {
                         if (DEBUG) {
@@ -439,6 +452,87 @@
         }
     };
 
+    @VisibleForTesting
+    void recheckAlarmsLocked() {
+        checkExpiredDeadlinesAndResetAlarm();
+        checkExpiredDelaysAndResetAlarm();
+    }
+
+    @VisibleForTesting
+    class TcConstants extends ContentObserver {
+        private ContentResolver mResolver;
+        private final KeyValueListParser mParser = new KeyValueListParser(',');
+
+        private static final String KEY_SKIP_NOT_READY_JOBS = "skip_not_ready_jobs";
+
+        private static final boolean DEFAULT_SKIP_NOT_READY_JOBS = true;
+
+        /**
+         * Whether or not TimeController should skip setting wakeup alarms for jobs that aren't
+         * ready now.
+         */
+        public boolean SKIP_NOT_READY_JOBS = DEFAULT_SKIP_NOT_READY_JOBS;
+
+        /**
+         * Creates a content observer.
+         *
+         * @param handler The handler to run {@link #onChange} on, or null if none.
+         */
+        TcConstants(Handler handler) {
+            super(handler);
+        }
+
+        private void start(ContentResolver resolver) {
+            mResolver = resolver;
+            mResolver.registerContentObserver(Settings.Global.getUriFor(
+                    Settings.Global.JOB_SCHEDULER_TIME_CONTROLLER_CONSTANTS), false, this);
+            onChange(true, null);
+        }
+
+        @Override
+        public void onChange(boolean selfChange, Uri uri) {
+            final String constants = Settings.Global.getString(
+                    mResolver, Settings.Global.JOB_SCHEDULER_TIME_CONTROLLER_CONSTANTS);
+
+            try {
+                mParser.setString(constants);
+            } catch (Exception e) {
+                // Failed to parse the settings string, log this and move on with defaults.
+                Slog.e(TAG, "Bad jobscheduler time controller settings", e);
+            }
+
+            final boolean oldVal = SKIP_NOT_READY_JOBS;
+            SKIP_NOT_READY_JOBS = mParser.getBoolean(
+                    KEY_SKIP_NOT_READY_JOBS, DEFAULT_SKIP_NOT_READY_JOBS);
+
+            if (oldVal != SKIP_NOT_READY_JOBS) {
+                synchronized (mLock) {
+                    recheckAlarmsLocked();
+                }
+            }
+        }
+
+        private void dump(IndentingPrintWriter pw) {
+            pw.println();
+            pw.println("TimeController:");
+            pw.increaseIndent();
+            pw.printPair(KEY_SKIP_NOT_READY_JOBS, SKIP_NOT_READY_JOBS).println();
+            pw.decreaseIndent();
+        }
+
+        private void dump(ProtoOutputStream proto) {
+            final long tcToken = proto.start(ConstantsProto.TIME_CONTROLLER);
+            proto.write(ConstantsProto.TimeController.SKIP_NOT_READY_JOBS, SKIP_NOT_READY_JOBS);
+            proto.end(tcToken);
+        }
+    }
+
+    @VisibleForTesting
+    @NonNull
+    TcConstants getTcConstants() {
+        return mTcConstants;
+    }
+
     @Override
     public void dumpControllerStateLocked(IndentingPrintWriter pw,
             Predicate<JobStatus> predicate) {
@@ -513,4 +607,14 @@
         proto.end(mToken);
         proto.end(token);
     }
+
+    @Override
+    public void dumpConstants(IndentingPrintWriter pw) {
+        mTcConstants.dump(pw);
+    }
+
+    @Override
+    public void dumpConstants(ProtoOutputStream proto) {
+        mTcConstants.dump(proto);
+    }
 }
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDb.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDb.java
index c739650..1dffcf9 100644
--- a/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDb.java
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDb.java
@@ -333,6 +333,7 @@
         String[] selectionArguments = new String[] {String.valueOf(userId)};
 
         ensureUserMetadataEntryExists(userId);
+        invalidateKeysForUser(userId);
         return db.update(UserMetadataEntry.TABLE_NAME, values, selection, selectionArguments);
     }
 
@@ -394,16 +395,13 @@
     /**
      * Updates status of old keys to {@code RecoveryController.RECOVERY_STATUS_PERMANENT_FAILURE}.
      */
-    public void invalidateKeysWithOldGenerationId(int userId, int newGenerationId) {
+    public void invalidateKeysForUser(int userId) {
         SQLiteDatabase db = mKeyStoreDbHelper.getWritableDatabase();
         ContentValues values = new ContentValues();
         values.put(KeysEntry.COLUMN_NAME_RECOVERY_STATUS,
                 RecoveryController.RECOVERY_STATUS_PERMANENT_FAILURE);
-        String selection =
-                KeysEntry.COLUMN_NAME_USER_ID + " = ? AND "
-                + KeysEntry.COLUMN_NAME_GENERATION_ID + " < ?";
-        db.update(KeysEntry.TABLE_NAME, values, selection,
-            new String[] {String.valueOf(userId), String.valueOf(newGenerationId)});
+        String selection = KeysEntry.COLUMN_NAME_USER_ID + " = ?";
+        db.update(KeysEntry.TABLE_NAME, values, selection, new String[] {String.valueOf(userId)});
     }
 
     /**
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 21a862a..5737532 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -26,6 +26,7 @@
 import static android.app.NotificationManager.ACTION_APP_BLOCK_STATE_CHANGED;
 import static android.app.NotificationManager.ACTION_NOTIFICATION_CHANNEL_BLOCK_STATE_CHANGED;
 import static android.app.NotificationManager.ACTION_NOTIFICATION_CHANNEL_GROUP_BLOCK_STATE_CHANGED;
+import static android.app.NotificationManager.ACTION_NOTIFICATION_POLICY_ACCESS_GRANTED_CHANGED;
 import static android.app.NotificationManager.IMPORTANCE_LOW;
 import static android.app.NotificationManager.IMPORTANCE_MIN;
 import static android.app.NotificationManager.IMPORTANCE_NONE;
@@ -1811,14 +1812,15 @@
     }
 
     private void registerDeviceConfigChange() {
-        DeviceConfig.addOnPropertyChangedListener(
+        DeviceConfig.addOnPropertiesChangedListener(
                 DeviceConfig.NAMESPACE_SYSTEMUI,
                 getContext().getMainExecutor(),
-                (namespace, name, value) -> {
-                    if (!DeviceConfig.NAMESPACE_SYSTEMUI.equals(namespace)) {
+                (properties) -> {
+                    if (!DeviceConfig.NAMESPACE_SYSTEMUI.equals(properties.getNamespace())) {
                         return;
                     }
-                    if (SystemUiDeviceConfigFlags.NAS_DEFAULT_SERVICE.equals(name)) {
+                    if (properties.getKeyset()
+                            .contains(SystemUiDeviceConfigFlags.NAS_DEFAULT_SERVICE)) {
                         mAssistants.resetDefaultAssistantsIfNecessary();
                     }
                 });
@@ -3753,7 +3755,7 @@
                             pkg, userId, true, granted);
 
                     getContext().sendBroadcastAsUser(new Intent(
-                            NotificationManager.ACTION_NOTIFICATION_POLICY_ACCESS_GRANTED_CHANGED)
+                            ACTION_NOTIFICATION_POLICY_ACCESS_GRANTED_CHANGED)
                                     .setPackage(pkg)
                                     .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT),
                             UserHandle.of(userId), null);
@@ -3913,7 +3915,7 @@
                             userId, true, granted);
 
                     getContext().sendBroadcastAsUser(new Intent(
-                            NotificationManager.ACTION_NOTIFICATION_POLICY_ACCESS_GRANTED_CHANGED)
+                            ACTION_NOTIFICATION_POLICY_ACCESS_GRANTED_CHANGED)
                                     .setPackage(listener.getPackageName())
                                     .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY),
                             UserHandle.of(userId), null);
@@ -3929,7 +3931,9 @@
         public void setNotificationAssistantAccessGrantedForUser(ComponentName assistant,
                 int userId, boolean granted) {
             checkCallerIsSystemOrSystemUiOrShell();
-            mAssistants.setUserSet(userId, true);
+            for (UserInfo ui : mUm.getEnabledProfiles(userId)) {
+                mAssistants.setUserSet(ui.id, true);
+            }
             final long identity = Binder.clearCallingIdentity();
             try {
                 setNotificationAssistantAccessGrantedForUserInternal(assistant, userId, granted);
@@ -4143,30 +4147,36 @@
 
     @VisibleForTesting
     protected void setNotificationAssistantAccessGrantedForUserInternal(
-            ComponentName assistant, int userId, boolean granted) {
-        if (assistant == null) {
-            ComponentName allowedAssistant = CollectionUtils.firstOrNull(
-                    mAssistants.getAllowedComponents(userId));
-            if (allowedAssistant != null) {
-                setNotificationAssistantAccessGrantedForUserInternal(
-                        allowedAssistant, userId, false);
+            ComponentName assistant, int baseUserId, boolean granted) {
+        List<UserInfo> users = mUm.getEnabledProfiles(baseUserId);
+        if (users != null) {
+            for (UserInfo user : users) {
+                int userId = user.id;
+                if (assistant == null) {
+                    ComponentName allowedAssistant = CollectionUtils.firstOrNull(
+                            mAssistants.getAllowedComponents(userId));
+                    if (allowedAssistant != null) {
+                        setNotificationAssistantAccessGrantedForUserInternal(
+                                allowedAssistant, userId, false);
+                    }
+                    continue;
+                }
+                if (!granted || mAllowedManagedServicePackages.test(assistant.getPackageName(),
+                        userId, mAssistants.getRequiredPermission())) {
+                    mConditionProviders.setPackageOrComponentEnabled(assistant.flattenToString(),
+                            userId, false, granted);
+                    mAssistants.setPackageOrComponentEnabled(assistant.flattenToString(),
+                            userId, true, granted);
+
+                    getContext().sendBroadcastAsUser(
+                            new Intent(ACTION_NOTIFICATION_POLICY_ACCESS_GRANTED_CHANGED)
+                                    .setPackage(assistant.getPackageName())
+                                    .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY),
+                            UserHandle.of(userId), null);
+
+                    handleSavePolicyFile();
+                }
             }
-            return;
-        }
-        if (!granted || mAllowedManagedServicePackages.test(assistant.getPackageName(), userId,
-                mAssistants.getRequiredPermission())) {
-            mConditionProviders.setPackageOrComponentEnabled(assistant.flattenToString(),
-                    userId, false, granted);
-            mAssistants.setPackageOrComponentEnabled(assistant.flattenToString(),
-                    userId, true, granted);
-
-            getContext().sendBroadcastAsUser(new Intent(
-                            NotificationManager.ACTION_NOTIFICATION_POLICY_ACCESS_GRANTED_CHANGED)
-                            .setPackage(assistant.getPackageName())
-                            .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY),
-                    UserHandle.of(userId), null);
-
-            handleSavePolicyFile();
         }
     }
 
diff --git a/services/core/java/com/android/server/om/OverlayManagerService.java b/services/core/java/com/android/server/om/OverlayManagerService.java
index ee07c7d..209ccda 100644
--- a/services/core/java/com/android/server/om/OverlayManagerService.java
+++ b/services/core/java/com/android/server/om/OverlayManagerService.java
@@ -720,6 +720,26 @@
         }
 
         @Override
+        public String[] getDefaultOverlayPackages() throws RemoteException {
+            try {
+                traceBegin(TRACE_TAG_RRO, "OMS#getDefaultOverlayPackages");
+                getContext().enforceCallingOrSelfPermission(
+                        android.Manifest.permission.MODIFY_THEME_OVERLAY, null);
+
+                final long ident = Binder.clearCallingIdentity();
+                try {
+                    synchronized (mLock) {
+                        return mImpl.getDefaultOverlayPackages();
+                    }
+                } finally {
+                    Binder.restoreCallingIdentity(ident);
+                }
+            } finally {
+                traceEnd(TRACE_TAG_RRO);
+            }
+        }
+
+        @Override
         public void onShellCommand(@NonNull final FileDescriptor in,
                 @NonNull final FileDescriptor out, @NonNull final FileDescriptor err,
                 @NonNull final String[] args, @NonNull final ShellCallback callback,
diff --git a/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java b/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java
index 3a84b1e..092dbc8 100644
--- a/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java
+++ b/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java
@@ -641,6 +641,10 @@
         pw.println("Default overlays: " + TextUtils.join(";", mDefaultOverlays));
     }
 
+    @NonNull String[] getDefaultOverlayPackages() {
+        return mDefaultOverlays;
+    }
+
     List<String> getEnabledOverlayPackageNames(@NonNull final String targetPackageName,
             final int userId) {
         final List<OverlayInfo> overlays = mSettings.getOverlaysForTarget(targetPackageName,
diff --git a/services/core/java/com/android/server/pm/CrossProfileAppsServiceImpl.java b/services/core/java/com/android/server/pm/CrossProfileAppsServiceImpl.java
index 3a7919a..d6108b7c 100644
--- a/services/core/java/com/android/server/pm/CrossProfileAppsServiceImpl.java
+++ b/services/core/java/com/android/server/pm/CrossProfileAppsServiceImpl.java
@@ -24,6 +24,7 @@
 import android.app.ActivityOptions;
 import android.app.AppOpsManager;
 import android.app.IApplicationThread;
+import android.app.admin.DevicePolicyEventLogger;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
@@ -37,6 +38,7 @@
 import android.os.RemoteException;
 import android.os.UserHandle;
 import android.os.UserManager;
+import android.stats.devicepolicy.DevicePolicyEnums;
 import android.text.TextUtils;
 
 import com.android.internal.annotations.VisibleForTesting;
@@ -69,6 +71,11 @@
 
         verifyCallingPackage(callingPackage);
 
+        DevicePolicyEventLogger
+                .createEvent(DevicePolicyEnums.CROSS_PROFILE_APPS_GET_TARGET_USER_PROFILES)
+                .setStrings(new String[] {callingPackage})
+                .write();
+
         return getTargetUserProfilesUnchecked(
                 callingPackage, mInjector.getCallingUserId());
     }
@@ -85,6 +92,11 @@
 
         verifyCallingPackage(callingPackage);
 
+        DevicePolicyEventLogger
+                .createEvent(DevicePolicyEnums.CROSS_PROFILE_APPS_START_ACTIVITY_AS_USER)
+                .setStrings(new String[] {callingPackage})
+                .write();
+
         final int callerUserId = mInjector.getCallingUserId();
         final int callingUid = mInjector.getCallingUid();
 
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index d725420..8e1eb83 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -18767,6 +18767,7 @@
         boolean installedStateChanged = false;
         if (deletedPs != null) {
             if ((flags & PackageManager.DELETE_KEEP_DATA) == 0) {
+                final SparseBooleanArray changedUsers = new SparseBooleanArray();
                 synchronized (mPackages) {
                     clearIntentFilterVerificationsLPw(deletedPs.name, UserHandle.USER_ALL);
                     clearDefaultBrowserIfNeeded(packageName);
@@ -18798,10 +18799,9 @@
                             }
                         }
                     }
+                    clearPackagePreferredActivitiesLPw(
+                            deletedPs.name, changedUsers, UserHandle.USER_ALL);
                 }
-                final SparseBooleanArray changedUsers = new SparseBooleanArray();
-                clearPackagePreferredActivitiesLPw(
-                        deletedPs.name, changedUsers, UserHandle.USER_ALL);
                 if (changedUsers.size() > 0) {
                     updateDefaultHomeNotLocked(changedUsers);
                     postPreferredActivityChangedBroadcast(UserHandle.USER_ALL);
diff --git a/services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java b/services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java
index d8f07fe..748a661 100644
--- a/services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java
+++ b/services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java
@@ -16,6 +16,7 @@
 
 package com.android.server.rollback;
 
+import android.annotation.Nullable;
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
@@ -32,7 +33,6 @@
 import android.os.HandlerThread;
 import android.os.PowerManager;
 import android.text.TextUtils;
-import android.util.Pair;
 import android.util.Slog;
 import android.util.StatsLog;
 
@@ -77,74 +77,50 @@
     @Override
     public int onHealthCheckFailed(VersionedPackage failedPackage) {
         VersionedPackage moduleMetadataPackage = getModuleMetadataPackage();
-        if (moduleMetadataPackage == null) {
-            // Ignore failure, no mainline update available
-            return PackageHealthObserverImpact.USER_IMPACT_NONE;
-        }
 
-        if (getAvailableRollback(mContext.getSystemService(RollbackManager.class),
-                        failedPackage, moduleMetadataPackage) == null) {
+        if (getAvailableRollback(mContext.getSystemService(RollbackManager.class), failedPackage)
+                == null) {
             // Don't handle the notification, no rollbacks available for the package
             return PackageHealthObserverImpact.USER_IMPACT_NONE;
+        } else {
+            // Rollback is available, we may get a callback into #execute
+            return PackageHealthObserverImpact.USER_IMPACT_MEDIUM;
         }
-        // Rollback is available, we may get a callback into #execute
-        return PackageHealthObserverImpact.USER_IMPACT_MEDIUM;
     }
 
     @Override
     public boolean execute(VersionedPackage failedPackage) {
-        VersionedPackage moduleMetadataPackage = getModuleMetadataPackage();
-        if (moduleMetadataPackage == null) {
-            // Ignore failure, no mainline update available
-            return false;
-        }
-
         RollbackManager rollbackManager = mContext.getSystemService(RollbackManager.class);
-        Pair<RollbackInfo, Boolean> rollbackPair = getAvailableRollback(rollbackManager,
-                failedPackage, moduleMetadataPackage);
-        if (rollbackPair == null) {
+        VersionedPackage moduleMetadataPackage = getModuleMetadataPackage();
+        RollbackInfo rollback = getAvailableRollback(rollbackManager, failedPackage);
+
+        if (rollback == null) {
             Slog.w(TAG, "Expected rollback but no valid rollback found for package: [ "
                     + failedPackage.getPackageName() + "] with versionCode: ["
                     + failedPackage.getVersionCode() + "]");
             return false;
         }
 
-        RollbackInfo rollback = rollbackPair.first;
-        // We only log mainline package rollbacks, so check if rollback contains the
-        // module metadata provider, if it does, the rollback is a mainline rollback
-        boolean hasModuleMetadataPackage = rollbackPair.second;
-
-        if (hasModuleMetadataPackage) {
-            StatsLog.write(StatsLog.WATCHDOG_ROLLBACK_OCCURRED,
-                    StatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_INITIATE,
-                    moduleMetadataPackage.getPackageName(),
-                    moduleMetadataPackage.getVersionCode());
-        }
+        logEvent(moduleMetadataPackage,
+                StatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_INITIATE);
         LocalIntentReceiver rollbackReceiver = new LocalIntentReceiver((Intent result) -> {
-            if (hasModuleMetadataPackage) {
-                int status = result.getIntExtra(RollbackManager.EXTRA_STATUS,
-                        RollbackManager.STATUS_FAILURE);
-                if (status == RollbackManager.STATUS_SUCCESS) {
-                    if (rollback.isStaged()) {
-                        int rollbackId = rollback.getRollbackId();
-                        BroadcastReceiver listener =
-                                listenForStagedSessionReady(rollbackManager, rollbackId,
-                                        moduleMetadataPackage);
-                        handleStagedSessionChange(rollbackManager, rollbackId, listener,
-                                moduleMetadataPackage);
-                    } else {
-                        StatsLog.write(StatsLog.WATCHDOG_ROLLBACK_OCCURRED,
-                                StatsLog
-                                .WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_SUCCESS,
-                                moduleMetadataPackage.getPackageName(),
-                                moduleMetadataPackage.getVersionCode());
-                    }
+            int status = result.getIntExtra(RollbackManager.EXTRA_STATUS,
+                    RollbackManager.STATUS_FAILURE);
+            if (status == RollbackManager.STATUS_SUCCESS) {
+                if (rollback.isStaged()) {
+                    int rollbackId = rollback.getRollbackId();
+                    BroadcastReceiver listener =
+                            listenForStagedSessionReady(rollbackManager, rollbackId,
+                                    moduleMetadataPackage);
+                    handleStagedSessionChange(rollbackManager, rollbackId, listener,
+                            moduleMetadataPackage);
                 } else {
-                    StatsLog.write(StatsLog.WATCHDOG_ROLLBACK_OCCURRED,
-                            StatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_FAILURE,
-                            moduleMetadataPackage.getPackageName(),
-                            moduleMetadataPackage.getVersionCode());
+                    logEvent(moduleMetadataPackage,
+                            StatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_SUCCESS);
                 }
+            } else {
+                logEvent(moduleMetadataPackage,
+                        StatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_FAILURE);
             }
         });
 
@@ -193,26 +169,17 @@
         }
 
         String moduleMetadataPackageName = getModuleMetadataPackageName();
-        if (moduleMetadataPackageName == null) {
-            // Only log mainline staged rollbacks
-            return;
-        }
 
         // Use the version of the metadata package that was installed before
         // we rolled back for logging purposes.
         VersionedPackage moduleMetadataPackage = null;
         for (PackageRollbackInfo packageRollback : rollback.getPackages()) {
-            if (moduleMetadataPackageName.equals(packageRollback.getPackageName())) {
+            if (packageRollback.getPackageName().equals(moduleMetadataPackageName)) {
                 moduleMetadataPackage = packageRollback.getVersionRolledBackFrom();
                 break;
             }
         }
 
-        if (moduleMetadataPackage == null) {
-            // Only log mainline staged rollbacks
-            return;
-        }
-
         int sessionId = rollback.getCommittedSessionId();
         PackageInstaller.SessionInfo sessionInfo = packageInstaller.getSessionInfo(sessionId);
         if (sessionInfo == null) {
@@ -220,42 +187,33 @@
             return;
         }
         if (sessionInfo.isStagedSessionApplied()) {
-            StatsLog.write(StatsLog.WATCHDOG_ROLLBACK_OCCURRED,
-                    StatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_SUCCESS,
-                    moduleMetadataPackage.getPackageName(),
-                    moduleMetadataPackage.getVersionCode());
+            logEvent(moduleMetadataPackage,
+                    StatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_SUCCESS);
         } else if (sessionInfo.isStagedSessionReady()) {
             // TODO: What do for staged session ready but not applied
         } else {
-            StatsLog.write(StatsLog.WATCHDOG_ROLLBACK_OCCURRED,
-                    StatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_FAILURE,
-                    moduleMetadataPackage.getPackageName(),
-                    moduleMetadataPackage.getVersionCode());
+            logEvent(moduleMetadataPackage,
+                    StatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_FAILURE);
         }
     }
 
-    private Pair<RollbackInfo, Boolean> getAvailableRollback(RollbackManager rollbackManager,
-            VersionedPackage failedPackage, VersionedPackage moduleMetadataPackage) {
+    private RollbackInfo getAvailableRollback(RollbackManager rollbackManager,
+            VersionedPackage failedPackage) {
         for (RollbackInfo rollback : rollbackManager.getAvailableRollbacks()) {
-            // We only rollback mainline packages, so check if rollback contains the
-            // module metadata provider, if it does, the rollback is a mainline rollback
-            boolean hasModuleMetadataPackage = false;
-            boolean hasFailedPackage = false;
             for (PackageRollbackInfo packageRollback : rollback.getPackages()) {
-                hasModuleMetadataPackage |= packageRollback.getPackageName().equals(
-                        moduleMetadataPackage.getPackageName());
-                hasFailedPackage |= packageRollback.getPackageName().equals(
+                boolean hasFailedPackage = packageRollback.getPackageName().equals(
                         failedPackage.getPackageName())
                         && packageRollback.getVersionRolledBackFrom().getVersionCode()
                         == failedPackage.getVersionCode();
-            }
-            if (hasFailedPackage) {
-                return new Pair<RollbackInfo, Boolean>(rollback, hasModuleMetadataPackage);
+                if (hasFailedPackage) {
+                    return rollback;
+                }
             }
         }
         return null;
     }
 
+    @Nullable
     private String getModuleMetadataPackageName() {
         String packageName = mContext.getResources().getString(
                 R.string.config_defaultModuleMetadataProvider);
@@ -265,6 +223,7 @@
         return packageName;
     }
 
+    @Nullable
     private VersionedPackage getModuleMetadataPackage() {
         String packageName = getModuleMetadataPackageName();
         if (packageName == null) {
@@ -311,18 +270,13 @@
                 if (sessionInfo.isStagedSessionReady()) {
                     mContext.unregisterReceiver(listener);
                     saveLastStagedRollbackId(rollbackId);
-                    StatsLog.write(StatsLog.WATCHDOG_ROLLBACK_OCCURRED,
+                    logEvent(moduleMetadataPackage,
                             StatsLog
-                            .WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_BOOT_TRIGGERED,
-                            moduleMetadataPackage.getPackageName(),
-                            moduleMetadataPackage.getVersionCode());
+                            .WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_BOOT_TRIGGERED);
                     mContext.getSystemService(PowerManager.class).reboot("Rollback staged install");
                 } else if (sessionInfo.isStagedSessionFailed()) {
-                    StatsLog.write(StatsLog.WATCHDOG_ROLLBACK_OCCURRED,
-                            StatsLog
-                            .WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_FAILURE,
-                            moduleMetadataPackage.getPackageName(),
-                            moduleMetadataPackage.getVersionCode());
+                    logEvent(moduleMetadataPackage,
+                            StatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_FAILURE);
                     mContext.unregisterReceiver(listener);
                 }
             }
@@ -358,4 +312,12 @@
         mLastStagedRollbackIdFile.delete();
         return rollbackId;
     }
+
+    private static void logEvent(@Nullable VersionedPackage moduleMetadataPackage, int type) {
+        Slog.i(TAG, "Watchdog event occurred of type: " + type);
+        if (moduleMetadataPackage != null) {
+            StatsLog.write(StatsLog.WATCHDOG_ROLLBACK_OCCURRED, type,
+                    moduleMetadataPackage.getPackageName(), moduleMetadataPackage.getVersionCode());
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java b/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java
index b9b5aae..7734d6b 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java
@@ -77,7 +77,8 @@
     void onCameraLaunchGestureDetected(int source);
     void topAppWindowChanged(int displayId, boolean menuVisible);
     void setSystemUiVisibility(int displayId, int vis, int fullscreenStackVis, int dockedStackVis,
-            int mask, Rect fullscreenBounds, Rect dockedBounds, String cause);
+            int mask, Rect fullscreenBounds, Rect dockedBounds, boolean isNavbarColorManagedByIme,
+            String cause);
     void toggleSplitScreen();
     void appTransitionFinished(int displayId);
 
diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
index a5656c3..b2d7084 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
@@ -262,9 +262,10 @@
         @Override
         public void setSystemUiVisibility(int displayId, int vis, int fullscreenStackVis,
                 int dockedStackVis, int mask, Rect fullscreenBounds, Rect dockedBounds,
-                String cause) {
+                boolean isNavbarColorManagedByIme, String cause) {
             StatusBarManagerService.this.setSystemUiVisibility(displayId, vis, fullscreenStackVis,
-                    dockedStackVis, mask, fullscreenBounds, dockedBounds, cause);
+                    dockedStackVis, mask, fullscreenBounds, dockedBounds, isNavbarColorManagedByIme,
+                    cause);
         }
 
         @Override
@@ -872,11 +873,13 @@
     public void setSystemUiVisibility(int displayId, int vis, int mask, String cause) {
         final UiState state = getUiState(displayId);
         setSystemUiVisibility(displayId, vis, 0, 0, mask,
-                state.mFullscreenStackBounds, state.mDockedStackBounds, cause);
+                state.mFullscreenStackBounds, state.mDockedStackBounds,
+                state.mNavbarColorManagedByIme, cause);
     }
 
     private void setSystemUiVisibility(int displayId, int vis, int fullscreenStackVis,
-            int dockedStackVis, int mask, Rect fullscreenBounds, Rect dockedBounds, String cause) {
+            int dockedStackVis, int mask, Rect fullscreenBounds, Rect dockedBounds,
+            boolean isNavbarColorManagedByIme, String cause) {
         // also allows calls from window manager which is in this process.
         enforceStatusBarService();
 
@@ -884,7 +887,7 @@
 
         synchronized (mLock) {
             updateUiVisibilityLocked(displayId, vis, fullscreenStackVis, dockedStackVis, mask,
-                    fullscreenBounds, dockedBounds);
+                    fullscreenBounds, dockedBounds, isNavbarColorManagedByIme);
             disableLocked(
                     displayId,
                     mCurrentUserId,
@@ -896,17 +899,19 @@
 
     private void updateUiVisibilityLocked(final int displayId, final int vis,
             final int fullscreenStackVis, final int dockedStackVis, final int mask,
-            final Rect fullscreenBounds, final Rect dockedBounds) {
+            final Rect fullscreenBounds, final Rect dockedBounds,
+            final boolean isNavbarColorManagedByIme) {
         final UiState state = getUiState(displayId);
         if (!state.systemUiStateEquals(vis, fullscreenStackVis, dockedStackVis,
-                fullscreenBounds, dockedBounds)) {
+                fullscreenBounds, dockedBounds, isNavbarColorManagedByIme)) {
             state.setSystemUiState(vis, fullscreenStackVis, dockedStackVis, fullscreenBounds,
-                    dockedBounds);
+                    dockedBounds, isNavbarColorManagedByIme);
             mHandler.post(() -> {
                 if (mBar != null) {
                     try {
                         mBar.setSystemUiVisibility(displayId, vis, fullscreenStackVis,
-                                dockedStackVis, mask, fullscreenBounds, dockedBounds);
+                                dockedStackVis, mask, fullscreenBounds, dockedBounds,
+                                isNavbarColorManagedByIme);
                     } catch (RemoteException ex) {
                         Log.w(TAG, "Can not get StatusBar!");
                     }
@@ -945,6 +950,7 @@
         private int mImeBackDisposition = 0;
         private boolean mShowImeSwitcher = false;
         private IBinder mImeToken = null;
+        private boolean mNavbarColorManagedByIme = false;
 
         private int getDisabled1() {
             return mDisabled1;
@@ -972,21 +978,25 @@
         }
 
         private void setSystemUiState(final int vis, final int fullscreenStackVis,
-                final int dockedStackVis, final Rect fullscreenBounds, final Rect dockedBounds) {
+                final int dockedStackVis, final Rect fullscreenBounds, final Rect dockedBounds,
+                final boolean navbarColorManagedByIme) {
             mSystemUiVisibility = vis;
             mFullscreenStackSysUiVisibility = fullscreenStackVis;
             mDockedStackSysUiVisibility = dockedStackVis;
             mFullscreenStackBounds.set(fullscreenBounds);
             mDockedStackBounds.set(dockedBounds);
+            mNavbarColorManagedByIme = navbarColorManagedByIme;
         }
 
         private boolean systemUiStateEquals(final int vis, final int fullscreenStackVis,
-                final int dockedStackVis, final Rect fullscreenBounds, final Rect dockedBounds) {
+                final int dockedStackVis, final Rect fullscreenBounds, final Rect dockedBounds,
+                final boolean navbarColorManagedByIme) {
             return mSystemUiVisibility == vis
                 && mFullscreenStackSysUiVisibility == fullscreenStackVis
                 && mDockedStackSysUiVisibility == dockedStackVis
                 && mFullscreenStackBounds.equals(fullscreenBounds)
-                && mDockedStackBounds.equals(dockedBounds);
+                && mDockedStackBounds.equals(dockedBounds)
+                && mNavbarColorManagedByIme == navbarColorManagedByIme;
         }
 
         private void setImeWindowState(final int vis, final int backDisposition,
@@ -1051,7 +1061,8 @@
                     state.mImeBackDisposition, state.mShowImeSwitcher,
                     gatherDisableActionsLocked(mCurrentUserId, 2),
                     state.mFullscreenStackSysUiVisibility, state.mDockedStackSysUiVisibility,
-                    state.mImeToken, state.mFullscreenStackBounds, state.mDockedStackBounds);
+                    state.mImeToken, state.mFullscreenStackBounds, state.mDockedStackBounds,
+                    state.mNavbarColorManagedByIme);
         }
     }
 
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index 4ef8753..6976aa9 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -1038,6 +1038,12 @@
                 }
             }
         }
+        // don't abort if the callingUid has SYSTEM_ALERT_WINDOW permission
+        if (mService.hasSystemAlertWindowPermission(callingUid, callingPid, callingPackage)) {
+            Slog.w(TAG, "Background activity start for " + callingPackage
+                    + " allowed because SYSTEM_ALERT_WINDOW permission is granted.");
+            return false;
+        }
         // anything that has fallen through would currently be aborted
         Slog.w(TAG, "Background activity start [callingPackage: " + callingPackage
                 + "; callingUid: " + callingUid
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index 7bc9600..4a6aa33 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -108,12 +108,10 @@
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_VISIBILITY;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_ATM;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME;
-import static com.android.server.wm.ActivityTaskManagerInternal.ASSIST_ACTIVITY_ID;
 import static com.android.server.wm.ActivityTaskManagerInternal.ASSIST_KEY_CONTENT;
 import static com.android.server.wm.ActivityTaskManagerInternal.ASSIST_KEY_DATA;
 import static com.android.server.wm.ActivityTaskManagerInternal.ASSIST_KEY_RECEIVER_EXTRAS;
 import static com.android.server.wm.ActivityTaskManagerInternal.ASSIST_KEY_STRUCTURE;
-import static com.android.server.wm.ActivityTaskManagerInternal.ASSIST_TASK_ID;
 import static com.android.server.wm.ActivityTaskManagerService.H.REPORT_TIME_TRACKER_MSG;
 import static com.android.server.wm.ActivityTaskManagerService.UiHandler.DISMISS_DIALOG_UI_MSG;
 import static com.android.server.wm.RecentsAnimationController.REORDER_KEEP_IN_PLACE;
@@ -136,6 +134,7 @@
 import android.app.ActivityThread;
 import android.app.AlertDialog;
 import android.app.AppGlobals;
+import android.app.AppOpsManager;
 import android.app.Dialog;
 import android.app.IActivityController;
 import android.app.IActivityTaskManager;
@@ -216,7 +215,6 @@
 import android.util.ArrayMap;
 import android.util.EventLog;
 import android.util.Log;
-import android.util.Pair;
 import android.util.Slog;
 import android.util.SparseArray;
 import android.util.SparseIntArray;
@@ -875,6 +873,16 @@
         return getUserManager().hasUserRestriction(restriction, userId);
     }
 
+    boolean hasSystemAlertWindowPermission(int callingUid, int callingPid, String callingPackage) {
+        final int mode = getAppOpsService().noteOperation(AppOpsManager.OP_SYSTEM_ALERT_WINDOW,
+                callingUid, callingPackage);
+        if (mode == AppOpsManager.MODE_DEFAULT) {
+            return checkPermission(Manifest.permission.SYSTEM_ALERT_WINDOW, callingPid, callingUid)
+                    == PERMISSION_GRANTED;
+        }
+        return mode == AppOpsManager.MODE_ALLOWED;
+    }
+
     protected RecentTasks createRecentTasks() {
         return new RecentTasks(this, mStackSupervisor);
     }
diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java
index 2321898..8785b01 100644
--- a/services/core/java/com/android/server/wm/AppWindowToken.java
+++ b/services/core/java/com/android/server/wm/AppWindowToken.java
@@ -78,6 +78,7 @@
 import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_NORMAL;
 import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_WILL_PLACE_SURFACES;
 import static com.android.server.wm.WindowManagerService.logWithStack;
+import static com.android.server.wm.WindowState.LEGACY_POLICY_VISIBILITY;
 import static com.android.server.wm.WindowStateAnimator.STACK_CLIP_AFTER_ANIM;
 import static com.android.server.wm.WindowStateAnimator.STACK_CLIP_BEFORE_ANIM;
 
@@ -638,8 +639,8 @@
                 // If we are being set visible, and the starting window is not yet displayed,
                 // then make sure it doesn't get displayed.
                 if (startingWindow != null && !startingWindow.isDrawnLw()) {
-                    startingWindow.mPolicyVisibility = false;
-                    startingWindow.mPolicyVisibilityAfterAnim = false;
+                    startingWindow.clearPolicyVisibilityFlag(LEGACY_POLICY_VISIBILITY);
+                    startingWindow.mLegacyPolicyVisibilityAfterAnim = false;
                 }
 
                 // We are becoming visible, so better freeze the screen with the windows that are
@@ -1932,7 +1933,7 @@
                         + ", isAnimationSet=" + isSelfAnimating());
                 if (!w.isDrawnLw()) {
                     Slog.v(TAG, "Not displayed: s=" + winAnimator.mSurfaceController
-                            + " pv=" + w.mPolicyVisibility
+                            + " pv=" + w.isVisibleByPolicy()
                             + " mDrawState=" + winAnimator.drawStateToString()
                             + " ph=" + w.isParentWindowHidden() + " th=" + hiddenRequested
                             + " a=" + isSelfAnimating());
@@ -2432,14 +2433,19 @@
         }
     }
 
-    private boolean shouldAnimate(int transit) {
+
+    @VisibleForTesting
+    boolean shouldAnimate(int transit) {
         final boolean isSplitScreenPrimary =
                 getWindowingMode() == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
         final boolean allowSplitScreenPrimaryAnimation = transit != TRANSIT_WALLPAPER_OPEN;
 
-        // Don't animate when the task runs recents animation.
+        // Don't animate while the task runs recents animation but only if we are in the mode
+        // where we cancel with deferred screenshot, which means that the controller has
+        // transformed the task.
         final RecentsAnimationController controller = mWmService.getRecentsAnimationController();
-        if (controller != null && controller.isAnimatingTask(getTask())) {
+        if (controller != null && controller.isAnimatingTask(getTask())
+                && controller.shouldCancelWithDeferredScreenshot()) {
             return false;
         }
 
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 41292d2..3f783f6 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -4562,7 +4562,7 @@
                         token2.mOwnerCanManageAppTokens) ? -1 : 1;
 
         private final Predicate<WindowState> mGetOrientingWindow = w -> {
-            if (!w.isVisibleLw() || !w.mPolicyVisibilityAfterAnim) {
+            if (!w.isVisibleLw() || !w.mLegacyPolicyVisibilityAfterAnim) {
                 return false;
             }
             final int req = w.mAttrs.screenOrientation;
diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java
index 3bb3653..32d0b32 100644
--- a/services/core/java/com/android/server/wm/DisplayPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayPolicy.java
@@ -112,7 +112,6 @@
 import android.annotation.Nullable;
 import android.annotation.Px;
 import android.app.ActivityManager;
-import android.app.ActivityManagerInternal;
 import android.app.ActivityThread;
 import android.app.LoadedApk;
 import android.app.ResourcesManager;
@@ -133,6 +132,7 @@
 import android.os.SystemProperties;
 import android.os.UserHandle;
 import android.util.ArraySet;
+import android.util.Pair;
 import android.util.PrintWriterPrinter;
 import android.util.Slog;
 import android.view.DisplayCutout;
@@ -3113,7 +3113,9 @@
                 WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_HOME, mNonDockedStackBounds);
         mService.getStackBounds(
                 WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD, mDockedStackBounds);
-        final int visibility = updateSystemBarsLw(win, mLastSystemUiFlags, tmpVisibility);
+        final Pair<Integer, Boolean> result =
+                updateSystemBarsLw(win, mLastSystemUiFlags, tmpVisibility);
+        final int visibility = result.first;
         final int diff = visibility ^ mLastSystemUiFlags;
         final int fullscreenDiff = fullscreenVisibility ^ mLastFullscreenStackSysUiFlags;
         final int dockedDiff = dockedVisibility ^ mLastDockedStackSysUiFlags;
@@ -3133,13 +3135,14 @@
         mLastDockedStackBounds.set(mDockedStackBounds);
         final Rect fullscreenStackBounds = new Rect(mNonDockedStackBounds);
         final Rect dockedStackBounds = new Rect(mDockedStackBounds);
+        final boolean isNavbarColorManagedByIme = result.second;
         mHandler.post(() -> {
             StatusBarManagerInternal statusBar = getStatusBarManagerInternal();
             if (statusBar != null) {
                 final int displayId = getDisplayId();
                 statusBar.setSystemUiVisibility(displayId, visibility, fullscreenVisibility,
                         dockedVisibility, 0xffffffff, fullscreenStackBounds,
-                        dockedStackBounds, win.toString());
+                        dockedStackBounds, isNavbarColorManagedByIme, win.toString());
                 statusBar.topAppWindowChanged(displayId, needsMenu);
             }
         });
@@ -3222,7 +3225,7 @@
         return vis;
     }
 
-    private int updateSystemBarsLw(WindowState win, int oldVis, int vis) {
+    private Pair<Integer, Boolean> updateSystemBarsLw(WindowState win, int oldVis, int vis) {
         final boolean dockedStackVisible =
                 mDisplayContent.isStackVisible(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
         final boolean freeformStackVisible =
@@ -3355,8 +3358,11 @@
         vis = updateLightNavigationBarLw(vis, mTopFullscreenOpaqueWindowState,
                 mTopFullscreenOpaqueOrDimmingWindowState,
                 mDisplayContent.mInputMethodWindow, navColorWin);
+        // Navbar color is controlled by the IME.
+        final boolean isManagedByIme =
+                navColorWin != null && navColorWin == mDisplayContent.mInputMethodWindow;
 
-        return vis;
+        return Pair.create(vis, isManagedByIme);
     }
 
     private boolean drawsBarBackground(int vis, WindowState win, BarController controller,
diff --git a/services/core/java/com/android/server/wm/InsetsSourceProvider.java b/services/core/java/com/android/server/wm/InsetsSourceProvider.java
index f67b11b..402ec59 100644
--- a/services/core/java/com/android/server/wm/InsetsSourceProvider.java
+++ b/services/core/java/com/android/server/wm/InsetsSourceProvider.java
@@ -29,12 +29,11 @@
 import android.graphics.Point;
 import android.graphics.Rect;
 import android.util.proto.ProtoOutputStream;
+import android.view.InsetsSource;
+import android.view.InsetsSourceControl;
 import android.view.InsetsState;
 import android.view.SurfaceControl;
 import android.view.SurfaceControl.Transaction;
-import android.view.InsetsSource;
-import android.view.InsetsSourceControl;
-import android.view.ViewRootImpl;
 
 import com.android.internal.util.function.TriConsumer;
 import com.android.internal.util.function.pooled.PooledLambda;
@@ -142,7 +141,7 @@
                 mStateController.notifyControlChanged(mControllingWin);
             }
         }
-        setServerVisible(mWin.wouldBeVisibleIfPolicyIgnored() && mWin.mPolicyVisibility
+        setServerVisible(mWin.wouldBeVisibleIfPolicyIgnored() && mWin.isVisibleByPolicy()
                 && !mWin.mGivenInsetsPending);
     }
 
diff --git a/services/core/java/com/android/server/wm/WindowManagerInternal.java b/services/core/java/com/android/server/wm/WindowManagerInternal.java
index 78c5dbd..75a8dd5 100644
--- a/services/core/java/com/android/server/wm/WindowManagerInternal.java
+++ b/services/core/java/com/android/server/wm/WindowManagerInternal.java
@@ -481,7 +481,15 @@
     public abstract int getTopFocusedDisplayId();
 
     /**
-     * Checks whether this display should support showing system decorations.
+     * Checks if this display is configured and allowed to show system decorations.
      */
     public abstract boolean shouldShowSystemDecorOnDisplay(int displayId);
+
+    /**
+     * Indicates that the display should show IME.
+     *
+     * @param displayId The id of the display.
+     * @return {@code true} if the display should show IME when an input field become focused on it.
+     */
+    public abstract boolean shouldShowIme(int displayId);
 }
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index dae29b2..4f849cd 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -35,6 +35,7 @@
 import static android.provider.Settings.Global.DEVELOPMENT_FORCE_DESKTOP_MODE_ON_EXTERNAL_DISPLAYS;
 import static android.view.Display.DEFAULT_DISPLAY;
 import static android.view.Display.INVALID_DISPLAY;
+import static android.view.Display.TYPE_VIRTUAL;
 import static android.view.WindowManager.DOCKED_INVALID;
 import static android.view.WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW;
 import static android.view.WindowManager.LayoutParams.FIRST_SUB_WINDOW;
@@ -163,6 +164,7 @@
 import android.os.PowerManager.ServiceType;
 import android.os.PowerManagerInternal;
 import android.os.PowerSaveState;
+import android.os.Process;
 import android.os.RemoteException;
 import android.os.ResultReceiver;
 import android.os.ServiceManager;
@@ -5298,7 +5300,7 @@
                     ": removed=" + win.mRemoved + " visible=" + win.isVisibleLw() +
                     " mHasSurface=" + win.mHasSurface +
                     " drawState=" + win.mWinAnimator.mDrawState);
-            if (win.mRemoved || !win.mHasSurface || !win.mPolicyVisibility) {
+            if (win.mRemoved || !win.mHasSurface || !win.isVisibleByPolicy()) {
                 // Window has been removed or hidden; no draw will now happen, so stop waiting.
                 if (DEBUG_SCREEN_ON) Slog.w(TAG_WM, "Aborted waiting for drawn: " + win);
                 mWaitingForDrawn.remove(win);
@@ -6871,10 +6873,21 @@
                         + "not exist: " + displayId);
                 return false;
             }
+            final Display display = displayContent.getDisplay();
+            if (isUntrustedVirtualDisplay(display)) {
+                return false;
+            }
             return displayContent.supportsSystemDecorations();
         }
     }
 
+    /**
+     * @return {@code true} if the display is non-system created virtual display.
+     */
+    private static boolean isUntrustedVirtualDisplay(Display display) {
+        return display.getType() == TYPE_VIRTUAL && display.getOwnerUid() != Process.SYSTEM_UID;
+    }
+
     @Override
     public void setShouldShowSystemDecors(int displayId, boolean shouldShow) {
         if (!checkCallingPermission(INTERNAL_SYSTEM_WINDOW, "setShouldShowSystemDecors()")) {
@@ -6908,7 +6921,12 @@
                         + displayId);
                 return false;
             }
-            return mDisplayWindowSettings.shouldShowImeLocked(displayContent);
+            final Display display = displayContent.getDisplay();
+            if (isUntrustedVirtualDisplay(display)) {
+                return false;
+            }
+            return mDisplayWindowSettings.shouldShowImeLocked(displayContent)
+                    || mForceDesktopModeOnExternalDisplays;
         }
     }
 
@@ -7352,6 +7370,14 @@
                 return WindowManagerService.this.shouldShowSystemDecors(displayId);
             }
         }
+
+        @Override
+        public boolean shouldShowIme(int displayId) {
+            synchronized (mGlobalLock) {
+                final DisplayContent displayContent = mRoot.getDisplayContent(displayId);
+                return mDisplayWindowSettings.shouldShowImeLocked(displayContent);
+            }
+        }
     }
 
     void registerAppFreezeListener(AppFreezeListener listener) {
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index e39cd56..8123182 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -251,18 +251,33 @@
     int mSeq;
     int mViewVisibility;
     int mSystemUiVisibility;
+
     /**
-     * The visibility of the window based on policy like {@link WindowManagerPolicy}.
+     * The visibility flag of the window based on policy like {@link WindowManagerPolicy}.
      * Normally set by calling {@link #showLw} and {@link #hideLw}.
+     *
+     * TODO: b/131253938 This will eventually be split into individual visibility policy flags.
      */
-    boolean mPolicyVisibility = true;
+    static final int LEGACY_POLICY_VISIBILITY = 1;
     /**
-     * What {@link #mPolicyVisibility} should be set to after a transition animation.
-     * For example, {@link #mPolicyVisibility} might true during an exit animation to hide it and
-     * then set to the value of {@link #mPolicyVisibilityAfterAnim} which is false after the exit
-     * animation is done.
+     * The visibility flag that determines whether this window is visible for the current user.
      */
-    boolean mPolicyVisibilityAfterAnim = true;
+    private static final int VISIBLE_FOR_USER = 1 << 1;
+    private static final int POLICY_VISIBILITY_ALL = VISIBLE_FOR_USER | LEGACY_POLICY_VISIBILITY;
+    /**
+     * The Bitwise-or of flags that contribute to visibility of the WindowState
+     */
+    private int mPolicyVisibility = POLICY_VISIBILITY_ALL;
+
+    /**
+     * Whether {@link #LEGACY_POLICY_VISIBILITY} flag should be set after a transition animation.
+     * For example, {@link #LEGACY_POLICY_VISIBILITY} might be set during an exit animation to hide
+     * it and then unset when the value of {@link #mLegacyPolicyVisibilityAfterAnim} is false
+     * after the exit animation is done.
+     *
+     * TODO: b/131253938 Determine whether this can be changed to use a visibility flag instead.
+     */
+    boolean mLegacyPolicyVisibilityAfterAnim = true;
     // overlay window is hidden because the owning app is suspended
     private boolean mHiddenWhileSuspended;
     private boolean mAppOpVisibility = true;
@@ -1414,13 +1429,33 @@
 
     @Override
     boolean isVisible() {
-        return wouldBeVisibleIfPolicyIgnored() && mPolicyVisibility
+        return wouldBeVisibleIfPolicyIgnored() && isVisibleByPolicy()
                 // If we don't have a provider, this window isn't used as a window generating
                 // insets, so nobody can hide it over the inset APIs.
                 && (mInsetProvider == null || mInsetProvider.isClientVisible());
     }
 
     /**
+     * Ensures that all the policy visibility bits are set.
+     * @return {@code true} if all flags about visiblity are set
+     */
+    boolean isVisibleByPolicy() {
+        return (mPolicyVisibility & POLICY_VISIBILITY_ALL) == POLICY_VISIBILITY_ALL;
+    }
+
+    void clearPolicyVisibilityFlag(int policyVisibilityFlag) {
+        mPolicyVisibility &= ~policyVisibilityFlag;
+    }
+
+    void setPolicyVisibilityFlag(int policyVisibilityFlag) {
+        mPolicyVisibility |= policyVisibilityFlag;
+    }
+
+    private boolean isLegacyPolicyVisibility() {
+        return (mPolicyVisibility & LEGACY_POLICY_VISIBILITY) != 0;
+    }
+
+    /**
      * @return {@code true} if the window would be visible if we'd ignore policy visibility,
      *         {@code false} otherwise.
      */
@@ -1470,7 +1505,7 @@
     boolean isVisibleOrAdding() {
         final AppWindowToken atoken = mAppToken;
         return (mHasSurface || (!mRelayoutCalled && mViewVisibility == View.VISIBLE))
-                && mPolicyVisibility && !isParentWindowHidden()
+                && isVisibleByPolicy() && !isParentWindowHidden()
                 && (atoken == null || !atoken.hiddenRequested)
                 && !mAnimatingExit && !mDestroying;
     }
@@ -1481,7 +1516,7 @@
      * being visible.
      */
     boolean isOnScreen() {
-        if (!mHasSurface || mDestroying || !mPolicyVisibility) {
+        if (!mHasSurface || mDestroying || !isVisibleByPolicy()) {
             return false;
         }
         final AppWindowToken atoken = mAppToken;
@@ -1522,7 +1557,7 @@
         }
         final boolean parentAndClientVisible = !isParentWindowHidden()
                 && mViewVisibility == View.VISIBLE && !mToken.isHidden();
-        return mHasSurface && mPolicyVisibility && !mDestroying
+        return mHasSurface && isVisibleByPolicy() && !mDestroying
                 && (parentAndClientVisible || isAnimating());
     }
 
@@ -1551,7 +1586,7 @@
     @Override
     public boolean isDisplayedLw() {
         final AppWindowToken atoken = mAppToken;
-        return isDrawnLw() && mPolicyVisibility
+        return isDrawnLw() && isVisibleByPolicy()
                 && ((!isParentWindowHidden() && (atoken == null || !atoken.hiddenRequested))
                         || isAnimating());
     }
@@ -2057,8 +2092,8 @@
                 Slog.i(TAG_WM, "  mSurfaceController=" + mWinAnimator.mSurfaceController
                         + " relayoutCalled=" + mRelayoutCalled
                         + " viewVis=" + mViewVisibility
-                        + " policyVis=" + mPolicyVisibility
-                        + " policyVisAfterAnim=" + mPolicyVisibilityAfterAnim
+                        + " policyVis=" + isVisibleByPolicy()
+                        + " policyVisAfterAnim=" + mLegacyPolicyVisibilityAfterAnim
                         + " parentHidden=" + isParentWindowHidden()
                         + " exiting=" + mAnimatingExit + " destroying=" + mDestroying);
                 if (mAppToken != null) {
@@ -2192,7 +2227,9 @@
         if (isHiddenFromUserLocked()) {
             if (DEBUG_VISIBILITY) Slog.w(TAG_WM, "user changing, hiding " + this
                     + ", attrs=" + mAttrs.type + ", belonging to " + mOwnerUid);
-            hideLw(false);
+            clearPolicyVisibilityFlag(VISIBLE_FOR_USER);
+        } else {
+            setPolicyVisibilityFlag(VISIBLE_FOR_USER);
         }
     }
 
@@ -2284,13 +2321,17 @@
     }
 
     void checkPolicyVisibilityChange() {
-        if (mPolicyVisibility != mPolicyVisibilityAfterAnim) {
+        if (isLegacyPolicyVisibility() != mLegacyPolicyVisibilityAfterAnim) {
             if (DEBUG_VISIBILITY) {
                 Slog.v(TAG, "Policy visibility changing after anim in " +
-                        mWinAnimator + ": " + mPolicyVisibilityAfterAnim);
+                        mWinAnimator + ": " + mLegacyPolicyVisibilityAfterAnim);
             }
-            mPolicyVisibility = mPolicyVisibilityAfterAnim;
-            if (!mPolicyVisibility) {
+            if (mLegacyPolicyVisibilityAfterAnim) {
+                setPolicyVisibilityFlag(LEGACY_POLICY_VISIBILITY);
+            } else {
+                clearPolicyVisibilityFlag(LEGACY_POLICY_VISIBILITY);
+            }
+            if (!isVisibleByPolicy()) {
                 mWinAnimator.hide("checkPolicyVisibilityChange");
                 if (isFocused()) {
                     if (DEBUG_FOCUS_LIGHT) Slog.i(TAG,
@@ -2531,7 +2572,7 @@
     }
 
     boolean showLw(boolean doAnimation, boolean requestAnim) {
-        if (mPolicyVisibility && mPolicyVisibilityAfterAnim) {
+        if (isLegacyPolicyVisibility() && mLegacyPolicyVisibilityAfterAnim) {
             // Already showing.
             return false;
         }
@@ -2558,18 +2599,18 @@
         if (DEBUG_VISIBILITY) Slog.v(TAG, "Policy visibility true: " + this);
         if (doAnimation) {
             if (DEBUG_VISIBILITY) Slog.v(TAG, "doAnimation: mPolicyVisibility="
-                    + mPolicyVisibility + " animating=" + isAnimating());
+                    + isLegacyPolicyVisibility() + " animating=" + isAnimating());
             if (!mToken.okToAnimate()) {
                 doAnimation = false;
-            } else if (mPolicyVisibility && !isAnimating()) {
+            } else if (isLegacyPolicyVisibility() && !isAnimating()) {
                 // Check for the case where we are currently visible and
                 // not animating; we do not want to do animation at such a
                 // point to become visible when we already are.
                 doAnimation = false;
             }
         }
-        mPolicyVisibility = true;
-        mPolicyVisibilityAfterAnim = true;
+        setPolicyVisibilityFlag(LEGACY_POLICY_VISIBILITY);
+        mLegacyPolicyVisibilityAfterAnim = true;
         if (doAnimation) {
             mWinAnimator.applyAnimationLocked(TRANSIT_ENTER, true);
         }
@@ -2593,7 +2634,8 @@
                 doAnimation = false;
             }
         }
-        boolean current = doAnimation ? mPolicyVisibilityAfterAnim : mPolicyVisibility;
+        boolean current =
+                doAnimation ? mLegacyPolicyVisibilityAfterAnim : isLegacyPolicyVisibility();
         if (!current) {
             // Already hiding.
             return false;
@@ -2604,11 +2646,11 @@
                 doAnimation = false;
             }
         }
-        mPolicyVisibilityAfterAnim = false;
+        mLegacyPolicyVisibilityAfterAnim = false;
         final boolean isFocused = isFocused();
         if (!doAnimation) {
             if (DEBUG_VISIBILITY) Slog.v(TAG, "Policy visibility false: " + this);
-            mPolicyVisibility = false;
+            clearPolicyVisibilityFlag(LEGACY_POLICY_VISIBILITY);
             // Window is no longer visible -- make sure if we were waiting
             // for it to be displayed before enabling the display, that
             // we allow the display to be enabled now.
@@ -3443,11 +3485,11 @@
             pw.println(prefix + "mSeq=" + mSeq
                     + " mSystemUiVisibility=0x" + Integer.toHexString(mSystemUiVisibility));
         }
-        if (!mPolicyVisibility || !mPolicyVisibilityAfterAnim || !mAppOpVisibility
+        if (!isVisibleByPolicy() || !mLegacyPolicyVisibilityAfterAnim || !mAppOpVisibility
                 || isParentWindowHidden() || mPermanentlyHidden || mForceHideNonSystemOverlayWindow
                 || mHiddenWhileSuspended) {
-            pw.println(prefix + "mPolicyVisibility=" + mPolicyVisibility
-                    + " mPolicyVisibilityAfterAnim=" + mPolicyVisibilityAfterAnim
+            pw.println(prefix + "mPolicyVisibility=" + isVisibleByPolicy()
+                    + " mLegacyPolicyVisibilityAfterAnim=" + mLegacyPolicyVisibilityAfterAnim
                     + " mAppOpVisibility=" + mAppOpVisibility
                     + " parentHidden=" + isParentWindowHidden()
                     + " mPermanentlyHidden=" + mPermanentlyHidden
@@ -3904,7 +3946,7 @@
                     + ": mDrawState=" + mWinAnimator.drawStateToString()
                     + " readyForDisplay=" + isReadyForDisplay()
                     + " starting=" + (mAttrs.type == TYPE_APPLICATION_STARTING)
-                    + " during animation: policyVis=" + mPolicyVisibility
+                    + " during animation: policyVis=" + isVisibleByPolicy()
                     + " parentHidden=" + isParentWindowHidden()
                     + " tok.hiddenRequested="
                     + (mAppToken != null && mAppToken.hiddenRequested)
@@ -4313,7 +4355,7 @@
                     + ", animating=" + isAnimating());
             if (!isDrawnLw()) {
                 Slog.v(TAG, "Not displayed: s=" + mWinAnimator.mSurfaceController
-                        + " pv=" + mPolicyVisibility
+                        + " pv=" + isVisibleByPolicy()
                         + " mDrawState=" + mWinAnimator.mDrawState
                         + " ph=" + isParentWindowHidden()
                         + " th=" + (mAppToken != null ? mAppToken.hiddenRequested : false)
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index 780d471..20e1ac6 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -256,7 +256,7 @@
 
         mWin.checkPolicyVisibilityChange();
         final DisplayContent displayContent = mWin.getDisplayContent();
-        if (mAttrType == LayoutParams.TYPE_STATUS_BAR && mWin.mPolicyVisibility) {
+        if (mAttrType == LayoutParams.TYPE_STATUS_BAR && mWin.isVisibleByPolicy()) {
             // Upon completion of a not-visible to visible status bar animation a relayout is
             // required.
             if (displayContent != null) {
diff --git a/services/tests/mockingservicestests/src/com/android/server/job/JobSchedulerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/job/JobSchedulerServiceTest.java
new file mode 100644
index 0000000..f7edf65
--- /dev/null
+++ b/services/tests/mockingservicestests/src/com/android/server/job/JobSchedulerServiceTest.java
@@ -0,0 +1,437 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.job;
+
+import static android.text.format.DateUtils.HOUR_IN_MILLIS;
+import static android.text.format.DateUtils.MINUTE_IN_MILLIS;
+
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
+import static com.android.server.job.JobSchedulerService.sElapsedRealtimeClock;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.when;
+
+import android.app.ActivityManager;
+import android.app.ActivityManagerInternal;
+import android.app.IActivityManager;
+import android.app.job.JobInfo;
+import android.app.usage.UsageStatsManagerInternal;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManagerInternal;
+import android.content.res.Resources;
+import android.net.ConnectivityManager;
+import android.net.NetworkPolicyManager;
+import android.os.BatteryManagerInternal;
+import android.os.Looper;
+import android.os.RemoteException;
+import android.os.SystemClock;
+
+import com.android.server.AppStateTracker;
+import com.android.server.DeviceIdleController;
+import com.android.server.LocalServices;
+import com.android.server.job.controllers.JobStatus;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoSession;
+import org.mockito.quality.Strictness;
+
+import java.time.Clock;
+import java.time.Duration;
+import java.time.ZoneOffset;
+
+public class JobSchedulerServiceTest {
+    private JobSchedulerService mService;
+
+    private MockitoSession mMockingSession;
+    @Mock
+    private ActivityManagerInternal mActivityMangerInternal;
+    @Mock
+    private Context mContext;
+
+    private class TestJobSchedulerService extends JobSchedulerService {
+        TestJobSchedulerService(Context context) {
+            super(context);
+        }
+
+        @Override
+        public boolean isChainedAttributionEnabled() {
+            return false;
+        }
+    }
+
+    @Before
+    public void setUp() {
+        mMockingSession = mockitoSession()
+                .initMocks(this)
+                .strictness(Strictness.LENIENT)
+                .mockStatic(LocalServices.class)
+                .startMocking();
+
+        // Called in JobSchedulerService constructor.
+        when(mContext.getMainLooper()).thenReturn(Looper.getMainLooper());
+        doReturn(mActivityMangerInternal)
+                .when(() -> LocalServices.getService(ActivityManagerInternal.class));
+        doReturn(mock(UsageStatsManagerInternal.class))
+                .when(() -> LocalServices.getService(UsageStatsManagerInternal.class));
+        // Called in BackgroundJobsController constructor.
+        doReturn(mock(AppStateTracker.class))
+                .when(() -> LocalServices.getService(AppStateTracker.class));
+        // Called in BatteryController constructor.
+        doReturn(mock(BatteryManagerInternal.class))
+                .when(() -> LocalServices.getService(BatteryManagerInternal.class));
+        // Called in ConnectivityController constructor.
+        when(mContext.getSystemService(ConnectivityManager.class))
+                .thenReturn(mock(ConnectivityManager.class));
+        when(mContext.getSystemService(NetworkPolicyManager.class))
+                .thenReturn(mock(NetworkPolicyManager.class));
+        // Called in DeviceIdleJobsController constructor.
+        doReturn(mock(DeviceIdleController.LocalService.class))
+                .when(() -> LocalServices.getService(DeviceIdleController.LocalService.class));
+        // Used in JobStatus.
+        doReturn(mock(PackageManagerInternal.class))
+                .when(() -> LocalServices.getService(PackageManagerInternal.class));
+        // Called via IdleController constructor.
+        when(mContext.getPackageManager()).thenReturn(mock(PackageManager.class));
+        when(mContext.getResources()).thenReturn(mock(Resources.class));
+        // Called in QuotaController constructor.
+        IActivityManager activityManager = ActivityManager.getService();
+        spyOn(activityManager);
+        try {
+            doNothing().when(activityManager).registerUidObserver(any(), anyInt(), anyInt(), any());
+        } catch (RemoteException e) {
+            fail("registerUidObserver threw exception: " + e.getMessage());
+        }
+
+        JobSchedulerService.sSystemClock = Clock.fixed(Clock.systemUTC().instant(), ZoneOffset.UTC);
+        JobSchedulerService.sElapsedRealtimeClock =
+                Clock.fixed(SystemClock.elapsedRealtimeClock().instant(), ZoneOffset.UTC);
+
+        mService = new TestJobSchedulerService(mContext);
+    }
+
+    @After
+    public void tearDown() {
+        if (mMockingSession != null) {
+            mMockingSession.finishMocking();
+        }
+    }
+
+    private Clock getAdvancedClock(Clock clock, long incrementMs) {
+        return Clock.offset(clock, Duration.ofMillis(incrementMs));
+    }
+
+    private void advanceElapsedClock(long incrementMs) {
+        JobSchedulerService.sElapsedRealtimeClock = getAdvancedClock(
+                JobSchedulerService.sElapsedRealtimeClock, incrementMs);
+    }
+
+    private static JobInfo.Builder createJobInfo() {
+        return new JobInfo.Builder(351, new ComponentName("foo", "bar"));
+    }
+
+    private JobStatus createJobStatus(String testTag, JobInfo.Builder jobInfoBuilder) {
+        return JobStatus.createFromJobInfo(
+                jobInfoBuilder.build(), 1234, "com.android.test", 0, testTag);
+    }
+
+    /**
+     * Confirm that {@link JobSchedulerService#getRescheduleJobForPeriodic(JobStatus)} returns a job
+     * with the correct delay and deadline constraints if the periodic job is completed and
+     * rescheduled while run in its expected running window.
+     */
+    @Test
+    public void testGetRescheduleJobForPeriodic_insideWindow() {
+        final long now = sElapsedRealtimeClock.millis();
+        JobStatus job = createJobStatus("testGetRescheduleJobForPeriodic_insideWindow",
+                createJobInfo().setPeriodic(HOUR_IN_MILLIS));
+        final long nextWindowStartTime = now + HOUR_IN_MILLIS;
+        final long nextWindowEndTime = now + 2 * HOUR_IN_MILLIS;
+
+        JobStatus rescheduledJob = mService.getRescheduleJobForPeriodic(job);
+        assertEquals(nextWindowStartTime, rescheduledJob.getEarliestRunTime());
+        assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed());
+
+        advanceElapsedClock(10 * MINUTE_IN_MILLIS); // now + 10 minutes
+
+        rescheduledJob = mService.getRescheduleJobForPeriodic(job);
+        assertEquals(nextWindowStartTime, rescheduledJob.getEarliestRunTime());
+        assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed());
+
+        advanceElapsedClock(45 * MINUTE_IN_MILLIS); // now + 55 minutes
+
+        rescheduledJob = mService.getRescheduleJobForPeriodic(job);
+        assertEquals(nextWindowStartTime, rescheduledJob.getEarliestRunTime());
+        assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed());
+
+        advanceElapsedClock(4 * MINUTE_IN_MILLIS); // now + 59 minutes
+
+        rescheduledJob = mService.getRescheduleJobForPeriodic(job);
+        assertEquals(nextWindowStartTime, rescheduledJob.getEarliestRunTime());
+        assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed());
+    }
+
+    /**
+     * Confirm that {@link JobSchedulerService#getRescheduleJobForPeriodic(JobStatus)} returns a job
+     * with the correct delay and deadline constraints if the periodic job with a custom flex
+     * setting is completed and rescheduled while run in its expected running window.
+     */
+    @Test
+    public void testGetRescheduleJobForPeriodic_insideWindow_flex() {
+        JobStatus job = createJobStatus("testGetRescheduleJobForPeriodic_insideWindow_flex",
+                createJobInfo().setPeriodic(HOUR_IN_MILLIS, 30 * MINUTE_IN_MILLIS));
+        // First window starts 30 minutes from now.
+        advanceElapsedClock(30 * MINUTE_IN_MILLIS);
+        final long now = sElapsedRealtimeClock.millis();
+        final long nextWindowStartTime = now + HOUR_IN_MILLIS;
+        final long nextWindowEndTime = now + 90 * MINUTE_IN_MILLIS;
+
+        JobStatus rescheduledJob = mService.getRescheduleJobForPeriodic(job);
+        assertEquals(nextWindowStartTime, rescheduledJob.getEarliestRunTime());
+        assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed());
+
+        advanceElapsedClock(10 * MINUTE_IN_MILLIS); // now + 10 minutes
+
+        rescheduledJob = mService.getRescheduleJobForPeriodic(job);
+        assertEquals(nextWindowStartTime, rescheduledJob.getEarliestRunTime());
+        assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed());
+
+        advanceElapsedClock(15 * MINUTE_IN_MILLIS); // now + 25 minutes
+
+        rescheduledJob = mService.getRescheduleJobForPeriodic(job);
+        assertEquals(nextWindowStartTime, rescheduledJob.getEarliestRunTime());
+        assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed());
+
+        advanceElapsedClock(4 * MINUTE_IN_MILLIS); // now + 29 minutes
+
+        rescheduledJob = mService.getRescheduleJobForPeriodic(job);
+        assertEquals(nextWindowStartTime, rescheduledJob.getEarliestRunTime());
+        assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed());
+    }
+
+    /**
+     * Confirm that {@link JobSchedulerService#getRescheduleJobForPeriodic(JobStatus)} returns a job
+     * with the correct delay and deadline constraints if the periodic job failed but then ran
+     * successfully and was rescheduled while run in its expected running window.
+     */
+    @Test
+    public void testGetRescheduleJobForPeriodic_insideWindow_failedJob() {
+        final long now = sElapsedRealtimeClock.millis();
+        final long nextWindowStartTime = now + HOUR_IN_MILLIS;
+        final long nextWindowEndTime = now + 2 * HOUR_IN_MILLIS;
+        JobStatus job = createJobStatus("testGetRescheduleJobForPeriodic_insideWindow_failedJob",
+                createJobInfo().setPeriodic(HOUR_IN_MILLIS));
+        JobStatus failedJob = mService.getRescheduleJobForFailureLocked(job);
+
+        JobStatus rescheduledJob = mService.getRescheduleJobForPeriodic(failedJob);
+        assertEquals(nextWindowStartTime, rescheduledJob.getEarliestRunTime());
+        assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed());
+
+        advanceElapsedClock(5 * MINUTE_IN_MILLIS); // now + 5 minutes
+        failedJob = mService.getRescheduleJobForFailureLocked(job);
+        advanceElapsedClock(5 * MINUTE_IN_MILLIS); // now + 10 minutes
+
+        rescheduledJob = mService.getRescheduleJobForPeriodic(failedJob);
+        assertEquals(nextWindowStartTime, rescheduledJob.getEarliestRunTime());
+        assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed());
+
+        advanceElapsedClock(35 * MINUTE_IN_MILLIS); // now + 45 minutes
+        failedJob = mService.getRescheduleJobForFailureLocked(job);
+        advanceElapsedClock(10 * MINUTE_IN_MILLIS); // now + 55 minutes
+
+        rescheduledJob = mService.getRescheduleJobForPeriodic(failedJob);
+        assertEquals(nextWindowStartTime, rescheduledJob.getEarliestRunTime());
+        assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed());
+
+        advanceElapsedClock(2 * MINUTE_IN_MILLIS); // now + 57 minutes
+        failedJob = mService.getRescheduleJobForFailureLocked(job);
+        advanceElapsedClock(2 * MINUTE_IN_MILLIS); // now + 59 minutes
+
+        rescheduledJob = mService.getRescheduleJobForPeriodic(failedJob);
+        assertEquals(nextWindowStartTime, rescheduledJob.getEarliestRunTime());
+        assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed());
+    }
+
+    /**
+     * Confirm that {@link JobSchedulerService#getRescheduleJobForPeriodic(JobStatus)} returns a job
+     * with the correct delay and deadline constraints if the periodic job is completed and
+     * rescheduled when run after its expected running window.
+     */
+    @Test
+    public void testGetRescheduleJobForPeriodic_outsideWindow() {
+        JobStatus job = createJobStatus("testGetRescheduleJobForPeriodic_outsideWindow",
+                createJobInfo().setPeriodic(HOUR_IN_MILLIS));
+        long now = sElapsedRealtimeClock.millis();
+        long nextWindowStartTime = now + HOUR_IN_MILLIS;
+        long nextWindowEndTime = now + 2 * HOUR_IN_MILLIS;
+
+        advanceElapsedClock(HOUR_IN_MILLIS + MINUTE_IN_MILLIS);
+        // Say the job ran at the very end of its previous window. The intended JSS behavior is to
+        // have consistent windows, so the new window should start as soon as the previous window
+        // ended and end PERIOD time after the previous window ended.
+        JobStatus rescheduledJob = mService.getRescheduleJobForPeriodic(job);
+        assertEquals(nextWindowStartTime, rescheduledJob.getEarliestRunTime());
+        assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed());
+
+        advanceElapsedClock(2 * HOUR_IN_MILLIS);
+        // Say that the job ran at this point, possibly due to device idle.
+        // The next window should be consistent (start and end at the time it would have had the job
+        // run normally in previous windows).
+        nextWindowStartTime += 2 * HOUR_IN_MILLIS;
+        nextWindowEndTime += 2 * HOUR_IN_MILLIS;
+
+        rescheduledJob = mService.getRescheduleJobForPeriodic(job);
+        assertEquals(nextWindowStartTime, rescheduledJob.getEarliestRunTime());
+        assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed());
+    }
+
+    /**
+     * Confirm that {@link JobSchedulerService#getRescheduleJobForPeriodic(JobStatus)} returns a job
+     * with the correct delay and deadline constraints if the periodic job with a custom flex
+     * setting is completed and rescheduled when run after its expected running window.
+     */
+    @Test
+    public void testGetRescheduleJobForPeriodic_outsideWindow_flex() {
+        JobStatus job = createJobStatus("testGetRescheduleJobForPeriodic_outsideWindow_flex",
+                createJobInfo().setPeriodic(HOUR_IN_MILLIS, 30 * MINUTE_IN_MILLIS));
+        // First window starts 30 minutes from now.
+        advanceElapsedClock(30 * MINUTE_IN_MILLIS);
+        long now = sElapsedRealtimeClock.millis();
+        long nextWindowStartTime = now + HOUR_IN_MILLIS;
+        long nextWindowEndTime = now + 90 * MINUTE_IN_MILLIS;
+
+        advanceElapsedClock(31 * MINUTE_IN_MILLIS);
+        // Say the job ran at the very end of its previous window. The intended JSS behavior is to
+        // have consistent windows, so the new window should start as soon as the previous window
+        // ended and end PERIOD time after the previous window ended.
+        JobStatus rescheduledJob = mService.getRescheduleJobForPeriodic(job);
+        assertEquals(nextWindowStartTime, rescheduledJob.getEarliestRunTime());
+        assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed());
+
+        // 5 minutes before the start of the next window. It's too close to the next window, so the
+        // returned job should be for the window after.
+        advanceElapsedClock(24 * MINUTE_IN_MILLIS);
+        nextWindowStartTime += HOUR_IN_MILLIS;
+        nextWindowEndTime += HOUR_IN_MILLIS;
+        rescheduledJob = mService.getRescheduleJobForPeriodic(job);
+        assertEquals(nextWindowStartTime, rescheduledJob.getEarliestRunTime());
+        assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed());
+
+        advanceElapsedClock(2 * HOUR_IN_MILLIS + 10 * MINUTE_IN_MILLIS);
+        // Say that the job ran at this point, possibly due to device idle.
+        // The next window should be consistent (start and end at the time it would have had the job
+        // run normally in previous windows).
+        nextWindowStartTime += 2 * HOUR_IN_MILLIS;
+        nextWindowEndTime += 2 * HOUR_IN_MILLIS;
+
+        rescheduledJob = mService.getRescheduleJobForPeriodic(job);
+        assertEquals(nextWindowStartTime, rescheduledJob.getEarliestRunTime());
+        assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed());
+    }
+
+    /**
+     * Confirm that {@link JobSchedulerService#getRescheduleJobForPeriodic(JobStatus)} returns a job
+     * with the correct delay and deadline constraints if the periodic job failed but then ran
+     * successfully and was rescheduled when run after its expected running window.
+     */
+    @Test
+    public void testGetRescheduleJobForPeriodic_outsideWindow_failedJob() {
+        JobStatus job = createJobStatus("testGetRescheduleJobForPeriodic_outsideWindow_failedJob",
+                createJobInfo().setPeriodic(HOUR_IN_MILLIS));
+        JobStatus failedJob = mService.getRescheduleJobForFailureLocked(job);
+        long now = sElapsedRealtimeClock.millis();
+        long nextWindowStartTime = now + HOUR_IN_MILLIS;
+        long nextWindowEndTime = now + 2 * HOUR_IN_MILLIS;
+
+        advanceElapsedClock(HOUR_IN_MILLIS + MINUTE_IN_MILLIS);
+        // Say the job ran at the very end of its previous window. The intended JSS behavior is to
+        // have consistent windows, so the new window should start as soon as the previous window
+        // ended and end PERIOD time after the previous window ended.
+        JobStatus rescheduledJob = mService.getRescheduleJobForPeriodic(failedJob);
+        assertEquals(nextWindowStartTime, rescheduledJob.getEarliestRunTime());
+        assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed());
+
+        advanceElapsedClock(2 * HOUR_IN_MILLIS);
+        // Say that the job ran at this point, possibly due to device idle.
+        // The next window should be consistent (start and end at the time it would have had the job
+        // run normally in previous windows).
+        nextWindowStartTime += 2 * HOUR_IN_MILLIS;
+        nextWindowEndTime += 2 * HOUR_IN_MILLIS;
+
+        rescheduledJob = mService.getRescheduleJobForPeriodic(failedJob);
+        assertEquals(nextWindowStartTime, rescheduledJob.getEarliestRunTime());
+        assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed());
+    }
+
+    /**
+     * Confirm that {@link JobSchedulerService#getRescheduleJobForPeriodic(JobStatus)} returns a job
+     * with the correct delay and deadline constraints if the periodic job with a custom flex
+     * setting failed but then ran successfully and was rescheduled when run after its expected
+     * running window.
+     */
+    @Test
+    public void testGetRescheduleJobForPeriodic_outsideWindow_flex_failedJob() {
+        JobStatus job = createJobStatus(
+                "testGetRescheduleJobForPeriodic_outsideWindow_flex_failedJob",
+                createJobInfo().setPeriodic(HOUR_IN_MILLIS, 30 * MINUTE_IN_MILLIS));
+        JobStatus failedJob = mService.getRescheduleJobForFailureLocked(job);
+        // First window starts 30 minutes from now.
+        advanceElapsedClock(30 * MINUTE_IN_MILLIS);
+        long now = sElapsedRealtimeClock.millis();
+        long nextWindowStartTime = now + HOUR_IN_MILLIS;
+        long nextWindowEndTime = now + 90 * MINUTE_IN_MILLIS;
+
+        advanceElapsedClock(31 * MINUTE_IN_MILLIS);
+        // Say the job ran at the very end of its previous window. The intended JSS behavior is to
+        // have consistent windows, so the new window should start as soon as the previous window
+        // ended and end PERIOD time after the previous window ended.
+        JobStatus rescheduledJob = mService.getRescheduleJobForPeriodic(failedJob);
+        assertEquals(nextWindowStartTime, rescheduledJob.getEarliestRunTime());
+        assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed());
+
+        // 5 minutes before the start of the next window. It's too close to the next window, so the
+        // returned job should be for the window after.
+        advanceElapsedClock(24 * MINUTE_IN_MILLIS);
+        nextWindowStartTime += HOUR_IN_MILLIS;
+        nextWindowEndTime += HOUR_IN_MILLIS;
+        rescheduledJob = mService.getRescheduleJobForPeriodic(job);
+        assertEquals(nextWindowStartTime, rescheduledJob.getEarliestRunTime());
+        assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed());
+
+        advanceElapsedClock(2 * HOUR_IN_MILLIS);
+        // Say that the job ran at this point, possibly due to device idle.
+        // The next window should be consistent (start and end at the time it would have had the job
+        // run normally in previous windows).
+        nextWindowStartTime += 2 * HOUR_IN_MILLIS;
+        nextWindowEndTime += 2 * HOUR_IN_MILLIS;
+
+        rescheduledJob = mService.getRescheduleJobForPeriodic(failedJob);
+        assertEquals(nextWindowStartTime, rescheduledJob.getEarliestRunTime());
+        assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed());
+    }
+}
diff --git a/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java
index 7c30f25..2e7283c 100644
--- a/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java
@@ -105,8 +105,9 @@
     private static final int SOURCE_USER_ID = 0;
 
     private BroadcastReceiver mChargingReceiver;
-    private Constants mConstants;
+    private Constants mJsConstants;
     private QuotaController mQuotaController;
+    private QuotaController.QcConstants mQcConstants;
     private int mSourceUid;
     private IUidObserver mUidObserver;
 
@@ -132,13 +133,13 @@
                 .mockStatic(LocalServices.class)
                 .startMocking();
         // Make sure constants turn on QuotaController.
-        mConstants = new Constants();
-        mConstants.USE_HEARTBEATS = false;
+        mJsConstants = new Constants();
+        mJsConstants.USE_HEARTBEATS = false;
 
         // Called in StateController constructor.
         when(mJobSchedulerService.getTestableContext()).thenReturn(mContext);
         when(mJobSchedulerService.getLock()).thenReturn(mJobSchedulerService);
-        when(mJobSchedulerService.getConstants()).thenReturn(mConstants);
+        when(mJobSchedulerService.getConstants()).thenReturn(mJsConstants);
         // Called in QuotaController constructor.
         IActivityManager activityManager = ActivityManager.getService();
         spyOn(activityManager);
@@ -196,6 +197,7 @@
         } catch (RemoteException e) {
             fail(e.getMessage());
         }
+        mQcConstants = mQuotaController.getQcConstants();
     }
 
     @After
@@ -531,7 +533,7 @@
         expectedStats.executionTimeInMaxPeriodMs = 22 * MINUTE_IN_MILLIS;
         expectedStats.bgJobCountInMaxPeriod = 15;
         expectedStats.quotaCutoffTimeElapsed = now - (2 * HOUR_IN_MILLIS - MINUTE_IN_MILLIS)
-                + mConstants.QUOTA_CONTROLLER_IN_QUOTA_BUFFER_MS;
+                + mQcConstants.IN_QUOTA_BUFFER_MS;
         mQuotaController.updateExecutionStatsLocked(0, "com.android.test", inputStats);
         assertEquals(expectedStats, inputStats);
 
@@ -544,7 +546,7 @@
         expectedStats.executionTimeInMaxPeriodMs = 22 * MINUTE_IN_MILLIS;
         expectedStats.bgJobCountInMaxPeriod = 15;
         expectedStats.quotaCutoffTimeElapsed = now - (2 * HOUR_IN_MILLIS - MINUTE_IN_MILLIS)
-                + mConstants.QUOTA_CONTROLLER_IN_QUOTA_BUFFER_MS;
+                + mQcConstants.IN_QUOTA_BUFFER_MS;
         mQuotaController.updateExecutionStatsLocked(0, "com.android.test", inputStats);
         assertEquals(expectedStats, inputStats);
 
@@ -557,7 +559,7 @@
         expectedStats.executionTimeInMaxPeriodMs = 22 * MINUTE_IN_MILLIS;
         expectedStats.bgJobCountInMaxPeriod = 15;
         expectedStats.quotaCutoffTimeElapsed = now - (2 * HOUR_IN_MILLIS - MINUTE_IN_MILLIS)
-                + mConstants.QUOTA_CONTROLLER_IN_QUOTA_BUFFER_MS;
+                + mQcConstants.IN_QUOTA_BUFFER_MS;
         mQuotaController.updateExecutionStatsLocked(0, "com.android.test", inputStats);
         assertEquals(expectedStats, inputStats);
 
@@ -574,7 +576,7 @@
         expectedStats.executionTimeInMaxPeriodMs = 23 * MINUTE_IN_MILLIS;
         expectedStats.bgJobCountInMaxPeriod = 18;
         expectedStats.quotaCutoffTimeElapsed = now - (2 * HOUR_IN_MILLIS - MINUTE_IN_MILLIS)
-                + mConstants.QUOTA_CONTROLLER_IN_QUOTA_BUFFER_MS;
+                + mQcConstants.IN_QUOTA_BUFFER_MS;
         mQuotaController.updateExecutionStatsLocked(0, "com.android.test", inputStats);
         assertEquals(expectedStats, inputStats);
 
@@ -590,7 +592,7 @@
         expectedStats.executionTimeInMaxPeriodMs = 24 * MINUTE_IN_MILLIS;
         expectedStats.bgJobCountInMaxPeriod = 20;
         expectedStats.quotaCutoffTimeElapsed = now - (2 * HOUR_IN_MILLIS - MINUTE_IN_MILLIS)
-                + mConstants.QUOTA_CONTROLLER_IN_QUOTA_BUFFER_MS;
+                + mQcConstants.IN_QUOTA_BUFFER_MS;
         mQuotaController.updateExecutionStatsLocked(0, "com.android.test", inputStats);
         assertEquals(expectedStats, inputStats);
     }
@@ -630,7 +632,7 @@
         expectedStats.executionTimeInMaxPeriodMs = 33 * MINUTE_IN_MILLIS;
         expectedStats.bgJobCountInMaxPeriod = 20;
         expectedStats.quotaCutoffTimeElapsed = now - (2 * HOUR_IN_MILLIS - 3 * MINUTE_IN_MILLIS)
-                + mConstants.QUOTA_CONTROLLER_IN_QUOTA_BUFFER_MS;
+                + mQcConstants.IN_QUOTA_BUFFER_MS;
         assertEquals(expectedStats,
                 mQuotaController.getExecutionStatsLocked(0, "com.android.test", WORKING_INDEX));
 
@@ -642,7 +644,7 @@
         expectedStats.executionTimeInMaxPeriodMs = 33 * MINUTE_IN_MILLIS;
         expectedStats.bgJobCountInMaxPeriod = 20;
         expectedStats.quotaCutoffTimeElapsed = now - (2 * HOUR_IN_MILLIS - 3 * MINUTE_IN_MILLIS)
-                + mConstants.QUOTA_CONTROLLER_IN_QUOTA_BUFFER_MS;
+                + mQcConstants.IN_QUOTA_BUFFER_MS;
         assertEquals(expectedStats,
                 mQuotaController.getExecutionStatsLocked(0, "com.android.test", FREQUENT_INDEX));
 
@@ -654,7 +656,7 @@
         expectedStats.executionTimeInMaxPeriodMs = 33 * MINUTE_IN_MILLIS;
         expectedStats.bgJobCountInMaxPeriod = 20;
         expectedStats.quotaCutoffTimeElapsed = now - (2 * HOUR_IN_MILLIS - 3 * MINUTE_IN_MILLIS)
-                + mConstants.QUOTA_CONTROLLER_IN_QUOTA_BUFFER_MS;
+                + mQcConstants.IN_QUOTA_BUFFER_MS;
         assertEquals(expectedStats,
                 mQuotaController.getExecutionStatsLocked(0, "com.android.test", RARE_INDEX));
     }
@@ -685,8 +687,8 @@
         // Advance clock so that the working stats shouldn't be the same.
         advanceElapsedClock(MINUTE_IN_MILLIS);
         // Change frequent bucket size so that the stats need to be recalculated.
-        mConstants.QUOTA_CONTROLLER_WINDOW_SIZE_FREQUENT_MS = 6 * HOUR_IN_MILLIS;
-        mQuotaController.onConstantsUpdatedLocked();
+        mQcConstants.WINDOW_SIZE_FREQUENT_MS = 6 * HOUR_IN_MILLIS;
+        mQcConstants.updateConstants();
 
         ExecutionStats expectedStats = new ExecutionStats();
         expectedStats.windowSizeMs = originalStatsActive.windowSizeMs;
@@ -778,7 +780,7 @@
         setStandbyBucket(ACTIVE_INDEX);
         assertEquals(7 * MINUTE_IN_MILLIS,
                 mQuotaController.getRemainingExecutionTimeLocked(SOURCE_USER_ID, SOURCE_PACKAGE));
-        assertEquals(mConstants.QUOTA_CONTROLLER_MAX_EXECUTION_TIME_MS - 9 * MINUTE_IN_MILLIS,
+        assertEquals(mQcConstants.MAX_EXECUTION_TIME_MS - 9 * MINUTE_IN_MILLIS,
                 mQuotaController.getTimeUntilQuotaConsumedLocked(SOURCE_USER_ID, SOURCE_PACKAGE));
     }
 
@@ -901,7 +903,7 @@
     public void testIsWithinQuotaLocked_UnderDuration_OverJobCount() {
         setDischarging();
         final long now = JobSchedulerService.sElapsedRealtimeClock.millis();
-        final int jobCount = mConstants.QUOTA_CONTROLLER_MAX_JOB_COUNT_PER_ALLOWED_TIME;
+        final int jobCount = mQcConstants.MAX_JOB_COUNT_PER_ALLOWED_TIME;
         mQuotaController.saveTimingSession(0, "com.android.test.spam",
                 createTimingSession(now - (HOUR_IN_MILLIS), 15 * MINUTE_IN_MILLIS, 25));
         mQuotaController.saveTimingSession(0, "com.android.test.spam",
@@ -936,7 +938,7 @@
     public void testIsWithinQuotaLocked_OverDuration_OverJobCount() {
         setDischarging();
         final long now = JobSchedulerService.sElapsedRealtimeClock.millis();
-        final int jobCount = mConstants.QUOTA_CONTROLLER_MAX_JOB_COUNT_PER_ALLOWED_TIME;
+        final int jobCount = mQcConstants.MAX_JOB_COUNT_PER_ALLOWED_TIME;
         mQuotaController.saveTimingSession(0, "com.android.test",
                 createTimingSession(now - (HOUR_IN_MILLIS), 15 * MINUTE_IN_MILLIS, 25));
         mQuotaController.saveTimingSession(0, "com.android.test",
@@ -1098,8 +1100,7 @@
         final long now = JobSchedulerService.sElapsedRealtimeClock.millis();
         // Test with timing sessions out of window but still under max execution limit.
         final long expectedAlarmTime =
-                (now - 18 * HOUR_IN_MILLIS) + 24 * HOUR_IN_MILLIS
-                        + mConstants.QUOTA_CONTROLLER_IN_QUOTA_BUFFER_MS;
+                (now - 18 * HOUR_IN_MILLIS) + 24 * HOUR_IN_MILLIS + mQcConstants.IN_QUOTA_BUFFER_MS;
         mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
                 createTimingSession(now - 18 * HOUR_IN_MILLIS, HOUR_IN_MILLIS, 1));
         mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
@@ -1156,8 +1157,7 @@
         final long end = now - (2 * HOUR_IN_MILLIS - 5 * MINUTE_IN_MILLIS);
         // Counting backwards, the quota will come back one minute before the end.
         final long expectedAlarmTime =
-                end - MINUTE_IN_MILLIS + 2 * HOUR_IN_MILLIS
-                        + mConstants.QUOTA_CONTROLLER_IN_QUOTA_BUFFER_MS;
+                end - MINUTE_IN_MILLIS + 2 * HOUR_IN_MILLIS + mQcConstants.IN_QUOTA_BUFFER_MS;
         mQuotaController.saveTimingSession(0, "com.android.test",
                 new TimingSession(now - 2 * HOUR_IN_MILLIS, end, 1));
         mQuotaController.maybeScheduleStartAlarmLocked(0, "com.android.test", standbyBucket);
@@ -1207,8 +1207,7 @@
 
         // Test with timing sessions in window but still in quota.
         final long start = now - (6 * HOUR_IN_MILLIS);
-        final long expectedAlarmTime =
-                start + 8 * HOUR_IN_MILLIS + mConstants.QUOTA_CONTROLLER_IN_QUOTA_BUFFER_MS;
+        final long expectedAlarmTime = start + 8 * HOUR_IN_MILLIS + mQcConstants.IN_QUOTA_BUFFER_MS;
         mQuotaController.saveTimingSession(0, "com.android.test",
                 createTimingSession(start, 5 * MINUTE_IN_MILLIS, 1));
         mQuotaController.maybeScheduleStartAlarmLocked(0, "com.android.test", standbyBucket);
@@ -1262,7 +1261,7 @@
         // needs to be excluded.
         final long expectedAlarmTime =
                 start + MINUTE_IN_MILLIS + 24 * HOUR_IN_MILLIS
-                        + mConstants.QUOTA_CONTROLLER_IN_QUOTA_BUFFER_MS;
+                        + mQcConstants.IN_QUOTA_BUFFER_MS;
         mQuotaController.saveTimingSession(0, "com.android.test",
                 createTimingSession(start, 5 * MINUTE_IN_MILLIS, 1));
         mQuotaController.maybeScheduleStartAlarmLocked(0, "com.android.test", standbyBucket);
@@ -1324,21 +1323,21 @@
         // And down from there.
         final long expectedWorkingAlarmTime =
                 outOfQuotaTime + (2 * HOUR_IN_MILLIS)
-                        + mConstants.QUOTA_CONTROLLER_IN_QUOTA_BUFFER_MS;
+                        + mQcConstants.IN_QUOTA_BUFFER_MS;
         mQuotaController.maybeScheduleStartAlarmLocked(0, "com.android.test", WORKING_INDEX);
         inOrder.verify(mAlarmManager, times(1))
                 .set(anyInt(), eq(expectedWorkingAlarmTime), eq(TAG_QUOTA_CHECK), any(), any());
 
         final long expectedFrequentAlarmTime =
                 outOfQuotaTime + (8 * HOUR_IN_MILLIS)
-                        + mConstants.QUOTA_CONTROLLER_IN_QUOTA_BUFFER_MS;
+                        + mQcConstants.IN_QUOTA_BUFFER_MS;
         mQuotaController.maybeScheduleStartAlarmLocked(0, "com.android.test", FREQUENT_INDEX);
         inOrder.verify(mAlarmManager, times(1))
                 .set(anyInt(), eq(expectedFrequentAlarmTime), eq(TAG_QUOTA_CHECK), any(), any());
 
         final long expectedRareAlarmTime =
                 outOfQuotaTime + (24 * HOUR_IN_MILLIS)
-                        + mConstants.QUOTA_CONTROLLER_IN_QUOTA_BUFFER_MS;
+                        + mQcConstants.IN_QUOTA_BUFFER_MS;
         mQuotaController.maybeScheduleStartAlarmLocked(0, "com.android.test", RARE_INDEX);
         inOrder.verify(mAlarmManager, times(1))
                 .set(anyInt(), eq(expectedRareAlarmTime), eq(TAG_QUOTA_CHECK), any(), any());
@@ -1365,7 +1364,7 @@
         ExecutionStats stats = mQuotaController.getExecutionStatsLocked(SOURCE_USER_ID,
                 SOURCE_PACKAGE, standbyBucket);
         stats.jobCountInAllowedTime =
-                mConstants.QUOTA_CONTROLLER_MAX_JOB_COUNT_PER_ALLOWED_TIME + 2;
+                mQcConstants.MAX_JOB_COUNT_PER_ALLOWED_TIME + 2;
 
         // Invalid time in the past, so the count shouldn't be used.
         stats.jobCountExpirationTimeElapsed =
@@ -1400,8 +1399,8 @@
     @Test
     public void testMaybeScheduleStartAlarmLocked_SmallRollingQuota_UpdatedBufferSize() {
         // Make sure any new value is used correctly.
-        mConstants.QUOTA_CONTROLLER_IN_QUOTA_BUFFER_MS *= 2;
-        mQuotaController.onConstantsUpdatedLocked();
+        mQcConstants.IN_QUOTA_BUFFER_MS *= 2;
+        mQcConstants.updateConstants();
         runTestMaybeScheduleStartAlarmLocked_SmallRollingQuota_AllowedTimeCheck();
         mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE).clear();
         runTestMaybeScheduleStartAlarmLocked_SmallRollingQuota_MaxTimeCheck();
@@ -1410,8 +1409,8 @@
     @Test
     public void testMaybeScheduleStartAlarmLocked_SmallRollingQuota_UpdatedAllowedTime() {
         // Make sure any new value is used correctly.
-        mConstants.QUOTA_CONTROLLER_ALLOWED_TIME_PER_PERIOD_MS /= 2;
-        mQuotaController.onConstantsUpdatedLocked();
+        mQcConstants.ALLOWED_TIME_PER_PERIOD_MS /= 2;
+        mQcConstants.updateConstants();
         runTestMaybeScheduleStartAlarmLocked_SmallRollingQuota_AllowedTimeCheck();
         mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE).clear();
         runTestMaybeScheduleStartAlarmLocked_SmallRollingQuota_MaxTimeCheck();
@@ -1420,8 +1419,8 @@
     @Test
     public void testMaybeScheduleStartAlarmLocked_SmallRollingQuota_UpdatedMaxTime() {
         // Make sure any new value is used correctly.
-        mConstants.QUOTA_CONTROLLER_MAX_EXECUTION_TIME_MS /= 2;
-        mQuotaController.onConstantsUpdatedLocked();
+        mQcConstants.MAX_EXECUTION_TIME_MS /= 2;
+        mQcConstants.updateConstants();
         runTestMaybeScheduleStartAlarmLocked_SmallRollingQuota_AllowedTimeCheck();
         mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE).clear();
         runTestMaybeScheduleStartAlarmLocked_SmallRollingQuota_MaxTimeCheck();
@@ -1430,10 +1429,10 @@
     @Test
     public void testMaybeScheduleStartAlarmLocked_SmallRollingQuota_UpdatedEverything() {
         // Make sure any new value is used correctly.
-        mConstants.QUOTA_CONTROLLER_IN_QUOTA_BUFFER_MS *= 2;
-        mConstants.QUOTA_CONTROLLER_ALLOWED_TIME_PER_PERIOD_MS /= 2;
-        mConstants.QUOTA_CONTROLLER_MAX_EXECUTION_TIME_MS /= 2;
-        mQuotaController.onConstantsUpdatedLocked();
+        mQcConstants.IN_QUOTA_BUFFER_MS *= 2;
+        mQcConstants.ALLOWED_TIME_PER_PERIOD_MS /= 2;
+        mQcConstants.MAX_EXECUTION_TIME_MS /= 2;
+        mQcConstants.updateConstants();
         runTestMaybeScheduleStartAlarmLocked_SmallRollingQuota_AllowedTimeCheck();
         mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE).clear();
         runTestMaybeScheduleStartAlarmLocked_SmallRollingQuota_MaxTimeCheck();
@@ -1448,9 +1447,8 @@
         final long now = JobSchedulerService.sElapsedRealtimeClock.millis();
         // Working set window size is 2 hours.
         final int standbyBucket = WORKING_INDEX;
-        final long contributionMs = mConstants.QUOTA_CONTROLLER_IN_QUOTA_BUFFER_MS / 2;
-        final long remainingTimeMs =
-                mConstants.QUOTA_CONTROLLER_ALLOWED_TIME_PER_PERIOD_MS - contributionMs;
+        final long contributionMs = mQcConstants.IN_QUOTA_BUFFER_MS / 2;
+        final long remainingTimeMs = mQcConstants.ALLOWED_TIME_PER_PERIOD_MS - contributionMs;
 
         // Session straddles edge of bucket window. Only the contribution should be counted towards
         // the quota.
@@ -1462,7 +1460,7 @@
         // Expected alarm time should be when the app will have QUOTA_BUFFER_MS time of quota, which
         // is 2 hours + (QUOTA_BUFFER_MS - contributionMs) after the start of the second session.
         final long expectedAlarmTime = now - HOUR_IN_MILLIS + 2 * HOUR_IN_MILLIS
-                + (mConstants.QUOTA_CONTROLLER_IN_QUOTA_BUFFER_MS - contributionMs);
+                + (mQcConstants.IN_QUOTA_BUFFER_MS - contributionMs);
         mQuotaController.maybeScheduleStartAlarmLocked(SOURCE_USER_ID, SOURCE_PACKAGE,
                 standbyBucket);
         verify(mAlarmManager, times(1))
@@ -1479,9 +1477,8 @@
         final long now = JobSchedulerService.sElapsedRealtimeClock.millis();
         // Working set window size is 2 hours.
         final int standbyBucket = WORKING_INDEX;
-        final long contributionMs = mConstants.QUOTA_CONTROLLER_IN_QUOTA_BUFFER_MS / 2;
-        final long remainingTimeMs =
-                mConstants.QUOTA_CONTROLLER_MAX_EXECUTION_TIME_MS - contributionMs;
+        final long contributionMs = mQcConstants.IN_QUOTA_BUFFER_MS / 2;
+        final long remainingTimeMs = mQcConstants.MAX_EXECUTION_TIME_MS - contributionMs;
 
         // Session straddles edge of 24 hour window. Only the contribution should be counted towards
         // the quota.
@@ -1493,9 +1490,8 @@
         // Expected alarm time should be when the app will have QUOTA_BUFFER_MS time of quota, which
         // is 24 hours + (QUOTA_BUFFER_MS - contributionMs) after the start of the second session.
         final long expectedAlarmTime = now - 20 * HOUR_IN_MILLIS
-                //+ mConstants.QUOTA_CONTROLLER_MAX_EXECUTION_TIME_MS
                 + 24 * HOUR_IN_MILLIS
-                + (mConstants.QUOTA_CONTROLLER_IN_QUOTA_BUFFER_MS - contributionMs);
+                + (mQcConstants.IN_QUOTA_BUFFER_MS - contributionMs);
         mQuotaController.maybeScheduleStartAlarmLocked(SOURCE_USER_ID, SOURCE_PACKAGE,
                 standbyBucket);
         verify(mAlarmManager, times(1))
@@ -1515,12 +1511,12 @@
         mQuotaController.maybeStartTrackingJobLocked(jobStatus, null);
         assertFalse(jobStatus.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_QUOTA));
 
-        mConstants.USE_HEARTBEATS = true;
+        mJsConstants.USE_HEARTBEATS = true;
         mQuotaController.onConstantsUpdatedLocked();
         Thread.sleep(SECOND_IN_MILLIS); // Job updates are done in the background.
         assertTrue(jobStatus.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_QUOTA));
 
-        mConstants.USE_HEARTBEATS = false;
+        mJsConstants.USE_HEARTBEATS = false;
         mQuotaController.onConstantsUpdatedLocked();
         Thread.sleep(SECOND_IN_MILLIS); // Job updates are done in the background.
         assertFalse(jobStatus.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_QUOTA));
@@ -1528,20 +1524,20 @@
 
     @Test
     public void testConstantsUpdating_ValidValues() {
-        mConstants.QUOTA_CONTROLLER_ALLOWED_TIME_PER_PERIOD_MS = 5 * MINUTE_IN_MILLIS;
-        mConstants.QUOTA_CONTROLLER_IN_QUOTA_BUFFER_MS = 2 * MINUTE_IN_MILLIS;
-        mConstants.QUOTA_CONTROLLER_WINDOW_SIZE_ACTIVE_MS = 15 * MINUTE_IN_MILLIS;
-        mConstants.QUOTA_CONTROLLER_WINDOW_SIZE_WORKING_MS = 30 * MINUTE_IN_MILLIS;
-        mConstants.QUOTA_CONTROLLER_WINDOW_SIZE_FREQUENT_MS = 45 * MINUTE_IN_MILLIS;
-        mConstants.QUOTA_CONTROLLER_WINDOW_SIZE_RARE_MS = 60 * MINUTE_IN_MILLIS;
-        mConstants.QUOTA_CONTROLLER_MAX_EXECUTION_TIME_MS = 3 * HOUR_IN_MILLIS;
-        mConstants.QUOTA_CONTROLLER_MAX_JOB_COUNT_ACTIVE = 5000;
-        mConstants.QUOTA_CONTROLLER_MAX_JOB_COUNT_WORKING = 4000;
-        mConstants.QUOTA_CONTROLLER_MAX_JOB_COUNT_FREQUENT = 3000;
-        mConstants.QUOTA_CONTROLLER_MAX_JOB_COUNT_RARE = 2000;
-        mConstants.QUOTA_CONTROLLER_MAX_JOB_COUNT_PER_ALLOWED_TIME = 500;
+        mQcConstants.ALLOWED_TIME_PER_PERIOD_MS = 5 * MINUTE_IN_MILLIS;
+        mQcConstants.IN_QUOTA_BUFFER_MS = 2 * MINUTE_IN_MILLIS;
+        mQcConstants.WINDOW_SIZE_ACTIVE_MS = 15 * MINUTE_IN_MILLIS;
+        mQcConstants.WINDOW_SIZE_WORKING_MS = 30 * MINUTE_IN_MILLIS;
+        mQcConstants.WINDOW_SIZE_FREQUENT_MS = 45 * MINUTE_IN_MILLIS;
+        mQcConstants.WINDOW_SIZE_RARE_MS = 60 * MINUTE_IN_MILLIS;
+        mQcConstants.MAX_EXECUTION_TIME_MS = 3 * HOUR_IN_MILLIS;
+        mQcConstants.MAX_JOB_COUNT_ACTIVE = 5000;
+        mQcConstants.MAX_JOB_COUNT_WORKING = 4000;
+        mQcConstants.MAX_JOB_COUNT_FREQUENT = 3000;
+        mQcConstants.MAX_JOB_COUNT_RARE = 2000;
+        mQcConstants.MAX_JOB_COUNT_PER_ALLOWED_TIME = 500;
 
-        mQuotaController.onConstantsUpdatedLocked();
+        mQcConstants.updateConstants();
 
         assertEquals(5 * MINUTE_IN_MILLIS, mQuotaController.getAllowedTimePerPeriodMs());
         assertEquals(2 * MINUTE_IN_MILLIS, mQuotaController.getInQuotaBufferMs());
@@ -1561,20 +1557,20 @@
     @Test
     public void testConstantsUpdating_InvalidValues() {
         // Test negatives/too low.
-        mConstants.QUOTA_CONTROLLER_ALLOWED_TIME_PER_PERIOD_MS = -MINUTE_IN_MILLIS;
-        mConstants.QUOTA_CONTROLLER_IN_QUOTA_BUFFER_MS = -MINUTE_IN_MILLIS;
-        mConstants.QUOTA_CONTROLLER_WINDOW_SIZE_ACTIVE_MS = -MINUTE_IN_MILLIS;
-        mConstants.QUOTA_CONTROLLER_WINDOW_SIZE_WORKING_MS = -MINUTE_IN_MILLIS;
-        mConstants.QUOTA_CONTROLLER_WINDOW_SIZE_FREQUENT_MS = -MINUTE_IN_MILLIS;
-        mConstants.QUOTA_CONTROLLER_WINDOW_SIZE_RARE_MS = -MINUTE_IN_MILLIS;
-        mConstants.QUOTA_CONTROLLER_MAX_EXECUTION_TIME_MS = -MINUTE_IN_MILLIS;
-        mConstants.QUOTA_CONTROLLER_MAX_JOB_COUNT_ACTIVE = -1;
-        mConstants.QUOTA_CONTROLLER_MAX_JOB_COUNT_WORKING = 1;
-        mConstants.QUOTA_CONTROLLER_MAX_JOB_COUNT_FREQUENT = 1;
-        mConstants.QUOTA_CONTROLLER_MAX_JOB_COUNT_RARE = 1;
-        mConstants.QUOTA_CONTROLLER_MAX_JOB_COUNT_PER_ALLOWED_TIME = 0;
+        mQcConstants.ALLOWED_TIME_PER_PERIOD_MS = -MINUTE_IN_MILLIS;
+        mQcConstants.IN_QUOTA_BUFFER_MS = -MINUTE_IN_MILLIS;
+        mQcConstants.WINDOW_SIZE_ACTIVE_MS = -MINUTE_IN_MILLIS;
+        mQcConstants.WINDOW_SIZE_WORKING_MS = -MINUTE_IN_MILLIS;
+        mQcConstants.WINDOW_SIZE_FREQUENT_MS = -MINUTE_IN_MILLIS;
+        mQcConstants.WINDOW_SIZE_RARE_MS = -MINUTE_IN_MILLIS;
+        mQcConstants.MAX_EXECUTION_TIME_MS = -MINUTE_IN_MILLIS;
+        mQcConstants.MAX_JOB_COUNT_ACTIVE = -1;
+        mQcConstants.MAX_JOB_COUNT_WORKING = 1;
+        mQcConstants.MAX_JOB_COUNT_FREQUENT = 1;
+        mQcConstants.MAX_JOB_COUNT_RARE = 1;
+        mQcConstants.MAX_JOB_COUNT_PER_ALLOWED_TIME = 0;
 
-        mQuotaController.onConstantsUpdatedLocked();
+        mQcConstants.updateConstants();
 
         assertEquals(MINUTE_IN_MILLIS, mQuotaController.getAllowedTimePerPeriodMs());
         assertEquals(0, mQuotaController.getInQuotaBufferMs());
@@ -1590,15 +1586,15 @@
         assertEquals(100, mQuotaController.getBucketMaxJobCounts()[RARE_INDEX]);
 
         // Test larger than a day. Controller should cap at one day.
-        mConstants.QUOTA_CONTROLLER_ALLOWED_TIME_PER_PERIOD_MS = 25 * HOUR_IN_MILLIS;
-        mConstants.QUOTA_CONTROLLER_IN_QUOTA_BUFFER_MS = 25 * HOUR_IN_MILLIS;
-        mConstants.QUOTA_CONTROLLER_WINDOW_SIZE_ACTIVE_MS = 25 * HOUR_IN_MILLIS;
-        mConstants.QUOTA_CONTROLLER_WINDOW_SIZE_WORKING_MS = 25 * HOUR_IN_MILLIS;
-        mConstants.QUOTA_CONTROLLER_WINDOW_SIZE_FREQUENT_MS = 25 * HOUR_IN_MILLIS;
-        mConstants.QUOTA_CONTROLLER_WINDOW_SIZE_RARE_MS = 25 * HOUR_IN_MILLIS;
-        mConstants.QUOTA_CONTROLLER_MAX_EXECUTION_TIME_MS = 25 * HOUR_IN_MILLIS;
+        mQcConstants.ALLOWED_TIME_PER_PERIOD_MS = 25 * HOUR_IN_MILLIS;
+        mQcConstants.IN_QUOTA_BUFFER_MS = 25 * HOUR_IN_MILLIS;
+        mQcConstants.WINDOW_SIZE_ACTIVE_MS = 25 * HOUR_IN_MILLIS;
+        mQcConstants.WINDOW_SIZE_WORKING_MS = 25 * HOUR_IN_MILLIS;
+        mQcConstants.WINDOW_SIZE_FREQUENT_MS = 25 * HOUR_IN_MILLIS;
+        mQcConstants.WINDOW_SIZE_RARE_MS = 25 * HOUR_IN_MILLIS;
+        mQcConstants.MAX_EXECUTION_TIME_MS = 25 * HOUR_IN_MILLIS;
 
-        mQuotaController.onConstantsUpdatedLocked();
+        mQcConstants.updateConstants();
 
         assertEquals(24 * HOUR_IN_MILLIS, mQuotaController.getAllowedTimePerPeriodMs());
         assertEquals(5 * MINUTE_IN_MILLIS, mQuotaController.getInQuotaBufferMs());
@@ -2219,7 +2215,7 @@
         verify(mAlarmManager, never()).set(anyInt(), anyLong(), eq(TAG_QUOTA_CHECK), any(), any());
 
         // Ran jobs up to the job limit. All of them should be allowed to run.
-        for (int i = 0; i < mConstants.QUOTA_CONTROLLER_MAX_JOB_COUNT_PER_ALLOWED_TIME; ++i) {
+        for (int i = 0; i < mQcConstants.MAX_JOB_COUNT_PER_ALLOWED_TIME; ++i) {
             JobStatus job = createJobStatus("testStartAlarmScheduled_JobCount_AllowedTime", i);
             mQuotaController.maybeStartTrackingJobLocked(job, null);
             assertTrue(job.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_QUOTA));
@@ -2240,7 +2236,7 @@
         ExecutionStats stats = mQuotaController.getExecutionStatsLocked(SOURCE_USER_ID,
                 SOURCE_PACKAGE, standbyBucket);
         final long expectedWorkingAlarmTime =
-                stats.jobCountExpirationTimeElapsed + mConstants.QUOTA_CONTROLLER_ALLOWED_TIME_PER_PERIOD_MS;
+                stats.jobCountExpirationTimeElapsed + mQcConstants.ALLOWED_TIME_PER_PERIOD_MS;
         verify(mAlarmManager, times(1))
                 .set(anyInt(), eq(expectedWorkingAlarmTime), eq(TAG_QUOTA_CHECK), any(), any());
     }
diff --git a/services/tests/mockingservicestests/src/com/android/server/job/controllers/TimeControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/job/controllers/TimeControllerTest.java
index 494677d..19369db 100644
--- a/services/tests/mockingservicestests/src/com/android/server/job/controllers/TimeControllerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/job/controllers/TimeControllerTest.java
@@ -39,6 +39,7 @@
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.pm.PackageManagerInternal;
+import android.os.Looper;
 import android.os.SystemClock;
 
 import androidx.test.runner.AndroidJUnit4;
@@ -70,7 +71,7 @@
     private static final String SOURCE_PACKAGE = "com.android.frameworks.mockingservicestests";
     private static final int SOURCE_USER_ID = 0;
 
-    private Constants mConstants;
+    private TimeController.TcConstants mConstants;
     private TimeController mTimeController;
 
     private MockitoSession mMockingSession;
@@ -88,14 +89,13 @@
                 .strictness(Strictness.LENIENT)
                 .mockStatic(LocalServices.class)
                 .startMocking();
-        // Use default constants for now.
-        mConstants = new Constants();
 
         // Called in StateController constructor.
         when(mJobSchedulerService.getTestableContext()).thenReturn(mContext);
         when(mJobSchedulerService.getLock()).thenReturn(mJobSchedulerService);
-        when(mJobSchedulerService.getConstants()).thenReturn(mConstants);
+        when(mJobSchedulerService.getConstants()).thenReturn(mock(Constants.class));
         // Called in TimeController constructor.
+        when(mContext.getMainLooper()).thenReturn(Looper.getMainLooper());
         when(mContext.getSystemService(Context.ALARM_SERVICE)).thenReturn(mAlarmManager);
         // Used in JobStatus.
         doReturn(mock(PackageManagerInternal.class))
@@ -111,6 +111,7 @@
 
         // Initialize real objects.
         mTimeController = new TimeController(mJobSchedulerService);
+        mConstants = mTimeController.getTcConstants();
         spyOn(mTimeController);
     }
 
@@ -159,7 +160,7 @@
 
     @Test
     public void testMaybeStartTrackingJobLocked_DelayInOrder_NoSkipping() {
-        mConstants.TIME_CONTROLLER_SKIP_NOT_READY_JOBS = false;
+        mConstants.SKIP_NOT_READY_JOBS = false;
         mTimeController.onConstantsUpdatedLocked();
 
         runTestMaybeStartTrackingJobLocked_DelayInOrder();
@@ -167,7 +168,7 @@
 
     @Test
     public void testMaybeStartTrackingJobLocked_DelayInOrder_WithSkipping_AllReady() {
-        mConstants.TIME_CONTROLLER_SKIP_NOT_READY_JOBS = true;
+        mConstants.SKIP_NOT_READY_JOBS = true;
         mTimeController.onConstantsUpdatedLocked();
 
         doReturn(true).when(mTimeController).wouldBeReadyWithConstraintLocked(any(), anyInt());
@@ -201,7 +202,7 @@
 
     @Test
     public void testMaybeStartTrackingJobLocked_DelayInOrder_WithSkipping_SomeNotReady() {
-        mConstants.TIME_CONTROLLER_SKIP_NOT_READY_JOBS = true;
+        mConstants.SKIP_NOT_READY_JOBS = true;
         mTimeController.onConstantsUpdatedLocked();
         final long now = JobSchedulerService.sElapsedRealtimeClock.millis();
 
@@ -235,7 +236,7 @@
 
     @Test
     public void testMaybeStartTrackingJobLocked_DelayReverseOrder_NoSkipping() {
-        mConstants.TIME_CONTROLLER_SKIP_NOT_READY_JOBS = false;
+        mConstants.SKIP_NOT_READY_JOBS = false;
         mTimeController.onConstantsUpdatedLocked();
 
         runTestMaybeStartTrackingJobLocked_DelayReverseOrder();
@@ -243,7 +244,7 @@
 
     @Test
     public void testMaybeStartTrackingJobLocked_DelayReverseOrder_WithSkipping_AllReady() {
-        mConstants.TIME_CONTROLLER_SKIP_NOT_READY_JOBS = true;
+        mConstants.SKIP_NOT_READY_JOBS = true;
         mTimeController.onConstantsUpdatedLocked();
 
         doReturn(true).when(mTimeController).wouldBeReadyWithConstraintLocked(any(), anyInt());
@@ -279,7 +280,7 @@
 
     @Test
     public void testMaybeStartTrackingJobLocked_DelayReverseOrder_WithSkipping_SomeNotReady() {
-        mConstants.TIME_CONTROLLER_SKIP_NOT_READY_JOBS = true;
+        mConstants.SKIP_NOT_READY_JOBS = true;
         mTimeController.onConstantsUpdatedLocked();
         final long now = JobSchedulerService.sElapsedRealtimeClock.millis();
 
@@ -315,7 +316,7 @@
 
     @Test
     public void testMaybeStartTrackingJobLocked_DeadlineInOrder_NoSkipping() {
-        mConstants.TIME_CONTROLLER_SKIP_NOT_READY_JOBS = false;
+        mConstants.SKIP_NOT_READY_JOBS = false;
         mTimeController.onConstantsUpdatedLocked();
 
         runTestMaybeStartTrackingJobLocked_DeadlineInOrder();
@@ -323,7 +324,7 @@
 
     @Test
     public void testMaybeStartTrackingJobLocked_DeadlineInOrder_WithSkipping_AllReady() {
-        mConstants.TIME_CONTROLLER_SKIP_NOT_READY_JOBS = true;
+        mConstants.SKIP_NOT_READY_JOBS = true;
         mTimeController.onConstantsUpdatedLocked();
 
         doReturn(true).when(mTimeController).wouldBeReadyWithConstraintLocked(any(), anyInt());
@@ -357,7 +358,7 @@
 
     @Test
     public void testMaybeStartTrackingJobLocked_DeadlineInOrder_WithSkipping_SomeNotReady() {
-        mConstants.TIME_CONTROLLER_SKIP_NOT_READY_JOBS = true;
+        mConstants.SKIP_NOT_READY_JOBS = true;
         mTimeController.onConstantsUpdatedLocked();
         final long now = JobSchedulerService.sElapsedRealtimeClock.millis();
 
@@ -391,7 +392,7 @@
 
     @Test
     public void testMaybeStartTrackingJobLocked_DeadlineReverseOrder_NoSkipping() {
-        mConstants.TIME_CONTROLLER_SKIP_NOT_READY_JOBS = false;
+        mConstants.SKIP_NOT_READY_JOBS = false;
         mTimeController.onConstantsUpdatedLocked();
 
         runTestMaybeStartTrackingJobLocked_DeadlineReverseOrder();
@@ -399,7 +400,7 @@
 
     @Test
     public void testMaybeStartTrackingJobLocked_DeadlineReverseOrder_WithSkipping_AllReady() {
-        mConstants.TIME_CONTROLLER_SKIP_NOT_READY_JOBS = true;
+        mConstants.SKIP_NOT_READY_JOBS = true;
         mTimeController.onConstantsUpdatedLocked();
 
         doReturn(true).when(mTimeController).wouldBeReadyWithConstraintLocked(any(), anyInt());
@@ -438,7 +439,7 @@
 
     @Test
     public void testMaybeStartTrackingJobLocked_DeadlineReverseOrder_WithSkipping_SomeNotReady() {
-        mConstants.TIME_CONTROLLER_SKIP_NOT_READY_JOBS = true;
+        mConstants.SKIP_NOT_READY_JOBS = true;
         mTimeController.onConstantsUpdatedLocked();
         final long now = JobSchedulerService.sElapsedRealtimeClock.millis();
 
@@ -493,8 +494,8 @@
                 .wouldBeReadyWithConstraintLocked(eq(jobEarliest), anyInt());
 
         // Starting off with the skipping off, we should still set an alarm for the earlier job.
-        mConstants.TIME_CONTROLLER_SKIP_NOT_READY_JOBS = false;
-        mTimeController.onConstantsUpdatedLocked();
+        mConstants.SKIP_NOT_READY_JOBS = false;
+        mTimeController.recheckAlarmsLocked();
         InOrder inOrder = inOrder(mAlarmManager);
 
         mTimeController.maybeStartTrackingJobLocked(jobEarliest, null);
@@ -504,16 +505,16 @@
                         eq(TAG_DEADLINE), any(), any(), any());
 
         // Turn it on, use alarm for later job.
-        mConstants.TIME_CONTROLLER_SKIP_NOT_READY_JOBS = true;
-        mTimeController.onConstantsUpdatedLocked();
+        mConstants.SKIP_NOT_READY_JOBS = true;
+        mTimeController.recheckAlarmsLocked();
 
         inOrder.verify(mAlarmManager, times(1))
                 .set(anyInt(), eq(now + HOUR_IN_MILLIS), anyLong(), anyLong(), eq(TAG_DEADLINE),
                         any(), any(), any());
 
         // Back off, use alarm for earlier job.
-        mConstants.TIME_CONTROLLER_SKIP_NOT_READY_JOBS = false;
-        mTimeController.onConstantsUpdatedLocked();
+        mConstants.SKIP_NOT_READY_JOBS = false;
+        mTimeController.recheckAlarmsLocked();
 
         inOrder.verify(mAlarmManager, times(1))
                 .set(anyInt(), eq(now + 5 * MINUTE_IN_MILLIS), anyLong(), anyLong(),
@@ -522,16 +523,16 @@
 
     @Test
     public void testCheckExpiredDelaysAndResetAlarm_NoSkipping() {
-        mConstants.TIME_CONTROLLER_SKIP_NOT_READY_JOBS = false;
-        mTimeController.onConstantsUpdatedLocked();
+        mConstants.SKIP_NOT_READY_JOBS = false;
+        mTimeController.recheckAlarmsLocked();
 
         runTestCheckExpiredDelaysAndResetAlarm();
     }
 
     @Test
     public void testCheckExpiredDelaysAndResetAlarm_WithSkipping_AllReady() {
-        mConstants.TIME_CONTROLLER_SKIP_NOT_READY_JOBS = true;
-        mTimeController.onConstantsUpdatedLocked();
+        mConstants.SKIP_NOT_READY_JOBS = true;
+        mTimeController.recheckAlarmsLocked();
 
         doReturn(true).when(mTimeController).wouldBeReadyWithConstraintLocked(any(), anyInt());
 
@@ -589,8 +590,8 @@
 
     @Test
     public void testCheckExpiredDelaysAndResetAlarm_WithSkipping_SomeNotReady() {
-        mConstants.TIME_CONTROLLER_SKIP_NOT_READY_JOBS = true;
-        mTimeController.onConstantsUpdatedLocked();
+        mConstants.SKIP_NOT_READY_JOBS = true;
+        mTimeController.recheckAlarmsLocked();
         final long now = JobSchedulerService.sElapsedRealtimeClock.millis();
 
         JobStatus jobLatest = createJobStatus("testCheckExpiredDelaysAndResetAlarm",
@@ -639,16 +640,16 @@
 
     @Test
     public void testCheckExpiredDeadlinesAndResetAlarm_NoSkipping() {
-        mConstants.TIME_CONTROLLER_SKIP_NOT_READY_JOBS = false;
-        mTimeController.onConstantsUpdatedLocked();
+        mConstants.SKIP_NOT_READY_JOBS = false;
+        mTimeController.recheckAlarmsLocked();
 
         runTestCheckExpiredDeadlinesAndResetAlarm();
     }
 
     @Test
     public void testCheckExpiredDeadlinesAndResetAlarm_WithSkipping_AllReady() {
-        mConstants.TIME_CONTROLLER_SKIP_NOT_READY_JOBS = true;
-        mTimeController.onConstantsUpdatedLocked();
+        mConstants.SKIP_NOT_READY_JOBS = true;
+        mTimeController.recheckAlarmsLocked();
 
         doReturn(true).when(mTimeController).wouldBeReadyWithConstraintLocked(any(), anyInt());
 
@@ -706,8 +707,8 @@
 
     @Test
     public void testCheckExpiredDeadlinesAndResetAlarm_WithSkipping_SomeNotReady() {
-        mConstants.TIME_CONTROLLER_SKIP_NOT_READY_JOBS = true;
-        mTimeController.onConstantsUpdatedLocked();
+        mConstants.SKIP_NOT_READY_JOBS = true;
+        mTimeController.recheckAlarmsLocked();
         final long now = JobSchedulerService.sElapsedRealtimeClock.millis();
 
         JobStatus jobLatest = createJobStatus("testCheckExpiredDeadlinesAndResetAlarm",
@@ -756,8 +757,8 @@
 
     @Test
     public void testEvaluateStateLocked_SkippingOff() {
-        mConstants.TIME_CONTROLLER_SKIP_NOT_READY_JOBS = false;
-        mTimeController.onConstantsUpdatedLocked();
+        mConstants.SKIP_NOT_READY_JOBS = false;
+        mTimeController.recheckAlarmsLocked();
         JobStatus job = createJobStatus("testEvaluateStateLocked_SkippingOff",
                 createJob().setOverrideDeadline(HOUR_IN_MILLIS));
 
@@ -768,8 +769,8 @@
 
     @Test
     public void testEvaluateStateLocked_SkippingOn_Delay() {
-        mConstants.TIME_CONTROLLER_SKIP_NOT_READY_JOBS = true;
-        mTimeController.onConstantsUpdatedLocked();
+        mConstants.SKIP_NOT_READY_JOBS = true;
+        mTimeController.recheckAlarmsLocked();
         final long now = JobSchedulerService.sElapsedRealtimeClock.millis();
 
         JobStatus jobLatest = createJobStatus("testEvaluateStateLocked_SkippingOn_Delay",
@@ -827,8 +828,8 @@
 
     @Test
     public void testEvaluateStateLocked_SkippingOn_Deadline() {
-        mConstants.TIME_CONTROLLER_SKIP_NOT_READY_JOBS = true;
-        mTimeController.onConstantsUpdatedLocked();
+        mConstants.SKIP_NOT_READY_JOBS = true;
+        mTimeController.recheckAlarmsLocked();
         final long now = JobSchedulerService.sElapsedRealtimeClock.millis();
 
         JobStatus jobLatest = createJobStatus("testEvaluateStateLocked_SkippingOn_Deadline",
diff --git a/services/tests/mockingservicestests/src/com/android/server/testables/TestableDeviceConfig.java b/services/tests/mockingservicestests/src/com/android/server/testables/TestableDeviceConfig.java
index eb90295..dae3d30 100644
--- a/services/tests/mockingservicestests/src/com/android/server/testables/TestableDeviceConfig.java
+++ b/services/tests/mockingservicestests/src/com/android/server/testables/TestableDeviceConfig.java
@@ -137,7 +137,6 @@
             return mKeyValueMap.get(getKey(namespace, name));
         }).when(() -> DeviceConfig.getProperty(anyString(), anyString()));
 
-
         return new TestWatcher() {
             @Override
             protected void succeeded(Description description) {
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDbTest.java b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDbTest.java
index bac8414..7a20af4 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDbTest.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDbTest.java
@@ -329,6 +329,31 @@
         assertEquals(serialNumber, mRecoverableKeyStoreDb.getUserSerialNumbers().get(userId));
     }
 
+    @Test
+    public void setPlatformKeyGenerationId_invalidatesExistingKeysForUser() {
+        int userId = 42;
+        int generationId = 110;
+        int uid = 1009;
+        int status = 120;
+        String alias = "test";
+        byte[] nonce = getUtf8Bytes("nonce");
+        byte[] keyMaterial = getUtf8Bytes("keymaterial");
+        byte[] keyMetadata = null;
+
+        WrappedKey wrappedKey =
+                new WrappedKey(nonce, keyMaterial, keyMetadata, generationId, status);
+        mRecoverableKeyStoreDb.insertKey(userId, uid, alias, wrappedKey);
+
+        WrappedKey retrievedKey = mRecoverableKeyStoreDb.getKey(uid, alias);
+        assertThat(retrievedKey.getRecoveryStatus()).isEqualTo(status);
+
+        mRecoverableKeyStoreDb.setPlatformKeyGenerationId(userId, generationId + 1);
+
+        retrievedKey = mRecoverableKeyStoreDb.getKey(uid, alias);
+        assertThat(retrievedKey.getRecoveryStatus())
+                .isEqualTo(RecoveryController.RECOVERY_STATUS_PERMANENT_FAILURE);
+    }
+
 
     @Test
     public void removeUserFromAllTables_removesData() throws Exception {
@@ -439,7 +464,7 @@
     }
 
     @Test
-    public void testInvalidateKeysWithOldGenerationId_withSingleKey() {
+    public void testInvalidateKeysForUser_withSingleKey() {
         int userId = 12;
         int uid = 1009;
         int generationId = 6;
@@ -458,7 +483,7 @@
         assertThat(retrievedKey.getRecoveryStatus()).isEqualTo(status);
 
         mRecoverableKeyStoreDb.setRecoveryStatus(uid, alias, status2);
-        mRecoverableKeyStoreDb.invalidateKeysWithOldGenerationId(userId, generationId + 1);
+        mRecoverableKeyStoreDb.invalidateKeysForUser(userId);
 
         retrievedKey = mRecoverableKeyStoreDb.getKey(uid, alias);
         assertThat(retrievedKey.getRecoveryStatus())
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
index cbca087..3ce1317 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -62,6 +62,7 @@
 import static org.mockito.Mockito.any;
 import static org.mockito.Mockito.anyInt;
 import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.doNothing;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.reset;
@@ -96,6 +97,7 @@
 import android.content.pm.IPackageManager;
 import android.content.pm.PackageManager;
 import android.content.pm.ParceledListSlice;
+import android.content.pm.UserInfo;
 import android.content.res.Resources;
 import android.graphics.Color;
 import android.graphics.drawable.Icon;
@@ -327,6 +329,8 @@
         LocalServices.removeServiceForTest(WindowManagerInternal.class);
         LocalServices.addService(WindowManagerInternal.class, mWindowManagerInternal);
 
+        doNothing().when(mContext).sendBroadcastAsUser(any(), any(), any());
+
         mService = new TestableNotificationManagerService(mContext);
 
         // Use this testable looper.
@@ -375,20 +379,16 @@
 
         when(mAssistants.isAdjustmentAllowed(anyString())).thenReturn(true);
 
-        try {
-            mService.init(mTestableLooper.getLooper(),
-                    mPackageManager, mPackageManagerClient, mockLightsManager,
-                    mListeners, mAssistants, mConditionProviders,
-                    mCompanionMgr, mSnoozeHelper, mUsageStats, mPolicyFile, mActivityManager,
-                    mGroupHelper, mAm, mAppUsageStats,
-                    mock(DevicePolicyManagerInternal.class), mUgm, mUgmInternal,
-                    mAppOpsManager, mUm);
-            mService.onBootPhase(SystemService.PHASE_SYSTEM_SERVICES_READY);
-        } catch (SecurityException e) {
-            if (!e.getMessage().contains("Permission Denial: not allowed to send broadcast")) {
-                throw e;
-            }
-        }
+
+        mService.init(mTestableLooper.getLooper(),
+                mPackageManager, mPackageManagerClient, mockLightsManager,
+                mListeners, mAssistants, mConditionProviders,
+                mCompanionMgr, mSnoozeHelper, mUsageStats, mPolicyFile, mActivityManager,
+                mGroupHelper, mAm, mAppUsageStats,
+                mock(DevicePolicyManagerInternal.class), mUgm, mUgmInternal,
+                mAppOpsManager, mUm);
+        mService.onBootPhase(SystemService.PHASE_SYSTEM_SERVICES_READY);
+
         mService.setAudioManager(mAudioManager);
 
         // Tests call directly into the Binder.
@@ -2080,14 +2080,8 @@
     public void testSetListenerAccessForUser() throws Exception {
         UserHandle user = UserHandle.of(10);
         ComponentName c = ComponentName.unflattenFromString("package/Component");
-        try {
-            mBinderService.setNotificationListenerAccessGrantedForUser(
-                    c, user.getIdentifier(), true);
-        } catch (SecurityException e) {
-            if (!e.getMessage().contains("Permission Denial: not allowed to send broadcast")) {
-                throw e;
-            }
-        }
+        mBinderService.setNotificationListenerAccessGrantedForUser(c, user.getIdentifier(), true);
+
 
         verify(mContext, times(1)).sendBroadcastAsUser(any(), eq(user), any());
         verify(mListeners, times(1)).setPackageOrComponentEnabled(
@@ -2101,15 +2095,14 @@
     @Test
     public void testSetAssistantAccessForUser() throws Exception {
         UserHandle user = UserHandle.of(10);
+        List<UserInfo> uis = new ArrayList<>();
+        UserInfo ui = new UserInfo();
+        ui.id = 10;
+        uis.add(ui);
         ComponentName c = ComponentName.unflattenFromString("package/Component");
-        try {
-            mBinderService.setNotificationAssistantAccessGrantedForUser(
-                    c, user.getIdentifier(), true);
-        } catch (SecurityException e) {
-            if (!e.getMessage().contains("Permission Denial: not allowed to send broadcast")) {
-                throw e;
-            }
-        }
+        when(mUm.getEnabledProfiles(10)).thenReturn(uis);
+
+        mBinderService.setNotificationAssistantAccessGrantedForUser(c, user.getIdentifier(), true);
 
         verify(mContext, times(1)).sendBroadcastAsUser(any(), eq(user), any());
         verify(mAssistants, times(1)).setPackageOrComponentEnabled(
@@ -2150,14 +2143,8 @@
     public void testSetDndAccessForUser() throws Exception {
         UserHandle user = UserHandle.of(10);
         ComponentName c = ComponentName.unflattenFromString("package/Component");
-        try {
-            mBinderService.setNotificationPolicyAccessGrantedForUser(
-                    c.getPackageName(), user.getIdentifier(), true);
-        } catch (SecurityException e) {
-            if (!e.getMessage().contains("Permission Denial: not allowed to send broadcast")) {
-                throw e;
-            }
-        }
+        mBinderService.setNotificationPolicyAccessGrantedForUser(
+                c.getPackageName(), user.getIdentifier(), true);
 
         verify(mContext, times(1)).sendBroadcastAsUser(any(), eq(user), any());
         verify(mConditionProviders, times(1)).setPackageOrComponentEnabled(
@@ -2171,13 +2158,7 @@
     @Test
     public void testSetListenerAccess() throws Exception {
         ComponentName c = ComponentName.unflattenFromString("package/Component");
-        try {
-            mBinderService.setNotificationListenerAccessGranted(c, true);
-        } catch (SecurityException e) {
-            if (!e.getMessage().contains("Permission Denial: not allowed to send broadcast")) {
-                throw e;
-            }
-        }
+        mBinderService.setNotificationListenerAccessGranted(c, true);
 
         verify(mListeners, times(1)).setPackageOrComponentEnabled(
                 c.flattenToString(), 0, true, true);
@@ -2189,14 +2170,14 @@
 
     @Test
     public void testSetAssistantAccess() throws Exception {
+        List<UserInfo> uis = new ArrayList<>();
+        UserInfo ui = new UserInfo();
+        ui.id = 0;
+        uis.add(ui);
+        when(mUm.getEnabledProfiles(ui.id)).thenReturn(uis);
         ComponentName c = ComponentName.unflattenFromString("package/Component");
-        try {
-            mBinderService.setNotificationAssistantAccessGranted(c, true);
-        } catch (SecurityException e) {
-            if (!e.getMessage().contains("Permission Denial: not allowed to send broadcast")) {
-                throw e;
-            }
-        }
+
+        mBinderService.setNotificationAssistantAccessGranted(c, true);
 
         verify(mAssistants, times(1)).setPackageOrComponentEnabled(
                 c.flattenToString(), 0, true, true);
@@ -2207,19 +2188,44 @@
     }
 
     @Test
+    public void testSetAssistantAccess_multiProfile() throws Exception {
+        List<UserInfo> uis = new ArrayList<>();
+        UserInfo ui = new UserInfo();
+        ui.id = 0;
+        uis.add(ui);
+        UserInfo ui10 = new UserInfo();
+        ui10.id = 10;
+        uis.add(ui10);
+        when(mUm.getEnabledProfiles(ui.id)).thenReturn(uis);
+        ComponentName c = ComponentName.unflattenFromString("package/Component");
+
+        mBinderService.setNotificationAssistantAccessGranted(c, true);
+
+        verify(mAssistants, times(1)).setPackageOrComponentEnabled(
+                c.flattenToString(), 0, true, true);
+        verify(mAssistants, times(1)).setPackageOrComponentEnabled(
+                c.flattenToString(), 10, true, true);
+        verify(mConditionProviders, times(1)).setPackageOrComponentEnabled(
+                c.flattenToString(), 0, false, true);
+        verify(mConditionProviders, times(1)).setPackageOrComponentEnabled(
+                c.flattenToString(), 10, false, true);
+        verify(mListeners, never()).setPackageOrComponentEnabled(
+                any(), anyInt(), anyBoolean(), anyBoolean());
+    }
+
+    @Test
     public void testSetAssistantAccess_nullWithAllowedAssistant() throws Exception {
         ArrayList<ComponentName> componentList = new ArrayList<>();
         ComponentName c = ComponentName.unflattenFromString("package/Component");
         componentList.add(c);
         when(mAssistants.getAllowedComponents(anyInt())).thenReturn(componentList);
+        List<UserInfo> uis = new ArrayList<>();
+        UserInfo ui = new UserInfo();
+        ui.id = 0;
+        uis.add(ui);
+        when(mUm.getEnabledProfiles(ui.id)).thenReturn(uis);
 
-        try {
-            mBinderService.setNotificationAssistantAccessGranted(null, true);
-        } catch (SecurityException e) {
-            if (!e.getMessage().contains("Permission Denial: not allowed to send broadcast")) {
-                throw e;
-            }
-        }
+        mBinderService.setNotificationAssistantAccessGranted(null, true);
 
         verify(mAssistants, times(1)).setPackageOrComponentEnabled(
                 c.flattenToString(), 0, true, false);
@@ -2231,23 +2237,23 @@
 
     @Test
     public void testSetAssistantAccessForUser_nullWithAllowedAssistant() throws Exception {
-        UserHandle user = UserHandle.of(10);
+        List<UserInfo> uis = new ArrayList<>();
+        UserInfo ui = new UserInfo();
+        ui.id = 10;
+        uis.add(ui);
+        UserHandle user = ui.getUserHandle();
         ArrayList<ComponentName> componentList = new ArrayList<>();
         ComponentName c = ComponentName.unflattenFromString("package/Component");
         componentList.add(c);
         when(mAssistants.getAllowedComponents(anyInt())).thenReturn(componentList);
+        when(mUm.getEnabledProfiles(10)).thenReturn(uis);
 
-        try {
-            mBinderService.setNotificationAssistantAccessGrantedForUser(
-                    null, user.getIdentifier(), true);
-        } catch (SecurityException e) {
-            if (!e.getMessage().contains("Permission Denial: not allowed to send broadcast")) {
-                throw e;
-            }
-        }
+        mBinderService.setNotificationAssistantAccessGrantedForUser(
+                null, user.getIdentifier(), true);
 
         verify(mAssistants, times(1)).setPackageOrComponentEnabled(
                 c.flattenToString(), user.getIdentifier(), true, false);
+        verify(mAssistants).setUserSet(10, true);
         verify(mConditionProviders, times(1)).setPackageOrComponentEnabled(
                 c.flattenToString(), user.getIdentifier(), false,  false);
         verify(mListeners, never()).setPackageOrComponentEnabled(
@@ -2255,15 +2261,44 @@
     }
 
     @Test
+    public void testSetAssistantAccessForUser_workProfile_nullWithAllowedAssistant()
+            throws Exception {
+        List<UserInfo> uis = new ArrayList<>();
+        UserInfo ui = new UserInfo();
+        ui.id = 0;
+        uis.add(ui);
+        UserInfo ui10 = new UserInfo();
+        ui10.id = 10;
+        uis.add(ui10);
+        UserHandle user = ui.getUserHandle();
+        ArrayList<ComponentName> componentList = new ArrayList<>();
+        ComponentName c = ComponentName.unflattenFromString("package/Component");
+        componentList.add(c);
+        when(mAssistants.getAllowedComponents(anyInt())).thenReturn(componentList);
+        when(mUm.getEnabledProfiles(ui.id)).thenReturn(uis);
+
+        mBinderService.setNotificationAssistantAccessGrantedForUser(
+                    null, user.getIdentifier(), true);
+
+        verify(mAssistants, times(1)).setPackageOrComponentEnabled(
+                c.flattenToString(), user.getIdentifier(), true, false);
+        verify(mAssistants, times(1)).setPackageOrComponentEnabled(
+                c.flattenToString(), ui10.id, true, false);
+        verify(mAssistants).setUserSet(0, true);
+        verify(mAssistants).setUserSet(10, true);
+        verify(mConditionProviders, times(1)).setPackageOrComponentEnabled(
+                c.flattenToString(), user.getIdentifier(), false,  false);
+        verify(mConditionProviders, times(1)).setPackageOrComponentEnabled(
+                c.flattenToString(), ui10.id, false,  false);
+        verify(mListeners, never()).setPackageOrComponentEnabled(
+                any(), anyInt(), anyBoolean(), anyBoolean());
+    }
+
+    @Test
     public void testSetDndAccess() throws Exception {
         ComponentName c = ComponentName.unflattenFromString("package/Component");
-        try {
-            mBinderService.setNotificationPolicyAccessGranted(c.getPackageName(), true);
-        } catch (SecurityException e) {
-            if (!e.getMessage().contains("Permission Denial: not allowed to send broadcast")) {
-                throw e;
-            }
-        }
+
+        mBinderService.setNotificationPolicyAccessGranted(c.getPackageName(), true);
 
         verify(mConditionProviders, times(1)).setPackageOrComponentEnabled(
                 c.getPackageName(), 0, true, true);
@@ -2291,6 +2326,12 @@
     public void testSetAssistantAccess_doesNothingOnLowRam() throws Exception {
         when(mActivityManager.isLowRamDevice()).thenReturn(true);
         ComponentName c = ComponentName.unflattenFromString("package/Component");
+        List<UserInfo> uis = new ArrayList<>();
+        UserInfo ui = new UserInfo();
+        ui.id = 0;
+        uis.add(ui);
+        when(mUm.getEnabledProfiles(ui.id)).thenReturn(uis);
+
         mBinderService.setNotificationAssistantAccessGranted(c, true);
 
         verify(mListeners, never()).setPackageOrComponentEnabled(
@@ -2320,13 +2361,8 @@
         when(mPackageManagerClient.hasSystemFeature(FEATURE_WATCH)).thenReturn(true);
         when(mActivityManager.isLowRamDevice()).thenReturn(true);
         ComponentName c = ComponentName.unflattenFromString("package/Component");
-        try {
-            mBinderService.setNotificationListenerAccessGranted(c, true);
-        } catch (SecurityException e) {
-            if (!e.getMessage().contains("Permission Denial: not allowed to send broadcast")) {
-                throw e;
-            }
-        }
+
+        mBinderService.setNotificationListenerAccessGranted(c, true);
 
         verify(mListeners, times(1)).setPackageOrComponentEnabled(
                 c.flattenToString(), 0, true, true);
@@ -2341,13 +2377,13 @@
         when(mPackageManagerClient.hasSystemFeature(FEATURE_WATCH)).thenReturn(true);
         when(mActivityManager.isLowRamDevice()).thenReturn(true);
         ComponentName c = ComponentName.unflattenFromString("package/Component");
-        try {
-            mBinderService.setNotificationAssistantAccessGranted(c, true);
-        } catch (SecurityException e) {
-            if (!e.getMessage().contains("Permission Denial: not allowed to send broadcast")) {
-                throw e;
-            }
-        }
+        List<UserInfo> uis = new ArrayList<>();
+        UserInfo ui = new UserInfo();
+        ui.id = 0;
+        uis.add(ui);
+        when(mUm.getEnabledProfiles(ui.id)).thenReturn(uis);
+
+        mBinderService.setNotificationAssistantAccessGranted(c, true);
 
         verify(mListeners, never()).setPackageOrComponentEnabled(
                 anyString(), anyInt(), anyBoolean(), anyBoolean());
@@ -2362,13 +2398,8 @@
         when(mPackageManagerClient.hasSystemFeature(FEATURE_WATCH)).thenReturn(true);
         when(mActivityManager.isLowRamDevice()).thenReturn(true);
         ComponentName c = ComponentName.unflattenFromString("package/Component");
-        try {
-            mBinderService.setNotificationPolicyAccessGranted(c.getPackageName(), true);
-        } catch (SecurityException e) {
-            if (!e.getMessage().contains("Permission Denial: not allowed to send broadcast")) {
-                throw e;
-            }
-        }
+
+        mBinderService.setNotificationPolicyAccessGranted(c.getPackageName(), true);
 
         verify(mListeners, never()).setPackageOrComponentEnabled(
                 anyString(), anyInt(), anyBoolean(), anyBoolean());
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java
index afd4ec1..2991dff 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java
@@ -41,6 +41,7 @@
 
 import android.app.ActivityManagerInternal;
 import android.app.ActivityOptions;
+import android.app.AppOpsManager;
 import android.app.IApplicationThread;
 import android.content.ComponentName;
 import android.content.Context;
@@ -413,12 +414,18 @@
         ActivityStackSupervisor mTestStackSupervisor;
 
         ActivityDisplay mDefaultDisplay;
+        AppOpsService mAppOpsService;
 
         TestActivityTaskManagerService(Context context) {
             super(context);
             spyOn(this);
 
             mUgmInternal = mock(UriGrantsManagerInternal.class);
+            mAppOpsService = mock(AppOpsService.class);
+
+            // Make sure permission checks aren't overridden.
+            doReturn(AppOpsManager.MODE_DEFAULT)
+                    .when(mAppOpsService).noteOperation(anyInt(), anyInt(), anyString());
 
             mSupportsMultiWindow = true;
             mSupportsMultiDisplay = true;
@@ -482,6 +489,11 @@
         }
 
         @Override
+        AppOpsService getAppOpsService() {
+            return mAppOpsService;
+        }
+
+        @Override
         void updateCpuStats() {
         }
 
diff --git a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java
index 0c2ce61..385748c 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java
@@ -21,6 +21,7 @@
 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
 import static android.view.Display.DEFAULT_DISPLAY;
 import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
+import static android.view.WindowManager.TRANSIT_ACTIVITY_CLOSE;
 
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.atLeast;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
@@ -166,6 +167,25 @@
         verify(mAnimationCallbacks).onAnimationFinished(REORDER_KEEP_IN_PLACE, true, false);
     }
 
+    @Test
+    public void testShouldAnimateWhenNoCancelWithDeferredScreenshot() {
+        mWm.setRecentsAnimationController(mController);
+        final AppWindowToken appWindow = createAppWindowToken(mDisplayContent,
+                WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
+        final WindowState win1 = createWindow(null, TYPE_BASE_APPLICATION, appWindow, "win1");
+        appWindow.addWindow(win1);
+        assertEquals(appWindow.getTask().getTopVisibleAppToken(), appWindow);
+        assertEquals(appWindow.findMainWindow(), win1);
+
+        mController.addAnimation(appWindow.getTask(), false /* isRecentTaskInvisible */);
+        assertTrue(mController.isAnimatingTask(appWindow.getTask()));
+
+        // Assume appWindow transition should animate when no
+        // IRecentsAnimationController#setCancelWithDeferredScreenshot called.
+        assertFalse(mController.shouldCancelWithDeferredScreenshot());
+        assertTrue(appWindow.shouldAnimate(TRANSIT_ACTIVITY_CLOSE));
+    }
+
     private static void verifyNoMoreInteractionsExceptAsBinder(IInterface binder) {
         verify(binder, atLeast(0)).asBinder();
         verifyNoMoreInteractions(binder);
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
index 715353e..98d055c 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
@@ -476,4 +476,21 @@
         root.prepareWindowToDisplayDuringRelayout(wasVisible /*wasVisible*/);
         verify(sPowerManagerWrapper).wakeUp(anyLong(), anyInt(), anyString());
     }
+
+    @Test
+    public void testVisibilityChangeSwitchUser() {
+        final WindowState window = createWindow(null, TYPE_APPLICATION, "app");
+        window.mHasSurface = true;
+        window.setShowToOwnerOnlyLocked(true);
+
+        mWm.mCurrentUserId = 1;
+        window.switchUser();
+        assertFalse(window.isVisible());
+        assertFalse(window.isVisibleByPolicy());
+
+        mWm.mCurrentUserId = 0;
+        window.switchUser();
+        assertTrue(window.isVisible());
+        assertTrue(window.isVisibleByPolicy());
+    }
 }
diff --git a/telephony/java/android/telephony/PhoneStateListener.java b/telephony/java/android/telephony/PhoneStateListener.java
index 373c5d2..3ce28a4 100644
--- a/telephony/java/android/telephony/PhoneStateListener.java
+++ b/telephony/java/android/telephony/PhoneStateListener.java
@@ -38,6 +38,8 @@
 import java.util.Map;
 import java.util.concurrent.Executor;
 
+import dalvik.system.VMRuntime;
+
 /**
  * A listener class for monitoring changes in specific telephony states
  * on the device, including service state, signal strength, message
@@ -400,8 +402,12 @@
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
     public PhoneStateListener(Integer subId) {
         this(subId, Looper.myLooper());
+        if (subId != null && VMRuntime.getRuntime().getTargetSdkVersion()
+                >= Build.VERSION_CODES.Q) {
+            throw new IllegalArgumentException("PhoneStateListener with subId: "
+                    + subId + " is not supported, use default constructor");
+        }
     }
-
     /**
      * Create a PhoneStateListener for the Phone using the specified subscription
      * and non-null Looper.
@@ -410,6 +416,11 @@
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
     public PhoneStateListener(Integer subId, Looper looper) {
         this(subId, new HandlerExecutor(new Handler(looper)));
+        if (subId != null && VMRuntime.getRuntime().getTargetSdkVersion()
+                >= Build.VERSION_CODES.Q) {
+            throw new IllegalArgumentException("PhoneStateListener with subId: "
+                    + subId + " is not supported, use default constructor");
+        }
     }
 
     /**
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 27fb1d0..4d8fa57 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -108,6 +108,8 @@
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
+import dalvik.system.VMRuntime;
+
 /**
  * Provides access to information about the telephony services on
  * the device. Applications can use the methods in this class to
@@ -4869,18 +4871,22 @@
      * Registers a listener object to receive notification of changes
      * in specified telephony states.
      * <p>
-     * To register a listener, pass a {@link PhoneStateListener}
-     * and specify at least one telephony state of interest in
-     * the events argument.
+     * To register a listener, pass a {@link PhoneStateListener} and specify at least one telephony
+     * state of interest in the events argument.
      *
-     * At registration, and when a specified telephony state
-     * changes, the telephony manager invokes the appropriate
-     * callback method on the listener object and passes the
-     * current (updated) values.
+     * At registration, and when a specified telephony state changes, the telephony manager invokes
+     * the appropriate callback method on the listener object and passes the current (updated)
+     * values.
      * <p>
-     * To unregister a listener, pass the listener object and set the
-     * events argument to
+     * To un-register a listener, pass the listener object and set the events argument to
      * {@link PhoneStateListener#LISTEN_NONE LISTEN_NONE} (0).
+     *
+     * If this TelephonyManager object has been created with {@link #createForSubscriptionId},
+     * applies to the given subId. Otherwise, applies to
+     * {@link SubscriptionManager#getDefaultSubscriptionId()}. To listen events for multiple subIds,
+     * pass a separate listener object to each TelephonyManager object created with
+     * {@link #createForSubscriptionId}.
+     *
      * Note: if you call this method while in the middle of a binder transaction, you <b>must</b>
      * call {@link android.os.Binder#clearCallingIdentity()} before calling this method. A
      * {@link SecurityException} will be thrown otherwise.
@@ -4895,17 +4901,26 @@
         if (mContext == null) return;
         try {
             boolean notifyNow = (getITelephony() != null);
-            // If the listener has not explicitly set the subId (for example, created with the
-            // default constructor), replace the subId so it will listen to the account the
-            // telephony manager is created with.
-            if (listener.mSubId == null) {
-                listener.mSubId = mSubId;
-            }
-
             ITelephonyRegistry registry = getTelephonyRegistry();
             if (registry != null) {
-                registry.listenForSubscriber(listener.mSubId, getOpPackageName(),
+                int subId;
+                // subId from phonestatelistner is deprecated Q on forward, use the subId from
+                // TelephonyManager instance.
+                if (VMRuntime.getRuntime().getTargetSdkVersion() >= Build.VERSION_CODES.Q
+                        || listener.mSubId == null) {
+                    subId = mSubId;
+                } else {
+                    subId = listener.mSubId;
+                }
+
+                registry.listenForSubscriber(subId, getOpPackageName(),
                         listener.callback, events, notifyNow);
+                // TODO: remove this once we remove PhoneStateListener constructor with subId.
+                if (events == PhoneStateListener.LISTEN_NONE) {
+                    listener.mSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
+                } else {
+                    listener.mSubId = subId;
+                }
             } else {
                 Rlog.w(TAG, "telephony registry not ready.");
             }
diff --git a/telephony/java/com/android/internal/telephony/TelephonyPermissions.java b/telephony/java/com/android/internal/telephony/TelephonyPermissions.java
index 12b20ef..64c1830 100644
--- a/telephony/java/com/android/internal/telephony/TelephonyPermissions.java
+++ b/telephony/java/com/android/internal/telephony/TelephonyPermissions.java
@@ -419,7 +419,8 @@
         // settings to individually disable the new restrictions for privileged, preloaded
         // non-privileged, and non-preinstalled apps.
         if (!isIdentifierCheckDisabled() && (
-                (!isPreinstalled && !relax3PDeviceIdentifierCheck)
+                (isPrivApp && !relaxPrivDeviceIdentifierCheck)
+                        || (!isPreinstalled && !relax3PDeviceIdentifierCheck)
                         || (isPreinstalled && !isPrivApp && !relaxNonPrivDeviceIdentifierCheck))) {
             // The current package should only be reported in StatsLog if it has not previously been
             // reported for the currently invoked device identifier method.
diff --git a/tests/net/common/Android.bp b/tests/net/common/Android.bp
index 3b2e34a..07525a6 100644
--- a/tests/net/common/Android.bp
+++ b/tests/net/common/Android.bp
@@ -23,6 +23,7 @@
         "androidx.test.rules",
         "frameworks-net-testutils",
         "junit",
+        "mockito-target-minus-junit4",
     ],
     libs: [
         "android.test.base.stubs",
diff --git a/tests/net/common/java/android/net/CaptivePortalTest.java b/tests/net/common/java/android/net/CaptivePortalTest.java
new file mode 100644
index 0000000..eed7159f
--- /dev/null
+++ b/tests/net/common/java/android/net/CaptivePortalTest.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net;
+
+import static org.junit.Assert.assertEquals;
+
+import android.os.RemoteException;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class CaptivePortalTest {
+    private static final int DEFAULT_TIMEOUT_MS = 5000;
+    private static final String TEST_PACKAGE_NAME = "com.google.android.test";
+
+    private final class MyCaptivePortalImpl extends ICaptivePortal.Stub {
+        int mCode = -1;
+        String mPackageName = null;
+
+        @Override
+        public void appResponse(final int response) throws RemoteException {
+            mCode = response;
+        }
+
+        @Override
+        public void logEvent(int eventId, String packageName) throws RemoteException {
+            mCode = eventId;
+            mPackageName = packageName;
+        }
+    }
+
+    private interface TestFunctor {
+        void useCaptivePortal(CaptivePortal o);
+    }
+
+    private MyCaptivePortalImpl runCaptivePortalTest(TestFunctor f) {
+        final MyCaptivePortalImpl cp = new MyCaptivePortalImpl();
+        f.useCaptivePortal(new CaptivePortal(cp.asBinder()));
+        return cp;
+    }
+
+    @Test
+    public void testReportCaptivePortalDismissed() {
+        final MyCaptivePortalImpl result =
+                runCaptivePortalTest(c -> c.reportCaptivePortalDismissed());
+        assertEquals(result.mCode, CaptivePortal.APP_RETURN_DISMISSED);
+    }
+
+    @Test
+    public void testIgnoreNetwork() {
+        final MyCaptivePortalImpl result = runCaptivePortalTest(c -> c.ignoreNetwork());
+        assertEquals(result.mCode, CaptivePortal.APP_RETURN_UNWANTED);
+    }
+
+    @Test
+    public void testUseNetwork() {
+        final MyCaptivePortalImpl result = runCaptivePortalTest(c -> c.useNetwork());
+        assertEquals(result.mCode, CaptivePortal.APP_RETURN_WANTED_AS_IS);
+    }
+
+    @Test
+    public void testLogEvent() {
+        final MyCaptivePortalImpl result = runCaptivePortalTest(c -> c.logEvent(
+                MetricsEvent.ACTION_CAPTIVE_PORTAL_LOGIN_ACTIVITY,
+                TEST_PACKAGE_NAME));
+        assertEquals(result.mCode, MetricsEvent.ACTION_CAPTIVE_PORTAL_LOGIN_ACTIVITY);
+        assertEquals(result.mPackageName, TEST_PACKAGE_NAME);
+    }
+}
diff --git a/tests/net/common/java/android/net/metrics/ApfProgramEventTest.kt b/tests/net/common/java/android/net/metrics/ApfProgramEventTest.kt
new file mode 100644
index 0000000..8d055c9
--- /dev/null
+++ b/tests/net/common/java/android/net/metrics/ApfProgramEventTest.kt
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.metrics;
+
+import android.os.Parcelable
+import androidx.test.filters.SmallTest
+import androidx.test.runner.AndroidJUnit4
+import com.android.internal.util.ParcelableTestUtil
+import com.android.internal.util.TestUtils
+import org.junit.Assert.assertEquals
+import org.junit.Assert.assertFalse
+import org.junit.Assert.assertTrue
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+@SmallTest
+class ApfProgramEventTest {
+    private fun <T: Parcelable> testParcel(obj: T, fieldCount: Int) {
+        ParcelableTestUtil.assertFieldCountEquals(fieldCount, obj::class.java)
+        TestUtils.assertParcelingIsLossless(obj)
+    }
+
+    private infix fun Int.hasFlag(flag: Int) = (this and (1 shl flag)) != 0
+
+    @Test
+    fun testBuilderAndParcel() {
+        val apfProgramEvent = ApfProgramEvent.Builder()
+                .setLifetime(1)
+                .setActualLifetime(2)
+                .setFilteredRas(3)
+                .setCurrentRas(4)
+                .setProgramLength(5)
+                .setFlags(true, true)
+                .build()
+
+        assertEquals(1, apfProgramEvent.lifetime)
+        assertEquals(2, apfProgramEvent.actualLifetime)
+        assertEquals(3, apfProgramEvent.filteredRas)
+        assertEquals(4, apfProgramEvent.currentRas)
+        assertEquals(5, apfProgramEvent.programLength)
+        assertEquals(ApfProgramEvent.flagsFor(true, true), apfProgramEvent.flags)
+
+        testParcel(apfProgramEvent, 6)
+    }
+
+    @Test
+    fun testFlagsFor() {
+        var flags = ApfProgramEvent.flagsFor(false, false)
+        assertFalse(flags hasFlag ApfProgramEvent.FLAG_HAS_IPV4_ADDRESS)
+        assertFalse(flags hasFlag ApfProgramEvent.FLAG_MULTICAST_FILTER_ON)
+
+        flags = ApfProgramEvent.flagsFor(true, false)
+        assertTrue(flags hasFlag ApfProgramEvent.FLAG_HAS_IPV4_ADDRESS)
+        assertFalse(flags hasFlag ApfProgramEvent.FLAG_MULTICAST_FILTER_ON)
+
+        flags = ApfProgramEvent.flagsFor(false, true)
+        assertFalse(flags hasFlag ApfProgramEvent.FLAG_HAS_IPV4_ADDRESS)
+        assertTrue(flags hasFlag ApfProgramEvent.FLAG_MULTICAST_FILTER_ON)
+
+        flags = ApfProgramEvent.flagsFor(true, true)
+        assertTrue(flags hasFlag ApfProgramEvent.FLAG_HAS_IPV4_ADDRESS)
+        assertTrue(flags hasFlag ApfProgramEvent.FLAG_MULTICAST_FILTER_ON)
+    }
+}
diff --git a/tests/net/common/java/android/net/metrics/ApfStatsTest.kt b/tests/net/common/java/android/net/metrics/ApfStatsTest.kt
new file mode 100644
index 0000000..f8eb40c
--- /dev/null
+++ b/tests/net/common/java/android/net/metrics/ApfStatsTest.kt
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.metrics
+
+import android.os.Parcelable
+import androidx.test.filters.SmallTest
+import androidx.test.runner.AndroidJUnit4
+import com.android.internal.util.ParcelableTestUtil
+import com.android.internal.util.TestUtils
+import org.junit.Assert.assertEquals
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+@SmallTest
+class ApfStatsTest {
+    private fun <T: Parcelable> testParcel(obj: T, fieldCount: Int) {
+        ParcelableTestUtil.assertFieldCountEquals(fieldCount, obj::class.java)
+        TestUtils.assertParcelingIsLossless(obj)
+    }
+
+    @Test
+    fun testBuilderAndParcel() {
+        val apfStats = ApfStats.Builder()
+                .setDurationMs(Long.MAX_VALUE)
+                .setReceivedRas(1)
+                .setMatchingRas(2)
+                .setDroppedRas(3)
+                .setZeroLifetimeRas(4)
+                .setParseErrors(5)
+                .setProgramUpdates(6)
+                .setProgramUpdatesAll(7)
+                .setProgramUpdatesAllowingMulticast(8)
+                .setMaxProgramSize(9)
+                .build()
+
+        assertEquals(Long.MAX_VALUE, apfStats.durationMs)
+        assertEquals(1, apfStats.receivedRas)
+        assertEquals(2, apfStats.matchingRas)
+        assertEquals(3, apfStats.droppedRas)
+        assertEquals(4, apfStats.zeroLifetimeRas)
+        assertEquals(5, apfStats.parseErrors)
+        assertEquals(6, apfStats.programUpdates)
+        assertEquals(7, apfStats.programUpdatesAll)
+        assertEquals(8, apfStats.programUpdatesAllowingMulticast)
+        assertEquals(9, apfStats.maxProgramSize)
+
+        testParcel(apfStats, 10)
+    }
+}
diff --git a/tests/net/common/java/android/net/metrics/DhcpClientEventTest.kt b/tests/net/common/java/android/net/metrics/DhcpClientEventTest.kt
new file mode 100644
index 0000000..36e9f8c
--- /dev/null
+++ b/tests/net/common/java/android/net/metrics/DhcpClientEventTest.kt
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.metrics
+
+import android.os.Parcelable
+import androidx.test.filters.SmallTest
+import androidx.test.runner.AndroidJUnit4
+import com.android.internal.util.ParcelableTestUtil
+import com.android.internal.util.TestUtils
+import org.junit.Assert.assertEquals
+import org.junit.Test
+import org.junit.runner.RunWith
+
+private const val FAKE_MESSAGE = "test"
+
+@RunWith(AndroidJUnit4::class)
+@SmallTest
+class DhcpClientEventTest {
+    private fun <T: Parcelable> testParcel(obj: T, fieldCount: Int) {
+        ParcelableTestUtil.assertFieldCountEquals(fieldCount, obj::class.java)
+        TestUtils.assertParcelingIsLossless(obj)
+    }
+
+    @Test
+    fun testBuilderAndParcel() {
+        val dhcpClientEvent = DhcpClientEvent.Builder()
+                .setMsg(FAKE_MESSAGE)
+                .setDurationMs(Integer.MAX_VALUE)
+                .build()
+
+        assertEquals(FAKE_MESSAGE, dhcpClientEvent.msg)
+        assertEquals(Integer.MAX_VALUE, dhcpClientEvent.durationMs)
+
+        testParcel(dhcpClientEvent, 2)
+    }
+}
diff --git a/tests/net/common/java/android/net/metrics/DhcpErrorEventTest.kt b/tests/net/common/java/android/net/metrics/DhcpErrorEventTest.kt
index e191953..e9d5e6d 100644
--- a/tests/net/common/java/android/net/metrics/DhcpErrorEventTest.kt
+++ b/tests/net/common/java/android/net/metrics/DhcpErrorEventTest.kt
@@ -13,9 +13,7 @@
 import org.junit.runner.RunWith
 
 private const val TEST_ERROR_CODE = 12345
-/**
- * DHCP Optional Type: DHCP Subnet Mask (Copy from DhcpPacket.java)
- */
+//DHCP Optional Type: DHCP Subnet Mask (Copy from DhcpPacket.java due to it's protected)
 private const val DHCP_SUBNET_MASK = 1
 
 @RunWith(AndroidJUnit4::class)
diff --git a/tests/net/common/java/android/net/metrics/IpConnectivityLogTest.java b/tests/net/common/java/android/net/metrics/IpConnectivityLogTest.java
new file mode 100644
index 0000000..d4780d3
--- /dev/null
+++ b/tests/net/common/java/android/net/metrics/IpConnectivityLogTest.java
@@ -0,0 +1,161 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.metrics;
+
+import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
+import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.timeout;
+import static org.mockito.Mockito.verify;
+
+import android.net.ConnectivityMetricsEvent;
+import android.net.IIpConnectivityMetrics;
+import android.net.Network;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.internal.util.BitUtils;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.Iterator;
+import java.util.List;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class IpConnectivityLogTest {
+    private static final int FAKE_NET_ID = 100;
+    private static final int[] FAKE_TRANSPORT_TYPES = BitUtils.unpackBits(TRANSPORT_WIFI);
+    private static final long FAKE_TIME_STAMP = System.currentTimeMillis();
+    private static final String FAKE_INTERFACE_NAME = "test";
+    private static final IpReachabilityEvent FAKE_EV =
+            new IpReachabilityEvent(IpReachabilityEvent.NUD_FAILED);
+
+    @Mock IIpConnectivityMetrics mMockService;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+    }
+
+    @Test
+    public void testLoggingEvents() throws Exception {
+        IpConnectivityLog logger = new IpConnectivityLog(mMockService);
+
+        assertTrue(logger.log(FAKE_EV));
+        assertTrue(logger.log(FAKE_TIME_STAMP, FAKE_EV));
+        assertTrue(logger.log(FAKE_NET_ID, FAKE_TRANSPORT_TYPES, FAKE_EV));
+        assertTrue(logger.log(new Network(FAKE_NET_ID), FAKE_TRANSPORT_TYPES, FAKE_EV));
+        assertTrue(logger.log(FAKE_INTERFACE_NAME, FAKE_EV));
+        assertTrue(logger.log(makeExpectedEvent(FAKE_TIME_STAMP, FAKE_NET_ID, TRANSPORT_WIFI,
+                FAKE_INTERFACE_NAME)));
+
+        List<ConnectivityMetricsEvent> got = verifyEvents(6);
+        assertEventsEqual(makeExpectedEvent(got.get(0).timestamp, 0, 0, null), got.get(0));
+        assertEventsEqual(makeExpectedEvent(FAKE_TIME_STAMP, 0, 0, null), got.get(1));
+        assertEventsEqual(makeExpectedEvent(got.get(2).timestamp, FAKE_NET_ID,
+                TRANSPORT_WIFI, null), got.get(2));
+        assertEventsEqual(makeExpectedEvent(got.get(3).timestamp, FAKE_NET_ID,
+                TRANSPORT_WIFI, null), got.get(3));
+        assertEventsEqual(makeExpectedEvent(got.get(4).timestamp, 0, 0, FAKE_INTERFACE_NAME),
+                got.get(4));
+        assertEventsEqual(makeExpectedEvent(FAKE_TIME_STAMP, FAKE_NET_ID,
+                TRANSPORT_WIFI, FAKE_INTERFACE_NAME), got.get(5));
+    }
+
+    @Test
+    public void testLoggingEventsWithMultipleCallers() throws Exception {
+        IpConnectivityLog logger = new IpConnectivityLog(mMockService);
+
+        final int nCallers = 10;
+        final int nEvents = 10;
+        for (int n = 0; n < nCallers; n++) {
+            final int i = n;
+            new Thread() {
+                public void run() {
+                    for (int j = 0; j < nEvents; j++) {
+                        assertTrue(logger.log(makeExpectedEvent(
+                                FAKE_TIME_STAMP + i * 100 + j,
+                                FAKE_NET_ID + i * 100 + j,
+                                ((i + j) % 2 == 0) ? TRANSPORT_WIFI : TRANSPORT_CELLULAR,
+                                FAKE_INTERFACE_NAME)));
+                    }
+                }
+            }.start();
+        }
+
+        List<ConnectivityMetricsEvent> got = verifyEvents(nCallers * nEvents, 200);
+        Collections.sort(got, EVENT_COMPARATOR);
+        Iterator<ConnectivityMetricsEvent> iter = got.iterator();
+        for (int i = 0; i < nCallers; i++) {
+            for (int j = 0; j < nEvents; j++) {
+                final long expectedTimestamp = FAKE_TIME_STAMP + i * 100 + j;
+                final int expectedNetId = FAKE_NET_ID + i * 100 + j;
+                final long expectedTransports =
+                        ((i + j) % 2 == 0) ? TRANSPORT_WIFI : TRANSPORT_CELLULAR;
+                assertEventsEqual(makeExpectedEvent(expectedTimestamp, expectedNetId,
+                        expectedTransports, FAKE_INTERFACE_NAME), iter.next());
+            }
+        }
+    }
+
+    private List<ConnectivityMetricsEvent> verifyEvents(int n, int timeoutMs) throws Exception {
+        ArgumentCaptor<ConnectivityMetricsEvent> captor =
+                ArgumentCaptor.forClass(ConnectivityMetricsEvent.class);
+        verify(mMockService, timeout(timeoutMs).times(n)).logEvent(captor.capture());
+        return captor.getAllValues();
+    }
+
+    private List<ConnectivityMetricsEvent> verifyEvents(int n) throws Exception {
+        return verifyEvents(n, 10);
+    }
+
+
+    private ConnectivityMetricsEvent makeExpectedEvent(long timestamp, int netId, long transports,
+            String ifname) {
+        ConnectivityMetricsEvent ev = new ConnectivityMetricsEvent();
+        ev.timestamp = timestamp;
+        ev.data = FAKE_EV;
+        ev.netId = netId;
+        ev.transports = transports;
+        ev.ifname = ifname;
+        return ev;
+    }
+
+    /** Outer equality for ConnectivityMetricsEvent to avoid overriding equals() and hashCode(). */
+    private void assertEventsEqual(ConnectivityMetricsEvent expected,
+            ConnectivityMetricsEvent got) {
+        assertEquals(expected.data, got.data);
+        assertEquals(expected.timestamp, got.timestamp);
+        assertEquals(expected.netId, got.netId);
+        assertEquals(expected.transports, got.transports);
+        assertEquals(expected.ifname, got.ifname);
+    }
+
+    static final Comparator<ConnectivityMetricsEvent> EVENT_COMPARATOR =
+            Comparator.comparingLong((ev) -> ev.timestamp);
+}
diff --git a/tests/net/common/java/android/net/metrics/IpManagerEventTest.kt b/tests/net/common/java/android/net/metrics/IpManagerEventTest.kt
new file mode 100644
index 0000000..5144ca5
--- /dev/null
+++ b/tests/net/common/java/android/net/metrics/IpManagerEventTest.kt
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.metrics
+
+import android.os.Parcelable
+import androidx.test.filters.SmallTest
+import androidx.test.runner.AndroidJUnit4
+import com.android.internal.util.ParcelableTestUtil
+import com.android.internal.util.TestUtils
+import org.junit.Assert.assertEquals
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+@SmallTest
+class IpManagerEventTest {
+    private fun <T: Parcelable> testParcel(obj: T, fieldCount: Int) {
+        ParcelableTestUtil.assertFieldCountEquals(fieldCount, obj::class.java)
+        TestUtils.assertParcelingIsLossless(obj)
+    }
+
+    @Test
+    fun testConstructorAndParcel() {
+        (IpManagerEvent.PROVISIONING_OK..IpManagerEvent.ERROR_INTERFACE_NOT_FOUND).forEach {
+            val ipManagerEvent = IpManagerEvent(it, Long.MAX_VALUE)
+            assertEquals(it, ipManagerEvent.eventType)
+            assertEquals(Long.MAX_VALUE, ipManagerEvent.durationMs)
+
+            testParcel(ipManagerEvent, 2)
+        }
+    }
+}
diff --git a/tests/net/common/java/android/net/metrics/IpReachabilityEventTest.kt b/tests/net/common/java/android/net/metrics/IpReachabilityEventTest.kt
new file mode 100644
index 0000000..d76ebf6
--- /dev/null
+++ b/tests/net/common/java/android/net/metrics/IpReachabilityEventTest.kt
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.metrics
+
+import android.os.Parcelable
+import androidx.test.filters.SmallTest
+import androidx.test.runner.AndroidJUnit4
+import com.android.internal.util.ParcelableTestUtil
+import com.android.internal.util.TestUtils
+import org.junit.Assert.assertEquals
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+@SmallTest
+class IpReachabilityEventTest {
+    private fun <T: Parcelable> testParcel(obj: T, fieldCount: Int) {
+        ParcelableTestUtil.assertFieldCountEquals(fieldCount, obj::class.java)
+        TestUtils.assertParcelingIsLossless(obj)
+    }
+
+    @Test
+    fun testConstructorAndParcel() {
+        (IpReachabilityEvent.PROBE..IpReachabilityEvent.PROVISIONING_LOST_ORGANIC).forEach {
+            val ipReachabilityEvent = IpReachabilityEvent(it)
+            assertEquals(it, ipReachabilityEvent.eventType)
+
+            testParcel(ipReachabilityEvent, 1)
+        }
+    }
+}
diff --git a/tests/net/common/java/android/net/metrics/NetworkEventTest.kt b/tests/net/common/java/android/net/metrics/NetworkEventTest.kt
new file mode 100644
index 0000000..8b52e81
--- /dev/null
+++ b/tests/net/common/java/android/net/metrics/NetworkEventTest.kt
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.metrics
+
+import android.os.Parcelable
+import androidx.test.filters.SmallTest
+import androidx.test.runner.AndroidJUnit4
+import com.android.internal.util.ParcelableTestUtil
+import com.android.internal.util.TestUtils
+import org.junit.Assert.assertEquals
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+@SmallTest
+class NetworkEventTest {
+    private fun <T: Parcelable> testParcel(obj: T, fieldCount: Int) {
+        ParcelableTestUtil.assertFieldCountEquals(fieldCount, obj::class.java)
+        TestUtils.assertParcelingIsLossless(obj)
+    }
+
+    @Test
+    fun testConstructorAndParcel() {
+        (NetworkEvent.NETWORK_CONNECTED..NetworkEvent.NETWORK_PARTIAL_CONNECTIVITY).forEach {
+            var networkEvent = NetworkEvent(it)
+            assertEquals(it, networkEvent.eventType)
+            assertEquals(0, networkEvent.durationMs)
+
+            networkEvent = NetworkEvent(it, Long.MAX_VALUE)
+            assertEquals(it, networkEvent.eventType)
+            assertEquals(Long.MAX_VALUE, networkEvent.durationMs)
+
+            testParcel(networkEvent, 2)
+        }
+    }
+}
diff --git a/tests/net/common/java/android/net/metrics/RaEventTest.kt b/tests/net/common/java/android/net/metrics/RaEventTest.kt
new file mode 100644
index 0000000..f38d328
--- /dev/null
+++ b/tests/net/common/java/android/net/metrics/RaEventTest.kt
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.metrics
+
+import android.os.Parcelable
+import androidx.test.filters.SmallTest
+import androidx.test.runner.AndroidJUnit4
+import com.android.internal.util.ParcelableTestUtil
+import com.android.internal.util.TestUtils
+import org.junit.Assert.assertEquals
+import org.junit.Test
+import org.junit.runner.RunWith
+
+private const val NO_LIFETIME: Long = -1L
+
+@RunWith(AndroidJUnit4::class)
+@SmallTest
+class RaEventTest {
+    private fun <T: Parcelable> testParcel(obj: T, fieldCount: Int) {
+        ParcelableTestUtil.assertFieldCountEquals(fieldCount, obj::class.java)
+        TestUtils.assertParcelingIsLossless(obj)
+    }
+
+    @Test
+    fun testConstructorAndParcel() {
+        var raEvent = RaEvent.Builder().build()
+        assertEquals(NO_LIFETIME, raEvent.routerLifetime)
+        assertEquals(NO_LIFETIME, raEvent.prefixValidLifetime)
+        assertEquals(NO_LIFETIME, raEvent.prefixPreferredLifetime)
+        assertEquals(NO_LIFETIME, raEvent.routeInfoLifetime)
+        assertEquals(NO_LIFETIME, raEvent.rdnssLifetime)
+        assertEquals(NO_LIFETIME, raEvent.dnsslLifetime)
+
+        raEvent = RaEvent.Builder()
+                .updateRouterLifetime(1)
+                .updatePrefixValidLifetime(2)
+                .updatePrefixPreferredLifetime(3)
+                .updateRouteInfoLifetime(4)
+                .updateRdnssLifetime(5)
+                .updateDnsslLifetime(6)
+                .build()
+        assertEquals(1, raEvent.routerLifetime)
+        assertEquals(2, raEvent.prefixValidLifetime)
+        assertEquals(3, raEvent.prefixPreferredLifetime)
+        assertEquals(4, raEvent.routeInfoLifetime)
+        assertEquals(5, raEvent.rdnssLifetime)
+        assertEquals(6, raEvent.dnsslLifetime)
+
+        raEvent = RaEvent.Builder()
+                .updateRouterLifetime(Long.MIN_VALUE)
+                .updateRouterLifetime(Long.MAX_VALUE)
+                .build()
+        assertEquals(Long.MIN_VALUE, raEvent.routerLifetime)
+
+        raEvent = RaEvent(1, 2, 3, 4, 5, 6)
+        assertEquals(1, raEvent.routerLifetime)
+        assertEquals(2, raEvent.prefixValidLifetime)
+        assertEquals(3, raEvent.prefixPreferredLifetime)
+        assertEquals(4, raEvent.routeInfoLifetime)
+        assertEquals(5, raEvent.rdnssLifetime)
+        assertEquals(6, raEvent.dnsslLifetime)
+
+        testParcel(raEvent, 6)
+    }
+}
diff --git a/tests/net/common/java/android/net/metrics/ValidationProbeEventTest.kt b/tests/net/common/java/android/net/metrics/ValidationProbeEventTest.kt
new file mode 100644
index 0000000..c0cef8f
--- /dev/null
+++ b/tests/net/common/java/android/net/metrics/ValidationProbeEventTest.kt
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.metrics
+
+import android.os.Parcelable
+import androidx.test.filters.SmallTest
+import androidx.test.runner.AndroidJUnit4
+import com.android.internal.util.ParcelableTestUtil
+import com.android.internal.util.TestUtils
+import java.lang.reflect.Modifier
+import org.junit.Assert.assertEquals
+import org.junit.Assert.assertTrue
+import org.junit.Test
+import org.junit.runner.RunWith
+
+private const val FIRST_VALIDATION: Int = 1 shl 8
+private const val REVALIDATION: Int = 2 shl 8
+
+@RunWith(AndroidJUnit4::class)
+@SmallTest
+class ValidationProbeEventTest {
+    private fun <T: Parcelable> testParcel(obj: T, fieldCount: Int) {
+        ParcelableTestUtil.assertFieldCountEquals(fieldCount, obj::class.java)
+        TestUtils.assertParcelingIsLossless(obj)
+    }
+
+    private infix fun Int.hasType(type: Int) = (type and this) == type
+
+    @Test
+    fun testBuilderAndParcel() {
+        var validationProbeEvent = ValidationProbeEvent.Builder()
+                .setProbeType(ValidationProbeEvent.PROBE_DNS, false).build()
+
+        assertTrue(validationProbeEvent.probeType hasType REVALIDATION)
+
+        validationProbeEvent = ValidationProbeEvent.Builder()
+                .setDurationMs(Long.MAX_VALUE)
+                .setProbeType(ValidationProbeEvent.PROBE_DNS, true)
+                .setReturnCode(ValidationProbeEvent.DNS_SUCCESS)
+                .build()
+
+        assertEquals(Long.MAX_VALUE, validationProbeEvent.durationMs)
+        assertTrue(validationProbeEvent.probeType hasType ValidationProbeEvent.PROBE_DNS)
+        assertTrue(validationProbeEvent.probeType hasType FIRST_VALIDATION)
+        assertEquals(ValidationProbeEvent.DNS_SUCCESS, validationProbeEvent.returnCode)
+
+        testParcel(validationProbeEvent, 3)
+    }
+
+    @Test
+    fun testGetProbeName() {
+        val probeFields = ValidationProbeEvent::class.java.declaredFields.filter {
+            it.type == Int::class.javaPrimitiveType
+              && Modifier.isPublic(it.modifiers) && Modifier.isStatic(it.modifiers)
+              && it.name.contains("PROBE")
+        }
+
+        probeFields.forEach {
+            val intValue = it.getInt(null)
+            val stringValue = ValidationProbeEvent.getProbeName(intValue)
+            assertEquals(it.name, stringValue)
+        }
+
+    }
+}
diff --git a/tests/net/java/com/android/server/connectivity/IpConnectivityMetricsTest.java b/tests/net/java/com/android/server/connectivity/IpConnectivityMetricsTest.java
index d5b2c87..3a07166 100644
--- a/tests/net/java/com/android/server/connectivity/IpConnectivityMetricsTest.java
+++ b/tests/net/java/com/android/server/connectivity/IpConnectivityMetricsTest.java
@@ -21,11 +21,8 @@
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.timeout;
-import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
 import android.content.Context;
@@ -59,16 +56,11 @@
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
-import org.mockito.ArgumentCaptor;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
 import java.io.PrintWriter;
 import java.io.StringWriter;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.Iterator;
-import java.util.List;
 
 @RunWith(AndroidJUnit4.class)
 @SmallTest
@@ -98,48 +90,6 @@
     }
 
     @Test
-    public void testLoggingEvents() throws Exception {
-        IpConnectivityLog logger = new IpConnectivityLog(mMockService);
-
-        assertTrue(logger.log(1, FAKE_EV));
-        assertTrue(logger.log(2, FAKE_EV));
-        assertTrue(logger.log(3, FAKE_EV));
-
-        List<ConnectivityMetricsEvent> got = verifyEvents(3);
-        assertEventsEqual(expectedEvent(1), got.get(0));
-        assertEventsEqual(expectedEvent(2), got.get(1));
-        assertEventsEqual(expectedEvent(3), got.get(2));
-    }
-
-    @Test
-    public void testLoggingEventsWithMultipleCallers() throws Exception {
-        IpConnectivityLog logger = new IpConnectivityLog(mMockService);
-
-        final int nCallers = 10;
-        final int nEvents = 10;
-        for (int n = 0; n < nCallers; n++) {
-            final int i = n;
-            new Thread() {
-                public void run() {
-                    for (int j = 0; j < nEvents; j++) {
-                        assertTrue(logger.log(1 + i * 100 + j, FAKE_EV));
-                    }
-                }
-            }.start();
-        }
-
-        List<ConnectivityMetricsEvent> got = verifyEvents(nCallers * nEvents, 200);
-        Collections.sort(got, EVENT_COMPARATOR);
-        Iterator<ConnectivityMetricsEvent> iter = got.iterator();
-        for (int i = 0; i < nCallers; i++) {
-            for (int j = 0; j < nEvents; j++) {
-                int expectedTimestamp = 1 + i * 100 + j;
-                assertEventsEqual(expectedEvent(expectedTimestamp), iter.next());
-            }
-        }
-    }
-
-    @Test
     public void testBufferFlushing() {
         String output1 = getdump("flush");
         assertEquals("", output1);
@@ -653,16 +603,7 @@
         return nai;
     }
 
-    List<ConnectivityMetricsEvent> verifyEvents(int n, int timeoutMs) throws Exception {
-        ArgumentCaptor<ConnectivityMetricsEvent> captor =
-                ArgumentCaptor.forClass(ConnectivityMetricsEvent.class);
-        verify(mMockService, timeout(timeoutMs).times(n)).logEvent(captor.capture());
-        return captor.getAllValues();
-    }
 
-    List<ConnectivityMetricsEvent> verifyEvents(int n) throws Exception {
-        return verifyEvents(n, 10);
-    }
 
     static void verifySerialization(String want, String output) {
         try {
@@ -674,28 +615,4 @@
             fail(e.toString());
         }
     }
-
-    static String joinLines(String ... elems) {
-        StringBuilder b = new StringBuilder();
-        for (String s : elems) {
-            b.append(s).append("\n");
-        }
-        return b.toString();
-    }
-
-    static ConnectivityMetricsEvent expectedEvent(int timestamp) {
-        ConnectivityMetricsEvent ev = new ConnectivityMetricsEvent();
-        ev.timestamp = timestamp;
-        ev.data = FAKE_EV;
-        return ev;
-    }
-
-    /** Outer equality for ConnectivityMetricsEvent to avoid overriding equals() and hashCode(). */
-    static void assertEventsEqual(ConnectivityMetricsEvent expected, ConnectivityMetricsEvent got) {
-        assertEquals(expected.timestamp, got.timestamp);
-        assertEquals(expected.data, got.data);
-    }
-
-    static final Comparator<ConnectivityMetricsEvent> EVENT_COMPARATOR =
-        Comparator.comparingLong((ev) -> ev.timestamp);
 }