Merge "startop: Only do perfetto trace when needed."
diff --git a/Android.bp b/Android.bp
index a106168..1eba3e2 100644
--- a/Android.bp
+++ b/Android.bp
@@ -953,6 +953,7 @@
         "test-base/src/**/*.java",
         ":opt-telephony-srcs",
         ":opt-net-voip-srcs",
+        ":core-current-stubs-source",
         ":core_public_api_files",
         ":updatable-media-srcs",
         "test-mock/src/**/*.java",
@@ -1017,6 +1018,7 @@
         "core/java/**/*.logtags",
         ":opt-telephony-srcs",
         ":opt-net-voip-srcs",
+        ":core-current-stubs-source",
         ":core_public_api_files",
         ":updatable-media-srcs",
         ":jobscheduler-framework-source",
@@ -1570,3 +1572,14 @@
     ],
 }
 
+// Avoid including Parcelable classes as we don't want to have two copies of
+// Parcelable cross the process.
+filegroup {
+    name: "framework-cellbroadcast-shared-srcs",
+    srcs: [
+        "core/java/android/util/LocalLog.java",
+        "core/java/android/util/Slog.java",
+        "core/java/com/android/internal/util/State.java",
+        "core/java/com/android/internal/util/StateMachine.java",
+    ],
+}
\ No newline at end of file
diff --git a/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java b/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java
index d6661c2..3dc9a28 100644
--- a/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java
+++ b/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java
@@ -48,7 +48,6 @@
 import android.os.Binder;
 import android.os.Bundle;
 import android.os.Environment;
-import android.os.FileUtils;
 import android.os.Handler;
 import android.os.IDeviceIdleController;
 import android.os.Looper;
@@ -3597,9 +3596,6 @@
             try {
                 stream = mConfigFile.startWrite();
                 memStream.writeTo(stream);
-                stream.flush();
-                FileUtils.sync(stream);
-                stream.close();
                 mConfigFile.finishWrite(stream);
             } catch (IOException e) {
                 Slog.w(TAG, "Error writing config file", e);
diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
index 329d4b7..7273ea7 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
@@ -109,6 +109,9 @@
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
 import java.time.Clock;
+import java.time.Instant;
+import java.time.ZoneId;
+import java.time.ZoneOffset;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
@@ -144,10 +147,53 @@
 
     @VisibleForTesting
     public static Clock sSystemClock = Clock.systemUTC();
+
+    private abstract static class MySimpleClock extends Clock {
+        private final ZoneId mZoneId;
+
+        MySimpleClock(ZoneId zoneId) {
+            this.mZoneId = zoneId;
+        }
+
+        @Override
+        public ZoneId getZone() {
+            return mZoneId;
+        }
+
+        @Override
+        public Clock withZone(ZoneId zone) {
+            return new MySimpleClock(zone) {
+                @Override
+                public long millis() {
+                    return MySimpleClock.this.millis();
+                }
+            };
+        }
+
+        @Override
+        public abstract long millis();
+
+        @Override
+        public Instant instant() {
+            return Instant.ofEpochMilli(millis());
+        }
+    }
+
     @VisibleForTesting
-    public static Clock sUptimeMillisClock = SystemClock.uptimeClock();
+    public static Clock sUptimeMillisClock = new MySimpleClock(ZoneOffset.UTC) {
+        @Override
+        public long millis() {
+            return SystemClock.uptimeMillis();
+        }
+    };
+
     @VisibleForTesting
-    public static Clock sElapsedRealtimeClock = SystemClock.elapsedRealtimeClock();
+    public static Clock sElapsedRealtimeClock =  new MySimpleClock(ZoneOffset.UTC) {
+        @Override
+        public long millis() {
+            return SystemClock.elapsedRealtime();
+        }
+    };
 
     /** Global local for all job scheduler state. */
     final Object mLock = new Object();
@@ -2126,7 +2172,7 @@
                     job.getServiceComponent(), PackageManager.MATCH_DEBUG_TRIAGED_MISSING,
                     job.getUserId());
         } catch (RemoteException e) {
-            throw e.rethrowAsRuntimeException();
+            throw new RuntimeException(e);
         }
 
         if (service == null) {
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java
index adb4314..c76346f 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java
@@ -28,7 +28,7 @@
 import android.net.Uri;
 import android.os.RemoteException;
 import android.os.UserHandle;
-import android.text.format.TimeMigrationUtils;
+import android.text.format.DateFormat;
 import android.util.ArraySet;
 import android.util.Pair;
 import android.util.Slog;
@@ -1518,7 +1518,7 @@
             if (job.getClipData() != null) {
                 pw.print(prefix); pw.print("  Clip data: ");
                 StringBuilder b = new StringBuilder(128);
-                job.getClipData().toShortString(b);
+                b.append(job.getClipData());
                 pw.println(b);
             }
             if (uriPerms != null) {
@@ -1659,14 +1659,18 @@
         }
         if (mLastSuccessfulRunTime != 0) {
             pw.print(prefix); pw.print("Last successful run: ");
-            pw.println(TimeMigrationUtils.formatMillisWithFixedFormat(mLastSuccessfulRunTime));
+            pw.println(formatTime(mLastSuccessfulRunTime));
         }
         if (mLastFailedRunTime != 0) {
             pw.print(prefix); pw.print("Last failed run: ");
-            pw.println(TimeMigrationUtils.formatMillisWithFixedFormat(mLastFailedRunTime));
+            pw.println(formatTime(mLastFailedRunTime));
         }
     }
 
+    private static CharSequence formatTime(long time) {
+        return DateFormat.format("yyyy-MM-dd HH:mm:ss", time);
+    }
+
     public void dump(ProtoOutputStream proto, long fieldId, boolean full, long elapsedRealtimeMillis) {
         final long token = proto.start(fieldId);
 
diff --git a/api/current.txt b/api/current.txt
index 8733aab..e6e2b40 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -25387,6 +25387,7 @@
     method public android.graphics.Bitmap getFrameAtIndex(int, @NonNull android.media.MediaMetadataRetriever.BitmapParams);
     method public android.graphics.Bitmap getFrameAtIndex(int);
     method public android.graphics.Bitmap getFrameAtTime(long, int);
+    method public android.graphics.Bitmap getFrameAtTime(long, int, @NonNull android.media.MediaMetadataRetriever.BitmapParams);
     method public android.graphics.Bitmap getFrameAtTime(long);
     method public android.graphics.Bitmap getFrameAtTime();
     method @NonNull public java.util.List<android.graphics.Bitmap> getFramesAtIndex(int, int, @NonNull android.media.MediaMetadataRetriever.BitmapParams);
@@ -25396,6 +25397,7 @@
     method public android.graphics.Bitmap getPrimaryImage(@NonNull android.media.MediaMetadataRetriever.BitmapParams);
     method public android.graphics.Bitmap getPrimaryImage();
     method public android.graphics.Bitmap getScaledFrameAtTime(long, int, int, int);
+    method public android.graphics.Bitmap getScaledFrameAtTime(long, int, int, int, @NonNull android.media.MediaMetadataRetriever.BitmapParams);
     method public void release();
     method public void setDataSource(String) throws java.lang.IllegalArgumentException;
     method public void setDataSource(String, java.util.Map<java.lang.String,java.lang.String>) throws java.lang.IllegalArgumentException;
@@ -28809,6 +28811,7 @@
     method @Nullable public String getPrivateDnsServerName();
     method @NonNull public java.util.List<android.net.RouteInfo> getRoutes();
     method public boolean isPrivateDnsActive();
+    method public boolean isWakeOnLanSupported();
     method public void setDnsServers(@NonNull java.util.Collection<java.net.InetAddress>);
     method public void setDomains(@Nullable String);
     method public void setHttpProxy(@Nullable android.net.ProxyInfo);
@@ -45169,6 +45172,7 @@
     method @RequiresPermission(android.Manifest.permission.CALL_PHONE) public void sendUssdRequest(String, android.telephony.TelephonyManager.UssdResponseCallback, android.os.Handler);
     method public void sendVisualVoicemailSms(String, int, String, android.app.PendingIntent);
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setDataEnabled(boolean);
+    method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public int setForbiddenPlmns(@NonNull java.util.List<java.lang.String>);
     method public boolean setLine1NumberForDisplay(String, String);
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setNetworkSelectionModeAutomatic();
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean setNetworkSelectionModeManual(String, boolean);
@@ -47912,6 +47916,7 @@
     ctor public ArraySet(int);
     ctor public ArraySet(android.util.ArraySet<E>);
     ctor public ArraySet(java.util.Collection<? extends E>);
+    ctor public ArraySet(@Nullable E[]);
     method public boolean add(E);
     method public void addAll(android.util.ArraySet<? extends E>);
     method public boolean addAll(java.util.Collection<? extends E>);
diff --git a/api/system-current.txt b/api/system-current.txt
index 8ed79a3..4db7f4a 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -6557,7 +6557,8 @@
     method public abstract int onDeleteSubscription(int, String);
     method public android.service.euicc.DownloadSubscriptionResult onDownloadSubscription(int, @NonNull android.telephony.euicc.DownloadableSubscription, boolean, boolean, @Nullable android.os.Bundle);
     method @Deprecated public int onDownloadSubscription(int, @NonNull android.telephony.euicc.DownloadableSubscription, boolean, boolean);
-    method public abstract int onEraseSubscriptions(int);
+    method @Deprecated public abstract int onEraseSubscriptions(int);
+    method public int onEraseSubscriptionsWithOptions(int, @android.telephony.euicc.EuiccCardManager.ResetOption int);
     method public abstract android.service.euicc.GetDefaultDownloadableSubscriptionListResult onGetDefaultDownloadableSubscriptionList(int, boolean);
     method public abstract android.service.euicc.GetDownloadableSubscriptionMetadataResult onGetDownloadableSubscriptionMetadata(int, android.telephony.euicc.DownloadableSubscription, boolean);
     method public abstract String onGetEid(int);
@@ -8620,7 +8621,8 @@
 
   public class EuiccManager {
     method @RequiresPermission(android.Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS) public void continueOperation(android.content.Intent, android.os.Bundle);
-    method @RequiresPermission(android.Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS) public void eraseSubscriptions(android.app.PendingIntent);
+    method @Deprecated @RequiresPermission(android.Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS) public void eraseSubscriptions(@NonNull android.app.PendingIntent);
+    method @RequiresPermission(android.Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS) public void eraseSubscriptionsWithOptions(@android.telephony.euicc.EuiccCardManager.ResetOption int, @NonNull android.app.PendingIntent);
     method @RequiresPermission(android.Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS) public void getDefaultDownloadableSubscriptionList(android.app.PendingIntent);
     method @RequiresPermission(android.Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS) public void getDownloadableSubscriptionMetadata(android.telephony.euicc.DownloadableSubscription, android.app.PendingIntent);
     method @RequiresPermission(android.Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS) public int getOtaStatus();
diff --git a/cmds/statsd/src/metrics/CountMetricProducer.cpp b/cmds/statsd/src/metrics/CountMetricProducer.cpp
index 1d0d2fb..b5c8e35 100644
--- a/cmds/statsd/src/metrics/CountMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/CountMetricProducer.cpp
@@ -37,6 +37,7 @@
 using std::string;
 using std::unordered_map;
 using std::vector;
+using std::shared_ptr;
 
 namespace android {
 namespace os {
@@ -67,8 +68,13 @@
 CountMetricProducer::CountMetricProducer(const ConfigKey& key, const CountMetric& metric,
                                          const int conditionIndex,
                                          const sp<ConditionWizard>& wizard,
-                                         const int64_t timeBaseNs, const int64_t startTimeNs)
-    : MetricProducer(metric.id(), key, timeBaseNs, conditionIndex, wizard) {
+                                         const int64_t timeBaseNs, const int64_t startTimeNs,
+                                         const unordered_map<int, shared_ptr<Activation>>&
+                                                 eventActivationMap,
+                                         const unordered_map<int, vector<shared_ptr<Activation>>>&
+                                                 eventDeactivationMap)
+    : MetricProducer(metric.id(), key, timeBaseNs, conditionIndex, wizard, eventActivationMap,
+                     eventDeactivationMap) {
     if (metric.has_bucket()) {
         mBucketSizeNs =
                 TimeUnitToBucketSizeInMillisGuardrailed(key.GetUid(), metric.bucket()) * 1000000;
diff --git a/cmds/statsd/src/metrics/CountMetricProducer.h b/cmds/statsd/src/metrics/CountMetricProducer.h
index b4a910c..61913c7 100644
--- a/cmds/statsd/src/metrics/CountMetricProducer.h
+++ b/cmds/statsd/src/metrics/CountMetricProducer.h
@@ -42,7 +42,11 @@
 public:
     CountMetricProducer(const ConfigKey& key, const CountMetric& countMetric,
                         const int conditionIndex, const sp<ConditionWizard>& wizard,
-                        const int64_t timeBaseNs, const int64_t startTimeNs);
+                        const int64_t timeBaseNs, const int64_t startTimeNs,
+                        const std::unordered_map<int, std::shared_ptr<Activation>>&
+                                eventActivationMap = {},
+                        const std::unordered_map<int, std::vector<std::shared_ptr<Activation>>>&
+                                eventDeactivationMap = {});
 
     virtual ~CountMetricProducer();
 
diff --git a/cmds/statsd/src/metrics/DurationMetricProducer.cpp b/cmds/statsd/src/metrics/DurationMetricProducer.cpp
index d7b46d1..31b90f3 100644
--- a/cmds/statsd/src/metrics/DurationMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/DurationMetricProducer.cpp
@@ -36,6 +36,7 @@
 using std::string;
 using std::unordered_map;
 using std::vector;
+using std::shared_ptr;
 
 namespace android {
 namespace os {
@@ -62,14 +63,15 @@
 const int FIELD_ID_START_BUCKET_ELAPSED_MILLIS = 5;
 const int FIELD_ID_END_BUCKET_ELAPSED_MILLIS = 6;
 
-DurationMetricProducer::DurationMetricProducer(const ConfigKey& key, const DurationMetric& metric,
-                                               const int conditionIndex, const size_t startIndex,
-                                               const size_t stopIndex, const size_t stopAllIndex,
-                                               const bool nesting,
-                                               const sp<ConditionWizard>& wizard,
-                                               const FieldMatcher& internalDimensions,
-                                               const int64_t timeBaseNs, const int64_t startTimeNs)
-    : MetricProducer(metric.id(), key, timeBaseNs, conditionIndex, wizard),
+DurationMetricProducer::DurationMetricProducer(
+        const ConfigKey& key, const DurationMetric& metric, const int conditionIndex,
+        const size_t startIndex, const size_t stopIndex, const size_t stopAllIndex,
+        const bool nesting, const sp<ConditionWizard>& wizard,
+        const FieldMatcher& internalDimensions, const int64_t timeBaseNs, const int64_t startTimeNs,
+        const unordered_map<int, shared_ptr<Activation>>& eventActivationMap,
+        const unordered_map<int, vector<shared_ptr<Activation>>>& eventDeactivationMap)
+    : MetricProducer(metric.id(), key, timeBaseNs, conditionIndex, wizard, eventActivationMap,
+                     eventDeactivationMap),
       mAggregationType(metric.aggregation_type()),
       mStartIndex(startIndex),
       mStopIndex(stopIndex),
diff --git a/cmds/statsd/src/metrics/DurationMetricProducer.h b/cmds/statsd/src/metrics/DurationMetricProducer.h
index 56c9fd6..0592b18 100644
--- a/cmds/statsd/src/metrics/DurationMetricProducer.h
+++ b/cmds/statsd/src/metrics/DurationMetricProducer.h
@@ -42,7 +42,12 @@
                            const int conditionIndex, const size_t startIndex,
                            const size_t stopIndex, const size_t stopAllIndex, const bool nesting,
                            const sp<ConditionWizard>& wizard,
-                           const FieldMatcher& internalDimensions, const int64_t timeBaseNs, const int64_t startTimeNs);
+                           const FieldMatcher& internalDimensions, const int64_t timeBaseNs,
+                           const int64_t startTimeNs,
+                           const unordered_map<int, shared_ptr<Activation>>&
+                                   eventActivationMap = {},
+                           const unordered_map<int, vector<shared_ptr<Activation>>>&
+                                   eventDeactivationMap = {});
 
     virtual ~DurationMetricProducer();
 
diff --git a/cmds/statsd/src/metrics/EventMetricProducer.cpp b/cmds/statsd/src/metrics/EventMetricProducer.cpp
index 96133bd..a60a916 100644
--- a/cmds/statsd/src/metrics/EventMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/EventMetricProducer.cpp
@@ -36,6 +36,7 @@
 using std::string;
 using std::unordered_map;
 using std::vector;
+using std::shared_ptr;
 
 namespace android {
 namespace os {
@@ -54,8 +55,13 @@
 EventMetricProducer::EventMetricProducer(const ConfigKey& key, const EventMetric& metric,
                                          const int conditionIndex,
                                          const sp<ConditionWizard>& wizard,
-                                         const int64_t startTimeNs)
-    : MetricProducer(metric.id(), key, startTimeNs, conditionIndex, wizard) {
+                                         const int64_t startTimeNs,
+                                         const unordered_map<int, shared_ptr<Activation>>&
+                                                 eventActivationMap,
+                                         const unordered_map<int, vector<shared_ptr<Activation>>>&
+                                                 eventDeactivationMap)
+    : MetricProducer(metric.id(), key, startTimeNs, conditionIndex, wizard, eventActivationMap,
+                     eventDeactivationMap) {
     if (metric.links().size() > 0) {
         for (const auto& link : metric.links()) {
             Metric2Condition mc;
diff --git a/cmds/statsd/src/metrics/EventMetricProducer.h b/cmds/statsd/src/metrics/EventMetricProducer.h
index 74e6bc8..aab53c8 100644
--- a/cmds/statsd/src/metrics/EventMetricProducer.h
+++ b/cmds/statsd/src/metrics/EventMetricProducer.h
@@ -35,7 +35,11 @@
 public:
     EventMetricProducer(const ConfigKey& key, const EventMetric& eventMetric,
                         const int conditionIndex, const sp<ConditionWizard>& wizard,
-                        const int64_t startTimeNs);
+                        const int64_t startTimeNs,
+                        const std::unordered_map<int, std::shared_ptr<Activation>>&
+                                eventActivationMap = {},
+                        const std::unordered_map<int, std::vector<std::shared_ptr<Activation>>>&
+                                eventDeactivationMap = {});
 
     virtual ~EventMetricProducer();
 
diff --git a/cmds/statsd/src/metrics/GaugeMetricProducer.cpp b/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
index efd05dc..e409b6fb 100644
--- a/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
@@ -72,8 +72,11 @@
         const sp<ConditionWizard>& wizard, const int whatMatcherIndex,
         const sp<EventMatcherWizard>& matcherWizard, const int pullTagId, const int triggerAtomId,
         const int atomId, const int64_t timeBaseNs, const int64_t startTimeNs,
-        const sp<StatsPullerManager>& pullerManager)
-    : MetricProducer(metric.id(), key, timeBaseNs, conditionIndex, wizard),
+        const sp<StatsPullerManager>& pullerManager,
+        const unordered_map<int, shared_ptr<Activation>>& eventActivationMap,
+        const unordered_map<int, vector<shared_ptr<Activation>>>& eventDeactivationMap)
+    : MetricProducer(metric.id(), key, timeBaseNs, conditionIndex, wizard, eventActivationMap,
+            eventDeactivationMap),
       mWhatMatcherIndex(whatMatcherIndex),
       mEventMatcherWizard(matcherWizard),
       mPullerManager(pullerManager),
@@ -133,8 +136,11 @@
                                          mBucketSizeNs);
     }
 
-    // Adjust start for partial bucket
+    // Adjust start for partial first bucket and then pull if needed
     mCurrentBucketStartTimeNs = startTimeNs;
+    if (mIsActive && mIsPulled && mSamplingType == GaugeMetric::RANDOM_ONE_SAMPLE) {
+        pullAndMatchEventsLocked(mCurrentBucketStartTimeNs);
+    }
 
     VLOG("Gauge metric %lld created. bucket size %lld start_time: %lld sliced %d",
          (long long)metric.id(), (long long)mBucketSizeNs, (long long)mTimeBaseNs,
@@ -295,11 +301,6 @@
     }
 }
 
-void GaugeMetricProducer::prepareFirstBucketLocked() {
-    if (mIsActive && mIsPulled && mSamplingType == GaugeMetric::RANDOM_ONE_SAMPLE) {
-        pullAndMatchEventsLocked(mCurrentBucketStartTimeNs);
-    }
-}
 
 void GaugeMetricProducer::pullAndMatchEventsLocked(const int64_t timestampNs) {
     bool triggerPuller = false;
diff --git a/cmds/statsd/src/metrics/GaugeMetricProducer.h b/cmds/statsd/src/metrics/GaugeMetricProducer.h
index a612adf..dfe1d56 100644
--- a/cmds/statsd/src/metrics/GaugeMetricProducer.h
+++ b/cmds/statsd/src/metrics/GaugeMetricProducer.h
@@ -58,11 +58,14 @@
 public:
     GaugeMetricProducer(const ConfigKey& key, const GaugeMetric& gaugeMetric,
                         const int conditionIndex, const sp<ConditionWizard>& conditionWizard,
-                        const int whatMatcherIndex,
-                        const sp<EventMatcherWizard>& matcherWizard,
+                        const int whatMatcherIndex,const sp<EventMatcherWizard>& matcherWizard,
                         const int pullTagId, const int triggerAtomId, const int atomId,
                         const int64_t timeBaseNs, const int64_t startTimeNs,
-                        const sp<StatsPullerManager>& pullerManager);
+                        const sp<StatsPullerManager>& pullerManager,
+                        const std::unordered_map<int, std::shared_ptr<Activation>>&
+                                eventActivationMap = {},
+                        const std::unordered_map<int, std::vector<std::shared_ptr<Activation>>>&
+                                eventDeactivationMap = {});
 
     virtual ~GaugeMetricProducer();
 
@@ -125,8 +128,6 @@
     void flushCurrentBucketLocked(const int64_t& eventTimeNs,
                                   const int64_t& nextBucketStartTimeNs) override;
 
-    void prepareFirstBucketLocked() override;
-
     void pullAndMatchEventsLocked(const int64_t timestampNs);
 
     const int mWhatMatcherIndex;
diff --git a/cmds/statsd/src/metrics/MetricProducer.cpp b/cmds/statsd/src/metrics/MetricProducer.cpp
index 1ab4fdf..3426a19 100644
--- a/cmds/statsd/src/metrics/MetricProducer.cpp
+++ b/cmds/statsd/src/metrics/MetricProducer.cpp
@@ -40,6 +40,29 @@
 const int FIELD_ID_ACTIVE_EVENT_ACTIVATION_REMAINING_TTL_NANOS = 2;
 const int FIELD_ID_ACTIVE_EVENT_ACTIVATION_STATE = 3;
 
+MetricProducer::MetricProducer(
+        const int64_t& metricId, const ConfigKey& key, const int64_t timeBaseNs,
+        const int conditionIndex, const sp<ConditionWizard>& wizard,
+        const std::unordered_map<int, std::shared_ptr<Activation>>& eventActivationMap,
+        const std::unordered_map<int, std::vector<std::shared_ptr<Activation>>>&
+                eventDeactivationMap)
+        : mMetricId(metricId),
+          mConfigKey(key),
+          mTimeBaseNs(timeBaseNs),
+          mCurrentBucketStartTimeNs(timeBaseNs),
+          mCurrentBucketNum(0),
+          mCondition(initialCondition(conditionIndex)),
+          mConditionTrackerIndex(conditionIndex),
+          mConditionSliced(false),
+          mWizard(wizard),
+          mContainANYPositionInDimensionsInWhat(false),
+          mSliceByPositionALL(false),
+          mHasLinksToAllConditionDimensionsInTracker(false),
+          mEventActivationMap(eventActivationMap),
+          mEventDeactivationMap(eventDeactivationMap),
+          mIsActive(mEventActivationMap.empty()) {
+    }
+
 void MetricProducer::onMatchedLogEventLocked(const size_t matcherIndex, const LogEvent& event) {
     if (!mIsActive) {
         return;
@@ -97,24 +120,6 @@
     }
 }
 
-void MetricProducer::addActivation(int activationTrackerIndex, const ActivationType& activationType,
-        int64_t ttl_seconds, int deactivationTrackerIndex) {
-    std::lock_guard<std::mutex> lock(mMutex);
-    // When a metric producer does not depend on any activation, its mIsActive is true.
-    // Therefore, if this is the 1st activation, mIsActive will turn to false. Otherwise it does not
-    // change.
-    if  (mEventActivationMap.empty()) {
-        mIsActive = false;
-    }
-    std::shared_ptr<Activation> activation =
-            std::make_shared<Activation>(activationType, ttl_seconds * NS_PER_SEC);
-    mEventActivationMap.emplace(activationTrackerIndex, activation);
-    if (-1 != deactivationTrackerIndex) {
-        auto& deactivationList = mEventDeactivationMap[deactivationTrackerIndex];
-        deactivationList.push_back(activation);
-    }
-}
-
 void MetricProducer::activateLocked(int activationTrackerIndex, int64_t elapsedTimestampNs) {
     auto it = mEventActivationMap.find(activationTrackerIndex);
     if (it == mEventActivationMap.end()) {
diff --git a/cmds/statsd/src/metrics/MetricProducer.h b/cmds/statsd/src/metrics/MetricProducer.h
index fdbdc83..1e1eb69 100644
--- a/cmds/statsd/src/metrics/MetricProducer.h
+++ b/cmds/statsd/src/metrics/MetricProducer.h
@@ -69,6 +69,19 @@
     NO_TIME_CONSTRAINTS = 2
 };
 
+struct Activation {
+    Activation(const ActivationType& activationType, const int64_t ttlNs)
+        : ttl_ns(ttlNs),
+          start_ns(0),
+          state(ActivationState::kNotActive),
+          activationType(activationType) {}
+
+    const int64_t ttl_ns;
+    int64_t start_ns;
+    ActivationState state;
+    const ActivationType activationType;
+};
+
 // A MetricProducer is responsible for compute one single metrics, creating stats log report, and
 // writing the report to dropbox. MetricProducers should respond to package changes as required in
 // PackageInfoListener, but if none of the metrics are slicing by package name, then the update can
@@ -76,21 +89,10 @@
 class MetricProducer : public virtual PackageInfoListener {
 public:
     MetricProducer(const int64_t& metricId, const ConfigKey& key, const int64_t timeBaseNs,
-                   const int conditionIndex, const sp<ConditionWizard>& wizard)
-        : mMetricId(metricId),
-          mConfigKey(key),
-          mTimeBaseNs(timeBaseNs),
-          mCurrentBucketStartTimeNs(timeBaseNs),
-          mCurrentBucketNum(0),
-          mCondition(initialCondition(conditionIndex)),
-          mConditionTrackerIndex(conditionIndex),
-          mConditionSliced(false),
-          mWizard(wizard),
-          mContainANYPositionInDimensionsInWhat(false),
-          mSliceByPositionALL(false),
-          mHasLinksToAllConditionDimensionsInTracker(false),
-          mIsActive(true) {
-    }
+                   const int conditionIndex, const sp<ConditionWizard>& wizard,
+                   const std::unordered_map<int, std::shared_ptr<Activation>>& eventActivationMap,
+                   const std::unordered_map<int, std::vector<std::shared_ptr<Activation>>>&
+                           eventDeactivationMap);
 
     virtual ~MetricProducer(){};
 
@@ -188,11 +190,6 @@
         dropDataLocked(dropTimeNs);
     }
 
-    void prepareFirstBucket() {
-        std::lock_guard<std::mutex> lock(mMutex);
-        prepareFirstBucketLocked();
-    }
-
     void loadActiveMetric(const ActiveMetric& activeMetric, int64_t currentTimeNs) {
         std::lock_guard<std::mutex> lock(mMutex);
         loadActiveMetricLocked(activeMetric, currentTimeNs);
@@ -215,9 +212,6 @@
 
     void flushIfExpire(int64_t elapsedTimestampNs);
 
-    void addActivation(int activationTrackerIndex, const ActivationType& activationType,
-            int64_t ttl_seconds, int deactivationTrackerIndex = -1);
-
     void writeActiveMetricToProtoOutputStream(
             int64_t currentTimeNs, const DumpReportReason reason, ProtoOutputStream* proto);
 
@@ -310,7 +304,6 @@
     virtual size_t byteSizeLocked() const = 0;
     virtual void dumpStatesLocked(FILE* out, bool verbose) const = 0;
     virtual void dropDataLocked(const int64_t dropTimeNs) = 0;
-    virtual void prepareFirstBucketLocked() {};
     void loadActiveMetricLocked(const ActiveMetric& activeMetric, int64_t currentTimeNs);
     void activateLocked(int activationTrackerIndex, int64_t elapsedTimestampNs);
     void cancelEventActivationLocked(int deactivationTrackerIndex);
@@ -379,19 +372,6 @@
 
     mutable std::mutex mMutex;
 
-    struct Activation {
-        Activation(const ActivationType& activationType, const int64_t ttlNs)
-            : ttl_ns(ttlNs),
-              start_ns(0),
-              state(ActivationState::kNotActive),
-              activationType(activationType) {}
-
-        const int64_t ttl_ns;
-        int64_t start_ns;
-        ActivationState state;
-        const ActivationType activationType;
-    };
-
     // When the metric producer has multiple activations, these activations are ORed to determine
     // whether the metric producer is ready to generate metrics.
     std::unordered_map<int, std::shared_ptr<Activation>> mEventActivationMap;
diff --git a/cmds/statsd/src/metrics/ValueMetricProducer.cpp b/cmds/statsd/src/metrics/ValueMetricProducer.cpp
index bc16024..7fe5a83 100644
--- a/cmds/statsd/src/metrics/ValueMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/ValueMetricProducer.cpp
@@ -81,8 +81,11 @@
         const ConfigKey& key, const ValueMetric& metric, const int conditionIndex,
         const sp<ConditionWizard>& conditionWizard, const int whatMatcherIndex,
         const sp<EventMatcherWizard>& matcherWizard, const int pullTagId, const int64_t timeBaseNs,
-        const int64_t startTimeNs, const sp<StatsPullerManager>& pullerManager)
-    : MetricProducer(metric.id(), key, timeBaseNs, conditionIndex, conditionWizard),
+        const int64_t startTimeNs, const sp<StatsPullerManager>& pullerManager,
+        const unordered_map<int, shared_ptr<Activation>>& eventActivationMap,
+        const unordered_map<int, vector<shared_ptr<Activation>>>& eventDeactivationMap)
+    : MetricProducer(metric.id(), key, timeBaseNs, conditionIndex, conditionWizard,
+                     eventActivationMap, eventDeactivationMap),
       mWhatMatcherIndex(whatMatcherIndex),
       mEventMatcherWizard(matcherWizard),
       mPullerManager(pullerManager),
@@ -108,7 +111,7 @@
       mMaxPullDelayNs(metric.max_pull_delay_sec() > 0 ? metric.max_pull_delay_sec() * NS_PER_SEC
                                                       : StatsdStats::kPullMaxDelayNs),
       mSplitBucketForAppUpgrade(metric.split_bucket_for_app_upgrade()),
-      // Condition timer will be set in prepareFirstBucketLocked.
+      // Condition timer will be set later within the constructor after pulling events
       mConditionTimer(false, timeBaseNs) {
     int64_t bucketSizeMills = 0;
     if (metric.has_bucket()) {
@@ -154,6 +157,15 @@
     // Adjust start for partial bucket
     mCurrentBucketStartTimeNs = startTimeNs;
     mConditionTimer.newBucketStart(mCurrentBucketStartTimeNs);
+
+     // Kicks off the puller immediately if condition is true and diff based.
+    if (mIsActive && mIsPulled && mCondition == ConditionState::kTrue && mUseDiff) {
+        pullAndMatchEventsLocked(mCurrentBucketStartTimeNs, mCondition);
+    }
+    // Now that activations are processed, start the condition timer if needed.
+    mConditionTimer.onConditionChanged(mIsActive && mCondition == ConditionState::kTrue,
+                                       mCurrentBucketStartTimeNs);
+
     VLOG("value metric %lld created. bucket size %lld start_time: %lld", (long long)metric.id(),
          (long long)mBucketSizeNs, (long long)mTimeBaseNs);
 }
@@ -165,16 +177,6 @@
     }
 }
 
-void ValueMetricProducer::prepareFirstBucketLocked() {
-    // Kicks off the puller immediately if condition is true and diff based.
-    if (mIsActive && mIsPulled && mCondition == ConditionState::kTrue && mUseDiff) {
-        pullAndMatchEventsLocked(mCurrentBucketStartTimeNs, mCondition);
-    }
-    // Now that activations are processed, start the condition timer if needed.
-    mConditionTimer.onConditionChanged(mIsActive && mCondition == ConditionState::kTrue,
-                                       mCurrentBucketStartTimeNs);
-}
-
 void ValueMetricProducer::onSlicedConditionMayChangeLocked(bool overallCondition,
                                                            const int64_t eventTime) {
     VLOG("Metric %lld onSlicedConditionMayChange", (long long)mMetricId);
diff --git a/cmds/statsd/src/metrics/ValueMetricProducer.h b/cmds/statsd/src/metrics/ValueMetricProducer.h
index 739f6ef..d7cd397 100644
--- a/cmds/statsd/src/metrics/ValueMetricProducer.h
+++ b/cmds/statsd/src/metrics/ValueMetricProducer.h
@@ -54,10 +54,13 @@
 public:
     ValueMetricProducer(const ConfigKey& key, const ValueMetric& valueMetric,
                         const int conditionIndex, const sp<ConditionWizard>& conditionWizard,
-                        const int whatMatcherIndex,
-                        const sp<EventMatcherWizard>& matcherWizard,
+                        const int whatMatcherIndex, const sp<EventMatcherWizard>& matcherWizard,
                         const int pullTagId, const int64_t timeBaseNs, const int64_t startTimeNs,
-                        const sp<StatsPullerManager>& pullerManager);
+                        const sp<StatsPullerManager>& pullerManager,
+                        const std::unordered_map<int, std::shared_ptr<Activation>>&
+                                eventActivationMap = {},
+                        const std::unordered_map<int, std::vector<std::shared_ptr<Activation>>>&
+                                eventDeactivationMap = {});
 
     virtual ~ValueMetricProducer();
 
@@ -116,8 +119,6 @@
     void flushCurrentBucketLocked(const int64_t& eventTimeNs,
                                   const int64_t& nextBucketStartTimeNs) override;
 
-    void prepareFirstBucketLocked() override;
-
     void dropDataLocked(const int64_t dropTimeNs) override;
 
     // Calculate previous bucket end time based on current time.
diff --git a/cmds/statsd/src/metrics/metrics_manager_util.cpp b/cmds/statsd/src/metrics/metrics_manager_util.cpp
index 40484f4..0fee71e 100644
--- a/cmds/statsd/src/metrics/metrics_manager_util.cpp
+++ b/cmds/statsd/src/metrics/metrics_manager_util.cpp
@@ -18,6 +18,7 @@
 #include "Log.h"
 
 #include "metrics_manager_util.h"
+#include "MetricProducer.h"
 
 #include "../condition/CombinationConditionTracker.h"
 #include "../condition/SimpleConditionTracker.h"
@@ -137,6 +138,62 @@
     return true;
 }
 
+// Validates a metricActivation and populates state.
+// EventActivationMap and EventDeactivationMap are supplied to a MetricProducer
+//      to provide the producer with state about its activators and deactivators.
+// Returns false if there are errors.
+bool handleMetricActivation(
+        const StatsdConfig& config,
+        const int64_t metricId,
+        const int metricIndex,
+        const unordered_map<int64_t, int>& metricToActivationMap,
+        const unordered_map<int64_t, int>& logTrackerMap,
+        unordered_map<int, vector<int>>& activationAtomTrackerToMetricMap,
+        unordered_map<int, vector<int>>& deactivationAtomTrackerToMetricMap,
+        vector<int>& metricsWithActivation,
+        unordered_map<int, shared_ptr<Activation>>& eventActivationMap,
+        unordered_map<int, vector<shared_ptr<Activation>>>& eventDeactivationMap) {
+    // Check if metric has an associated activation
+    auto itr = metricToActivationMap.find(metricId);
+    if (itr == metricToActivationMap.end()) return true;
+
+    int activationIndex = itr->second;
+    const MetricActivation& metricActivation = config.metric_activation(activationIndex);
+
+    for (int i = 0; i < metricActivation.event_activation_size(); i++) {
+        const EventActivation& activation = metricActivation.event_activation(i);
+
+        auto itr = logTrackerMap.find(activation.atom_matcher_id());
+        if (itr == logTrackerMap.end()) {
+            ALOGE("Atom matcher not found for event activation.");
+            return false;
+        }
+
+        ActivationType activationType = (activation.has_activation_type()) ?
+                activation.activation_type() : metricActivation.activation_type();
+        std::shared_ptr<Activation> activationWrapper = std::make_shared<Activation>(
+                activationType, activation.ttl_seconds() * NS_PER_SEC);
+
+        int atomMatcherIndex = itr->second;
+        activationAtomTrackerToMetricMap[atomMatcherIndex].push_back(metricIndex);
+        eventActivationMap.emplace(atomMatcherIndex, activationWrapper);
+
+        if (activation.has_deactivation_atom_matcher_id()) {
+            itr = logTrackerMap.find(activation.deactivation_atom_matcher_id());
+            if (itr == logTrackerMap.end()) {
+                ALOGE("Atom matcher not found for event deactivation.");
+                return false;
+            }
+            int deactivationAtomMatcherIndex = itr->second;
+            deactivationAtomTrackerToMetricMap[deactivationAtomMatcherIndex].push_back(metricIndex);
+            eventDeactivationMap[deactivationAtomMatcherIndex].push_back(activationWrapper);
+        }
+    }
+
+    metricsWithActivation.push_back(metricIndex);
+    return true;
+}
+
 bool initLogTrackers(const StatsdConfig& config, const UidMap& uidMap,
                      unordered_map<int64_t, int>& logTrackerMap,
                      vector<sp<LogMatchingTracker>>& allAtomMatchers, set<int>& allTagIds) {
@@ -293,16 +350,33 @@
                  const vector<sp<LogMatchingTracker>>& allAtomMatchers,
                  vector<sp<ConditionTracker>>& allConditionTrackers,
                  vector<sp<MetricProducer>>& allMetricProducers,
-                 unordered_map<int, std::vector<int>>& conditionToMetricMap,
-                 unordered_map<int, std::vector<int>>& trackerToMetricMap,
-                 unordered_map<int64_t, int>& metricMap, std::set<int64_t>& noReportMetricIds) {
+                 unordered_map<int, vector<int>>& conditionToMetricMap,
+                 unordered_map<int, vector<int>>& trackerToMetricMap,
+                 unordered_map<int64_t, int>& metricMap, std::set<int64_t>& noReportMetricIds,
+                 unordered_map<int, vector<int>>& activationAtomTrackerToMetricMap,
+                 unordered_map<int, vector<int>>& deactivationAtomTrackerToMetricMap,
+                 vector<int>& metricsWithActivation) {
     sp<ConditionWizard> wizard = new ConditionWizard(allConditionTrackers);
     sp<EventMatcherWizard> matcherWizard = new EventMatcherWizard(allAtomMatchers);
     const int allMetricsCount = config.count_metric_size() + config.duration_metric_size() +
-                                config.event_metric_size() + config.value_metric_size();
+                                config.event_metric_size() + config.gauge_metric_size() +
+                                config.value_metric_size();
     allMetricProducers.reserve(allMetricsCount);
     StatsPullerManager statsPullerManager;
 
+    // Construct map from metric id to metric activation index. The map will be used to determine
+    // the metric activation corresponding to a metric.
+    unordered_map<int64_t, int> metricToActivationMap;
+    for (int i = 0; i < config.metric_activation_size(); i++) {
+        const MetricActivation& metricActivation = config.metric_activation(i);
+        int64_t metricId = metricActivation.metric_id();
+        if (metricToActivationMap.find(metricId) != metricToActivationMap.end()) {
+            ALOGE("Metric %lld has multiple MetricActivations", (long long) metricId);
+            return false;
+        }
+        metricToActivationMap.insert({metricId, i});
+    }
+
     // Build MetricProducers for each metric defined in config.
     // build CountMetricProducer
     for (int i = 0; i < config.count_metric_size(); i++) {
@@ -337,8 +411,17 @@
             }
         }
 
-        sp<MetricProducer> countProducer =
-                new CountMetricProducer(key, metric, conditionIndex, wizard, timeBaseTimeNs, currentTimeNs);
+        unordered_map<int, shared_ptr<Activation>> eventActivationMap;
+        unordered_map<int, vector<shared_ptr<Activation>>> eventDeactivationMap;
+        bool success = handleMetricActivation(config, metric.id(), metricIndex,
+                metricToActivationMap, logTrackerMap, activationAtomTrackerToMetricMap,
+                deactivationAtomTrackerToMetricMap, metricsWithActivation, eventActivationMap,
+                eventDeactivationMap);
+        if (!success) return false;
+
+        sp<MetricProducer> countProducer = new CountMetricProducer(
+                key, metric, conditionIndex, wizard, timeBaseTimeNs, currentTimeNs,
+                eventActivationMap, eventDeactivationMap);
         allMetricProducers.push_back(countProducer);
     }
 
@@ -406,9 +489,18 @@
             }
         }
 
+        unordered_map<int, shared_ptr<Activation>> eventActivationMap;
+        unordered_map<int, vector<shared_ptr<Activation>>> eventDeactivationMap;
+        bool success = handleMetricActivation(config, metric.id(), metricIndex,
+                metricToActivationMap, logTrackerMap, activationAtomTrackerToMetricMap,
+                deactivationAtomTrackerToMetricMap, metricsWithActivation, eventActivationMap,
+                eventDeactivationMap);
+        if (!success) return false;
+
         sp<MetricProducer> durationMetric = new DurationMetricProducer(
                 key, metric, conditionIndex, trackerIndices[0], trackerIndices[1],
-                trackerIndices[2], nesting, wizard, internalDimensions, timeBaseTimeNs, currentTimeNs);
+                trackerIndices[2], nesting, wizard, internalDimensions, timeBaseTimeNs,
+                currentTimeNs, eventActivationMap, eventDeactivationMap);
 
         allMetricProducers.push_back(durationMetric);
     }
@@ -443,8 +535,17 @@
             }
         }
 
-        sp<MetricProducer> eventMetric =
-                new EventMetricProducer(key, metric, conditionIndex, wizard, timeBaseTimeNs);
+        unordered_map<int, shared_ptr<Activation>> eventActivationMap;
+        unordered_map<int, vector<shared_ptr<Activation>>> eventDeactivationMap;
+        bool success = handleMetricActivation(config, metric.id(), metricIndex,
+                metricToActivationMap, logTrackerMap, activationAtomTrackerToMetricMap,
+                deactivationAtomTrackerToMetricMap, metricsWithActivation, eventActivationMap,
+                eventDeactivationMap);
+        if (!success) return false;
+
+        sp<MetricProducer> eventMetric = new EventMetricProducer(
+                key, metric, conditionIndex, wizard, timeBaseTimeNs, eventActivationMap,
+                eventDeactivationMap);
 
         allMetricProducers.push_back(eventMetric);
     }
@@ -500,9 +601,18 @@
             }
         }
 
+        unordered_map<int, shared_ptr<Activation>> eventActivationMap;
+        unordered_map<int, vector<shared_ptr<Activation>>> eventDeactivationMap;
+        bool success = handleMetricActivation(config, metric.id(), metricIndex,
+                metricToActivationMap, logTrackerMap, activationAtomTrackerToMetricMap,
+                deactivationAtomTrackerToMetricMap, metricsWithActivation, eventActivationMap,
+                eventDeactivationMap);
+        if (!success) return false;
+
         sp<MetricProducer> valueProducer = new ValueMetricProducer(
                 key, metric, conditionIndex, wizard, trackerIndex, matcherWizard, pullTagId,
-                timeBaseTimeNs, currentTimeNs, pullerManager);
+                timeBaseTimeNs, currentTimeNs, pullerManager, eventActivationMap,
+                eventDeactivationMap);
         allMetricProducers.push_back(valueProducer);
     }
 
@@ -586,10 +696,19 @@
             }
         }
 
+        unordered_map<int, shared_ptr<Activation>> eventActivationMap;
+        unordered_map<int, vector<shared_ptr<Activation>>> eventDeactivationMap;
+        bool success = handleMetricActivation(config, metric.id(), metricIndex,
+                metricToActivationMap, logTrackerMap, activationAtomTrackerToMetricMap,
+                deactivationAtomTrackerToMetricMap, metricsWithActivation, eventActivationMap,
+                eventDeactivationMap);
+        if (!success) return false;
+
         sp<MetricProducer> gaugeProducer = new GaugeMetricProducer(
                 key, metric, conditionIndex, wizard,
                 trackerIndex, matcherWizard, pullTagId, triggerAtomId, atomTagId,
-                timeBaseTimeNs, currentTimeNs, pullerManager);
+                timeBaseTimeNs, currentTimeNs, pullerManager,
+                eventActivationMap, eventDeactivationMap);
         allMetricProducers.push_back(gaugeProducer);
     }
     for (int i = 0; i < config.no_report_metric_size(); ++i) {
@@ -707,73 +826,6 @@
     return true;
 }
 
-bool initMetricActivations(const ConfigKey& key, const StatsdConfig& config,
-                           const int64_t currentTimeNs,
-                           const unordered_map<int64_t, int> &logEventTrackerMap,
-                           const unordered_map<int64_t, int> &metricProducerMap,
-                           vector<sp<MetricProducer>>& allMetricProducers,
-                           unordered_map<int, std::vector<int>>& activationAtomTrackerToMetricMap,
-                           unordered_map<int, std::vector<int>>& deactivationAtomTrackerToMetricMap,
-                           vector<int>& metricsWithActivation) {
-    for (int i = 0; i < config.metric_activation_size(); ++i) {
-        const MetricActivation& metric_activation = config.metric_activation(i);
-        auto itr = metricProducerMap.find(metric_activation.metric_id());
-        if (itr == metricProducerMap.end()) {
-            ALOGE("Metric id not found in metric activation: %lld",
-                (long long)metric_activation.metric_id());
-            return false;
-        }
-        const int metricTrackerIndex = itr->second;
-        if (metricTrackerIndex < 0 || metricTrackerIndex >= (int)allMetricProducers.size()) {
-            ALOGE("Invalid metric tracker index.");
-            return false;
-        }
-        const sp<MetricProducer>& metric = allMetricProducers[metricTrackerIndex];
-        metricsWithActivation.push_back(metricTrackerIndex);
-        for (int j = 0; j < metric_activation.event_activation_size(); ++j) {
-            const EventActivation& activation = metric_activation.event_activation(j);
-            auto logTrackerIt = logEventTrackerMap.find(activation.atom_matcher_id());
-            if (logTrackerIt == logEventTrackerMap.end()) {
-                ALOGE("Atom matcher not found for event activation.");
-                return false;
-            }
-            const int atomMatcherIndex = logTrackerIt->second;
-            activationAtomTrackerToMetricMap[atomMatcherIndex].push_back(
-                metricTrackerIndex);
-
-            ActivationType activationType;
-            if (activation.has_activation_type()) {
-                activationType = activation.activation_type();
-            } else {
-                activationType = metric_activation.activation_type();
-            }
-
-            if (activation.has_deactivation_atom_matcher_id()) {
-                auto deactivationAtomMatcherIt =
-                        logEventTrackerMap.find(activation.deactivation_atom_matcher_id());
-                if (deactivationAtomMatcherIt == logEventTrackerMap.end()) {
-                    ALOGE("Atom matcher not found for event deactivation.");
-                    return false;
-                }
-                const int deactivationMatcherIndex = deactivationAtomMatcherIt->second;
-                deactivationAtomTrackerToMetricMap[deactivationMatcherIndex]
-                        .push_back(metricTrackerIndex);
-                metric->addActivation(atomMatcherIndex, activationType, activation.ttl_seconds(),
-                                      deactivationMatcherIndex);
-            } else {
-                metric->addActivation(atomMatcherIndex, activationType, activation.ttl_seconds());
-            }
-        }
-    }
-    return true;
-}
-
-void prepareFirstBucket(const vector<sp<MetricProducer>>& allMetricProducers) {
-    for (const auto& metric: allMetricProducers) {
-        metric->prepareFirstBucket();
-    }
-}
-
 bool initStatsdConfig(const ConfigKey& key, const StatsdConfig& config, UidMap& uidMap,
                       const sp<StatsPullerManager>& pullerManager,
                       const sp<AlarmMonitor>& anomalyAlarmMonitor,
@@ -810,7 +862,8 @@
     if (!initMetrics(key, config, timeBaseNs, currentTimeNs, uidMap, pullerManager, logTrackerMap,
                      conditionTrackerMap, allAtomMatchers, allConditionTrackers, allMetricProducers,
                      conditionToMetricMap, trackerToMetricMap, metricProducerMap,
-                     noReportMetricIds)) {
+                     noReportMetricIds, activationAtomTrackerToMetricMap,
+                     deactivationAtomTrackerToMetricMap, metricsWithActivation)) {
         ALOGE("initMetricProducers failed");
         return false;
     }
@@ -824,14 +877,6 @@
         ALOGE("initAlarms failed");
         return false;
     }
-    if (!initMetricActivations(key, config, currentTimeNs, logTrackerMap, metricProducerMap,
-            allMetricProducers, activationAtomTrackerToMetricMap,
-            deactivationAtomTrackerToMetricMap, metricsWithActivation)) {
-        ALOGE("initMetricActivations failed");
-        return false;
-    }
-
-    prepareFirstBucket(allMetricProducers);
 
     return true;
 }
diff --git a/cmds/statsd/src/metrics/metrics_manager_util.h b/cmds/statsd/src/metrics/metrics_manager_util.h
index 3704969..3802948 100644
--- a/cmds/statsd/src/metrics/metrics_manager_util.h
+++ b/cmds/statsd/src/metrics/metrics_manager_util.h
@@ -91,7 +91,10 @@
         std::vector<sp<MetricProducer>>& allMetricProducers,
         std::unordered_map<int, std::vector<int>>& conditionToMetricMap,
         std::unordered_map<int, std::vector<int>>& trackerToMetricMap,
-        std::set<int64_t>& noReportMetricIds);
+        std::set<int64_t>& noReportMetricIds,
+        std::unordered_map<int, std::vector<int>>& activationAtomTrackerToMetricMap,
+        std::unordered_map<int, std::vector<int>>& deactivationAtomTrackerToMetricMap,
+        std::vector<int>& metricsWithActivation);
 
 // Initialize MetricsManager from StatsdConfig.
 // Parameters are the members of MetricsManager. See MetricsManager for declaration.
diff --git a/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp b/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp
index 47c21aa..b027e8e 100644
--- a/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp
+++ b/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp
@@ -79,8 +79,6 @@
                                       logEventMatcherIndex, eventMatcherWizard,
                                       -1, -1, tagId, 5, 600 * NS_PER_SEC + NS_PER_SEC / 2,
                                       pullerManager);
-    gaugeProducer.prepareFirstBucket();
-
 
     EXPECT_EQ(600500000000, gaugeProducer.mCurrentBucketStartTimeNs);
     EXPECT_EQ(10, gaugeProducer.mCurrentBucketNum);
@@ -126,8 +124,6 @@
                                       tagId, -1, tagId, bucketStartTimeNs, bucketStartTimeNs,
                                       pullerManager);
 
-    gaugeProducer.prepareFirstBucket();
-
     vector<shared_ptr<LogEvent>> allData;
     allData.clear();
     shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 1);
@@ -211,7 +207,6 @@
                                       logEventMatcherIndex, eventMatcherWizard,
                                       -1 /* -1 means no pulling */, -1, tagId, bucketStartTimeNs,
                                       bucketStartTimeNs, pullerManager);
-    gaugeProducer.prepareFirstBucket();
 
     sp<AnomalyTracker> anomalyTracker = gaugeProducer.addAnomalyTracker(alert, alarmMonitor);
     EXPECT_TRUE(anomalyTracker != nullptr);
@@ -303,7 +298,6 @@
     GaugeMetricProducer gaugeProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard,
                                       logEventMatcherIndex, eventMatcherWizard, tagId, -1, tagId,
                                       bucketStartTimeNs, bucketStartTimeNs, pullerManager);
-    gaugeProducer.prepareFirstBucket();
 
     vector<shared_ptr<LogEvent>> allData;
     shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 1);
@@ -370,7 +364,6 @@
                                       logEventMatcherIndex, eventMatcherWizard,
                                       tagId, -1, tagId, bucketStartTimeNs, bucketStartTimeNs,
                                       pullerManager);
-    gaugeProducer.prepareFirstBucket();
 
     vector<shared_ptr<LogEvent>> allData;
     shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 1);
@@ -431,7 +424,6 @@
     GaugeMetricProducer gaugeProducer(kConfigKey, metric, 1, wizard,
                                       logEventMatcherIndex, eventMatcherWizard, tagId, -1, tagId,
                                       bucketStartTimeNs, bucketStartTimeNs, pullerManager);
-    gaugeProducer.prepareFirstBucket();
 
     gaugeProducer.onConditionChanged(true, bucketStartTimeNs + 8);
     EXPECT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size());
@@ -521,7 +513,6 @@
     GaugeMetricProducer gaugeProducer(kConfigKey, metric, 1, wizard,
                                       logEventMatcherIndex, eventMatcherWizard, tagId, -1, tagId,
                                       bucketStartTimeNs, bucketStartTimeNs, pullerManager);
-    gaugeProducer.prepareFirstBucket();
 
     gaugeProducer.onSlicedConditionMayChange(true, bucketStartTimeNs + 8);
 
@@ -572,7 +563,6 @@
                                       logEventMatcherIndex, eventMatcherWizard,
                                       tagId, -1, tagId, bucketStartTimeNs, bucketStartTimeNs,
                                       pullerManager);
-    gaugeProducer.prepareFirstBucket();
 
     Alert alert;
     alert.set_id(101);
@@ -681,7 +671,6 @@
                                       logEventMatcherIndex, eventMatcherWizard,
                                       tagId, triggerId, tagId, bucketStartTimeNs, bucketStartTimeNs,
                                       pullerManager);
-    gaugeProducer.prepareFirstBucket();
 
     vector<shared_ptr<LogEvent>> allData;
 
@@ -766,7 +755,6 @@
                                       logEventMatcherIndex, eventMatcherWizard,
                                       tagId, triggerId, tagId, bucketStartTimeNs, bucketStartTimeNs,
                                       pullerManager);
-    gaugeProducer.prepareFirstBucket();
 
     vector<shared_ptr<LogEvent>> allData;
 
diff --git a/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp b/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp
index 2262c76..4b9d0c0 100644
--- a/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp
+++ b/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp
@@ -105,7 +105,6 @@
                 kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard,
                 logEventMatcherIndex, eventMatcherWizard, tagId,
                 bucketStartTimeNs, bucketStartTimeNs, pullerManager);
-        valueProducer->prepareFirstBucket();
         return valueProducer;
     }
 
@@ -125,7 +124,6 @@
                 new ValueMetricProducer(kConfigKey, metric, 1, wizard, logEventMatcherIndex,
                                         eventMatcherWizard, tagId, bucketStartTimeNs,
                                         bucketStartTimeNs, pullerManager);
-        valueProducer->prepareFirstBucket();
         valueProducer->mCondition = ConditionState::kFalse;
         return valueProducer;
     }
@@ -169,7 +167,6 @@
     ValueMetricProducer valueProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard,
                                       logEventMatcherIndex, eventMatcherWizard, -1, startTimeBase,
                                       22, pullerManager);
-    valueProducer.prepareFirstBucket();
 
     EXPECT_EQ(startTimeBase, valueProducer.calcPreviousBucketEndTime(60 * NS_PER_SEC + 10));
     EXPECT_EQ(startTimeBase, valueProducer.calcPreviousBucketEndTime(60 * NS_PER_SEC + 10));
@@ -199,7 +196,6 @@
     ValueMetricProducer valueProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard,
                                       logEventMatcherIndex, eventMatcherWizard, -1, 5,
                                       600 * NS_PER_SEC + NS_PER_SEC / 2, pullerManager);
-    valueProducer.prepareFirstBucket();
 
     EXPECT_EQ(600500000000, valueProducer.mCurrentBucketStartTimeNs);
     EXPECT_EQ(10, valueProducer.mCurrentBucketNum);
@@ -381,7 +377,6 @@
             kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard,
             logEventMatcherIndex, eventMatcherWizard, tagId,
             bucketStartTimeNs, bucketStartTimeNs, pullerManager);
-    valueProducer->prepareFirstBucket();
 
     vector<shared_ptr<LogEvent>> allData;
     allData.clear();
@@ -670,7 +665,6 @@
     ValueMetricProducer valueProducer(kConfigKey, metric, -1, wizard, logEventMatcherIndex,
                                       eventMatcherWizard, -1, bucketStartTimeNs, bucketStartTimeNs,
                                       pullerManager);
-    valueProducer.prepareFirstBucket();
 
     shared_ptr<LogEvent> event1 = make_shared<LogEvent>(tagId, bucketStartTimeNs + 10);
     event1->write(1);
@@ -728,7 +722,6 @@
     ValueMetricProducer valueProducer(kConfigKey, metric, -1, wizard, logEventMatcherIndex,
                                       eventMatcherWizard, tagId, bucketStartTimeNs,
                                       bucketStartTimeNs, pullerManager);
-    valueProducer.prepareFirstBucket();
 
     vector<shared_ptr<LogEvent>> allData;
     allData.clear();
@@ -779,7 +772,6 @@
     ValueMetricProducer valueProducer(kConfigKey, metric, -1, wizard, logEventMatcherIndex,
                                       eventMatcherWizard, tagId, bucketStartTimeNs,
                                       bucketStartTimeNs, pullerManager);
-    valueProducer.prepareFirstBucket();
 
     vector<shared_ptr<LogEvent>> allData;
     allData.clear();
@@ -854,7 +846,6 @@
     ValueMetricProducer valueProducer(kConfigKey, metric, -1, wizard, logEventMatcherIndex,
                                       eventMatcherWizard, -1, bucketStartTimeNs, bucketStartTimeNs,
                                       pullerManager);
-    valueProducer.prepareFirstBucket();
 
     shared_ptr<LogEvent> event1 = make_shared<LogEvent>(tagId, bucketStartTimeNs + 10);
     event1->write(1);
@@ -897,7 +888,6 @@
     ValueMetricProducer valueProducer(kConfigKey, metric, 1, wizard, logEventMatcherIndex,
                                       eventMatcherWizard, -1, bucketStartTimeNs, bucketStartTimeNs,
                                       pullerManager);
-    valueProducer.prepareFirstBucket();
     valueProducer.mCondition = ConditionState::kFalse;
 
     shared_ptr<LogEvent> event1 = make_shared<LogEvent>(tagId, bucketStartTimeNs + 10);
@@ -972,7 +962,6 @@
     ValueMetricProducer valueProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard,
                                       logEventMatcherIndex, eventMatcherWizard, -1 /*not pulled*/,
                                       bucketStartTimeNs, bucketStartTimeNs, pullerManager);
-    valueProducer.prepareFirstBucket();
 
     sp<AnomalyTracker> anomalyTracker = valueProducer.addAnomalyTracker(alert, alarmMonitor);
 
@@ -1269,7 +1258,6 @@
     ValueMetricProducer valueProducer(kConfigKey, metric, -1, wizard, logEventMatcherIndex,
                                       eventMatcherWizard, -1, bucketStartTimeNs, bucketStartTimeNs,
                                       pullerManager);
-    valueProducer.prepareFirstBucket();
 
     shared_ptr<LogEvent> event1 = make_shared<LogEvent>(tagId, bucketStartTimeNs + 10);
     event1->write(1);
@@ -1314,7 +1302,6 @@
     ValueMetricProducer valueProducer(kConfigKey, metric, -1, wizard, logEventMatcherIndex,
                                       eventMatcherWizard, -1, bucketStartTimeNs, bucketStartTimeNs,
                                       pullerManager);
-    valueProducer.prepareFirstBucket();
 
     shared_ptr<LogEvent> event1 = make_shared<LogEvent>(tagId, bucketStartTimeNs + 10);
     event1->write(1);
@@ -1361,7 +1348,6 @@
     ValueMetricProducer valueProducer(kConfigKey, metric, -1, wizard, logEventMatcherIndex,
                                       eventMatcherWizard, -1, bucketStartTimeNs, bucketStartTimeNs,
                                       pullerManager);
-    valueProducer.prepareFirstBucket();
 
     shared_ptr<LogEvent> event1 = make_shared<LogEvent>(tagId, bucketStartTimeNs + 10);
     event1->write(1);
@@ -1412,7 +1398,6 @@
     ValueMetricProducer valueProducer(kConfigKey, metric, -1, wizard, logEventMatcherIndex,
                                       eventMatcherWizard, -1, bucketStartTimeNs, bucketStartTimeNs,
                                       pullerManager);
-    valueProducer.prepareFirstBucket();
 
     shared_ptr<LogEvent> event1 = make_shared<LogEvent>(tagId, bucketStartTimeNs + 10);
     event1->write(1);
@@ -1458,7 +1443,6 @@
     ValueMetricProducer valueProducer(kConfigKey, metric, -1, wizard, logEventMatcherIndex,
                                       eventMatcherWizard, -1, bucketStartTimeNs, bucketStartTimeNs,
                                       pullerManager);
-    valueProducer.prepareFirstBucket();
 
     shared_ptr<LogEvent> event1 = make_shared<LogEvent>(tagId, bucketStartTimeNs + 10);
     event1->write(1);
@@ -1532,7 +1516,6 @@
     ValueMetricProducer valueProducer(kConfigKey, metric, -1, wizard, logEventMatcherIndex,
                                       eventMatcherWizard, -1, bucketStartTimeNs, bucketStartTimeNs,
                                       pullerManager);
-    valueProducer.prepareFirstBucket();
 
     shared_ptr<LogEvent> event1 = make_shared<LogEvent>(tagId, bucketStartTimeNs + 10);
     event1->write(1);
@@ -2081,7 +2064,6 @@
     ValueMetricProducer valueProducer(kConfigKey, metric, 1, wizard, logEventMatcherIndex,
                                       eventMatcherWizard, tagId, bucket2StartTimeNs,
                                       bucket2StartTimeNs, pullerManager);
-    valueProducer.prepareFirstBucket();
     valueProducer.mCondition = ConditionState::kFalse;
 
     // Event should be skipped since it is from previous bucket.
@@ -2862,7 +2844,6 @@
     ValueMetricProducer valueProducer(kConfigKey, metric, -1, wizard, logEventMatcherIndex,
                                       eventMatcherWizard, tagId, bucketStartTimeNs,
                                       bucketStartTimeNs, pullerManager);
-    valueProducer.prepareFirstBucket();
 
     ProtoOutputStream output;
     std::set<string> strSet;
@@ -2905,7 +2886,6 @@
     ValueMetricProducer valueProducer(kConfigKey, metric, -1, wizard, logEventMatcherIndex,
                                       eventMatcherWizard, tagId, bucketStartTimeNs,
                                       bucketStartTimeNs, pullerManager);
-    valueProducer.prepareFirstBucket();
 
     vector<shared_ptr<LogEvent>> allData;
     allData.clear();
@@ -2969,7 +2949,6 @@
     ValueMetricProducer valueProducer(kConfigKey, metric, -1, wizard, logEventMatcherIndex,
                                       eventMatcherWizard, tagId, bucketStartTimeNs,
                                       bucketStartTimeNs, pullerManager);
-    valueProducer.prepareFirstBucket();
 
     ProtoOutputStream output;
     std::set<string> strSet;
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 38aac1b..7f27368 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -1163,6 +1163,10 @@
             sendMessage(H.ATTACH_AGENT, agent);
         }
 
+        public void attachStartupAgents(String dataDir) {
+            sendMessage(H.ATTACH_STARTUP_AGENTS, dataDir);
+        }
+
         public void setSchedulingGroup(int group) {
             // Note: do this immediately, since going into the foreground
             // should happen regardless of what pending work we have to do
@@ -1812,6 +1816,7 @@
         public static final int EXECUTE_TRANSACTION = 159;
         public static final int RELAUNCH_ACTIVITY = 160;
         public static final int PURGE_RESOURCES = 161;
+        public static final int ATTACH_STARTUP_AGENTS = 162;
 
         String codeToString(int code) {
             if (DEBUG_MESSAGES) {
@@ -1855,6 +1860,7 @@
                     case EXECUTE_TRANSACTION: return "EXECUTE_TRANSACTION";
                     case RELAUNCH_ACTIVITY: return "RELAUNCH_ACTIVITY";
                     case PURGE_RESOURCES: return "PURGE_RESOURCES";
+                    case ATTACH_STARTUP_AGENTS: return "ATTACH_STARTUP_AGENTS";
                 }
             }
             return Integer.toString(code);
@@ -2043,6 +2049,9 @@
                 case PURGE_RESOURCES:
                     schedulePurgeIdler();
                     break;
+                case ATTACH_STARTUP_AGENTS:
+                    handleAttachStartupAgents((String) msg.obj);
+                    break;
             }
             Object obj = msg.obj;
             if (obj instanceof SomeArgs) {
@@ -3779,6 +3788,27 @@
         }
     }
 
+    static void handleAttachStartupAgents(String dataDir) {
+        try {
+            Path code_cache = ContextImpl.getCodeCacheDirBeforeBind(new File(dataDir)).toPath();
+            if (!Files.exists(code_cache)) {
+                return;
+            }
+            Path startup_path = code_cache.resolve("startup_agents");
+            if (Files.exists(startup_path)) {
+                for (Path p : Files.newDirectoryStream(startup_path)) {
+                    handleAttachAgent(
+                            p.toAbsolutePath().toString()
+                            + "="
+                            + dataDir,
+                            null);
+                }
+            }
+        } catch (Exception e) {
+            // Ignored.
+        }
+    }
+
     private static final ThreadLocal<Intent> sCurrentBroadcastIntent = new ThreadLocal<Intent>();
 
     /**
@@ -6427,26 +6457,6 @@
         NetworkSecurityConfigProvider.install(appContext);
         Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
 
-
-        if (isAppDebuggable) {
-            try {
-                // Load all the agents in the code_cache/startup_agents directory.
-                // We pass the absolute path to the data_dir as an argument.
-                Path startup_path = appContext.getCodeCacheDir().toPath().resolve("startup_agents");
-                if (Files.exists(startup_path)) {
-                    for (Path p : Files.newDirectoryStream(startup_path)) {
-                        handleAttachAgent(
-                                p.toAbsolutePath().toString()
-                                + "="
-                                + appContext.getDataDir().toPath().toAbsolutePath().toString(),
-                                data.info);
-                    }
-                }
-            } catch (Exception e) {
-                // Ignored.
-            }
-        }
-
         // Continue loading instrumentation.
         if (ii != null) {
             ApplicationInfo instrApp;
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index a201307..86bf20a 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -3169,6 +3169,15 @@
     }
 
     @Override
+    public String getSetupWizardPackageName() {
+        try {
+            return mPM.getSetupWizardPackageName();
+        } catch (RemoteException e) {
+            throw e.rethrowAsRuntimeException();
+        }
+    }
+
+    @Override
     public String getIncidentReportApproverPackageName() {
         try {
             return mPM.getIncidentReportApproverPackageName();
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index ef23d5e..5b211e14 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -741,12 +741,21 @@
     public File getCodeCacheDir() {
         synchronized (mSync) {
             if (mCodeCacheDir == null) {
-                mCodeCacheDir = new File(getDataDir(), "code_cache");
+                mCodeCacheDir = getCodeCacheDirBeforeBind(getDataDir());
             }
             return ensurePrivateCacheDirExists(mCodeCacheDir, XATTR_INODE_CODE_CACHE);
         }
     }
 
+    /**
+     * Helper for getting code-cache dir potentially before application bind.
+     *
+     * @hide
+     */
+    static File getCodeCacheDirBeforeBind(File dataDir) {
+        return new File(dataDir, "code_cache");
+    }
+
     @Override
     public File getExternalCacheDir() {
         // Operates on primary external storage
diff --git a/core/java/android/app/IApplicationThread.aidl b/core/java/android/app/IApplicationThread.aidl
index cfa065b..51a64ff 100644
--- a/core/java/android/app/IApplicationThread.aidl
+++ b/core/java/android/app/IApplicationThread.aidl
@@ -137,6 +137,7 @@
             IVoiceInteractor voiceInteractor);
     void handleTrustStorageUpdate();
     void attachAgent(String path);
+    void attachStartupAgents(String dataDir);
     void scheduleApplicationInfoChanged(in ApplicationInfo ai);
     void setNetworkBlockSeq(long procStateSeq);
     void scheduleTransaction(in ClientTransaction transaction);
diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index 4d7c43a..19d8edf 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -686,6 +686,8 @@
 
     String getSystemCaptionsServicePackageName();
 
+    String getSetupWizardPackageName();
+
     String getIncidentReportApproverPackageName();
 
     boolean isPackageStateProtected(String packageName, int userId);
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index fd14d23..9513ce8 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -7427,6 +7427,17 @@
     }
 
     /**
+     * @return the system defined setup wizard package name, or null if there's none.
+     *
+     * @hide
+     */
+    @Nullable
+    public String getSetupWizardPackageName() {
+        throw new UnsupportedOperationException(
+                "getSetupWizardPackageName not implemented in subclass");
+    }
+
+    /**
      * @return the incident report approver app package name, or null if it's not defined
      * by the OEM.
      *
diff --git a/core/java/android/content/pm/UserInfo.java b/core/java/android/content/pm/UserInfo.java
index 9f0bade..ccfa184 100644
--- a/core/java/android/content/pm/UserInfo.java
+++ b/core/java/android/content/pm/UserInfo.java
@@ -350,7 +350,7 @@
 
     @UnsupportedAppUsage
     public UserHandle getUserHandle() {
-        return new UserHandle(id);
+        return UserHandle.of(id);
     }
 
     @Override
diff --git a/core/java/android/net/LinkProperties.java b/core/java/android/net/LinkProperties.java
index d3f48ac..3ec0aea 100644
--- a/core/java/android/net/LinkProperties.java
+++ b/core/java/android/net/LinkProperties.java
@@ -68,6 +68,7 @@
     // in the format "rmem_min,rmem_def,rmem_max,wmem_min,wmem_def,wmem_max"
     private String mTcpBufferSizes;
     private IpPrefix mNat64Prefix;
+    private boolean mWakeOnLanSupported;
 
     private static final int MIN_MTU    = 68;
     private static final int MIN_MTU_V6 = 1280;
@@ -193,6 +194,7 @@
             setMtu(source.mMtu);
             mTcpBufferSizes = source.mTcpBufferSizes;
             mNat64Prefix = source.mNat64Prefix;
+            mWakeOnLanSupported = source.mWakeOnLanSupported;
         }
     }
 
@@ -852,6 +854,7 @@
         mMtu = 0;
         mTcpBufferSizes = null;
         mNat64Prefix = null;
+        mWakeOnLanSupported = false;
     }
 
     /**
@@ -913,6 +916,10 @@
         resultJoiner.add("MTU:");
         resultJoiner.add(Integer.toString(mMtu));
 
+        if (mWakeOnLanSupported) {
+            resultJoiner.add("WakeOnLanSupported: true");
+        }
+
         if (mTcpBufferSizes != null) {
             resultJoiner.add("TcpBufferSizes:");
             resultJoiner.add(mTcpBufferSizes);
@@ -1425,6 +1432,37 @@
     }
 
     /**
+     * Compares this {@code LinkProperties} WakeOnLan supported against the target.
+     *
+     * @param target LinkProperties to compare.
+     * @return {@code true} if both are identical, {@code false} otherwise.
+     * @hide
+     */
+    public boolean isIdenticalWakeOnLan(LinkProperties target) {
+        return isWakeOnLanSupported() == target.isWakeOnLanSupported();
+    }
+
+    /**
+     * Set whether the network interface supports WakeOnLAN
+     *
+     * @param supported WakeOnLAN supported value
+     *
+     * @hide
+     */
+    public void setWakeOnLanSupported(boolean supported) {
+        mWakeOnLanSupported = supported;
+    }
+
+    /**
+     * Returns whether the network interface supports WakeOnLAN
+     *
+     * @return {@code true} if interface supports WakeOnLAN, {@code false} otherwise.
+     */
+    public boolean isWakeOnLanSupported() {
+        return mWakeOnLanSupported;
+    }
+
+    /**
      * Compares this {@code LinkProperties} instance against the target
      * LinkProperties in {@code obj}. Two LinkPropertieses are equal if
      * all their fields are equal in values.
@@ -1461,7 +1499,8 @@
                 && isIdenticalStackedLinks(target)
                 && isIdenticalMtu(target)
                 && isIdenticalTcpBufferSizes(target)
-                && isIdenticalNat64Prefix(target);
+                && isIdenticalNat64Prefix(target)
+                && isIdenticalWakeOnLan(target);
     }
 
     /**
@@ -1577,7 +1616,8 @@
                 + (mUsePrivateDns ? 57 : 0)
                 + mPcscfs.size() * 67
                 + ((null == mPrivateDnsServerName) ? 0 : mPrivateDnsServerName.hashCode())
-                + Objects.hash(mNat64Prefix);
+                + Objects.hash(mNat64Prefix)
+                + (mWakeOnLanSupported ? 71 : 0);
     }
 
     /**
@@ -1622,6 +1662,8 @@
 
         ArrayList<LinkProperties> stackedLinks = new ArrayList<>(mStackedLinks.values());
         dest.writeList(stackedLinks);
+
+        dest.writeBoolean(mWakeOnLanSupported);
     }
 
     /**
@@ -1677,6 +1719,7 @@
                 for (LinkProperties stackedLink: stackedLinks) {
                     netProp.addStackedLink(stackedLink);
                 }
+                netProp.setWakeOnLanSupported(in.readBoolean());
                 return netProp;
             }
 
diff --git a/core/java/android/os/Build.java b/core/java/android/os/Build.java
index b3125d8..6a709b5 100755
--- a/core/java/android/os/Build.java
+++ b/core/java/android/os/Build.java
@@ -243,7 +243,8 @@
         public static final String BASE_OS = SystemProperties.get("ro.build.version.base_os", "");
 
         /**
-         * The user-visible security patch level.
+         * The user-visible security patch level. This value represents the date when the device
+         * most recently applied a security patch.
          */
         public static final String SECURITY_PATCH = SystemProperties.get(
                 "ro.build.version.security_patch", "");
diff --git a/core/java/android/os/HwParcel.java b/core/java/android/os/HwParcel.java
index cfb582e..5e8929c 100644
--- a/core/java/android/os/HwParcel.java
+++ b/core/java/android/os/HwParcel.java
@@ -23,6 +23,8 @@
 import android.annotation.TestApi;
 import android.annotation.UnsupportedAppUsage;
 
+import dalvik.annotation.optimization.FastNative;
+
 import libcore.util.NativeAllocationRegistry;
 
 import java.lang.annotation.Retention;
@@ -72,46 +74,54 @@
 
     /**
      * Writes an interface token into the parcel used to verify that
-     * a transaction has made it to the write type of interface.
+     * a transaction has made it to the right type of interface.
      *
      * @param interfaceName fully qualified name of interface message
      *     is being sent to.
      */
+    @FastNative
     public native final void writeInterfaceToken(String interfaceName);
     /**
      * Writes a boolean value to the end of the parcel.
      * @param val to write
      */
+    @FastNative
     public native final void writeBool(boolean val);
     /**
      * Writes a byte value to the end of the parcel.
      * @param val to write
      */
+    @FastNative
     public native final void writeInt8(byte val);
     /**
      * Writes a short value to the end of the parcel.
      * @param val to write
      */
+    @FastNative
     public native final void writeInt16(short val);
     /**
      * Writes a int value to the end of the parcel.
      * @param val to write
      */
+    @FastNative
     public native final void writeInt32(int val);
     /**
      * Writes a long value to the end of the parcel.
      * @param val to write
      */
+    @FastNative
     public native final void writeInt64(long val);
     /**
      * Writes a float value to the end of the parcel.
      * @param val to write
      */
+    @FastNative
     public native final void writeFloat(float val);
     /**
      * Writes a double value to the end of the parcel.
      * @param val to write
      */
+    @FastNative
     public native final void writeDouble(double val);
     /**
      * Writes a String value to the end of the parcel.
@@ -120,6 +130,7 @@
      *
      * @param val to write
      */
+    @FastNative
     public native final void writeString(String val);
     /**
      * Writes a native handle (without duplicating the underlying
@@ -127,42 +138,50 @@
      *
      * @param val to write
      */
+    @FastNative
     public native final void writeNativeHandle(@Nullable NativeHandle val);
 
     /**
      * Writes an array of boolean values to the end of the parcel.
      * @param val to write
      */
+    @FastNative
     private native final void writeBoolVector(boolean[] val);
     /**
      * Writes an array of byte values to the end of the parcel.
      * @param val to write
      */
+    @FastNative
     private native final void writeInt8Vector(byte[] val);
     /**
      * Writes an array of short values to the end of the parcel.
      * @param val to write
      */
+    @FastNative
     private native final void writeInt16Vector(short[] val);
     /**
      * Writes an array of int values to the end of the parcel.
      * @param val to write
      */
+    @FastNative
     private native final void writeInt32Vector(int[] val);
     /**
      * Writes an array of long values to the end of the parcel.
      * @param val to write
      */
+    @FastNative
     private native final void writeInt64Vector(long[] val);
     /**
      * Writes an array of float values to the end of the parcel.
      * @param val to write
      */
+    @FastNative
     private native final void writeFloatVector(float[] val);
     /**
      * Writes an array of double values to the end of the parcel.
      * @param val to write
      */
+    @FastNative
     private native final void writeDoubleVector(double[] val);
     /**
      * Writes an array of String values to the end of the parcel.
@@ -171,6 +190,7 @@
      *
      * @param val to write
      */
+    @FastNative
     private native final void writeStringVector(String[] val);
     /**
      * Writes an array of native handles to the end of the parcel.
@@ -179,6 +199,7 @@
      *
      * @param val array of {@link NativeHandle} objects to write
      */
+    @FastNative
     private native final void writeNativeHandleVector(NativeHandle[] val);
 
     /**
@@ -299,6 +320,7 @@
      * Write a hwbinder object to the end of the parcel.
      * @param binder value to write
      */
+    @FastNative
     public native final void writeStrongBinder(IHwBinder binder);
 
     /**
@@ -314,48 +336,56 @@
      * @return value parsed from the parcel
      * @throws IllegalArgumentException if the parcel has no more data
      */
+    @FastNative
     public native final boolean readBool();
     /**
      * Reads a byte value from the current location in the parcel.
      * @return value parsed from the parcel
      * @throws IllegalArgumentException if the parcel has no more data
      */
+    @FastNative
     public native final byte readInt8();
     /**
      * Reads a short value from the current location in the parcel.
      * @return value parsed from the parcel
      * @throws IllegalArgumentException if the parcel has no more data
      */
+    @FastNative
     public native final short readInt16();
     /**
      * Reads a int value from the current location in the parcel.
      * @return value parsed from the parcel
      * @throws IllegalArgumentException if the parcel has no more data
      */
+    @FastNative
     public native final int readInt32();
     /**
      * Reads a long value from the current location in the parcel.
      * @return value parsed from the parcel
      * @throws IllegalArgumentException if the parcel has no more data
      */
+    @FastNative
     public native final long readInt64();
     /**
      * Reads a float value from the current location in the parcel.
      * @return value parsed from the parcel
      * @throws IllegalArgumentException if the parcel has no more data
      */
+    @FastNative
     public native final float readFloat();
     /**
      * Reads a double value from the current location in the parcel.
      * @return value parsed from the parcel
      * @throws IllegalArgumentException if the parcel has no more data
      */
+    @FastNative
     public native final double readDouble();
     /**
      * Reads a String value from the current location in the parcel.
      * @return value parsed from the parcel
      * @throws IllegalArgumentException if the parcel has no more data
      */
+    @FastNative
     public native final String readString();
     /**
      * Reads a native handle (without duplicating the underlying file
@@ -366,6 +396,7 @@
      * @return a {@link NativeHandle} instance parsed from the parcel
      * @throws IllegalArgumentException if the parcel has no more data
      */
+    @FastNative
     public native final @Nullable NativeHandle readNativeHandle();
     /**
      * Reads an embedded native handle (without duplicating the underlying
@@ -379,6 +410,7 @@
      * @return a {@link NativeHandle} instance parsed from the parcel
      * @throws IllegalArgumentException if the parcel has no more data
      */
+    @FastNative
     public native final @Nullable NativeHandle readEmbeddedNativeHandle(
             long parentHandle, long offset);
 
@@ -387,54 +419,63 @@
      * @return array of parsed values
      * @throws IllegalArgumentException if the parcel has no more data
      */
+    @FastNative
     private native final boolean[] readBoolVectorAsArray();
     /**
      * Reads an array of byte values from the parcel.
      * @return array of parsed values
      * @throws IllegalArgumentException if the parcel has no more data
      */
+    @FastNative
     private native final byte[] readInt8VectorAsArray();
     /**
      * Reads an array of short values from the parcel.
      * @return array of parsed values
      * @throws IllegalArgumentException if the parcel has no more data
      */
+    @FastNative
     private native final short[] readInt16VectorAsArray();
     /**
      * Reads an array of int values from the parcel.
      * @return array of parsed values
      * @throws IllegalArgumentException if the parcel has no more data
      */
+    @FastNative
     private native final int[] readInt32VectorAsArray();
     /**
      * Reads an array of long values from the parcel.
      * @return array of parsed values
      * @throws IllegalArgumentException if the parcel has no more data
      */
+    @FastNative
     private native final long[] readInt64VectorAsArray();
     /**
      * Reads an array of float values from the parcel.
      * @return array of parsed values
      * @throws IllegalArgumentException if the parcel has no more data
      */
+    @FastNative
     private native final float[] readFloatVectorAsArray();
     /**
      * Reads an array of double values from the parcel.
      * @return array of parsed values
      * @throws IllegalArgumentException if the parcel has no more data
      */
+    @FastNative
     private native final double[] readDoubleVectorAsArray();
     /**
      * Reads an array of String values from the parcel.
      * @return array of parsed values
      * @throws IllegalArgumentException if the parcel has no more data
      */
+    @FastNative
     private native final String[] readStringVectorAsArray();
     /**
      * Reads an array of native handles from the parcel.
      * @return array of {@link NativeHandle} objects
      * @throws IllegalArgumentException if the parcel has no more data
      */
+    @FastNative
     private native final NativeHandle[] readNativeHandleAsArray();
 
     /**
@@ -537,6 +578,7 @@
      * @return binder object read from parcel or null if no binder can be read
      * @throws IllegalArgumentException if the parcel has no more data
      */
+    @FastNative
     public native final IHwBinder readStrongBinder();
 
     /**
@@ -544,6 +586,7 @@
      * @return blob of size expectedSize
      * @throws IllegalArgumentException if the parcel has no more data
      */
+    @FastNative
     public native final HwBlob readBuffer(long expectedSize);
 
     /**
@@ -559,6 +602,7 @@
      * @throws NullPointerException if the transaction specified the blob to be null
      *    but nullable is false
      */
+    @FastNative
     public native final HwBlob readEmbeddedBuffer(
             long expectedSize, long parentHandle, long offset,
             boolean nullable);
@@ -567,26 +611,31 @@
      * Write a buffer into the transaction.
      * @param blob blob to write into the parcel.
      */
+    @FastNative
     public native final void writeBuffer(HwBlob blob);
     /**
      * Write a status value into the blob.
      * @param status value to write
      */
+    @FastNative
     public native final void writeStatus(int status);
     /**
      * @throws IllegalArgumentException if a success vaue cannot be read
      * @throws RemoteException if success value indicates a transaction error
      */
+    @FastNative
     public native final void verifySuccess();
     /**
      * Should be called to reduce memory pressure when this object no longer needs
      * to be written to.
      */
+    @FastNative
     public native final void releaseTemporaryStorage();
     /**
      * Should be called when object is no longer needed to reduce possible memory
      * pressure if the Java GC does not get to this object in time.
      */
+    @FastNative
     public native final void release();
 
     /**
@@ -597,6 +646,7 @@
     // Returns address of the "freeFunction".
     private static native final long native_init();
 
+    @FastNative
     private native final void native_setup(boolean allocate);
 
     static {
diff --git a/core/java/android/service/euicc/EuiccService.java b/core/java/android/service/euicc/EuiccService.java
index ff8b135..8a9f689 100644
--- a/core/java/android/service/euicc/EuiccService.java
+++ b/core/java/android/service/euicc/EuiccService.java
@@ -15,6 +15,8 @@
  */
 package android.service.euicc;
 
+import static android.telephony.euicc.EuiccCardManager.ResetOption;
+
 import android.annotation.CallSuper;
 import android.annotation.IntDef;
 import android.annotation.NonNull;
@@ -503,7 +505,7 @@
             String nickname);
 
     /**
-     * Erase all of the subscriptions on the device.
+     * Erase all operational subscriptions on the device.
      *
      * <p>This is intended to be used for device resets. As such, the reset should be performed even
      * if an active SIM must be deactivated in order to access the eUICC.
@@ -512,10 +514,31 @@
      * @return the result of the erase operation. May be one of the predefined {@code RESULT_}
      *     constants or any implementation-specific code starting with {@link #RESULT_FIRST_USER}.
      * @see android.telephony.euicc.EuiccManager#eraseSubscriptions
+     *
+     * @deprecated From R, callers should specify a flag for specific set of subscriptions to erase
+     * and use @link{onEraseSubscriptionsWithOptions} instead
      */
+    @Deprecated
     public abstract int onEraseSubscriptions(int slotId);
 
     /**
+     * Erase specific subscriptions on the device.
+     *
+     * <p>This is intended to be used for device resets. As such, the reset should be performed even
+     * if an active SIM must be deactivated in order to access the eUICC.
+     *
+     * @param slotIndex index of the SIM slot to use for the operation.
+     * @param options flag for specific group of subscriptions to erase
+     * @return the result of the erase operation. May be one of the predefined {@code RESULT_}
+     *     constants or any implementation-specific code starting with {@link #RESULT_FIRST_USER}.
+     * @see android.telephony.euicc.EuiccManager#eraseSubscriptionsWithOptions
+     */
+    public int onEraseSubscriptionsWithOptions(int slotIndex, @ResetOption int options) {
+        throw new UnsupportedOperationException(
+                "This method must be overridden to enable the ResetOption parameter");
+    }
+
+    /**
      * Ensure that subscriptions will be retained on the next factory reset.
      *
      * <p>Called directly before a factory reset. Assumes that a normal factory reset will lead to
@@ -751,6 +774,23 @@
         }
 
         @Override
+        public void eraseSubscriptionsWithOptions(
+                int slotIndex, @ResetOption int options, IEraseSubscriptionsCallback callback) {
+            mExecutor.execute(new Runnable() {
+                @Override
+                public void run() {
+                    int result = EuiccService.this.onEraseSubscriptionsWithOptions(
+                            slotIndex, options);
+                    try {
+                        callback.onComplete(result);
+                    } catch (RemoteException e) {
+                        // Can't communicate with the phone process; ignore.
+                    }
+                }
+            });
+        }
+
+        @Override
         public void retainSubscriptionsForFactoryReset(int slotId,
                 IRetainSubscriptionsForFactoryResetCallback callback) {
             mExecutor.execute(new Runnable() {
diff --git a/core/java/android/service/euicc/IEuiccService.aidl b/core/java/android/service/euicc/IEuiccService.aidl
index c2cdf09..2acc47a 100644
--- a/core/java/android/service/euicc/IEuiccService.aidl
+++ b/core/java/android/service/euicc/IEuiccService.aidl
@@ -52,6 +52,8 @@
     void updateSubscriptionNickname(int slotId, String iccid, String nickname,
             in IUpdateSubscriptionNicknameCallback callback);
     void eraseSubscriptions(int slotId, in IEraseSubscriptionsCallback callback);
+    void eraseSubscriptionsWithOptions(
+            int slotIndex, int options, in IEraseSubscriptionsCallback callback);
     void retainSubscriptionsForFactoryReset(
             int slotId, in IRetainSubscriptionsForFactoryResetCallback callback);
 }
\ No newline at end of file
diff --git a/core/java/android/util/ArraySet.java b/core/java/android/util/ArraySet.java
index 44c5af2..4dda709 100644
--- a/core/java/android/util/ArraySet.java
+++ b/core/java/android/util/ArraySet.java
@@ -16,6 +16,7 @@
 
 package android.util;
 
+import android.annotation.Nullable;
 import android.annotation.TestApi;
 import android.annotation.UnsupportedAppUsage;
 
@@ -329,6 +330,18 @@
     }
 
     /**
+     * Create a new ArraySet with items from the given array
+     */
+    public ArraySet(@Nullable E[] array) {
+        this();
+        if (array != null) {
+            for (E value : array) {
+                add(value);
+            }
+        }
+    }
+
+    /**
      * Make the array map empty.  All storage is released.
      */
     @Override
diff --git a/core/java/android/util/FeatureFlagUtils.java b/core/java/android/util/FeatureFlagUtils.java
index b66764e..1be57dd 100644
--- a/core/java/android/util/FeatureFlagUtils.java
+++ b/core/java/android/util/FeatureFlagUtils.java
@@ -59,6 +59,7 @@
         DEFAULT_FLAGS.put("settings_wifi_details_datausage_header", "false");
         DEFAULT_FLAGS.put("settings_skip_direction_mutable", "true");
         DEFAULT_FLAGS.put(SETTINGS_WIFITRACKER2, "false");
+        DEFAULT_FLAGS.put("settings_work_profile", "false");
     }
 
     /**
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index b685cf0..6637c5b0 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -181,7 +181,6 @@
     private static native void nativeSeverChildren(long transactionObj, long nativeObject);
     private static native void nativeSetOverrideScalingMode(long transactionObj, long nativeObject,
             int scalingMode);
-    private static native boolean nativeGetTransformToDisplayInverse(long nativeObject);
 
     private static native Display.HdrCapabilities nativeGetHdrCapabilities(IBinder displayToken);
 
@@ -303,8 +302,8 @@
     /**
      * Surface creation flag: Creates a Dim surface.
      * Everything behind this surface is dimmed by the amount specified
-     * in {@link #setAlpha}.  It is an error to lock a Dim surface, since it
-     * doesn't have a backing store.
+     * in {@link Transaction#setAlpha(SurfaceControl, float)}.  It is an error to lock a Dim
+     * surface, since it doesn't have a backing store.
      *
      * @hide
      */
@@ -740,20 +739,20 @@
      * <p>
      * Good practice is to first create the surface with the {@link #HIDDEN} flag
      * specified, open a transaction, set the surface layer, layer stack, alpha,
-     * and position, call {@link #show} if appropriate, and close the transaction.
+     * and position, call {@link Transaction#show(SurfaceControl)} if appropriate, and close the
+     * transaction.
      * <p>
      * Bounds of the surface is determined by its crop and its buffer size. If the
      * surface has no buffer or crop, the surface is boundless and only constrained
      * by the size of its parent bounds.
      *
-     * @param session The surface session, must not be null.
-     * @param name The surface name, must not be null.
-     * @param w The surface initial width.
-     * @param h The surface initial height.
-     * @param flags The surface creation flags.  Should always include {@link #HIDDEN}
-     * in the creation flags.
+     * @param session  The surface session, must not be null.
+     * @param name     The surface name, must not be null.
+     * @param w        The surface initial width.
+     * @param h        The surface initial height.
+     * @param flags    The surface creation flags.  Should always include {@link #HIDDEN}
+     *                 in the creation flags.
      * @param metadata Initial metadata.
-     *
      * @throws throws OutOfResourcesException If the SurfaceControl cannot be created.
      */
     private SurfaceControl(SurfaceSession session, String name, int w, int h, int format, int flags,
@@ -1014,15 +1013,6 @@
     /**
      * @hide
      */
-    public void deferTransactionUntil(Surface barrier, long frame) {
-        synchronized(SurfaceControl.class) {
-            sGlobalTransaction.deferTransactionUntilSurface(this, barrier, frame);
-        }
-    }
-
-    /**
-     * @hide
-     */
     public void reparentChildren(SurfaceControl newParent) {
         synchronized(SurfaceControl.class) {
             sGlobalTransaction.reparentChildren(this, newParent);
@@ -1032,15 +1022,6 @@
     /**
      * @hide
      */
-    public void reparent(SurfaceControl newParent) {
-        synchronized(SurfaceControl.class) {
-            sGlobalTransaction.reparent(this, newParent);
-        }
-    }
-
-    /**
-     * @hide
-     */
     public void detachChildren() {
         synchronized(SurfaceControl.class) {
             sGlobalTransaction.detachChildren(this);
@@ -1060,15 +1041,6 @@
     /**
      * @hide
      */
-    public static void setAnimationTransaction() {
-        synchronized (SurfaceControl.class) {
-            sGlobalTransaction.setAnimationTransaction();
-        }
-    }
-
-    /**
-     * @hide
-     */
     @UnsupportedAppUsage
     public void setLayer(int zorder) {
         checkNotReleased();
@@ -1080,16 +1052,6 @@
     /**
      * @hide
      */
-    public void setRelativeLayer(SurfaceControl relativeTo, int zorder) {
-        checkNotReleased();
-        synchronized(SurfaceControl.class) {
-            sGlobalTransaction.setRelativeLayer(this, relativeTo, zorder);
-        }
-    }
-
-    /**
-     * @hide
-     */
     @UnsupportedAppUsage
     public void setPosition(float x, float y) {
         checkNotReleased();
@@ -1183,16 +1145,6 @@
     /**
      * @hide
      */
-    public void setColor(@Size(3) float[] color) {
-        checkNotReleased();
-        synchronized (SurfaceControl.class) {
-            sGlobalTransaction.setColor(this, color);
-        }
-    }
-
-    /**
-     * @hide
-     */
     public void setMatrix(float dsdx, float dtdx, float dtdy, float dsdy) {
         checkNotReleased();
         synchronized(SurfaceControl.class) {
@@ -1201,36 +1153,6 @@
     }
 
     /**
-     * Sets the transform and position of a {@link SurfaceControl} from a 3x3 transformation matrix.
-     *
-     * @param matrix The matrix to apply.
-     * @param float9 An array of 9 floats to be used to extract the values from the matrix.
-     * @hide
-     */
-    public void setMatrix(Matrix matrix, float[] float9) {
-        checkNotReleased();
-        matrix.getValues(float9);
-        synchronized (SurfaceControl.class) {
-            sGlobalTransaction.setMatrix(this, float9[MSCALE_X], float9[MSKEW_Y],
-                    float9[MSKEW_X], float9[MSCALE_Y]);
-            sGlobalTransaction.setPosition(this, float9[MTRANS_X], float9[MTRANS_Y]);
-        }
-    }
-
-    /**
-     * Sets the color transform for the Surface.
-     * @param matrix A float array with 9 values represents a 3x3 transform matrix
-     * @param translation A float array with 3 values represents a translation vector
-     * @hide
-     */
-    public void setColorTransform(@Size(9) float[] matrix, @Size(3) float[] translation) {
-        checkNotReleased();
-        synchronized (SurfaceControl.class) {
-            sGlobalTransaction.setColorTransform(this, matrix, translation);
-        }
-    }
-
-    /**
      * Sets the Surface to be color space agnostic. If a surface is color space agnostic,
      * the color can be interpreted in any color space.
      * @param agnostic A boolean to indicate whether the surface is color space agnostic
@@ -1260,43 +1182,6 @@
     }
 
     /**
-     * Same as {@link SurfaceControl#setWindowCrop(Rect)} but sets the crop rect top left at 0, 0.
-     *
-     * @param width width of crop rect
-     * @param height height of crop rect
-     * @hide
-     */
-    public void setWindowCrop(int width, int height) {
-        checkNotReleased();
-        synchronized (SurfaceControl.class) {
-            sGlobalTransaction.setWindowCrop(this, width, height);
-        }
-    }
-
-    /**
-     * Sets the corner radius of a {@link SurfaceControl}.
-     *
-     * @param cornerRadius Corner radius in pixels.
-     * @hide
-     */
-    public void setCornerRadius(float cornerRadius) {
-        checkNotReleased();
-        synchronized (SurfaceControl.class) {
-            sGlobalTransaction.setCornerRadius(this, cornerRadius);
-        }
-    }
-
-    /**
-     * @hide
-     */
-    public void setLayerStack(int layerStack) {
-        checkNotReleased();
-        synchronized(SurfaceControl.class) {
-            sGlobalTransaction.setLayerStack(this, layerStack);
-        }
-    }
-
-    /**
      * @hide
      */
     public void setOpaque(boolean isOpaque) {
@@ -2302,6 +2187,12 @@
         }
 
         /**
+         * Sets the transform and position of a {@link SurfaceControl} from a 3x3 transformation
+         * matrix.
+         *
+         * @param sc     SurfaceControl to set matrix of
+         * @param matrix The matrix to apply.
+         * @param float9 An array of 9 floats to be used to extract the values from the matrix.
          * @hide
          */
         @UnsupportedAppUsage
@@ -2315,7 +2206,9 @@
 
         /**
          * Sets the color transform for the Surface.
-         * @param matrix A float array with 9 values represents a 3x3 transform matrix
+         *
+         * @param sc          SurfaceControl to set color transform of
+         * @param matrix      A float array with 9 values represents a 3x3 transform matrix
          * @param translation A float array with 3 values represents a translation vector
          * @hide
          */
@@ -2339,6 +2232,13 @@
         }
 
         /**
+         * Bounds the surface and its children to the bounds specified. Size of the surface will be
+         * ignored and only the crop and buffer size will be used to determine the bounds of the
+         * surface. If no crop is specified and the surface has no buffer, the surface bounds is
+         * only constrained by the size of its parent bounds.
+         *
+         * @param sc   SurfaceControl to set crop of.
+         * @param crop Bounds of the crop to apply.
          * @hide
          */
         @UnsupportedAppUsage
@@ -2355,6 +2255,12 @@
         }
 
         /**
+         * Same as {@link Transaction#setWindowCrop(SurfaceControl, Rect)} but sets the crop rect
+         * top left at 0, 0.
+         *
+         * @param sc     SurfaceControl to set crop of.
+         * @param width  width of crop rect
+         * @param height height of crop rect
          * @hide
          */
         public Transaction setWindowCrop(SurfaceControl sc, int width, int height) {
diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java
index a858300..2f0a4eb 100644
--- a/core/java/android/view/SurfaceView.java
+++ b/core/java/android/view/SurfaceView.java
@@ -418,12 +418,7 @@
                     Log.d(TAG, System.identityHashCode(this)
                             + " updateSurfaceAlpha: set alpha=" + alpha);
                 }
-                SurfaceControl.openTransaction();
-                try {
-                    mSurfaceControl.setAlpha(alpha);
-                } finally {
-                    SurfaceControl.closeTransaction();
-                }
+                mTmpTransaction.setAlpha(mSurfaceControl, alpha).apply();
             }
             mSurfaceAlpha = alpha;
         }
@@ -701,17 +696,18 @@
         }
     }
 
-    private void updateBackgroundVisibilityInTransaction() {
+    private void updateBackgroundVisibility(Transaction t) {
         if (mBackgroundControl == null) {
             return;
         }
         if ((mSubLayer < 0) && ((mSurfaceFlags & SurfaceControl.OPAQUE) != 0)) {
-            mBackgroundControl.show();
+            t.show(mBackgroundControl);
         } else {
-            mBackgroundControl.hide();
+            t.hide(mBackgroundControl);
         }
     }
 
+
     private void releaseSurfaces() {
         mSurfaceAlpha = 1f;
 
@@ -853,60 +849,60 @@
                     if (DEBUG) Log.i(TAG, System.identityHashCode(this) + " "
                             + "Cur surface: " + mSurface);
 
-                    SurfaceControl.openTransaction();
-                    try {
-                        // If we are creating the surface control or the parent surface has not
-                        // changed, then set relative z. Otherwise allow the parent
-                        // SurfaceChangedCallback to update the relative z. This is needed so that
-                        // we do not change the relative z before the server is ready to swap the
-                        // parent surface.
-                        if (creating || (mParentSurfaceGenerationId
-                                == viewRoot.mSurface.getGenerationId())) {
-                            SurfaceControl.mergeToGlobalTransaction(updateRelativeZ());
-                        }
-                        mParentSurfaceGenerationId = viewRoot.mSurface.getGenerationId();
-
-                        if (mViewVisibility) {
-                            mSurfaceControl.show();
-                        } else {
-                            mSurfaceControl.hide();
-                        }
-                        updateBackgroundVisibilityInTransaction();
-                        if (mUseAlpha) {
-                            mSurfaceControl.setAlpha(alpha);
-                            mSurfaceAlpha = alpha;
-                        }
-
-                        // While creating the surface, we will set it's initial
-                        // geometry. Outside of that though, we should generally
-                        // leave it to the RenderThread.
-                        //
-                        // There is one more case when the buffer size changes we aren't yet
-                        // prepared to sync (as even following the transaction applying
-                        // we still need to latch a buffer).
-                        // b/28866173
-                        if (sizeChanged || creating || !mRtHandlingPositionUpdates) {
-                            mSurfaceControl.setPosition(mScreenRect.left, mScreenRect.top);
-                            mSurfaceControl.setMatrix(mScreenRect.width() / (float) mSurfaceWidth,
-                                    0.0f, 0.0f,
-                                    mScreenRect.height() / (float) mSurfaceHeight);
-                            // Set a window crop when creating the surface or changing its size to
-                            // crop the buffer to the surface size since the buffer producer may
-                            // use SCALING_MODE_SCALE and submit a larger size than the surface
-                            // size.
-                            if (mClipSurfaceToBounds && mClipBounds != null) {
-                                mSurfaceControl.setWindowCrop(mClipBounds);
-                            } else {
-                                mSurfaceControl.setWindowCrop(mSurfaceWidth, mSurfaceHeight);
-                            }
-                        }
-                        mSurfaceControl.setCornerRadius(mCornerRadius);
-                        if (sizeChanged && !creating) {
-                            mSurfaceControl.setBufferSize(mSurfaceWidth, mSurfaceHeight);
-                        }
-                    } finally {
-                        SurfaceControl.closeTransaction();
+                    // If we are creating the surface control or the parent surface has not
+                    // changed, then set relative z. Otherwise allow the parent
+                    // SurfaceChangedCallback to update the relative z. This is needed so that
+                    // we do not change the relative z before the server is ready to swap the
+                    // parent surface.
+                    if (creating || (mParentSurfaceGenerationId
+                            == viewRoot.mSurface.getGenerationId())) {
+                        updateRelativeZ(mTmpTransaction);
                     }
+                    mParentSurfaceGenerationId = viewRoot.mSurface.getGenerationId();
+
+                    if (mViewVisibility) {
+                        mTmpTransaction.show(mSurfaceControl);
+                    } else {
+                        mTmpTransaction.hide(mSurfaceControl);
+                    }
+                    updateBackgroundVisibility(mTmpTransaction);
+                    if (mUseAlpha) {
+                        mTmpTransaction.setAlpha(mSurfaceControl, alpha);
+                        mSurfaceAlpha = alpha;
+                    }
+
+                    // While creating the surface, we will set it's initial
+                    // geometry. Outside of that though, we should generally
+                    // leave it to the RenderThread.
+                    //
+                    // There is one more case when the buffer size changes we aren't yet
+                    // prepared to sync (as even following the transaction applying
+                    // we still need to latch a buffer).
+                    // b/28866173
+                    if (sizeChanged || creating || !mRtHandlingPositionUpdates) {
+                        mTmpTransaction.setPosition(mSurfaceControl, mScreenRect.left,
+                                mScreenRect.top);
+                        mTmpTransaction.setMatrix(mSurfaceControl,
+                                mScreenRect.width() / (float) mSurfaceWidth, 0.0f, 0.0f,
+                                mScreenRect.height() / (float) mSurfaceHeight);
+                        // Set a window crop when creating the surface or changing its size to
+                        // crop the buffer to the surface size since the buffer producer may
+                        // use SCALING_MODE_SCALE and submit a larger size than the surface
+                        // size.
+                        if (mClipSurfaceToBounds && mClipBounds != null) {
+                            mTmpTransaction.setWindowCrop(mSurfaceControl, mClipBounds);
+                        } else {
+                            mTmpTransaction.setWindowCrop(mSurfaceControl, mSurfaceWidth,
+                                    mSurfaceHeight);
+                        }
+                    }
+                    mTmpTransaction.setCornerRadius(mSurfaceControl, mCornerRadius);
+                    if (sizeChanged && !creating) {
+                        mTmpTransaction.setBufferSize(mSurfaceControl, mSurfaceWidth,
+                                mSurfaceHeight);
+                    }
+
+                    mTmpTransaction.apply();
 
                     if (sizeChanged || creating) {
                         redrawNeeded = true;
@@ -1260,12 +1256,7 @@
         final float[] colorComponents = new float[] { Color.red(bgColor) / 255.f,
                 Color.green(bgColor) / 255.f, Color.blue(bgColor) / 255.f };
 
-        SurfaceControl.openTransaction();
-        try {
-            mBackgroundControl.setColor(colorComponents);
-        } finally {
-            SurfaceControl.closeTransaction();
-        }
+        mTmpTransaction.setColor(mBackgroundControl, colorComponents).apply();
     }
 
     @UnsupportedAppUsage
@@ -1480,15 +1471,13 @@
     @Override
     public void surfaceReplaced(Transaction t) {
         if (mSurfaceControl != null && mBackgroundControl != null) {
-            t.merge(updateRelativeZ());
+            updateRelativeZ(t);
         }
     }
 
-    private Transaction updateRelativeZ() {
-        Transaction t = new Transaction();
+    private void updateRelativeZ(Transaction t) {
         SurfaceControl viewRoot = getViewRootImpl().getSurfaceControl();
         t.setRelativeLayer(mBackgroundControl, viewRoot, Integer.MIN_VALUE);
         t.setRelativeLayer(mSurfaceControl, viewRoot, mSubLayer);
-        return t;
     }
 }
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index cfb6a79a..c5d0a9b0 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -4496,8 +4496,9 @@
      * When non-null and valid, this is expected to contain an up-to-date copy
      * of the background drawable. It is cleared on temporary detach, and reset
      * on cleanup.
+     * @hide
      */
-    private RenderNode mBackgroundRenderNode;
+    RenderNode mBackgroundRenderNode;
 
     @UnsupportedAppUsage
     private int mBackgroundResource;
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index fedd6fb..cf06985 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -7297,26 +7297,42 @@
         }
     }
 
-    public void dumpGfxInfo(int[] info) {
-        info[0] = info[1] = 0;
-        if (mView != null) {
-            getGfxInfo(mView, info);
+    static final class GfxInfo {
+        public int viewCount;
+        public long renderNodeMemoryUsage;
+        public long renderNodeMemoryAllocated;
+
+        void add(GfxInfo other) {
+            viewCount += other.viewCount;
+            renderNodeMemoryUsage += other.renderNodeMemoryUsage;
+            renderNodeMemoryAllocated += other.renderNodeMemoryAllocated;
         }
     }
 
-    private static void getGfxInfo(View view, int[] info) {
-        RenderNode renderNode = view.mRenderNode;
-        info[0]++;
-        if (renderNode != null) {
-            info[1] += (int) renderNode.computeApproximateMemoryUsage();
+    GfxInfo getGfxInfo() {
+        GfxInfo info = new GfxInfo();
+        if (mView != null) {
+            appendGfxInfo(mView, info);
         }
+        return info;
+    }
 
+    private static void computeRenderNodeUsage(RenderNode node, GfxInfo info) {
+        if (node == null) return;
+        info.renderNodeMemoryUsage += node.computeApproximateMemoryUsage();
+        info.renderNodeMemoryAllocated += node.computeApproximateMemoryAllocated();
+    }
+
+    private static void appendGfxInfo(View view, GfxInfo info) {
+        info.viewCount++;
+        computeRenderNodeUsage(view.mRenderNode, info);
+        computeRenderNodeUsage(view.mBackgroundRenderNode, info);
         if (view instanceof ViewGroup) {
             ViewGroup group = (ViewGroup) view;
 
             int count = group.getChildCount();
             for (int i = 0; i < count; i++) {
-                getGfxInfo(group.getChildAt(i), info);
+                appendGfxInfo(group.getChildAt(i), info);
             }
         }
     }
diff --git a/core/java/android/view/WindowManagerGlobal.java b/core/java/android/view/WindowManagerGlobal.java
index 379acbe..55b2a2a 100644
--- a/core/java/android/view/WindowManagerGlobal.java
+++ b/core/java/android/view/WindowManagerGlobal.java
@@ -604,26 +604,24 @@
 
                 pw.println("\nView hierarchy:\n");
 
-                int viewsCount = 0;
-                int displayListsSize = 0;
-                int[] info = new int[2];
+                ViewRootImpl.GfxInfo totals = new ViewRootImpl.GfxInfo();
 
                 for (int i = 0; i < count; i++) {
                     ViewRootImpl root = mRoots.get(i);
-                    root.dumpGfxInfo(info);
+                    ViewRootImpl.GfxInfo info = root.getGfxInfo();
+                    totals.add(info);
 
                     String name = getWindowName(root);
-                    pw.printf("  %s\n  %d views, %.2f kB of display lists",
-                            name, info[0], info[1] / 1024.0f);
+                    pw.printf("  %s\n  %d views, %.2f kB of render nodes",
+                            name, info.viewCount, info.renderNodeMemoryUsage / 1024.f);
                     pw.printf("\n\n");
-
-                    viewsCount += info[0];
-                    displayListsSize += info[1];
                 }
 
-                pw.printf("\nTotal ViewRootImpl: %d\n", count);
-                pw.printf("Total Views:        %d\n", viewsCount);
-                pw.printf("Total DisplayList:  %.2f kB\n\n", displayListsSize / 1024.0f);
+                pw.printf("\nTotal %-15s: %d\n", "ViewRootImpl", count);
+                pw.printf("Total %-15s: %d\n", "attached Views", totals.viewCount);
+                pw.printf("Total %-15s: %.2f kB (used) / %.2f kB (capacity)\n\n", "RenderNode",
+                        totals.renderNodeMemoryUsage / 1024.0f,
+                        totals.renderNodeMemoryAllocated / 1024.0f);
             }
         } finally {
             pw.flush();
diff --git a/core/java/com/android/internal/os/KernelWakelockReader.java b/core/java/com/android/internal/os/KernelWakelockReader.java
index e09e0e6..cffb0ad 100644
--- a/core/java/com/android/internal/os/KernelWakelockReader.java
+++ b/core/java/com/android/internal/os/KernelWakelockReader.java
@@ -29,6 +29,7 @@
 
 import java.io.File;
 import java.io.FileInputStream;
+import java.util.Arrays;
 import java.util.Iterator;
 
 /**
@@ -66,6 +67,7 @@
     private final String[] mProcWakelocksName = new String[3];
     private final long[] mProcWakelocksData = new long[3];
     private ISuspendControlService mSuspendControlService = null;
+    private byte[] mKernelWakelockBuffer = new byte[32 * 1024];
 
     /**
      * Reads kernel wakelock stats and updates the staleStats with the new information.
@@ -84,7 +86,7 @@
             }
             return removeOldStats(staleStats);
         } else {
-            byte[] buffer = new byte[32*1024];
+            Arrays.fill(mKernelWakelockBuffer, (byte) 0);
             int len = 0;
             boolean wakeup_sources;
             final long startTime = SystemClock.uptimeMillis();
@@ -107,7 +109,8 @@
                 }
 
                 int cnt;
-                while ((cnt = is.read(buffer, len, buffer.length - len)) > 0) {
+                while ((cnt = is.read(mKernelWakelockBuffer, len,
+                                mKernelWakelockBuffer.length - len)) > 0) {
                     len += cnt;
                 }
 
@@ -125,12 +128,13 @@
             }
 
             if (len > 0) {
-                if (len >= buffer.length) {
-                    Slog.wtf(TAG, "Kernel wake locks exceeded buffer size " + buffer.length);
+                if (len >= mKernelWakelockBuffer.length) {
+                    Slog.wtf(TAG, "Kernel wake locks exceeded mKernelWakelockBuffer size "
+                            + mKernelWakelockBuffer.length);
                 }
                 int i;
                 for (i=0; i<len; i++) {
-                    if (buffer[i] == '\0') {
+                    if (mKernelWakelockBuffer[i] == '\0') {
                         len = i;
                         break;
                     }
@@ -143,7 +147,7 @@
                 Slog.w(TAG, "Failed to get Native wakelock stats from SystemSuspend");
             }
             // Get kernel wakelock stats
-            parseProcWakelocks(buffer, len, wakeup_sources, staleStats);
+            parseProcWakelocks(mKernelWakelockBuffer, len, wakeup_sources, staleStats);
             return removeOldStats(staleStats);
         }
     }
diff --git a/core/jni/Android.bp b/core/jni/Android.bp
index 0505fe3..0feab7f 100644
--- a/core/jni/Android.bp
+++ b/core/jni/Android.bp
@@ -257,8 +257,6 @@
                 "libnativeloader_lazy",
                 "libmemunreachable",
                 "libhidlbase",
-                "libhidltransport",
-                "libhwbinder",
                 "libvintf",
                 "libnativewindow",
                 "libdl",
diff --git a/core/jni/android_view_RenderNode.cpp b/core/jni/android_view_RenderNode.cpp
index 222a873..538861e 100644
--- a/core/jni/android_view_RenderNode.cpp
+++ b/core/jni/android_view_RenderNode.cpp
@@ -52,9 +52,14 @@
     renderNode->output();
 }
 
-static jint android_view_RenderNode_getDebugSize(JNIEnv* env, jobject clazz, jlong renderNodePtr) {
+static jint android_view_RenderNode_getUsageSize(JNIEnv* env, jobject clazz, jlong renderNodePtr) {
     RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr);
-    return renderNode->getDebugSize();
+    return renderNode->getUsageSize();
+}
+
+static jint android_view_RenderNode_getAllocatedSize(JNIEnv* env, jobject clazz, jlong renderNodePtr) {
+    RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr);
+    return renderNode->getAllocatedSize();
 }
 
 static jlong android_view_RenderNode_create(JNIEnv* env, jobject, jstring name) {
@@ -647,7 +652,8 @@
     { "nCreate",               "(Ljava/lang/String;)J", (void*) android_view_RenderNode_create },
     { "nGetNativeFinalizer",   "()J",    (void*) android_view_RenderNode_getNativeFinalizer },
     { "nOutput",               "(J)V",    (void*) android_view_RenderNode_output },
-    { "nGetDebugSize",         "(J)I",    (void*) android_view_RenderNode_getDebugSize },
+    { "nGetUsageSize",         "(J)I",    (void*) android_view_RenderNode_getUsageSize },
+    { "nGetAllocatedSize",         "(J)I",    (void*) android_view_RenderNode_getAllocatedSize },
     { "nAddAnimator",              "(JJ)V", (void*) android_view_RenderNode_addAnimator },
     { "nEndAllAnimators",          "(J)V", (void*) android_view_RenderNode_endAllAnimators },
     { "nRequestPositionUpdates",   "(JLandroid/graphics/RenderNode$PositionUpdateListener;)V", (void*) android_view_RenderNode_requestPositionUpdates },
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 18f8d7b..b030b33 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -798,7 +798,7 @@
          broadcast module. This is required in order to bind to the cell broadcast service, and
          ensures that only the system can forward messages to it.
 
-         <p>Protection level: signature|privileged
+         <p>Protection level: signature
 
          @hide -->
     <permission android:name="android.permission.BIND_CELL_BROADCAST_SERVICE"
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 7eca699..3fef7a2d 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -433,6 +433,14 @@
         -->
     </string-array>
 
+    <!-- Configuration of network interfaces that support WakeOnLAN -->
+    <string-array translatable="false" name="config_wakeonlan_supported_interfaces">
+        <!--
+        <item>wlan0</item>
+        <item>eth0</item>
+        -->
+    </string-array>
+
     <!-- Package name for the default CellBroadcastService module [DO NOT TRANSLATE] -->
     <string name="cellbroadcast_default_package" translatable="false">com.android.cellbroadcastreceiver
     </string>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index a8b5340..363bc9d 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -747,6 +747,7 @@
   <java-symbol type="string" name="config_default_dns_server" />
   <java-symbol type="string" name="config_ethernet_iface_regex" />
   <java-symbol type="array" name="config_ethernet_interfaces" />
+  <java-symbol type="array" name="config_wakeonlan_supported_interfaces" />
   <java-symbol type="string" name="cellbroadcast_default_package" />
   <java-symbol type="string" name="config_forceVoiceInteractionServicePackage" />
   <java-symbol type="string" name="config_mms_user_agent" />
diff --git a/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java b/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java
index 51da0c8..39bf742 100644
--- a/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java
+++ b/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java
@@ -611,6 +611,10 @@
         }
 
         @Override
+        public void attachStartupAgents(String s) throws RemoteException {
+        }
+
+        @Override
         public void scheduleApplicationInfoChanged(ApplicationInfo applicationInfo)
                 throws RemoteException {
         }
diff --git a/graphics/java/android/graphics/RenderNode.java b/graphics/java/android/graphics/RenderNode.java
index 0e635c7..17e3b44 100644
--- a/graphics/java/android/graphics/RenderNode.java
+++ b/graphics/java/android/graphics/RenderNode.java
@@ -1380,7 +1380,22 @@
      * @return Approximate memory usage in bytes.
      */
     public @BytesLong long computeApproximateMemoryUsage() {
-        return nGetDebugSize(mNativeRenderNode);
+        return nGetUsageSize(mNativeRenderNode);
+    }
+
+    /**
+     * Gets the approximate amount of memory allocated for the RenderNode for debug purposes.
+     * Does not include the memory allocated by any child RenderNodes nor any bitmaps, only the
+     * memory allocated for this RenderNode and any data it owns.
+     *
+     * The difference between this and {@link #computeApproximateMemoryUsage()} is this includes
+     * memory allocated but not used. In particular structures such as DisplayLists are similar
+     * to things like ArrayLists - they need to resize as commands are added to them. As such,
+     * memory used can be less than memory allocated.
+     *
+     * @hide */
+    public @BytesLong long computeApproximateMemoryAllocated() {
+        return nGetAllocatedSize(mNativeRenderNode);
     }
 
     /**
@@ -1485,7 +1500,8 @@
 
     private static native void nOutput(long renderNode);
 
-    private static native int nGetDebugSize(long renderNode);
+    private static native int nGetUsageSize(long renderNode);
+    private static native int nGetAllocatedSize(long renderNode);
 
     private static native void nRequestPositionUpdates(long renderNode,
             PositionUpdateListener callback);
diff --git a/libs/hwui/RecordingCanvas.h b/libs/hwui/RecordingCanvas.h
index a79b7c0..322eff2 100644
--- a/libs/hwui/RecordingCanvas.h
+++ b/libs/hwui/RecordingCanvas.h
@@ -69,6 +69,7 @@
 
     bool hasText() const { return mHasText; }
     size_t usedSize() const { return fUsed; }
+    size_t allocatedSize() const { return fReserved; }
 
 private:
     friend class RecordingCanvas;
diff --git a/libs/hwui/RenderNode.cpp b/libs/hwui/RenderNode.cpp
index 8eb5e3d..6761435 100644
--- a/libs/hwui/RenderNode.cpp
+++ b/libs/hwui/RenderNode.cpp
@@ -108,7 +108,7 @@
     output << std::endl;
 }
 
-int RenderNode::getDebugSize() {
+int RenderNode::getUsageSize() {
     int size = sizeof(RenderNode);
     if (mStagingDisplayList) {
         size += mStagingDisplayList->getUsedSize();
@@ -119,6 +119,18 @@
     return size;
 }
 
+int RenderNode::getAllocatedSize() {
+    int size = sizeof(RenderNode);
+    if (mStagingDisplayList) {
+        size += mStagingDisplayList->getAllocatedSize();
+    }
+    if (mDisplayList && mDisplayList != mStagingDisplayList) {
+        size += mDisplayList->getAllocatedSize();
+    }
+    return size;
+}
+
+
 void RenderNode::prepareTree(TreeInfo& info) {
     ATRACE_CALL();
     LOG_ALWAYS_FATAL_IF(!info.damageAccumulator, "DamageAccumulator missing");
diff --git a/libs/hwui/RenderNode.h b/libs/hwui/RenderNode.h
index c6db7f1..d55e5b0 100644
--- a/libs/hwui/RenderNode.h
+++ b/libs/hwui/RenderNode.h
@@ -102,7 +102,8 @@
     ANDROID_API void setStagingDisplayList(DisplayList* newData);
 
     ANDROID_API void output();
-    ANDROID_API int getDebugSize();
+    ANDROID_API int getUsageSize();
+    ANDROID_API int getAllocatedSize();
 
     bool isRenderable() const { return mDisplayList && !mDisplayList->isEmpty(); }
 
diff --git a/libs/hwui/pipeline/skia/SkiaDisplayList.h b/libs/hwui/pipeline/skia/SkiaDisplayList.h
index e3c3273..cdd00db 100644
--- a/libs/hwui/pipeline/skia/SkiaDisplayList.h
+++ b/libs/hwui/pipeline/skia/SkiaDisplayList.h
@@ -47,6 +47,7 @@
 class SkiaDisplayList {
 public:
     size_t getUsedSize() { return allocator.usedSize() + mDisplayList.usedSize(); }
+    size_t getAllocatedSize() { return allocator.allocatedSize() + mDisplayList.allocatedSize(); }
 
     ~SkiaDisplayList() {
         /* Given that we are using a LinearStdAllocator to store some of the
diff --git a/libs/hwui/utils/LinearAllocator.h b/libs/hwui/utils/LinearAllocator.h
index 9c4a1be..539e654 100644
--- a/libs/hwui/utils/LinearAllocator.h
+++ b/libs/hwui/utils/LinearAllocator.h
@@ -115,6 +115,7 @@
      * wasted)
      */
     size_t usedSize() const { return mTotalAllocated - mWastedSpace; }
+    size_t allocatedSize() const { return mTotalAllocated; }
 
 private:
     LinearAllocator(const LinearAllocator& other);
diff --git a/location/java/android/location/LocationManager.java b/location/java/android/location/LocationManager.java
index f3d6875..90e29df 100644
--- a/location/java/android/location/LocationManager.java
+++ b/location/java/android/location/LocationManager.java
@@ -145,7 +145,7 @@
      * Key used for an extra holding a boolean enabled/disabled status value when a provider
      * enabled/disabled event is broadcast using a PendingIntent.
      *
-     * @see #requestLocationUpdates(String, long, long, PendingIntent)
+     * @see #requestLocationUpdates(String, long, float, PendingIntent)
      */
     public static final String KEY_PROVIDER_ENABLED = "providerEnabled";
 
@@ -153,7 +153,7 @@
      * Key used for an extra holding a {@link Location} value when a location change is broadcast
      * using a PendingIntent.
      *
-     * @see #requestLocationUpdates(String, long, long, PendingIntent)
+     * @see #requestLocationUpdates(String, long, float, PendingIntent)
      */
     public static final String KEY_LOCATION_CHANGED = "location";
 
@@ -1256,44 +1256,46 @@
     }
 
     /**
-     * Creates a mock location provider and adds it to the set of active providers.
+     * Creates a test location provider and adds it to the set of active providers. This provider
+     * will replace any provider with the same name that exists prior to this call.
      *
-     * @param name the provider name
+     * @param provider the provider name
      *
+     * @throws IllegalArgumentException if provider is null
      * @throws SecurityException if {@link android.app.AppOpsManager#OPSTR_MOCK_LOCATION
      * mock location app op} is not set to {@link android.app.AppOpsManager#MODE_ALLOWED
      * allowed} for your app.
-     * @throws IllegalArgumentException if a provider with the given name already exists
      */
     public void addTestProvider(
-            @NonNull String name, boolean requiresNetwork, boolean requiresSatellite,
+            @NonNull String provider, boolean requiresNetwork, boolean requiresSatellite,
             boolean requiresCell, boolean hasMonetaryCost, boolean supportsAltitude,
             boolean supportsSpeed, boolean supportsBearing, int powerRequirement, int accuracy) {
+        Preconditions.checkArgument(provider != null, "invalid null provider");
+
         ProviderProperties properties = new ProviderProperties(requiresNetwork,
                 requiresSatellite, requiresCell, hasMonetaryCost, supportsAltitude, supportsSpeed,
                 supportsBearing, powerRequirement, accuracy);
-        if (name.matches(LocationProvider.BAD_CHARS_REGEX)) {
-            throw new IllegalArgumentException("provider name contains illegal character: " + name);
-        }
-
         try {
-            mService.addTestProvider(name, properties, mContext.getOpPackageName());
+            mService.addTestProvider(provider, properties, mContext.getOpPackageName());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
     }
 
     /**
-     * Removes the mock location provider with the given name.
+     * Removes the test location provider with the given name or does nothing if no such test
+     * location provider exists.
      *
      * @param provider the provider name
      *
+     * @throws IllegalArgumentException if provider is null
      * @throws SecurityException if {@link android.app.AppOpsManager#OPSTR_MOCK_LOCATION
      * mock location app op} is not set to {@link android.app.AppOpsManager#MODE_ALLOWED
      * allowed} for your app.
-     * @throws IllegalArgumentException if no provider with the given name exists
      */
     public void removeTestProvider(@NonNull String provider) {
+        Preconditions.checkArgument(provider != null, "invalid null provider");
+
         try {
             mService.removeTestProvider(provider, mContext.getOpPackageName());
         } catch (RemoteException e) {
@@ -1302,60 +1304,53 @@
     }
 
     /**
-     * Sets a mock location for the given provider.
-     * <p>This location will be used in place of any actual location from the provider.
-     * The location object must have a minimum number of fields set to be
-     * considered a valid LocationProvider Location, as per documentation
-     * on {@link Location} class.
+     * Sets a new location for the given test provider. This location will be identiable as a mock
+     * location to all clients via {@link Location#isFromMockProvider()}.
+     *
+     * <p>The location object must have a minimum number of fields set to be considered valid, as
+     * per documentation on {@link Location} class.
      *
      * @param provider the provider name
-     * @param loc the mock location
+     * @param location the mock location
      *
      * @throws SecurityException if {@link android.app.AppOpsManager#OPSTR_MOCK_LOCATION
      * mock location app op} is not set to {@link android.app.AppOpsManager#MODE_ALLOWED
      * allowed} for your app.
-     * @throws IllegalArgumentException if no provider with the given name exists
-     * @throws IllegalArgumentException if the location is incomplete
+     * @throws IllegalArgumentException if the provider is null or not a test provider
+     * @throws IllegalArgumentException if the location is null or incomplete
      */
-    public void setTestProviderLocation(@NonNull String provider, @NonNull Location loc) {
-        if (!loc.isComplete()) {
+    public void setTestProviderLocation(@NonNull String provider, @NonNull Location location) {
+        Preconditions.checkArgument(provider != null, "invalid null provider");
+        Preconditions.checkArgument(location != null, "invalid null location");
+
+        if (!location.isComplete()) {
             IllegalArgumentException e = new IllegalArgumentException(
-                    "Incomplete location object, missing timestamp or accuracy? " + loc);
+                    "Incomplete location object, missing timestamp or accuracy? " + location);
             if (mContext.getApplicationInfo().targetSdkVersion <= Build.VERSION_CODES.JELLY_BEAN) {
-                // just log on old platform (for backwards compatibility)
                 Log.w(TAG, e);
-                loc.makeComplete();
+                location.makeComplete();
             } else {
-                // really throw it!
                 throw e;
             }
         }
 
         try {
-            mService.setTestProviderLocation(provider, loc, mContext.getOpPackageName());
+            mService.setTestProviderLocation(provider, location, mContext.getOpPackageName());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
     }
 
     /**
-     * Removes any mock location associated with the given provider.
+     * Does nothing.
      *
-     * @param provider the provider name
-     *
-     * @throws SecurityException if {@link android.app.AppOpsManager#OPSTR_MOCK_LOCATION
-     * mock location app op} is not set to {@link android.app.AppOpsManager#MODE_ALLOWED
-     * allowed} for your app.
-     * @throws IllegalArgumentException if no provider with the given name exists
-     *
-     * @deprecated This function has always been a no-op, and may be removed in the future.
+     * @deprecated This method has always been a no-op, and may be removed in the future.
      */
     @Deprecated
     public void clearTestProviderLocation(@NonNull String provider) {}
 
     /**
-     * Sets a mock enabled value for the given provider.  This value will be used in place
-     * of any actual value from the provider.
+     * Sets the given test provider to be enabled or disabled.
      *
      * @param provider the provider name
      * @param enabled the mock enabled value
@@ -1363,9 +1358,11 @@
      * @throws SecurityException if {@link android.app.AppOpsManager#OPSTR_MOCK_LOCATION
      * mock location app op} is not set to {@link android.app.AppOpsManager#MODE_ALLOWED
      * allowed} for your app.
-     * @throws IllegalArgumentException if no provider with the given name exists
+     * @throws IllegalArgumentException if provider is null or not a test provider
      */
     public void setTestProviderEnabled(@NonNull String provider, boolean enabled) {
+        Preconditions.checkArgument(provider != null, "invalid null provider");
+
         try {
             mService.setTestProviderEnabled(provider, enabled, mContext.getOpPackageName());
         } catch (RemoteException e) {
@@ -1374,14 +1371,8 @@
     }
 
     /**
-     * Removes any mock enabled value associated with the given provider.
-     *
-     * @param provider the provider name
-     *
-     * @throws SecurityException if {@link android.app.AppOpsManager#OPSTR_MOCK_LOCATION
-     * mock location app op} is not set to {@link android.app.AppOpsManager#MODE_ALLOWED
-     * allowed} for your app.
-     * @throws IllegalArgumentException if no provider with the given name exists
+     * Equivalent to calling {@link #setTestProviderEnabled(String, boolean)} to disable a test
+     * provider.
      *
      * @deprecated Use {@link #setTestProviderEnabled(String, boolean)} instead.
      */
diff --git a/location/java/android/location/LocationProvider.java b/location/java/android/location/LocationProvider.java
index b69a9d7..52a03b6 100644
--- a/location/java/android/location/LocationProvider.java
+++ b/location/java/android/location/LocationProvider.java
@@ -53,28 +53,10 @@
     @Deprecated
     public static final int AVAILABLE = 2;
 
-    /**
-     * A regular expression matching characters that may not appear
-     * in the name of a LocationProvider
-     * @hide
-     */
-    public static final String BAD_CHARS_REGEX = "[^a-zA-Z0-9]";
-
     private final String mName;
     private final ProviderProperties mProperties;
 
-    /**
-     * Constructs a LocationProvider with the given name.   Provider names must
-     * consist only of the characters [a-zA-Z0-9].
-     *
-     * @throws IllegalArgumentException if name contains an illegal character
-     *
-     * @hide
-     */
-    public LocationProvider(String name, ProviderProperties properties) {
-        if (name.matches(BAD_CHARS_REGEX)) {
-            throw new IllegalArgumentException("provider name contains illegal character: " + name);
-        }
+    LocationProvider(String name, ProviderProperties properties) {
         mName = name;
         mProperties = properties;
     }
diff --git a/media/java/android/media/MediaMetadataRetriever.java b/media/java/android/media/MediaMetadataRetriever.java
index 0346010..f421029 100644
--- a/media/java/android/media/MediaMetadataRetriever.java
+++ b/media/java/android/media/MediaMetadataRetriever.java
@@ -227,6 +227,44 @@
     public native String extractMetadata(int keyCode);
 
     /**
+     * This method is similar to {@link #getFrameAtTime(long, int, BitmapParams)}
+     * except that the device will choose the actual {@link Bitmap.Config} to use.
+     *
+     * @param timeUs The time position where the frame will be retrieved.
+     * When retrieving the frame at the given time position, there is no
+     * guarantee that the data source has a frame located at the position.
+     * When this happens, a frame nearby will be returned. If timeUs is
+     * negative, time position and option will ignored, and any frame
+     * that the implementation considers as representative may be returned.
+     *
+     * @param option a hint on how the frame is found. Use
+     * {@link #OPTION_PREVIOUS_SYNC} if one wants to retrieve a sync frame
+     * that has a timestamp earlier than or the same as timeUs. Use
+     * {@link #OPTION_NEXT_SYNC} if one wants to retrieve a sync frame
+     * that has a timestamp later than or the same as timeUs. Use
+     * {@link #OPTION_CLOSEST_SYNC} if one wants to retrieve a sync frame
+     * that has a timestamp closest to or the same as timeUs. Use
+     * {@link #OPTION_CLOSEST} if one wants to retrieve a frame that may
+     * or may not be a sync frame but is closest to or the same as timeUs.
+     * {@link #OPTION_CLOSEST} often has larger performance overhead compared
+     * to the other options if there is no sync frame located at timeUs.
+     *
+     * @return A Bitmap containing a representative video frame, which can be null,
+     *         if such a frame cannot be retrieved. {@link Bitmap#getConfig()} can
+     *         be used to query the actual {@link Bitmap.Config}.
+     *
+     * @see {@link #getFrameAtTime(long, int, BitmapParams)}
+     */
+    public Bitmap getFrameAtTime(long timeUs, @Option int option) {
+        if (option < OPTION_PREVIOUS_SYNC ||
+            option > OPTION_CLOSEST) {
+            throw new IllegalArgumentException("Unsupported option: " + option);
+        }
+
+        return _getFrameAtTime(timeUs, option, -1 /*dst_width*/, -1 /*dst_height*/, null);
+    }
+
+    /**
      * Call this method after setDataSource(). This method finds a
      * representative frame close to the given time position by considering
      * the given option if possible, and returns it as a bitmap.
@@ -255,16 +293,60 @@
      * {@link #OPTION_CLOSEST} often has larger performance overhead compared
      * to the other options if there is no sync frame located at timeUs.
      *
+     * @param params BitmapParams that controls the returned bitmap config
+     *        (such as pixel formats).
+     *
      * @return A Bitmap containing a representative video frame, which
      *         can be null, if such a frame cannot be retrieved.
+     *
+     * @see {@link #getFrameAtTime(long, int)}
      */
-    public Bitmap getFrameAtTime(long timeUs, @Option int option) {
+    public Bitmap getFrameAtTime(
+            long timeUs, @Option int option, @NonNull BitmapParams params) {
         if (option < OPTION_PREVIOUS_SYNC ||
             option > OPTION_CLOSEST) {
             throw new IllegalArgumentException("Unsupported option: " + option);
         }
 
-        return _getFrameAtTime(timeUs, option, -1 /*dst_width*/, -1 /*dst_height*/);
+        return _getFrameAtTime(timeUs, option, -1 /*dst_width*/, -1 /*dst_height*/, params);
+    }
+
+    /**
+     * This method is similar to {@link #getScaledFrameAtTime(long, int, int, int, BitmapParams)}
+     * except that the device will choose the actual {@link Bitmap.Config} to use.
+     *
+     * @param timeUs The time position in microseconds where the frame will be retrieved.
+     * When retrieving the frame at the given time position, there is no
+     * guarantee that the data source has a frame located at the position.
+     * When this happens, a frame nearby will be returned. If timeUs is
+     * negative, time position and option will ignored, and any frame
+     * that the implementation considers as representative may be returned.
+     *
+     * @param option a hint on how the frame is found. Use
+     * {@link #OPTION_PREVIOUS_SYNC} if one wants to retrieve a sync frame
+     * that has a timestamp earlier than or the same as timeUs. Use
+     * {@link #OPTION_NEXT_SYNC} if one wants to retrieve a sync frame
+     * that has a timestamp later than or the same as timeUs. Use
+     * {@link #OPTION_CLOSEST_SYNC} if one wants to retrieve a sync frame
+     * that has a timestamp closest to or the same as timeUs. Use
+     * {@link #OPTION_CLOSEST} if one wants to retrieve a frame that may
+     * or may not be a sync frame but is closest to or the same as timeUs.
+     * {@link #OPTION_CLOSEST} often has larger performance overhead compared
+     * to the other options if there is no sync frame located at timeUs.
+     *
+     * @param dstWidth expected output bitmap width
+     * @param dstHeight expected output bitmap height
+     * @return A Bitmap containing a representative video frame, which can be null,
+     *         if such a frame cannot be retrieved. {@link Bitmap#getConfig()} can
+     *         be used to query the actual {@link Bitmap.Config}.
+     * @throws IllegalArgumentException if passed in invalid option or width by height
+     *         is less than or equal to 0.
+     * @see {@link #getScaledFrameAtTime(long, int, int, int, BitmapParams)}
+     */
+    public Bitmap getScaledFrameAtTime(
+            long timeUs, @Option int option, int dstWidth, int dstHeight) {
+        validate(option, dstWidth, dstHeight);
+        return _getFrameAtTime(timeUs, option, dstWidth, dstHeight, null);
     }
 
     /**
@@ -297,15 +379,23 @@
      *
      * @param dstWidth expected output bitmap width
      * @param dstHeight expected output bitmap height
+     * @param params BitmapParams that controls the returned bitmap config
+     *        (such as pixel formats).
+     *
      * @return A Bitmap of size not larger than dstWidth by dstHeight containing a
      *         scaled video frame, which can be null, if such a frame cannot be retrieved.
      * @throws IllegalArgumentException if passed in invalid option or width by height
      *         is less than or equal to 0.
+     * @see {@link #getScaledFrameAtTime(long, int, int, int)}
      */
-    public Bitmap getScaledFrameAtTime(
-            long timeUs, @Option int option, int dstWidth, int dstHeight) {
-        if (option < OPTION_PREVIOUS_SYNC ||
-            option > OPTION_CLOSEST) {
+    public Bitmap getScaledFrameAtTime(long timeUs, @Option int option,
+            int dstWidth, int dstHeight, @NonNull BitmapParams params) {
+        validate(option, dstWidth, dstHeight);
+        return _getFrameAtTime(timeUs, option, dstWidth, dstHeight, params);
+    }
+
+    private void validate(@Option int option, int dstWidth, int dstHeight) {
+        if (option < OPTION_PREVIOUS_SYNC || option > OPTION_CLOSEST) {
             throw new IllegalArgumentException("Unsupported option: " + option);
         }
         if (dstWidth <= 0) {
@@ -314,8 +404,6 @@
         if (dstHeight <= 0) {
             throw new IllegalArgumentException("Invalid height: " + dstHeight);
         }
-
-        return _getFrameAtTime(timeUs, option, dstWidth, dstHeight);
     }
 
     /**
@@ -365,10 +453,12 @@
      * @see #getFrameAtTime(long, int)
      */
     public Bitmap getFrameAtTime() {
-        return _getFrameAtTime(-1, OPTION_CLOSEST_SYNC, -1 /*dst_width*/, -1 /*dst_height*/);
+        return _getFrameAtTime(
+                -1, OPTION_CLOSEST_SYNC, -1 /*dst_width*/, -1 /*dst_height*/, null);
     }
 
-    private native Bitmap _getFrameAtTime(long timeUs, int option, int width, int height);
+    private native Bitmap _getFrameAtTime(
+            long timeUs, int option, int width, int height, @Nullable BitmapParams params);
 
     public static final class BitmapParams {
         private Bitmap.Config inPreferredConfig = Bitmap.Config.ARGB_8888;
diff --git a/media/jni/android_media_MediaMetadataRetriever.cpp b/media/jni/android_media_MediaMetadataRetriever.cpp
index 18fd1a0..bc4bceb 100644
--- a/media/jni/android_media_MediaMetadataRetriever.cpp
+++ b/media/jni/android_media_MediaMetadataRetriever.cpp
@@ -350,9 +350,10 @@
     return jBitmap;
 }
 
-static int getColorFormat(JNIEnv *env, jobject options) {
+static int getColorFormat(JNIEnv *env, jobject options,
+        int defaultPreferred = HAL_PIXEL_FORMAT_RGBA_8888) {
     if (options == NULL) {
-        return HAL_PIXEL_FORMAT_RGBA_8888;
+        return defaultPreferred;
     }
 
     ScopedLocalRef<jobject> inConfig(env, env->GetObjectField(options, fields.inPreferredConfig));
@@ -383,7 +384,8 @@
 }
 
 static jobject android_media_MediaMetadataRetriever_getFrameAtTime(
-        JNIEnv *env, jobject thiz, jlong timeUs, jint option, jint dst_width, jint dst_height)
+        JNIEnv *env, jobject thiz, jlong timeUs, jint option,
+        jint dst_width, jint dst_height, jobject params)
 {
     ALOGV("getFrameAtTime: %lld us option: %d dst width: %d heigh: %d",
             (long long)timeUs, option, dst_width, dst_height);
@@ -392,10 +394,13 @@
         jniThrowException(env, "java/lang/IllegalStateException", "No retriever available");
         return NULL;
     }
+    // For getFrameAtTime family of calls, default to HAL_PIXEL_FORMAT_RGB_565
+    // to keep the behavior consistent with older releases
+    int colorFormat = getColorFormat(env, params, HAL_PIXEL_FORMAT_RGB_565);
 
     // Call native method to retrieve a video frame
     VideoFrame *videoFrame = NULL;
-    sp<IMemory> frameMemory = retriever->getFrameAtTime(timeUs, option);
+    sp<IMemory> frameMemory = retriever->getFrameAtTime(timeUs, option, colorFormat);
     // TODO: Using unsecurePointer() has some associated security pitfalls
     //       (see declaration for details).
     //       Either document why it is safe in this case or address the
@@ -408,7 +413,9 @@
         return NULL;
     }
 
-    return getBitmapFromVideoFrame(env, videoFrame, dst_width, dst_height, kRGB_565_SkColorType);
+    SkColorType outColorType = setOutColorType(env, colorFormat, params);
+
+    return getBitmapFromVideoFrame(env, videoFrame, dst_width, dst_height, outColorType);
 }
 
 static jobject android_media_MediaMetadataRetriever_getImageAtIndex(
@@ -739,7 +746,7 @@
                 (void *)android_media_MediaMetadataRetriever_setDataSourceFD},
         {"_setDataSource",   "(Landroid/media/MediaDataSource;)V",
                 (void *)android_media_MediaMetadataRetriever_setDataSourceCallback},
-        {"_getFrameAtTime", "(JIII)Landroid/graphics/Bitmap;",
+        {"_getFrameAtTime", "(JIIILandroid/media/MediaMetadataRetriever$BitmapParams;)Landroid/graphics/Bitmap;",
                 (void *)android_media_MediaMetadataRetriever_getFrameAtTime},
         {
             "_getImageAtIndex",
diff --git a/mime/Android.bp b/mime/Android.bp
index 8b2b059..23a8fbf 100644
--- a/mime/Android.bp
+++ b/mime/Android.bp
@@ -60,7 +60,7 @@
     tools: [
         "soong_zip",
     ],
-    srcs: [":mime.types"],
+    srcs: [":mime.types.minimized"],
     out: ["mimemap-res.jar"],
     cmd: "mkdir $(genDir)/res/ && cp $(in) $(genDir)/res/ && $(location soong_zip) -C $(genDir) -o $(out) -D $(genDir)/res/",
 }
@@ -73,42 +73,49 @@
     tools: [
         "soong_zip",
     ],
-    srcs: [":mime.types"],
+    srcs: [":mime.types.minimized"],
     out: ["mimemap-testing-res.jar"],
     cmd: "mkdir $(genDir)/testres/ && cp $(in) $(genDir)/testres/ && $(location soong_zip) -C $(genDir) -o $(out) -D $(genDir)/testres/",
 }
 
-// Combination of all *mime.types resources.
+// Combination of all *mime.types.minimized resources.
 filegroup {
-    name: "mime.types",
+    name: "mime.types.minimized",
     visibility: [
         "//visibility:private",
     ],
     srcs: [
-        ":debian.mime.types",
-        ":android.mime.types",
-        ":vendor.mime.types",
+        ":debian.mime.types.minimized",
+        ":android.mime.types.minimized",
+        ":vendor.mime.types.minimized",
     ],
 }
 
-filegroup {
-    name: "android.mime.types",
+java_genrule {
+    name: "android.mime.types.minimized",
     visibility: [
         "//visibility:private",
     ],
-    path: "java-res/",
+    out: ["android.mime.types"],
     srcs: [
         "java-res/android.mime.types",
     ],
+    //    strip comments            normalize whitepace       drop empty lines
+    cmd: "awk '{gsub(/#.*$$/,\"\"); $$1=$$1; print;}' $(in) | grep ' ' > $(out)",
 }
 
-filegroup {
-    name: "vendor.mime.types",
+// Unlike the other *mime.types files, vendor.mime.types gets '?' prepended to
+// every field so that its mappings will never overwrite earlier mappings by
+// the other resource files. http://b/141842825
+java_genrule {
+    name: "vendor.mime.types.minimized",
     visibility: [
         "//visibility:private",
     ],
-    path: "java-res/",
+    out: ["vendor.mime.types"],
     srcs: [
         "java-res/vendor.mime.types",
     ],
+    //    strip comments            normalize whitepace       drop empty lines   prepend ? to fields that are missing it
+    cmd: "awk '{gsub(/#.*$$/,\"\"); $$1=$$1; print;}' $(in) | grep ' '         | awk '{for(i=1;i<=NF;i++) { sub(/^\\??/, \"?\", $$i); }; print}' > $(out)",
 }
diff --git a/mime/java/android/content/type/DefaultMimeMapFactory.java b/mime/java/android/content/type/DefaultMimeMapFactory.java
index 03b685d..11d20d4 100644
--- a/mime/java/android/content/type/DefaultMimeMapFactory.java
+++ b/mime/java/android/content/type/DefaultMimeMapFactory.java
@@ -23,11 +23,9 @@
 import java.io.InputStream;
 import java.io.InputStreamReader;
 import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.List;
 import java.util.Objects;
 import java.util.function.Function;
-import java.util.regex.Pattern;
 
 /**
  * Creates the framework default {@link MimeMap}, a bidirectional mapping
@@ -53,8 +51,6 @@
         return create(resourceName -> c.getResourceAsStream("/res/" + resourceName));
     }
 
-    private static final Pattern SPLIT_PATTERN = Pattern.compile("\\s+");
-
     /**
      * Creates a {@link MimeMap} instance whose resources are loaded from the
      * InputStreams looked up in {@code resourceSupplier}.
@@ -63,33 +59,43 @@
      */
     public static MimeMap create(Function<String, InputStream> resourceSupplier) {
         MimeMap.Builder builder = MimeMap.builder();
-        parseTypes(builder, true, resourceSupplier, "mime.types");
-        parseTypes(builder, true, resourceSupplier, "android.mime.types");
-        parseTypes(builder, false, resourceSupplier, "vendor.mime.types");
+        // The files loaded here must be in minimized format with lines of the
+        // form "mime/type ext1 ext2 ext3", i.e. no comments, no empty lines, no
+        // leading/trailing whitespace and with a single space between entries on
+        // each line.  See http://b/142267887
+        //
+        // Note: the order here matters - later entries can overwrite earlier ones
+        // (except that vendor.mime.types entries are prefixed with '?' which makes
+        // them never overwrite).
+        parseTypes(builder, resourceSupplier, "debian.mime.types");
+        parseTypes(builder, resourceSupplier, "android.mime.types");
+        parseTypes(builder, resourceSupplier, "vendor.mime.types");
         return builder.build();
     }
 
-    private static void parseTypes(MimeMap.Builder builder, boolean allowOverwrite,
+    private static void parseTypes(MimeMap.Builder builder,
             Function<String, InputStream> resourceSupplier, String resourceName) {
         try (InputStream inputStream = Objects.requireNonNull(resourceSupplier.apply(resourceName));
              BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream))) {
             String line;
+            List<String> specs = new ArrayList<>(10); // re-use for each line
             while ((line = reader.readLine()) != null) {
-                int commentPos = line.indexOf('#');
-                if (commentPos >= 0) {
-                    line = line.substring(0, commentPos);
-                }
-                line = line.trim();
-                if (line.isEmpty()) {
-                    continue;
-                }
-                List<String> specs = Arrays.asList(SPLIT_PATTERN.split(line));
-                if (!allowOverwrite) {
-                    // Pretend that the mimeType and each file extension listed in the line
-                    // carries a "?" prefix, which means that it can add new mappings but
-                    // not modify existing mappings (putIfAbsent() semantics).
-                    specs = ensurePrefix("?", specs);
-                }
+                specs.clear();
+                // Lines are of the form "mimeSpec extSpec extSpec[...]" with a single space
+                // separating them and no leading/trailing spaces and no empty lines.
+                int startIdx = 0;
+                do {
+                    int endIdx = line.indexOf(' ', startIdx);
+                    if (endIdx < 0) {
+                        endIdx = line.length();
+                    }
+                    String spec = line.substring(startIdx, endIdx);
+                    if (spec.isEmpty()) {
+                        throw new IllegalArgumentException("Malformed line: " + line);
+                    }
+                    specs.add(spec);
+                    startIdx = endIdx + 1; // skip over the space
+                } while (startIdx < line.length());
                 builder.put(specs.get(0), specs.subList(1, specs.size()));
             }
         } catch (IOException | RuntimeException e) {
@@ -97,15 +103,4 @@
         }
     }
 
-    private static List<String> ensurePrefix(String prefix, List<String> strings) {
-        List<String> result = new ArrayList<>(strings.size());
-        for (String s : strings) {
-            if (!s.startsWith(prefix)) {
-                s = prefix + s;
-            }
-            result.add(s);
-        }
-        return result;
-    }
-
 }
diff --git a/packages/BackupEncryption/src/com/android/server/backup/encryption/StreamUtils.java b/packages/BackupEncryption/src/com/android/server/backup/encryption/StreamUtils.java
index 91b2926..66be25b 100644
--- a/packages/BackupEncryption/src/com/android/server/backup/encryption/StreamUtils.java
+++ b/packages/BackupEncryption/src/com/android/server/backup/encryption/StreamUtils.java
@@ -18,9 +18,13 @@
 
 import java.io.Closeable;
 import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
 
 /** Utility methods for dealing with Streams */
 public class StreamUtils {
+    private static final int MAX_COPY_BUFFER_SIZE = 1024; // 1k copy buffer size.
+
     /**
      * Close a Closeable and silently ignore any IOExceptions.
      *
@@ -33,4 +37,28 @@
             // Silently ignore
         }
     }
+
+    /**
+     * Copy data from an InputStream to an OutputStream upto a given number of bytes.
+     *
+     * @param in The source InputStream
+     * @param out The destination OutputStream
+     * @param limit The maximum number of bytes to copy
+     * @throws IOException Thrown if there is a problem performing the copy.
+     */
+    public static void copyStream(InputStream in, OutputStream out, int limit) throws IOException {
+        int bufferSize = Math.min(MAX_COPY_BUFFER_SIZE, limit);
+        byte[] buffer = new byte[bufferSize];
+
+        int copied = 0;
+        while (copied < limit) {
+            int maxReadSize = Math.min(bufferSize, limit - copied);
+            int read = in.read(buffer, 0, maxReadSize);
+            if (read < 0) {
+                return; // Reached the stream end before the limit
+            }
+            out.write(buffer, 0, read);
+            copied += read;
+        }
+    }
 }
diff --git a/packages/BackupEncryption/src/com/android/server/backup/encryption/tasks/EncryptedFullBackupDataProcessor.java b/packages/BackupEncryption/src/com/android/server/backup/encryption/tasks/EncryptedFullBackupDataProcessor.java
new file mode 100644
index 0000000..0baec8b
--- /dev/null
+++ b/packages/BackupEncryption/src/com/android/server/backup/encryption/tasks/EncryptedFullBackupDataProcessor.java
@@ -0,0 +1,210 @@
+/*
+ * 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.backup.encryption.tasks;
+
+import static com.android.internal.util.Preconditions.checkNotNull;
+import static com.android.internal.util.Preconditions.checkState;
+
+import android.annotation.Nullable;
+import android.app.backup.BackupTransport;
+import android.content.Context;
+import android.util.Slog;
+
+import com.android.server.backup.encryption.FullBackupDataProcessor;
+import com.android.server.backup.encryption.StreamUtils;
+import com.android.server.backup.encryption.client.CryptoBackupServer;
+import com.android.server.backup.encryption.keys.RecoverableKeyStoreSecondaryKey;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.PipedInputStream;
+import java.io.PipedOutputStream;
+import java.security.SecureRandom;
+import java.util.Optional;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Future;
+
+/**
+ * Accepts backup data from a {@link InputStream} and passes it to the encrypted full data backup
+ * path.
+ */
+public class EncryptedFullBackupDataProcessor implements FullBackupDataProcessor {
+
+    private static final String TAG = "EncryptedFullBackupDP";
+
+    private final Context mContext;
+    private final ExecutorService mExecutorService;
+    private final CryptoBackupServer mCryptoBackupServer;
+    private final SecureRandom mSecureRandom;
+    private final RecoverableKeyStoreSecondaryKey mSecondaryKey;
+    private final String mPackageName;
+
+    @Nullable private InputStream mInputStream;
+    @Nullable private PipedOutputStream mOutputStream;
+    @Nullable private EncryptedFullBackupTask mBackupTask;
+    @Nullable private Future<Void> mBackupTaskFuture;
+    @Nullable private FullBackupCallbacks mFullBackupCallbacks;
+
+    public EncryptedFullBackupDataProcessor(
+            Context context,
+            ExecutorService executorService,
+            CryptoBackupServer cryptoBackupServer,
+            SecureRandom secureRandom,
+            RecoverableKeyStoreSecondaryKey secondaryKey,
+            String packageName) {
+        mContext = checkNotNull(context);
+        mExecutorService = checkNotNull(executorService);
+        mCryptoBackupServer = checkNotNull(cryptoBackupServer);
+        mSecureRandom = checkNotNull(secureRandom);
+        mSecondaryKey = checkNotNull(secondaryKey);
+        mPackageName = checkNotNull(packageName);
+    }
+
+    @Override
+    public boolean initiate(InputStream inputStream) throws IOException {
+        checkState(mBackupTask == null, "initiate() twice");
+
+        this.mInputStream = inputStream;
+        mOutputStream = new PipedOutputStream();
+
+        mBackupTask =
+                EncryptedFullBackupTask.newInstance(
+                        mContext,
+                        mCryptoBackupServer,
+                        mSecureRandom,
+                        mSecondaryKey,
+                        mPackageName,
+                        new PipedInputStream(mOutputStream));
+
+        return true;
+    }
+
+    @Override
+    public void start() {
+        checkState(mBackupTask != null, "start() before initiate()");
+        mBackupTaskFuture = mExecutorService.submit(mBackupTask);
+    }
+
+    @Override
+    public int pushData(int numBytes) {
+        checkState(
+                mBackupTaskFuture != null && mInputStream != null && mOutputStream != null,
+                "pushData() before start()");
+
+        // If the upload has failed then stop without pushing any more bytes.
+        if (mBackupTaskFuture.isDone()) {
+            Optional<Exception> exception = getTaskException();
+            Slog.e(TAG, "Encrypted upload failed", exception.orElse(null));
+            if (exception.isPresent()) {
+                reportNetworkFailureIfNecessary(exception.get());
+
+                if (exception.get().getCause() instanceof SizeQuotaExceededException) {
+                    return BackupTransport.TRANSPORT_QUOTA_EXCEEDED;
+                }
+            }
+
+            return BackupTransport.TRANSPORT_ERROR;
+        }
+
+        try {
+            StreamUtils.copyStream(mInputStream, mOutputStream, numBytes);
+        } catch (IOException e) {
+            Slog.e(TAG, "IOException when processing backup", e);
+            return BackupTransport.TRANSPORT_ERROR;
+        }
+
+        return BackupTransport.TRANSPORT_OK;
+    }
+
+    @Override
+    public void cancel() {
+        checkState(mBackupTaskFuture != null && mBackupTask != null, "cancel() before start()");
+        mBackupTask.cancel();
+        closeStreams();
+    }
+
+    @Override
+    public int finish() {
+        checkState(mBackupTaskFuture != null, "finish() before start()");
+
+        // getTaskException() waits for the task to finish. We must close the streams first, which
+        // causes the task to finish, otherwise it will block forever.
+        closeStreams();
+        Optional<Exception> exception = getTaskException();
+
+        if (exception.isPresent()) {
+            Slog.e(TAG, "Exception during encrypted full backup", exception.get());
+            reportNetworkFailureIfNecessary(exception.get());
+
+            if (exception.get().getCause() instanceof SizeQuotaExceededException) {
+                return BackupTransport.TRANSPORT_QUOTA_EXCEEDED;
+            }
+            return BackupTransport.TRANSPORT_ERROR;
+
+        } else {
+            if (mFullBackupCallbacks != null) {
+                mFullBackupCallbacks.onSuccess();
+            }
+
+            return BackupTransport.TRANSPORT_OK;
+        }
+    }
+
+    private void closeStreams() {
+        StreamUtils.closeQuietly(mInputStream);
+        StreamUtils.closeQuietly(mOutputStream);
+    }
+
+    @Override
+    public void handleCheckSizeRejectionZeroBytes() {
+        cancel();
+    }
+
+    @Override
+    public void handleCheckSizeRejectionQuotaExceeded() {
+        cancel();
+    }
+
+    @Override
+    public void handleSendBytesQuotaExceeded() {
+        cancel();
+    }
+
+    @Override
+    public void attachCallbacks(FullBackupCallbacks fullBackupCallbacks) {
+        this.mFullBackupCallbacks = fullBackupCallbacks;
+    }
+
+    private void reportNetworkFailureIfNecessary(Exception exception) {
+        if (!(exception.getCause() instanceof SizeQuotaExceededException)
+                && mFullBackupCallbacks != null) {
+            mFullBackupCallbacks.onTransferFailed();
+        }
+    }
+
+    private Optional<Exception> getTaskException() {
+        if (mBackupTaskFuture != null) {
+            try {
+                mBackupTaskFuture.get();
+            } catch (InterruptedException | ExecutionException e) {
+                return Optional.of(e);
+            }
+        }
+        return Optional.empty();
+    }
+}
diff --git a/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/StreamUtilsTest.java b/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/StreamUtilsTest.java
new file mode 100644
index 0000000..a95e87e
--- /dev/null
+++ b/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/StreamUtilsTest.java
@@ -0,0 +1,75 @@
+/*
+ * 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.backup.encryption;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+@RunWith(RobolectricTestRunner.class)
+public class StreamUtilsTest {
+    private static final int SOURCE_DATA_SIZE = 64;
+
+    private byte[] mSourceData;
+
+    private InputStream mSource;
+    private ByteArrayOutputStream mDestination;
+
+    @Before
+    public void setUp() {
+        mSourceData = new byte[SOURCE_DATA_SIZE];
+        for (byte i = 0; i < SOURCE_DATA_SIZE; i++) {
+            mSourceData[i] = i;
+        }
+        mSource = new ByteArrayInputStream(mSourceData);
+        mDestination = new ByteArrayOutputStream();
+    }
+
+    @Test
+    public void copyStream_copiesAllBytesIfAsked() throws IOException {
+        StreamUtils.copyStream(mSource, mDestination, mSourceData.length);
+        assertOutputHasBytes(mSourceData.length);
+    }
+
+    @Test
+    public void copyStream_stopsShortIfAsked() throws IOException {
+        StreamUtils.copyStream(mSource, mDestination, mSourceData.length - 10);
+        assertOutputHasBytes(mSourceData.length - 10);
+    }
+
+    @Test
+    public void copyStream_stopsShortIfAskedToCopyMoreThanAvailable() throws IOException {
+        StreamUtils.copyStream(mSource, mDestination, mSourceData.length + 10);
+        assertOutputHasBytes(mSourceData.length);
+    }
+
+    private void assertOutputHasBytes(int count) {
+        byte[] output = mDestination.toByteArray();
+        assertThat(output.length).isEqualTo(count);
+        for (int i = 0; i < count; i++) {
+            assertThat(output[i]).isEqualTo(mSourceData[i]);
+        }
+    }
+}
diff --git a/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/tasks/EncryptedFullBackupDataProcessorTest.java b/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/tasks/EncryptedFullBackupDataProcessorTest.java
new file mode 100644
index 0000000..675d03f
--- /dev/null
+++ b/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/tasks/EncryptedFullBackupDataProcessorTest.java
@@ -0,0 +1,387 @@
+/*
+ * 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.backup.encryption.tasks;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+import static org.testng.Assert.assertThrows;
+
+import android.annotation.Nullable;
+import android.app.backup.BackupTransport;
+import android.platform.test.annotations.Presubmit;
+
+import androidx.test.core.app.ApplicationProvider;
+
+import com.android.server.backup.encryption.FullBackupDataProcessor;
+import com.android.server.backup.encryption.chunking.ProtoStore;
+import com.android.server.backup.encryption.client.CryptoBackupServer;
+import com.android.server.backup.encryption.keys.RecoverableKeyStoreSecondaryKey;
+import com.android.server.backup.encryption.keys.TertiaryKeyManager;
+import com.android.server.backup.encryption.protos.nano.ChunksMetadataProto;
+import com.android.server.backup.encryption.testing.QueuingNonAutomaticExecutorService;
+
+import com.google.common.io.ByteStreams;
+import com.google.common.primitives.Bytes;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.annotation.Config;
+import org.robolectric.annotation.Implementation;
+import org.robolectric.annotation.Implements;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.security.GeneralSecurityException;
+import java.security.SecureRandom;
+
+import javax.crypto.spec.SecretKeySpec;
+
+@RunWith(RobolectricTestRunner.class)
+@Presubmit
+@Config(
+        shadows = {
+            EncryptedFullBackupDataProcessorTest.ShadowEncryptedFullBackupTask.class,
+        })
+public class EncryptedFullBackupDataProcessorTest {
+
+    private static final String KEY_GENERATOR_ALGORITHM = "AES";
+
+    private static final String TEST_PACKAGE = "com.example.app1";
+    private static final byte[] TEST_DATA_1 = {1, 2, 3, 4};
+    private static final byte[] TEST_DATA_2 = {5, 6, 7, 8};
+
+    private final RecoverableKeyStoreSecondaryKey mTestSecondaryKey =
+            new RecoverableKeyStoreSecondaryKey(
+                    /*alias=*/ "test_key",
+                    new SecretKeySpec(
+                            new byte[] {
+                                1, 2, 3,
+                            },
+                            KEY_GENERATOR_ALGORITHM));
+
+    private QueuingNonAutomaticExecutorService mExecutorService;
+    private FullBackupDataProcessor mFullBackupDataProcessor;
+    @Mock private FullBackupDataProcessor.FullBackupCallbacks mFullBackupCallbacks;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+
+        mExecutorService = new QueuingNonAutomaticExecutorService();
+        mFullBackupDataProcessor =
+                new EncryptedFullBackupDataProcessor(
+                        ApplicationProvider.getApplicationContext(),
+                        mExecutorService,
+                        mock(CryptoBackupServer.class),
+                        new SecureRandom(),
+                        mTestSecondaryKey,
+                        TEST_PACKAGE);
+    }
+
+    @After
+    public void tearDown() {
+        ShadowEncryptedFullBackupTask.reset();
+    }
+
+    @Test
+    public void initiate_callTwice_throws() throws Exception {
+        mFullBackupDataProcessor.initiate(new ByteArrayInputStream(new byte[10]));
+
+        assertThrows(
+                IllegalStateException.class,
+                () -> mFullBackupDataProcessor.initiate(new ByteArrayInputStream(new byte[10])));
+    }
+
+    @Test
+    public void pushData_writesDataToTask() throws Exception {
+        byte[] inputData = Bytes.concat(TEST_DATA_1, TEST_DATA_2);
+
+        mFullBackupDataProcessor.initiate(new ByteArrayInputStream(inputData));
+        mFullBackupDataProcessor.start();
+        mFullBackupDataProcessor.pushData(TEST_DATA_1.length);
+        mFullBackupDataProcessor.pushData(TEST_DATA_2.length);
+        finishBackupTask();
+        mFullBackupDataProcessor.finish();
+
+        byte[] result = ByteStreams.toByteArray(ShadowEncryptedFullBackupTask.sInputStream);
+        assertThat(result).isEqualTo(Bytes.concat(TEST_DATA_1, TEST_DATA_2));
+    }
+
+    @Test
+    public void pushData_noError_returnsOk() throws Exception {
+        mFullBackupDataProcessor.initiate(new ByteArrayInputStream(TEST_DATA_1));
+        mFullBackupDataProcessor.start();
+        int result = mFullBackupDataProcessor.pushData(TEST_DATA_1.length);
+        finishBackupTask();
+        mFullBackupDataProcessor.finish();
+
+        assertThat(result).isEqualTo(BackupTransport.TRANSPORT_OK);
+    }
+
+    @Test
+    public void pushData_ioExceptionOnCopy_returnsError() throws Exception {
+        mFullBackupDataProcessor.initiate(new ByteArrayInputStream(TEST_DATA_1));
+        mFullBackupDataProcessor.start();
+
+        // Close the stream so there's an IO error when the processor tries to write to it.
+        ShadowEncryptedFullBackupTask.sInputStream.close();
+        int result = mFullBackupDataProcessor.pushData(TEST_DATA_1.length);
+
+        finishBackupTask();
+        mFullBackupDataProcessor.finish();
+
+        assertThat(result).isEqualTo(BackupTransport.TRANSPORT_ERROR);
+    }
+
+    @Test
+    public void pushData_exceptionDuringUpload_returnsError() throws Exception {
+        byte[] inputData = Bytes.concat(TEST_DATA_1, TEST_DATA_2);
+
+        mFullBackupDataProcessor.initiate(new ByteArrayInputStream(inputData));
+        mFullBackupDataProcessor.start();
+        mFullBackupDataProcessor.pushData(TEST_DATA_1.length);
+        finishBackupTaskWithException(new IOException("Test exception"));
+        int result = mFullBackupDataProcessor.pushData(TEST_DATA_2.length);
+
+        assertThat(result).isEqualTo(BackupTransport.TRANSPORT_ERROR);
+    }
+
+    @Test
+    public void pushData_quotaExceptionDuringUpload_doesNotLogAndReturnsQuotaExceeded()
+            throws Exception {
+        mFullBackupDataProcessor.attachCallbacks(mFullBackupCallbacks);
+        byte[] inputData = Bytes.concat(TEST_DATA_1, TEST_DATA_2);
+
+        mFullBackupDataProcessor.initiate(new ByteArrayInputStream(inputData));
+        mFullBackupDataProcessor.start();
+        mFullBackupDataProcessor.pushData(TEST_DATA_1.length);
+        finishBackupTaskWithException(new SizeQuotaExceededException());
+        int result = mFullBackupDataProcessor.pushData(TEST_DATA_2.length);
+
+        assertThat(result).isEqualTo(BackupTransport.TRANSPORT_QUOTA_EXCEEDED);
+
+        verify(mFullBackupCallbacks, never()).onSuccess();
+        verify(mFullBackupCallbacks, never())
+                .onTransferFailed(); // FullBackupSession will handle this.
+    }
+
+    @Test
+    public void pushData_unexpectedEncryptedBackup_logs() throws Exception {
+        byte[] inputData = Bytes.concat(TEST_DATA_1, TEST_DATA_2);
+
+        mFullBackupDataProcessor.initiate(new ByteArrayInputStream(inputData));
+        mFullBackupDataProcessor.start();
+        mFullBackupDataProcessor.pushData(TEST_DATA_1.length);
+        finishBackupTaskWithException(new GeneralSecurityException());
+        int result = mFullBackupDataProcessor.pushData(TEST_DATA_2.length);
+
+        assertThat(result).isEqualTo(BackupTransport.TRANSPORT_ERROR);
+    }
+
+    @Test
+    public void pushData_permanentExceptionDuringUpload_callsErrorCallback() throws Exception {
+        mFullBackupDataProcessor.attachCallbacks(mFullBackupCallbacks);
+        byte[] inputData = Bytes.concat(TEST_DATA_1, TEST_DATA_2);
+
+        mFullBackupDataProcessor.initiate(new ByteArrayInputStream(inputData));
+        mFullBackupDataProcessor.start();
+        mFullBackupDataProcessor.pushData(TEST_DATA_1.length);
+        finishBackupTaskWithException(new IOException());
+        mFullBackupDataProcessor.pushData(TEST_DATA_2.length);
+
+        verify(mFullBackupCallbacks, never()).onSuccess();
+        verify(mFullBackupCallbacks).onTransferFailed();
+    }
+
+    @Test
+    public void pushData_beforeInitiate_throws() {
+        assertThrows(
+                IllegalStateException.class,
+                () -> mFullBackupDataProcessor.pushData(/*numBytes=*/ 10));
+    }
+
+    @Test
+    public void cancel_cancelsTask() throws Exception {
+        mFullBackupDataProcessor.initiate(new ByteArrayInputStream(TEST_DATA_1));
+        mFullBackupDataProcessor.start();
+        mFullBackupDataProcessor.pushData(TEST_DATA_1.length);
+        mFullBackupDataProcessor.cancel();
+
+        assertThat(ShadowEncryptedFullBackupTask.sCancelled).isTrue();
+    }
+
+    @Test
+    public void cancel_beforeInitiate_throws() {
+        assertThrows(IllegalStateException.class, () -> mFullBackupDataProcessor.cancel());
+    }
+
+    @Test
+    public void finish_noException_returnsTransportOk() throws Exception {
+        mFullBackupDataProcessor.initiate(new ByteArrayInputStream(TEST_DATA_1));
+        mFullBackupDataProcessor.start();
+        mFullBackupDataProcessor.pushData(TEST_DATA_1.length);
+        finishBackupTask();
+        int result = mFullBackupDataProcessor.finish();
+
+        assertThat(result).isEqualTo(BackupTransport.TRANSPORT_OK);
+    }
+
+    @Test
+    public void finish_exceptionDuringUpload_returnsTransportError() throws Exception {
+        mFullBackupDataProcessor.initiate(new ByteArrayInputStream(TEST_DATA_1));
+        mFullBackupDataProcessor.start();
+        mFullBackupDataProcessor.pushData(TEST_DATA_1.length);
+        finishBackupTaskWithException(new IOException("Test exception"));
+        int result = mFullBackupDataProcessor.finish();
+
+        assertThat(result).isEqualTo(BackupTransport.TRANSPORT_ERROR);
+    }
+
+    @Test
+    public void finish_successfulBackup_callsSuccessCallback() throws Exception {
+        mFullBackupDataProcessor.attachCallbacks(mFullBackupCallbacks);
+
+        mFullBackupDataProcessor.initiate(new ByteArrayInputStream(TEST_DATA_1));
+        mFullBackupDataProcessor.start();
+        mFullBackupDataProcessor.pushData(TEST_DATA_1.length);
+        finishBackupTask();
+        mFullBackupDataProcessor.finish();
+
+        verify(mFullBackupCallbacks).onSuccess();
+        verify(mFullBackupCallbacks, never()).onTransferFailed();
+    }
+
+    @Test
+    public void finish_backupFailedWithPermanentError_callsErrorCallback() throws Exception {
+        mFullBackupDataProcessor.attachCallbacks(mFullBackupCallbacks);
+
+        mFullBackupDataProcessor.initiate(new ByteArrayInputStream(TEST_DATA_1));
+        mFullBackupDataProcessor.start();
+        mFullBackupDataProcessor.pushData(TEST_DATA_1.length);
+        finishBackupTaskWithException(new IOException());
+        mFullBackupDataProcessor.finish();
+
+        verify(mFullBackupCallbacks, never()).onSuccess();
+        verify(mFullBackupCallbacks).onTransferFailed();
+    }
+
+    @Test
+    public void finish_backupFailedWithQuotaException_doesNotCallbackAndReturnsQuotaExceeded()
+            throws Exception {
+        mFullBackupDataProcessor.attachCallbacks(mFullBackupCallbacks);
+
+        mFullBackupDataProcessor.initiate(new ByteArrayInputStream(TEST_DATA_1));
+        mFullBackupDataProcessor.start();
+        mFullBackupDataProcessor.pushData(TEST_DATA_1.length);
+        finishBackupTaskWithException(new SizeQuotaExceededException());
+        int result = mFullBackupDataProcessor.finish();
+
+        assertThat(result).isEqualTo(BackupTransport.TRANSPORT_QUOTA_EXCEEDED);
+        verify(mFullBackupCallbacks, never()).onSuccess();
+        verify(mFullBackupCallbacks, never())
+                .onTransferFailed(); // FullBackupSession will handle this.
+    }
+
+    @Test
+    public void finish_beforeInitiate_throws() {
+        assertThrows(IllegalStateException.class, () -> mFullBackupDataProcessor.finish());
+    }
+
+    @Test
+    public void handleCheckSizeRejectionZeroBytes_cancelsTask() throws Exception {
+        mFullBackupDataProcessor.initiate(new ByteArrayInputStream(new byte[10]));
+        mFullBackupDataProcessor.start();
+        mFullBackupDataProcessor.handleCheckSizeRejectionZeroBytes();
+
+        assertThat(ShadowEncryptedFullBackupTask.sCancelled).isTrue();
+    }
+
+    @Test
+    public void handleCheckSizeRejectionQuotaExceeded_cancelsTask() throws Exception {
+        mFullBackupDataProcessor.initiate(new ByteArrayInputStream(TEST_DATA_1));
+        mFullBackupDataProcessor.start();
+        mFullBackupDataProcessor.pushData(TEST_DATA_1.length);
+        mFullBackupDataProcessor.handleCheckSizeRejectionQuotaExceeded();
+
+        assertThat(ShadowEncryptedFullBackupTask.sCancelled).isTrue();
+    }
+
+    @Test
+    public void handleSendBytesQuotaExceeded_cancelsTask() throws Exception {
+        mFullBackupDataProcessor.initiate(new ByteArrayInputStream(TEST_DATA_1));
+        mFullBackupDataProcessor.start();
+        mFullBackupDataProcessor.pushData(TEST_DATA_1.length);
+        mFullBackupDataProcessor.handleSendBytesQuotaExceeded();
+
+        assertThat(ShadowEncryptedFullBackupTask.sCancelled).isTrue();
+    }
+
+    private void finishBackupTask() {
+        mExecutorService.runNext();
+    }
+
+    private void finishBackupTaskWithException(Exception exception) {
+        ShadowEncryptedFullBackupTask.sOnCallException = exception;
+        finishBackupTask();
+    }
+
+    @Implements(EncryptedFullBackupTask.class)
+    public static class ShadowEncryptedFullBackupTask {
+
+        private static InputStream sInputStream;
+        @Nullable private static Exception sOnCallException;
+        private static boolean sCancelled;
+
+        public void __constructor__(
+                ProtoStore<ChunksMetadataProto.ChunkListing> chunkListingStore,
+                TertiaryKeyManager tertiaryKeyManager,
+                EncryptedBackupTask task,
+                InputStream inputStream,
+                String packageName,
+                SecureRandom secureRandom) {
+            sInputStream = inputStream;
+        }
+
+        @Implementation
+        public Void call() throws Exception {
+            if (sOnCallException != null) {
+                throw sOnCallException;
+            }
+
+            return null;
+        }
+
+        @Implementation
+        public void cancel() {
+            sCancelled = true;
+        }
+
+        public static void reset() {
+            sOnCallException = null;
+            sCancelled = false;
+        }
+    }
+}
diff --git a/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/testing/QueuingNonAutomaticExecutorService.java b/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/testing/QueuingNonAutomaticExecutorService.java
new file mode 100644
index 0000000..9d2272e
--- /dev/null
+++ b/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/testing/QueuingNonAutomaticExecutorService.java
@@ -0,0 +1,83 @@
+/*
+ * 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.backup.encryption.testing;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.AbstractExecutorService;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * ExecutorService which needs to be stepped through the jobs in its' queue.
+ *
+ * <p>This is a deliberately simple implementation because it's only used in testing. The queued
+ * jobs are run on the main thread to eliminate any race condition bugs.
+ */
+public class QueuingNonAutomaticExecutorService extends AbstractExecutorService {
+
+    private List<Runnable> mWaitingJobs = new ArrayList<>();
+    private int mWaitingJobCount = 0;
+
+    @Override
+    public void shutdown() {
+        mWaitingJobCount = mWaitingJobs.size();
+        mWaitingJobs = null; // This will force an error if jobs are submitted after shutdown
+    }
+
+    @Override
+    public List<Runnable> shutdownNow() {
+        List<Runnable> queuedJobs = mWaitingJobs;
+        shutdown();
+        return queuedJobs;
+    }
+
+    @Override
+    public boolean isShutdown() {
+        return mWaitingJobs == null;
+    }
+
+    @Override
+    public boolean isTerminated() {
+        return mWaitingJobs == null && mWaitingJobCount == 0;
+    }
+
+    @Override
+    public boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException {
+        long expiry = System.currentTimeMillis() + unit.toMillis(timeout);
+        for (Runnable job : mWaitingJobs) {
+            if (System.currentTimeMillis() > expiry) {
+                return false;
+            }
+
+            job.run();
+        }
+        return true;
+    }
+
+    @Override
+    public void execute(Runnable command) {
+        mWaitingJobs.add(command);
+    }
+
+    public void runNext() {
+        if (mWaitingJobs.isEmpty()) {
+            throw new IllegalStateException("Attempted to run jobs on an empty paused executor");
+        }
+
+        mWaitingJobs.remove(0).run();
+    }
+}
diff --git a/packages/CarSystemUI/src/com/android/systemui/car/CarNotificationEntryManager.java b/packages/CarSystemUI/src/com/android/systemui/car/CarNotificationEntryManager.java
index c205bb4..53a88a9 100644
--- a/packages/CarSystemUI/src/com/android/systemui/car/CarNotificationEntryManager.java
+++ b/packages/CarSystemUI/src/com/android/systemui/car/CarNotificationEntryManager.java
@@ -20,6 +20,7 @@
 
 import com.android.systemui.statusbar.notification.NotificationEntryManager;
 import com.android.systemui.statusbar.notification.collection.NotificationData;
+import com.android.systemui.statusbar.notification.logging.NotifLog;
 
 import javax.inject.Inject;
 import javax.inject.Singleton;
@@ -34,8 +35,8 @@
 public class CarNotificationEntryManager extends NotificationEntryManager {
 
     @Inject
-    public CarNotificationEntryManager(NotificationData notificationData) {
-        super(notificationData);
+    public CarNotificationEntryManager(NotificationData notificationData, NotifLog notifLog) {
+        super(notificationData, notifLog);
     }
 
     @Override
diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
index cc6e842..681d8f5 100644
--- a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
+++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
@@ -108,6 +108,7 @@
 import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider;
 import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator;
 import com.android.systemui.statusbar.notification.VisualStabilityManager;
+import com.android.systemui.statusbar.notification.logging.NotifLog;
 import com.android.systemui.statusbar.notification.logging.NotificationLogger;
 import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
 import com.android.systemui.statusbar.phone.AutoHideController;
@@ -296,7 +297,8 @@
             NotificationListener notificationListener,
             ConfigurationController configurationController,
             StatusBarWindowController statusBarWindowController,
-            StatusBarWindowViewController.Builder statusBarWindowViewControllerBuild) {
+            StatusBarWindowViewController.Builder statusBarWindowViewControllerBuild,
+            NotifLog notifLog) {
         super(
                 lightBarController,
                 autoHideController,
@@ -350,7 +352,8 @@
                 notificationListener,
                 configurationController,
                 statusBarWindowController,
-                statusBarWindowViewControllerBuild);
+                statusBarWindowViewControllerBuild,
+                notifLog);
         mNavigationBarController = navigationBarController;
     }
 
diff --git a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
index 67062b7..8c97057 100644
--- a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
+++ b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
@@ -107,9 +107,6 @@
         Settings.Secure.FACE_UNLOCK_DISMISSES_KEYGUARD,
         Settings.Secure.FACE_UNLOCK_APP_ENABLED,
         Settings.Secure.FACE_UNLOCK_ALWAYS_REQUIRE_CONFIRMATION,
-        Settings.Secure.ASSIST_GESTURE_ENABLED,
-        Settings.Secure.ASSIST_GESTURE_SILENCE_ALERTS_ENABLED,
-        Settings.Secure.ASSIST_GESTURE_WAKE_ENABLED,
         Settings.Secure.VR_DISPLAY_MODE,
         Settings.Secure.NOTIFICATION_BADGING,
         Settings.Secure.NOTIFICATION_DISMISS_RTL,
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
index c05c4cdf..de6a3a8 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
@@ -24,7 +24,6 @@
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
-import android.content.pm.PackageManagerInternal;
 import android.content.pm.Signature;
 import android.os.Binder;
 import android.os.Build;
@@ -49,7 +48,6 @@
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.util.ArrayUtils;
-import com.android.server.LocalServices;
 
 import libcore.io.IoUtils;
 
@@ -1175,9 +1173,8 @@
                 }
 
                 // If SetupWizard, done.
-                PackageManagerInternal packageManagerInternal = LocalServices.getService(
-                        PackageManagerInternal.class);
-                if (packageName.equals(packageManagerInternal.getSetupWizardPackageName())) {
+                String setupWizPackage = context.getPackageManager().getSetupWizardPackageName();
+                if (packageName.equals(setupWizPackage)) {
                     sSystemUids.put(uid, uid);
                     return true;
                 }
diff --git a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
index ebb9e86..8437eae 100644
--- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
+++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
@@ -594,7 +594,10 @@
                  Settings.Secure.ANR_SHOW_BACKGROUND,
                  Settings.Secure.ASSISTANT,
                  Settings.Secure.ASSIST_DISCLOSURE_ENABLED,
+                 Settings.Secure.ASSIST_GESTURE_ENABLED,
                  Settings.Secure.ASSIST_GESTURE_SENSITIVITY,
+                 Settings.Secure.ASSIST_GESTURE_WAKE_ENABLED,
+                 Settings.Secure.ASSIST_GESTURE_SILENCE_ALERTS_ENABLED,
                  Settings.Secure.ASSIST_GESTURE_SETUP_COMPLETE,
                  Settings.Secure.ASSIST_SCREENSHOT_ENABLED,
                  Settings.Secure.ASSIST_STRUCTURE_ENABLED,
diff --git a/packages/Shell/src/com/android/shell/BugreportProgressService.java b/packages/Shell/src/com/android/shell/BugreportProgressService.java
index d71d009..a097249 100644
--- a/packages/Shell/src/com/android/shell/BugreportProgressService.java
+++ b/packages/Shell/src/com/android/shell/BugreportProgressService.java
@@ -381,6 +381,7 @@
 
     private final class BugreportCallbackImpl extends BugreportCallback {
 
+        @GuardedBy("mLock")
         private final BugreportInfo mInfo;
 
         BugreportCallbackImpl(BugreportInfo info) {
@@ -389,10 +390,12 @@
 
         @Override
         public void onProgress(float progress) {
-            if (progress == 0) {
-                trackInfoWithId();
+            synchronized (mLock) {
+                if (progress == 0) {
+                    trackInfoWithIdLocked();
+                }
+                checkProgressUpdatedLocked(mInfo, (int) progress);
             }
-            checkProgressUpdated(mInfo, (int) progress);
         }
 
         /**
@@ -401,18 +404,21 @@
          */
         @Override
         public void onError(@BugreportErrorCode int errorCode) {
-            trackInfoWithId();
-            stopProgress(mInfo.id);
+            synchronized (mLock) {
+                trackInfoWithIdLocked();
+                stopProgressLocked(mInfo.id);
+            }
             Log.e(TAG, "Bugreport API callback onError() errorCode = " + errorCode);
             return;
         }
 
         @Override
         public void onFinished() {
-            // TODO: Make all callback functions lock protected.
             mInfo.renameBugreportFile();
             mInfo.renameScreenshots(mScreenshotsDir);
-            sendBugreportFinishedBroadcast();
+            synchronized (mLock) {
+                sendBugreportFinishedBroadcastLocked();
+            }
         }
 
         /**
@@ -421,7 +427,8 @@
          * when dumpstate calls one of the callback functions (onProgress, onFinished, onError)
          * after the id has been incremented.
          */
-        private void trackInfoWithId() {
+        @GuardedBy("mLock")
+        private void trackInfoWithIdLocked() {
             final int id = SystemProperties.getInt(PROPERTY_LAST_ID, 1);
             if (mBugreportInfos.get(id) == null) {
                 mInfo.id = id;
@@ -430,74 +437,75 @@
             return;
         }
 
-        private void sendBugreportFinishedBroadcast() {
+        @GuardedBy("mLock")
+        private void sendBugreportFinishedBroadcastLocked() {
             final String bugreportFilePath = mInfo.bugreportFile.getAbsolutePath();
             if (mInfo.bugreportFile.length() == 0) {
                 Log.e(TAG, "Bugreport file empty. File path = " + bugreportFilePath);
                 return;
             }
             if (mInfo.type == BugreportParams.BUGREPORT_MODE_REMOTE) {
-                sendRemoteBugreportFinishedBroadcast(bugreportFilePath, mInfo.bugreportFile);
+                sendRemoteBugreportFinishedBroadcast(mContext, bugreportFilePath,
+                        mInfo.bugreportFile);
             } else {
-                trackInfoWithId();
+                trackInfoWithIdLocked();
                 cleanupOldFiles(MIN_KEEP_COUNT, MIN_KEEP_AGE);
                 final Intent intent = new Intent(INTENT_BUGREPORT_FINISHED);
                 intent.putExtra(EXTRA_BUGREPORT, bugreportFilePath);
-                addScreenshotToIntent(intent);
+                addScreenshotToIntent(intent, mInfo);
                 mContext.sendBroadcast(intent, android.Manifest.permission.DUMP);
                 onBugreportFinished(mInfo.id);
             }
         }
+    }
 
-        private void sendRemoteBugreportFinishedBroadcast(String bugreportFileName,
-                File bugreportFile) {
-            cleanupOldFiles(REMOTE_BUGREPORT_FILES_AMOUNT, REMOTE_MIN_KEEP_AGE);
-            final Intent intent = new Intent(DevicePolicyManager.ACTION_REMOTE_BUGREPORT_DISPATCH);
-            final Uri bugreportUri = getUri(mContext, bugreportFile);
-            final String bugreportHash = generateFileHash(bugreportFileName);
-            if (bugreportHash == null) {
-                Log.e(TAG, "Error generating file hash for remote bugreport");
-                return;
-            }
-            intent.setDataAndType(bugreportUri, BUGREPORT_MIMETYPE);
-            intent.putExtra(DevicePolicyManager.EXTRA_REMOTE_BUGREPORT_HASH, bugreportHash);
-            intent.putExtra(EXTRA_BUGREPORT, bugreportFileName);
-            mContext.sendBroadcastAsUser(intent, UserHandle.SYSTEM,
-                    android.Manifest.permission.DUMP);
+    private static void sendRemoteBugreportFinishedBroadcast(Context context,
+            String bugreportFileName, File bugreportFile) {
+        cleanupOldFiles(REMOTE_BUGREPORT_FILES_AMOUNT, REMOTE_MIN_KEEP_AGE);
+        final Intent intent = new Intent(DevicePolicyManager.ACTION_REMOTE_BUGREPORT_DISPATCH);
+        final Uri bugreportUri = getUri(context, bugreportFile);
+        final String bugreportHash = generateFileHash(bugreportFileName);
+        if (bugreportHash == null) {
+            Log.e(TAG, "Error generating file hash for remote bugreport");
         }
+        intent.setDataAndType(bugreportUri, BUGREPORT_MIMETYPE);
+        intent.putExtra(DevicePolicyManager.EXTRA_REMOTE_BUGREPORT_HASH, bugreportHash);
+        intent.putExtra(EXTRA_BUGREPORT, bugreportFileName);
+        context.sendBroadcastAsUser(intent, UserHandle.SYSTEM,
+                android.Manifest.permission.DUMP);
+    }
 
-        private void addScreenshotToIntent(Intent intent) {
-            final File screenshotFile = mInfo.screenshotFiles.isEmpty()
-                    ? null : mInfo.screenshotFiles.get(0);
-            if (screenshotFile != null && screenshotFile.length() > 0) {
-                final String screenshotFilePath = screenshotFile.getAbsolutePath();
-                intent.putExtra(EXTRA_SCREENSHOT, screenshotFilePath);
-            }
-            return;
+    private static void addScreenshotToIntent(Intent intent, BugreportInfo info) {
+        final String screenshotFileName = info.name + ".png";
+        final File screenshotFile = new File(BUGREPORT_DIR, screenshotFileName);
+        final String screenshotFilePath = screenshotFile.getAbsolutePath();
+        if (screenshotFile.length() > 0) {
+            intent.putExtra(EXTRA_SCREENSHOT, screenshotFilePath);
         }
+        return;
+    }
 
-        private String generateFileHash(String fileName) {
-            String fileHash = null;
-            try {
-                MessageDigest md = MessageDigest.getInstance("SHA-256");
-                FileInputStream input = new FileInputStream(new File(fileName));
-                byte[] buffer = new byte[65536];
-                int size;
-                while ((size = input.read(buffer)) > 0) {
-                    md.update(buffer, 0, size);
-                }
-                input.close();
-                byte[] hashBytes = md.digest();
-                StringBuilder sb = new StringBuilder();
-                for (int i = 0; i < hashBytes.length; i++) {
-                    sb.append(String.format("%02x", hashBytes[i]));
-                }
-                fileHash = sb.toString();
-            } catch (IOException | NoSuchAlgorithmException e) {
-                Log.e(TAG, "generating file hash for bugreport file failed " + fileName, e);
+    private static String generateFileHash(String fileName) {
+        String fileHash = null;
+        try {
+            MessageDigest md = MessageDigest.getInstance("SHA-256");
+            FileInputStream input = new FileInputStream(new File(fileName));
+            byte[] buffer = new byte[65536];
+            int size;
+            while ((size = input.read(buffer)) > 0) {
+                md.update(buffer, 0, size);
             }
-            return fileHash;
+            input.close();
+            byte[] hashBytes = md.digest();
+            StringBuilder sb = new StringBuilder();
+            for (int i = 0; i < hashBytes.length; i++) {
+                sb.append(String.format("%02x", hashBytes[i]));
+            }
+            fileHash = sb.toString();
+        } catch (IOException | NoSuchAlgorithmException e) {
+            Log.e(TAG, "generating file hash for bugreport file failed " + fileName, e);
         }
+        return fileHash;
     }
 
     static void cleanupOldFiles(final int minCount, final long minAge) {
@@ -852,7 +860,8 @@
     /**
      * Finalizes the progress on a given bugreport and cancel its notification.
      */
-    private void stopProgress(int id) {
+    @GuardedBy("mLock")
+    private void stopProgressLocked(int id) {
         if (mBugreportInfos.indexOfKey(id) < 0) {
             Log.w(TAG, "ID not watched: " + id);
         } else {
@@ -883,7 +892,9 @@
             }
             deleteScreenshots(info);
         }
-        stopProgress(id);
+        synchronized (mLock) {
+            stopProgressLocked(id);
+        }
     }
 
     /**
@@ -1178,7 +1189,9 @@
         if (!info.bugreportFile.exists() || !info.bugreportFile.canRead()) {
             Log.e(TAG, "Could not read bugreport file " + info.bugreportFile);
             Toast.makeText(context, R.string.bugreport_unreadable_text, Toast.LENGTH_LONG).show();
-            stopProgress(info.id);
+            synchronized (mLock) {
+                stopProgressLocked(info.id);
+            }
             return;
         }
 
@@ -1290,7 +1303,9 @@
         final Intent sendIntent = buildSendIntent(mContext, info);
         if (sendIntent == null) {
             Log.w(TAG, "Stopping progres on ID " + id + " because share intent could not be built");
-            stopProgress(id);
+            synchronized (mLock) {
+                stopProgressLocked(id);
+            }
             return;
         }
 
@@ -1313,9 +1328,10 @@
         } else {
             mContext.startActivity(notifIntent);
         }
-
-        // ... and stop watching this process.
-        stopProgress(id);
+        synchronized (mLock) {
+            // ... and stop watching this process.
+            stopProgressLocked(id);
+        }
     }
 
     static void sendShareIntent(Context context, Intent intent) {
@@ -2361,14 +2377,18 @@
                 // The right, long-term solution is to provide an onFinished() callback
                 // on IDumpstateListener and call it instead of using a broadcast.
                 Log.w(TAG, "Dumpstate process died:\n" + info);
-                stopProgress(info.id);
+                synchronized (mLock) {
+                    stopProgressLocked(info.id);
+                }
             }
             token.asBinder().unlinkToDeath(this, 0);
         }
 
         @Override
         public void onProgress(int progress) throws RemoteException {
-            checkProgressUpdated(info, progress);
+            synchronized (mLock) {
+                checkProgressUpdatedLocked(info, progress);
+            }
         }
 
         @Override
@@ -2387,7 +2407,8 @@
 
     }
 
-    private void checkProgressUpdated(BugreportInfo info, int progress) {
+    @GuardedBy("mLock")
+    private void checkProgressUpdatedLocked(BugreportInfo info, int progress) {
         if (progress > CAPPED_PROGRESS) {
             progress = CAPPED_PROGRESS;
         }
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index 403e894..b288eb7 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -243,6 +243,9 @@
     <!-- Permission to change the display color -->
     <uses-permission android:name="android.permission.CONTROL_DISPLAY_COLOR_TRANSFORMS" />
 
+    <!-- Query all packages on device on R+ -->
+    <uses-permission android:name="android.permission.QUERY_ALL_PACKAGES" />
+
     <protected-broadcast android:name="com.android.settingslib.action.REGISTER_SLICE_RECEIVER" />
     <protected-broadcast android:name="com.android.settingslib.action.UNREGISTER_SLICE_RECEIVER" />
     <protected-broadcast android:name="com.android.settings.flashlight.action.FLASHLIGHT_CHANGED" />
diff --git a/packages/SystemUI/src/com/android/systemui/assist/AssistHandleViewController.java b/packages/SystemUI/src/com/android/systemui/assist/AssistHandleViewController.java
index f9ffb80..5010f31 100644
--- a/packages/SystemUI/src/com/android/systemui/assist/AssistHandleViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/assist/AssistHandleViewController.java
@@ -43,6 +43,7 @@
     private Handler mHandler;
     private CornerHandleView mAssistHintLeft;
     private CornerHandleView mAssistHintRight;
+    private int mBottomOffset;
 
     @VisibleForTesting
     boolean mAssistHintVisible;
@@ -62,6 +63,23 @@
     }
 
     /**
+     * Set the bottom offset.
+     *
+     * @param bottomOffset the bottom offset to translate.
+     */
+    public void setBottomOffset(int bottomOffset) {
+        if (mBottomOffset != bottomOffset) {
+            mBottomOffset = bottomOffset;
+            if (mAssistHintVisible) {
+                // If assist handles are visible, hide them without animation and then make them
+                // show once again (with corrected bottom offset).
+                hideAssistHandles();
+                setAssistHintVisible(true);
+            }
+        }
+    }
+
+    /**
      * Controls the visibility of the assist gesture handles.
      *
      * @param visible whether the handles should be shown
@@ -126,7 +144,8 @@
                 xDirection * translationStart * view.getWidth(),
                 xDirection * translationEnd * view.getWidth());
         Animator translateY = ObjectAnimator.ofFloat(view, View.TRANSLATION_Y,
-                translationStart * view.getHeight(), translationEnd * view.getHeight());
+                translationStart * view.getHeight() + mBottomOffset,
+                translationEnd * view.getHeight() + mBottomOffset);
 
         AnimatorSet set = new AnimatorSet();
         set.play(scaleX).with(scaleY);
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
index 8240345..cc548d0 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
@@ -569,7 +569,8 @@
                     if (mStackView != null) {
                         mStackView.updateDotVisibility(entry.key);
                     }
-                    mNotificationEntryManager.updateNotifications();
+                    mNotificationEntryManager.updateNotifications(
+                            "BubbleController.onNotificationRemoveRequested");
                     return true;
                 } else if (!userRemovedNotif && entry != null) {
                     // This wasn't a user removal so we should remove the bubble as well
@@ -609,7 +610,8 @@
                 mBubbleData.addSummaryToSuppress(summary.notification.getGroupKey(),
                         summary.key);
                 // Tell shade to update for the suppression
-                mNotificationEntryManager.updateNotifications();
+                mNotificationEntryManager.updateNotifications(
+                        "BubbleController.handleSummaryRemovalInterception");
             }
             return !isAutogroupSummary;
         } else {
@@ -760,7 +762,8 @@
                 mStackView.setExpanded(true);
             }
 
-            mNotificationEntryManager.updateNotifications();
+            mNotificationEntryManager.updateNotifications(
+                    "BubbleData.Listener.applyUpdate");
             updateStack();
 
             if (DEBUG_BUBBLE_CONTROLLER) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationListener.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationListener.java
index 6c0f90a..c4de2d3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationListener.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationListener.java
@@ -127,7 +127,7 @@
                         mEntryManager.removeNotification(key, rankingMap, UNDEFINED_DISMISS_REASON);
                     } else {
                         mEntryManager.getNotificationData()
-                                .updateRanking(rankingMap);
+                                .updateRanking(rankingMap, "onNotificationPosted");
                     }
                     return;
                 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java
index 4ba1114..6ffea79 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java
@@ -106,7 +106,7 @@
                     isCurrentProfile(getSendingUserId())) {
                 mUsersAllowingPrivateNotifications.clear();
                 updateLockscreenNotificationSetting();
-                getEntryManager().updateNotifications();
+                getEntryManager().updateNotifications("ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED");
             }
         }
     };
@@ -124,7 +124,7 @@
                 updatePublicMode();
                 // The filtering needs to happen before the update call below in order to make sure
                 // the presenter has the updated notifications from the new user
-                getEntryManager().getNotificationData().filterAndSort();
+                getEntryManager().getNotificationData().filterAndSort("user switched");
                 mPresenter.onUserSwitched(mCurrentUserId);
 
                 for (UserChangedListener listener : mListeners) {
@@ -205,7 +205,8 @@
                 mUsersAllowingNotifications.clear();
                 // ... and refresh all the notifications
                 updateLockscreenNotificationSetting();
-                getEntryManager().updateNotifications();
+                getEntryManager().updateNotifications("LOCK_SCREEN_SHOW_NOTIFICATIONS,"
+                        + " or LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS change");
             }
         };
 
@@ -214,7 +215,8 @@
             public void onChange(boolean selfChange) {
                 updateLockscreenNotificationSetting();
                 if (mDeviceProvisionedController.isDeviceProvisioned()) {
-                    getEntryManager().updateNotifications();
+                    getEntryManager().updateNotifications("LOCK_SCREEN_ALLOW_REMOTE_INPUT"
+                            + " or ZEN_MODE change");
                 }
             }
         };
@@ -532,7 +534,7 @@
             setLockscreenPublicMode(isProfilePublic, userId);
             mUsersWithSeperateWorkChallenge.put(userId, needsSeparateChallenge);
         }
-        getEntryManager().updateNotifications();
+        getEntryManager().updateNotifications("NotificationLockscreenUserManager.updatePublicMode");
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
index c50fb3d..3616b54 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
@@ -361,7 +361,7 @@
         }
 
         if (metaDataChanged) {
-            mEntryManager.updateNotifications();
+            mEntryManager.updateNotifications("NotificationMediaManager - metaDataChanged");
         }
 
         dispatchUpdateMediaMetaData(metaDataChanged, true /* allowEnterAnimation */);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
index 148a1a8..01c79b3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
@@ -40,6 +40,8 @@
 import com.android.systemui.statusbar.notification.collection.NotificationData.KeyguardEnvironment;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.collection.NotificationRowBinder;
+import com.android.systemui.statusbar.notification.logging.NotifEvent;
+import com.android.systemui.statusbar.notification.logging.NotifLog;
 import com.android.systemui.statusbar.notification.logging.NotificationLogger;
 import com.android.systemui.statusbar.notification.row.NotificationContentInflater;
 import com.android.systemui.statusbar.notification.row.NotificationContentInflater.InflationFlag;
@@ -92,6 +94,7 @@
     private NotificationListenerService.RankingMap mLatestRankingMap;
     @VisibleForTesting
     protected NotificationData mNotificationData;
+    private NotifLog mNotifLog;
 
     @VisibleForTesting
     final ArrayList<NotificationLifetimeExtender> mNotificationLifetimeExtenders
@@ -123,8 +126,9 @@
     }
 
     @Inject
-    public NotificationEntryManager(NotificationData notificationData) {
+    public NotificationEntryManager(NotificationData notificationData, NotifLog notifLog) {
         mNotificationData = notificationData;
+        mNotifLog = notifLog;
     }
 
     /** Adds a {@link NotificationEntryListener}. */
@@ -178,7 +182,7 @@
 
     @Override
     public void onReorderingAllowed() {
-        updateNotifications();
+        updateNotifications("reordering is now allowed");
     }
 
     /**
@@ -203,15 +207,19 @@
         return NotificationVisibility.obtain(key, rank, count, true, location);
     }
 
-    private void abortExistingInflation(String key) {
+    private void abortExistingInflation(String key, String reason) {
         if (mPendingNotifications.containsKey(key)) {
             NotificationEntry entry = mPendingNotifications.get(key);
             entry.abortTask();
             mPendingNotifications.remove(key);
+            mNotifLog.log(NotifEvent.INFLATION_ABORTED, entry.sbn(), null,
+                    "PendingNotification aborted. " + reason);
         }
         NotificationEntry addedEntry = mNotificationData.get(key);
         if (addedEntry != null) {
             addedEntry.abortTask();
+            mNotifLog.log(NotifEvent.INFLATION_ABORTED, addedEntry.sbn(),
+                    null, reason);
         }
     }
 
@@ -247,7 +255,7 @@
                 for (NotificationEntryListener listener : mNotificationEntryListeners) {
                     listener.onBeforeNotificationAdded(entry);
                 }
-                updateNotifications();
+                updateNotifications("onAsyncInflationFinished");
                 for (NotificationEntryListener listener : mNotificationEntryListeners) {
                     listener.onNotificationAdded(entry);
                 }
@@ -276,7 +284,8 @@
 
         if (mRemoveInterceptor != null
                 && mRemoveInterceptor.onNotificationRemoveRequested(key, reason)) {
-            // Remove intercepted; skip
+            // Remove intercepted; log and skip
+            mNotifLog.log(NotifEvent.REMOVE_INTERCEPTED);
             return;
         }
 
@@ -291,13 +300,17 @@
                     if (extender.shouldExtendLifetimeForPendingNotification(pendingEntry)) {
                         extendLifetime(pendingEntry, extender);
                         lifetimeExtended = true;
+                        mNotifLog.log(
+                                NotifEvent.LIFETIME_EXTENDED,
+                                pendingEntry.sbn(),
+                                "pendingEntry extendedBy=" + extender.toString());
                     }
                 }
             }
         }
 
         if (!lifetimeExtended) {
-            abortExistingInflation(key);
+            abortExistingInflation(key, "removeNotification");
         }
 
         if (entry != null) {
@@ -310,6 +323,10 @@
                         mLatestRankingMap = ranking;
                         extendLifetime(entry, extender);
                         lifetimeExtended = true;
+                        mNotifLog.log(
+                                NotifEvent.LIFETIME_EXTENDED,
+                                entry.sbn(),
+                                "entry extendedBy=" + extender.toString());
                         break;
                     }
                 }
@@ -329,10 +346,12 @@
                 handleGroupSummaryRemoved(key);
 
                 mNotificationData.remove(key, ranking);
-                updateNotifications();
+                updateNotifications("removeNotificationInternal");
                 Dependency.get(LeakDetector.class).trackGarbage(entry);
                 removedByUser |= entryDismissed;
 
+                mNotifLog.log(NotifEvent.NOTIF_REMOVED, entry.sbn(),
+                        "removedByUser=" + removedByUser);
                 for (NotificationEntryListener listener : mNotificationEntryListeners) {
                     listener.onEntryRemoved(entry, visibility, removedByUser);
                 }
@@ -389,7 +408,7 @@
             Log.d(TAG, "addNotification key=" + key);
         }
 
-        mNotificationData.updateRanking(rankingMap);
+        mNotificationData.updateRanking(rankingMap, "addNotificationInternal");
         Ranking ranking = new Ranking();
         rankingMap.getRanking(key, ranking);
 
@@ -400,9 +419,9 @@
         requireBinder().inflateViews(entry, () -> performRemoveNotification(notification,
                 REASON_CANCEL));
 
-        abortExistingInflation(key);
-
+        abortExistingInflation(key, "addNotification");
         mPendingNotifications.put(key, entry);
+        mNotifLog.log(NotifEvent.NOTIF_ADDED, entry.sbn());
         for (NotificationEntryListener listener : mNotificationEntryListeners) {
             listener.onPendingEntryAdded(entry);
         }
@@ -423,7 +442,7 @@
         if (DEBUG) Log.d(TAG, "updateNotification(" + notification + ")");
 
         final String key = notification.getKey();
-        abortExistingInflation(key);
+        abortExistingInflation(key, "updateNotification");
         NotificationEntry entry = mNotificationData.get(key);
         if (entry == null) {
             return;
@@ -433,15 +452,15 @@
         // to keep its lifetime extended.
         cancelLifetimeExtension(entry);
 
-        mNotificationData.update(entry, ranking, notification);
-
+        mNotificationData.update(entry, ranking, notification, "updateNotificationInternal");
+        mNotifLog.log(NotifEvent.NOTIF_UPDATED, entry.sbn(), entry.ranking());
         for (NotificationEntryListener listener : mNotificationEntryListeners) {
             listener.onPreEntryUpdated(entry);
         }
 
         requireBinder().inflateViews(entry, () -> performRemoveNotification(notification,
                 REASON_CANCEL));
-        updateNotifications();
+        updateNotifications("updateNotificationInternal");
 
         if (DEBUG) {
             // Is this for you?
@@ -465,8 +484,12 @@
         }
     }
 
-    public void updateNotifications() {
-        mNotificationData.filterAndSort();
+    /**
+     * Update the notifications
+     * @param reason why the notifications are updating
+     */
+    public void updateNotifications(String reason) {
+        mNotificationData.filterAndSort(reason);
         if (mPresenter != null) {
             mPresenter.updateNotificationViews();
         }
@@ -489,7 +512,7 @@
         }
 
         // Populate notification entries from the new rankings.
-        mNotificationData.updateRanking(rankingMap);
+        mNotificationData.updateRanking(rankingMap, "updateNotificationRanking");
         updateRankingOfPendingNotifications(rankingMap);
 
         // By comparing the old and new UI adjustments, reinflate the view accordingly.
@@ -501,7 +524,7 @@
                     NotificationUiAdjustment.extractFromNotificationEntry(entry));
         }
 
-        updateNotifications();
+        updateNotifications("updateNotificationRanking");
 
         for (NotificationEntryListener listener : mNotificationEntryListeners) {
             listener.onNotificationRankingUpdated(rankingMap);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationListController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationListController.java
index 769cbb7..970cbf9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationListController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationListController.java
@@ -81,7 +81,7 @@
             new DeviceProvisionedListener() {
                 @Override
                 public void onDeviceProvisionedChanged() {
-                    mEntryManager.updateNotifications();
+                    mEntryManager.updateNotifications("device provisioned changed");
                 }
             };
 
@@ -106,7 +106,7 @@
         if (foregroundKey != null) {
             mEntryManager
                     .getNotificationData().updateAppOp(appOp, uid, pkg, foregroundKey, showIcon);
-            mEntryManager.updateNotifications();
+            mEntryManager.updateNotifications("app opp changed pkg=" + pkg);
         }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationData.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationData.java
index cf0fbbb..a98fa66 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationData.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationData.java
@@ -35,6 +35,8 @@
 import com.android.systemui.statusbar.NotificationMediaManager;
 import com.android.systemui.statusbar.notification.NotificationFilter;
 import com.android.systemui.statusbar.notification.NotificationSectionsFeatureManager;
+import com.android.systemui.statusbar.notification.logging.NotifEvent;
+import com.android.systemui.statusbar.notification.logging.NotifLog;
 import com.android.systemui.statusbar.phone.NotificationGroupManager;
 import com.android.systemui.statusbar.policy.HeadsUpManager;
 
@@ -73,10 +75,13 @@
     private RankingMap mRankingMap;
     private final Ranking mTmpRanking = new Ranking();
     private final boolean mUsePeopleFiltering;
+    private final NotifLog mNotifLog;
 
     @Inject
-    public NotificationData(NotificationSectionsFeatureManager sectionsFeatureManager) {
+    public NotificationData(NotificationSectionsFeatureManager sectionsFeatureManager,
+            NotifLog notifLog) {
         mUsePeopleFiltering = sectionsFeatureManager.isFilteringEnabled();
+        mNotifLog = notifLog;
     }
 
     public void setHeadsUpManager(HeadsUpManager headsUpManager) {
@@ -179,7 +184,7 @@
         }
         mGroupManager.onEntryAdded(entry);
 
-        updateRankingAndSort(mRankingMap);
+        updateRankingAndSort(mRankingMap, "addEntry=" + entry.sbn());
     }
 
     public NotificationEntry remove(String key, RankingMap ranking) {
@@ -189,7 +194,7 @@
         }
         if (removed == null) return null;
         mGroupManager.onEntryRemoved(removed);
-        updateRankingAndSort(ranking);
+        updateRankingAndSort(ranking, "removeEntry=" + removed.sbn());
         return removed;
     }
 
@@ -197,15 +202,19 @@
     public void update(
             NotificationEntry entry,
             RankingMap ranking,
-            StatusBarNotification notification) {
-        updateRanking(ranking);
+            StatusBarNotification notification,
+            String reason) {
+        updateRanking(ranking, reason);
         final StatusBarNotification oldNotification = entry.notification;
         entry.setNotification(notification);
         mGroupManager.onEntryUpdated(entry, oldNotification);
     }
 
-    public void updateRanking(RankingMap ranking) {
-        updateRankingAndSort(ranking);
+    /**
+     * Update ranking and trigger a re-sort
+     */
+    public void updateRanking(RankingMap ranking, String reason) {
+        updateRankingAndSort(ranking, reason);
     }
 
     public void updateAppOp(int appOp, int uid, String pkg, String key, boolean showIcon) {
@@ -352,7 +361,7 @@
         return false;
     }
 
-    private void updateRankingAndSort(RankingMap rankingMap) {
+    private void updateRankingAndSort(RankingMap rankingMap, String reason) {
         if (rankingMap != null) {
             mRankingMap = rankingMap;
             synchronized (mEntries) {
@@ -375,7 +384,7 @@
                 }
             }
         }
-        filterAndSort();
+        filterAndSort(reason);
     }
 
     /**
@@ -393,7 +402,11 @@
 
     // TODO: This should not be public. Instead the Environment should notify this class when
     // anything changed, and this class should call back the UI so it updates itself.
-    public void filterAndSort() {
+    /**
+     * Filters and sorts the list of notification entries
+     */
+    public void filterAndSort(String reason) {
+        mNotifLog.log(NotifEvent.FILTER_AND_SORT, reason);
         mSortedAndFiltered.clear();
 
         synchronized (mEntries) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationRowBinderImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationRowBinderImpl.java
index 60cf995..e5571b6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationRowBinderImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationRowBinderImpl.java
@@ -41,6 +41,8 @@
 import com.android.systemui.statusbar.notification.InflationException;
 import com.android.systemui.statusbar.notification.NotificationClicker;
 import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider;
+import com.android.systemui.statusbar.notification.logging.NotifEvent;
+import com.android.systemui.statusbar.notification.logging.NotifLog;
 import com.android.systemui.statusbar.notification.logging.NotificationLogger;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 import com.android.systemui.statusbar.notification.row.NotificationContentInflater;
@@ -72,6 +74,7 @@
     private final boolean mAllowLongPress;
     private final KeyguardBypassController mKeyguardBypassController;
     private final StatusBarStateController mStatusBarStateController;
+    private final NotifLog mNotifLog;
 
     private NotificationRemoteInputManager mRemoteInputManager;
     private NotificationPresenter mPresenter;
@@ -85,12 +88,14 @@
 
     public NotificationRowBinderImpl(Context context, boolean allowLongPress,
             KeyguardBypassController keyguardBypassController,
-            StatusBarStateController statusBarStateController) {
+            StatusBarStateController statusBarStateController,
+            NotifLog notifLog) {
         mContext = context;
         mMessagingUtil = new NotificationMessagingUtil(context);
         mAllowLongPress = allowLongPress;
         mKeyguardBypassController = keyguardBypassController;
         mStatusBarStateController = statusBarStateController;
+        mNotifLog = notifLog;
     }
 
     private NotificationRemoteInputManager getRemoteInputManager() {
@@ -143,6 +148,7 @@
                     row -> {
                         bindRow(entry, pmUser, sbn, row, onDismissRunnable);
                         updateNotification(entry, pmUser, sbn, row);
+                        mNotifLog.log(NotifEvent.INFLATED, sbn);
                     });
         }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotifEvent.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotifEvent.java
index 2396d28..7703cbd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotifEvent.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotifEvent.java
@@ -30,9 +30,9 @@
  * and triaging purposes.
  */
 public class NotifEvent extends RichEvent {
-    public static final int TOTAL_EVENT_TYPES = 8;
-    private StatusBarNotification mSbn;
-    private Ranking mRanking;
+    public static final int TOTAL_EVENT_TYPES = 11;
+    private final StatusBarNotification mSbn;
+    private final Ranking mRanking;
 
     /**
      * Creates a NotifEvent with an event type that matches with an index in the array
@@ -44,9 +44,20 @@
     public NotifEvent(int logLevel, int type, String reason, StatusBarNotification sbn,
             Ranking ranking) {
         super(logLevel, type, reason);
-        mSbn = sbn.clone();
-        mRanking = new Ranking();
-        mRanking.populate(ranking);
+
+        if (sbn != null) {
+            mSbn = sbn.cloneLight();
+        } else {
+            mSbn = null;
+        }
+
+        if (ranking != null) {
+            mRanking = new Ranking();
+            mRanking.populate(ranking);
+        } else {
+            mRanking = null;
+        }
+
         mMessage += getExtraInfo();
     }
 
@@ -76,11 +87,14 @@
                 "NotifAdded",
                 "NotifRemoved",
                 "NotifUpdated",
-                "HeadsUpStarted",
-                "HeadsUpEnded",
                 "Filter",
                 "Sort",
+                "FilterAndSort",
                 "NotifVisibilityChanged",
+                "LifetimeExtended",
+                "RemoveIntercepted",
+                "InflationAborted",
+                "Inflated"
         };
 
         if (events.length != TOTAL_EVENT_TYPES) {
@@ -135,8 +149,19 @@
         }
     }
 
-    @IntDef({NOTIF_ADDED, NOTIF_REMOVED, NOTIF_UPDATED, HEADS_UP_STARTED, HEADS_UP_ENDED, FILTER,
-            SORT, NOTIF_VISIBILITY_CHANGED})
+    @IntDef({NOTIF_ADDED,
+            NOTIF_REMOVED,
+            NOTIF_UPDATED,
+            FILTER,
+            SORT,
+            FILTER_AND_SORT,
+            NOTIF_VISIBILITY_CHANGED,
+            LIFETIME_EXTENDED,
+            REMOVE_INTERCEPTED,
+            INFLATION_ABORTED,
+            INFLATED
+    })
+
     /**
      * Types of NotifEvents
      */
@@ -145,9 +170,13 @@
     public static final int NOTIF_ADDED = 0;
     public static final int NOTIF_REMOVED = 1;
     public static final int NOTIF_UPDATED = 2;
-    public static final int HEADS_UP_STARTED = 3;
-    public static final int HEADS_UP_ENDED = 4;
-    public static final int FILTER = 5;
-    public static final int SORT = 6;
-    public static final int NOTIF_VISIBILITY_CHANGED = 7;
+    public static final int FILTER = 3;
+    public static final int SORT = 4;
+    public static final int FILTER_AND_SORT = 5;
+    public static final int NOTIF_VISIBILITY_CHANGED = 6;
+    public static final int LIFETIME_EXTENDED = 7;
+    // unable to remove notif - removal intercepted by {@link NotificationRemoveInterceptor}
+    public static final int REMOVE_INTERCEPTED = 8;
+    public static final int INFLATION_ABORTED = 9;
+    public static final int INFLATED = 10;
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotifLog.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotifLog.java
index d42cd82..8466d2e8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotifLog.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotifLog.java
@@ -82,6 +82,14 @@
     }
 
     /**
+     * Logs a {@link NotifEvent} with a notification
+     * @return true if successfully logged, else false
+     */
+    public boolean log(@NotifEvent.EventType int eventType, StatusBarNotification sbn, String msg) {
+        return log(eventType, sbn, null, msg);
+    }
+
+    /**
      * Logs a {@link NotifEvent} with a ranking
      * @return true if successfully logged, else false
      */
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
index 9f4b026..924a347 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
@@ -1447,6 +1447,7 @@
         }
         setDismissed(fromAccessibility);
         if (mEntry.isClearable()) {
+            // TODO: beverlyt, log dismissal
             // TODO: track dismiss sentiment
             if (mOnDismissRunnable != null) {
                 mOnDismissRunnable.run();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBlockingHelperManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBlockingHelperManager.java
index 73093c6f..37f63c9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBlockingHelperManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBlockingHelperManager.java
@@ -139,7 +139,8 @@
 
             mBlockingHelperRow.setBlockingHelperShowing(false);
             if (mBlockingHelperRow.isAttachedToWindow()) {
-                Dependency.get(NotificationEntryManager.class).updateNotifications();
+                Dependency.get(NotificationEntryManager.class).updateNotifications(
+                        "dismissCurrentBlockingHelper");
             }
             mBlockingHelperRow = null;
             return true;
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 f5705c5..7bbe818 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
@@ -5333,7 +5333,7 @@
         requestChildrenUpdate();
         onUpdateRowStates();
 
-        mEntryManager.updateNotifications();
+        mEntryManager.updateNotifications("StatusBar state changed");
         updateVisibility();
     }
 
@@ -6492,12 +6492,12 @@
 
         @Override
         public void onGroupCreatedFromChildren(NotificationGroupManager.NotificationGroup group) {
-            mStatusBar.requestNotificationUpdate();
+            mStatusBar.requestNotificationUpdate("onGroupCreatedFromChildren");
         }
 
         @Override
         public void onGroupsChanged() {
-            mStatusBar.requestNotificationUpdate();
+            mStatusBar.requestNotificationUpdate("onGroupsChanged");
         }
     };
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/EdgeBackGestureHandler.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/EdgeBackGestureHandler.java
index 4cd3ad2..ca7c227 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/EdgeBackGestureHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/EdgeBackGestureHandler.java
@@ -16,6 +16,8 @@
 package com.android.systemui.statusbar.phone;
 
 import static android.view.Display.INVALID_DISPLAY;
+import static android.view.View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY;
+import static android.view.View.NAVIGATION_BAR_TRANSIENT;
 
 import android.content.Context;
 import android.content.res.Resources;
@@ -131,6 +133,7 @@
     private boolean mIsAttached;
     private boolean mIsGesturalModeEnabled;
     private boolean mIsEnabled;
+    private boolean mIsInTransientImmersiveStickyState;
 
     private InputMonitor mInputMonitor;
     private InputEventReceiver mInputEventReceiver;
@@ -195,6 +198,12 @@
         updateCurrentUserResources(currentUserContext.getResources());
     }
 
+    public void onSystemUiVisibilityChanged(int systemUiVisibility) {
+        mIsInTransientImmersiveStickyState =
+                (systemUiVisibility & SYSTEM_UI_FLAG_IMMERSIVE_STICKY) != 0
+                && (systemUiVisibility & NAVIGATION_BAR_TRANSIENT) != 0;
+    }
+
     private void disposeInputChannel() {
         if (mInputEventReceiver != null) {
             mInputEventReceiver.dispose();
@@ -296,13 +305,21 @@
     }
 
     private boolean isWithinTouchRegion(int x, int y) {
+        // Disallow if over the IME
         if (y > (mDisplaySize.y - Math.max(mImeHeight, mNavBarHeight))) {
             return false;
         }
 
+        // Disallow if too far from the edge
         if (x > mEdgeWidth + mLeftInset && x < (mDisplaySize.x - mEdgeWidth - mRightInset)) {
             return false;
         }
+
+        // Always allow if the user is in a transient sticky immersive state
+        if (mIsInTransientImmersiveStickyState) {
+            return true;
+        }
+
         boolean isInExcludedRegion = mExcludeRegion.contains(x, y);
         if (isInExcludedRegion) {
             mOverviewProxyService.notifyBackAction(false /* completed */, -1, -1,
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 6e61d7c..38dc5ea 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
@@ -560,6 +560,9 @@
                 }
                 mAutoHideController.touchAutoHide();
             }
+            if (mNavigationBarView != null) {
+                mNavigationBarView.onSystemUiVisibilityChanged(mSystemUiVisibility);
+            }
         }
         mLightBarController.onNavigationVisibilityChanged(
                 vis, mask, nbModeChanged, mNavigationBarMode, navbarColorManagedByIme);
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 a1a47e1..9804f9f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
@@ -67,6 +67,7 @@
 import com.android.systemui.Interpolators;
 import com.android.systemui.R;
 import com.android.systemui.SysUiServiceProvider;
+import com.android.systemui.assist.AssistHandleViewController;
 import com.android.systemui.assist.AssistManager;
 import com.android.systemui.model.SysUiState;
 import com.android.systemui.recents.OverviewProxyService;
@@ -75,6 +76,7 @@
 import com.android.systemui.shared.system.ActivityManagerWrapper;
 import com.android.systemui.shared.system.QuickStepContract;
 import com.android.systemui.shared.system.WindowManagerWrapper;
+import com.android.systemui.statusbar.NavigationBarController;
 import com.android.systemui.statusbar.policy.DeadZone;
 import com.android.systemui.statusbar.policy.KeyButtonDrawable;
 
@@ -364,6 +366,10 @@
         return super.onTouchEvent(event);
     }
 
+    void onSystemUiVisibilityChanged(int systemUiVisibility) {
+        mEdgeBackGestureHandler.onSystemUiVisibilityChanged(systemUiVisibility);
+    }
+
     void onBarTransition(int newMode) {
         if (newMode == MODE_OPAQUE) {
             // If the nav bar background is opaque, stop auto tinting since we know the icons are
@@ -1198,6 +1204,19 @@
         // we're passing the insets onto the gesture handler since the back arrow is only
         // conditionally added and doesn't always get all the insets.
         mEdgeBackGestureHandler.setInsets(leftInset, rightInset);
+
+        // this allows assist handle to be drawn outside its bound so that it can align screen
+        // bottom by translating its y position.
+        final boolean shouldClip =
+                !isGesturalMode(mNavBarMode) || insets.getSystemWindowInsetBottom() == 0;
+        setClipChildren(shouldClip);
+        setClipToPadding(shouldClip);
+
+        AssistHandleViewController controller = Dependency.get(NavigationBarController.class)
+                .getAssistHandlerViewController();
+        if (controller != null) {
+            controller.setBottomOffset(insets.getSystemWindowInsetBottom());
+        }
         return super.onApplyWindowInsets(insets);
     }
 
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 6ce6dfa..2b80d22 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -210,6 +210,7 @@
 import com.android.systemui.statusbar.notification.VisualStabilityManager;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.collection.NotificationRowBinderImpl;
+import com.android.systemui.statusbar.notification.logging.NotifLog;
 import com.android.systemui.statusbar.notification.logging.NotificationLogger;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
@@ -385,6 +386,7 @@
     private final BroadcastDispatcher mBroadcastDispatcher;
     private final ConfigurationController mConfigurationController;
     private final StatusBarWindowViewController.Builder mStatusBarWindowViewControllerBuilder;
+    private final NotifLog mNotifLog;
 
     // expanded notifications
     protected NotificationPanelView mNotificationPanel; // the sliding/resizing panel within the notification window
@@ -585,7 +587,7 @@
                 @Override
                 public void onStrongAuthStateChanged(int userId) {
                     super.onStrongAuthStateChanged(userId);
-                    mEntryManager.updateNotifications();
+                    mEntryManager.updateNotifications("onStrongAuthStateChanged");
                 }
             };
     private final Handler mMainThreadHandler = new Handler(Looper.getMainLooper());
@@ -599,6 +601,7 @@
     private boolean mPulsing;
     private final BubbleController mBubbleController;
     private final BubbleController.BubbleExpandListener mBubbleExpandListener;
+
     private ActivityIntentHelper mActivityIntentHelper;
 
     @Override
@@ -669,7 +672,8 @@
             NotificationListener notificationListener,
             ConfigurationController configurationController,
             StatusBarWindowController statusBarWindowController,
-            StatusBarWindowViewController.Builder statusBarWindowViewControllerBuilder) {
+            StatusBarWindowViewController.Builder statusBarWindowViewControllerBuilder,
+            NotifLog notifLog) {
         mLightBarController = lightBarController;
         mAutoHideController = autoHideController;
         mKeyguardUpdateMonitor = keyguardUpdateMonitor;
@@ -723,10 +727,11 @@
         mConfigurationController = configurationController;
         mStatusBarWindowController = statusBarWindowController;
         mStatusBarWindowViewControllerBuilder = statusBarWindowViewControllerBuilder;
+        mNotifLog = notifLog;
 
         mBubbleExpandListener =
                 (isExpanding, key) -> {
-                    mEntryManager.updateNotifications();
+                    mEntryManager.updateNotifications("onBubbleExpandChanged");
                     updateScrimController();
                 };
     }
@@ -1160,7 +1165,8 @@
                         mContext,
                         mAllowNotificationLongPress,
                         mKeyguardBypassController,
-                        mStatusBarStateController);
+                        mStatusBarStateController,
+                        mNotifLog);
 
         mPresenter = new StatusBarNotificationPresenter(mContext, mNotificationPanel,
                 mHeadsUpManager, mStatusBarWindow, mStackScroller, mDozeScrimController,
@@ -1443,8 +1449,12 @@
         return mZenController.areNotificationsHiddenInShade();
     }
 
-    public void requestNotificationUpdate() {
-        mEntryManager.updateNotifications();
+    /**
+     * Request a notification update
+     * @param reason why we're requesting a notification update
+     */
+    public void requestNotificationUpdate(String reason) {
+        mEntryManager.updateNotifications(reason);
     }
 
     /**
@@ -1685,7 +1695,7 @@
 
     @Override
     public void onHeadsUpStateChanged(NotificationEntry entry, boolean isHeadsUp) {
-        mEntryManager.updateNotifications();
+        mEntryManager.updateNotifications("onHeadsUpStateChanged");
         if (isDozing() && isHeadsUp) {
             entry.setPulseSuppressed(false);
             mDozeServiceHost.fireNotificationPulse(entry);
@@ -3566,7 +3576,7 @@
         updateQsExpansionEnabled();
         mKeyguardViewMediator.setDozing(mDozing);
 
-        mEntryManager.updateNotifications();
+        mEntryManager.updateNotifications("onDozingChanged");
         updateDozingState();
         updateScrimController();
         updateReportRejectedTouchVisibility();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java
index 2798c6b..5a2b5e3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java
@@ -33,6 +33,7 @@
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.Mockito.atLeastOnce;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
@@ -91,7 +92,6 @@
 @RunWith(AndroidTestingRunner.class)
 @TestableLooper.RunWithLooper(setAsMainLooper = true)
 public class BubbleControllerTest extends SysuiTestCase {
-
     @Mock
     private NotificationEntryManager mNotificationEntryManager;
     @Mock
@@ -223,13 +223,13 @@
         mBubbleController.updateBubble(mRow.getEntry());
         assertNotNull(mBubbleData.getBubbleWithKey(mRow.getEntry().key));
         assertTrue(mBubbleController.hasBubbles());
-        verify(mNotificationEntryManager).updateNotifications();
+        verify(mNotificationEntryManager).updateNotifications(any());
         verify(mBubbleStateChangeListener).onHasBubblesChanged(true);
 
         mBubbleController.removeBubble(mRow.getEntry().key, BubbleController.DISMISS_USER_GESTURE);
         assertFalse(mStatusBarWindowController.getBubblesShowing());
         assertNull(mBubbleData.getBubbleWithKey(mRow.getEntry().key));
-        verify(mNotificationEntryManager, times(2)).updateNotifications();
+        verify(mNotificationEntryManager, times(2)).updateNotifications(anyString());
         verify(mBubbleStateChangeListener).onHasBubblesChanged(false);
     }
 
@@ -257,16 +257,16 @@
     @Test
     public void testDismissStack() {
         mBubbleController.updateBubble(mRow.getEntry());
-        verify(mNotificationEntryManager, times(1)).updateNotifications();
+        verify(mNotificationEntryManager, times(1)).updateNotifications(any());
         assertNotNull(mBubbleData.getBubbleWithKey(mRow.getEntry().key));
         mBubbleController.updateBubble(mRow2.getEntry());
-        verify(mNotificationEntryManager, times(2)).updateNotifications();
+        verify(mNotificationEntryManager, times(2)).updateNotifications(any());
         assertNotNull(mBubbleData.getBubbleWithKey(mRow2.getEntry().key));
         assertTrue(mBubbleController.hasBubbles());
 
         mBubbleController.dismissStack(BubbleController.DISMISS_USER_GESTURE);
         assertFalse(mStatusBarWindowController.getBubblesShowing());
-        verify(mNotificationEntryManager, times(3)).updateNotifications();
+        verify(mNotificationEntryManager, times(3)).updateNotifications(any());
         assertNull(mBubbleData.getBubbleWithKey(mRow.getEntry().key));
         assertNull(mBubbleData.getBubbleWithKey(mRow2.getEntry().key));
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NavigationBarControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NavigationBarControllerTest.java
index 618272c..cfa4065a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NavigationBarControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NavigationBarControllerTest.java
@@ -23,6 +23,7 @@
 import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.ArgumentMatchers.argThat;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.doNothing;
 import static org.mockito.Mockito.doReturn;
@@ -31,15 +32,10 @@
 import static org.mockito.Mockito.reset;
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
 
-import android.content.Context;
-import android.hardware.display.DisplayManager;
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper.RunWithLooper;
 import android.util.SparseArray;
-import android.view.Display;
-import android.view.WindowManager;
 
 import androidx.test.filters.SmallTest;
 
@@ -59,7 +55,6 @@
 public class NavigationBarControllerTest extends SysuiTestCase {
 
     private NavigationBarController mNavigationBarController;
-    private Display mDisplay;
     private NavigationBarFragment mDefaultNavBar;
     private NavigationBarFragment mSecondaryNavBar;
 
@@ -89,37 +84,28 @@
     @After
     public void tearDown() {
         mNavigationBarController = null;
-        mDisplay = null;
         mDefaultNavBar = null;
         mSecondaryNavBar = null;
     }
 
     @Test
     public void testCreateNavigationBarsIncludeDefaultTrue() {
-        initializeDisplayManager();
         doNothing().when(mNavigationBarController).createNavigationBar(any(), any());
 
         mNavigationBarController.createNavigationBars(true, null);
 
-        verify(mNavigationBarController).createNavigationBar(any(Display.class), any());
+        verify(mNavigationBarController).createNavigationBar(
+                argThat(display -> display.getDisplayId() == DEFAULT_DISPLAY), any());
     }
 
     @Test
     public void testCreateNavigationBarsIncludeDefaultFalse() {
-        initializeDisplayManager();
         doNothing().when(mNavigationBarController).createNavigationBar(any(), any());
 
         mNavigationBarController.createNavigationBars(false, null);
 
-        verify(mNavigationBarController, never()).createNavigationBar(any(), any());
-    }
-
-    private void initializeDisplayManager() {
-        DisplayManager displayManager = mock(DisplayManager.class);
-        mDisplay = mContext.getSystemService(WindowManager.class).getDefaultDisplay();
-        Display[] displays = {mDisplay};
-        when(displayManager.getDisplays()).thenReturn(displays);
-        mContext.addMockSystemService(Context.DISPLAY_SERVICE, displayManager);
+        verify(mNavigationBarController, never()).createNavigationBar(
+                argThat(display -> display.getDisplayId() == DEFAULT_DISPLAY), any());
     }
 
     // Tests if NPE occurs when call checkNavBarModes() with invalid display.
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java
index a027643..0569c55 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java
@@ -25,6 +25,7 @@
 import static junit.framework.Assert.assertTrue;
 
 import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
@@ -99,7 +100,7 @@
     @Test
     public void testLockScreenShowNotificationsChangeUpdatesNotifications() {
         mLockscreenUserManager.getLockscreenSettingsObserverForTest().onChange(false);
-        verify(mEntryManager, times(1)).updateNotifications();
+        verify(mEntryManager, times(1)).updateNotifications(anyString());
     }
 
     @Test
@@ -138,7 +139,7 @@
     public void testSettingsObserverUpdatesNotifications() {
         when(mDeviceProvisionedController.isDeviceProvisioned()).thenReturn(true);
         mLockscreenUserManager.getSettingsObserverForTest().onChange(false);
-        verify(mEntryManager, times(1)).updateNotifications();
+        verify(mEntryManager, times(1)).updateNotifications(anyString());
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java
index 866ea51..e52a258 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java
@@ -28,6 +28,7 @@
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.atLeastOnce;
 import static org.mockito.Mockito.doAnswer;
@@ -80,6 +81,7 @@
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.collection.NotificationRowBinder;
 import com.android.systemui.statusbar.notification.collection.NotificationRowBinderImpl;
+import com.android.systemui.statusbar.notification.logging.NotifLog;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 import com.android.systemui.statusbar.notification.row.NotificationContentInflater.InflationFlag;
 import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
@@ -146,7 +148,8 @@
         private final CountDownLatch mCountDownLatch;
 
         TestableNotificationEntryManager() {
-            super(new NotificationData(mock(NotificationSectionsFeatureManager.class)));
+            super(new NotificationData(mock(NotificationSectionsFeatureManager.class),
+                    mock(NotifLog.class)), mock(NotifLog.class));
             mCountDownLatch = new CountDownLatch(1);
         }
 
@@ -259,7 +262,9 @@
 
         NotificationRowBinderImpl notificationRowBinder =
                 new NotificationRowBinderImpl(mContext, true, /* allowLongPress */
-                        mock(KeyguardBypassController.class), mock(StatusBarStateController.class));
+                        mock(KeyguardBypassController.class),
+                        mock(StatusBarStateController.class),
+                        mock(NotifLog.class));
         notificationRowBinder.setUpWithPresenter(
                 mPresenter, mListContainer, mHeadsUpManager, mEntryManager, mBindCallback);
         notificationRowBinder.setNotificationClicker(mock(NotificationClicker.class));
@@ -350,7 +355,7 @@
         // Ensure that update callbacks happen in correct order
         InOrder order = inOrder(mEntryListener, notifData, mPresenter, mEntryListener);
         order.verify(mEntryListener).onPreEntryUpdated(mEntry);
-        order.verify(notifData).filterAndSort();
+        order.verify(notifData).filterAndSort(anyString());
         order.verify(mPresenter).updateNotificationViews();
         order.verify(mEntryListener).onPostEntryUpdated(mEntry);
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationListControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationListControllerTest.java
index 6d275419..8207a04 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationListControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationListControllerTest.java
@@ -21,6 +21,7 @@
 
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
@@ -43,6 +44,7 @@
 import com.android.systemui.statusbar.NotificationEntryBuilder;
 import com.android.systemui.statusbar.notification.collection.NotificationData;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.notification.logging.NotifLog;
 import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
 import com.android.systemui.statusbar.policy.DeviceProvisionedController;
 import com.android.systemui.statusbar.policy.DeviceProvisionedController.DeviceProvisionedListener;
@@ -76,7 +78,7 @@
     // TODO: Remove this once EntryManager no longer needs to be mocked
     private NotificationData mNotificationData =
             new NotificationData(new NotificationSectionsFeatureManager(
-                    new DeviceConfigProxyFake(), mContext));
+                    new DeviceConfigProxyFake(), mContext), mock(NotifLog.class));
 
     private int mNextNotifId = 0;
 
@@ -113,7 +115,7 @@
     @Test
     public void testCallUpdateNotificationsOnDeviceProvisionedChange() {
         mProvisionedListener.onDeviceProvisionedChanged();
-        verify(mEntryManager).updateNotifications();
+        verify(mEntryManager).updateNotifications(anyString());
     }
 
     @Test
@@ -133,8 +135,8 @@
 
         // THEN the app op is added to the entry
         assertTrue(entry.mActiveAppOps.contains(AppOpsManager.OP_CAMERA));
-        // THEN updateNotifications() is called
-        verify(mEntryManager, times(1)).updateNotifications();
+        // THEN updateNotifications(TEST) is called
+        verify(mEntryManager, times(1)).updateNotifications(anyString());
     }
 
     @Test
@@ -146,8 +148,8 @@
         // WHEN An unrelated notification gets a new app op
         mController.updateNotificationsForAppOp(AppOpsManager.OP_CAMERA, 1000, "pkg", true);
 
-        // THEN We never call updateNotifications()
-        verify(mEntryManager, never()).updateNotifications();
+        // THEN We never call updateNotifications(TEST)
+        verify(mEntryManager, never()).updateNotifications(anyString());
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationDataTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationDataTest.java
index 5fbacb1..59c76a8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationDataTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationDataTest.java
@@ -79,6 +79,7 @@
 import com.android.systemui.statusbar.SbnBuilder;
 import com.android.systemui.statusbar.notification.NotificationSectionsFeatureManager;
 import com.android.systemui.statusbar.notification.collection.NotificationData.KeyguardEnvironment;
+import com.android.systemui.statusbar.notification.logging.NotifLog;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 import com.android.systemui.statusbar.phone.NotificationGroupManager;
 import com.android.systemui.statusbar.phone.ShadeController;
@@ -141,7 +142,7 @@
         when(mEnvironment.isNotificationForCurrentProfiles(any())).thenReturn(true);
         mNotificationData = new TestableNotificationData(
                 mock(NotificationSectionsFeatureManager.class));
-        mNotificationData.updateRanking(mock(NotificationListenerService.RankingMap.class));
+        mNotificationData.updateRanking(mock(NotificationListenerService.RankingMap.class), "");
         mRow = new NotificationTestHelper(getContext()).createRow();
         Dependency.get(InitController.class).executePostInitTasks();
     }
@@ -633,7 +634,7 @@
 
     public static class TestableNotificationData extends NotificationData {
         public TestableNotificationData(NotificationSectionsFeatureManager sectionsFeatureManager) {
-            super(sectionsFeatureManager);
+            super(sectionsFeatureManager, mock(NotifLog.class));
         }
 
         public static final String OVERRIDE_RANK = "r";
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationBlockingHelperManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationBlockingHelperManagerTest.java
index 6d64395..cc89504 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationBlockingHelperManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationBlockingHelperManagerTest.java
@@ -27,6 +27,7 @@
 import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.times;
@@ -62,7 +63,6 @@
 @org.junit.runner.RunWith(AndroidTestingRunner.class)
 @TestableLooper.RunWithLooper
 public class NotificationBlockingHelperManagerTest extends SysuiTestCase {
-
     private NotificationBlockingHelperManager mBlockingHelperManager;
 
     private NotificationTestHelper mHelper;
@@ -112,7 +112,7 @@
         assertTrue(mBlockingHelperManager.dismissCurrentBlockingHelper());
         assertTrue(mBlockingHelperManager.isBlockingHelperRowNull());
 
-        verify(mEntryManager, times(0)).updateNotifications();
+        verify(mEntryManager, times(0)).updateNotifications(anyString());
     }
 
     @Test
@@ -125,7 +125,7 @@
         assertTrue(mBlockingHelperManager.dismissCurrentBlockingHelper());
         assertTrue(mBlockingHelperManager.isBlockingHelperRowNull());
 
-        verify(mEntryManager).updateNotifications();
+        verify(mEntryManager).updateNotifications(anyString());
     }
 
     @Test
@@ -267,7 +267,7 @@
         assertTrue(mBlockingHelperManager.dismissCurrentBlockingHelper());
         assertTrue(mBlockingHelperManager.isBlockingHelperRowNull());
 
-        verify(mEntryManager).updateNotifications();
+        verify(mEntryManager).updateNotifications(anyString());
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java
index 98485b3..95e9e67 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java
@@ -56,6 +56,7 @@
 import com.android.systemui.statusbar.notification.NotificationSectionsFeatureManager;
 import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator;
 import com.android.systemui.statusbar.notification.collection.NotificationData;
+import com.android.systemui.statusbar.notification.logging.NotifLog;
 import com.android.systemui.statusbar.notification.stack.NotificationRoundnessManager;
 import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
 import com.android.systemui.statusbar.policy.ConfigurationController;
@@ -232,7 +233,8 @@
                     mock(ShadeController.class),
                     mock(NotificationLockscreenUserManager.class),
                     new NotificationEntryManager(new NotificationData(mock(
-                            NotificationSectionsFeatureManager.class))),
+                            NotificationSectionsFeatureManager.class), mock(NotifLog.class)),
+                            mock(NotifLog.class)),
                     mock(DozeLog.class));
             mNotificationStackScroller = mNotificationStackScrollLayout;
             mKeyguardStatusView = NotificationPanelViewTest.this.mKeyguardStatusView;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
index 914717c..12e9be1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
@@ -117,6 +117,7 @@
 import com.android.systemui.statusbar.notification.VisualStabilityManager;
 import com.android.systemui.statusbar.notification.collection.NotificationData;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.notification.logging.NotifLog;
 import com.android.systemui.statusbar.notification.logging.NotificationLogger;
 import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
 import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
@@ -218,6 +219,7 @@
     @Mock private NotificationIconAreaController mNotificationIconAreaController;
     @Mock private StatusBarWindowViewController.Builder mStatusBarWindowViewControllerBuilder;
     @Mock private StatusBarWindowViewController mStatusBarWindowViewController;
+    @Mock private NotifLog mNotifLog;
 
     @Before
     public void setup() throws Exception {
@@ -339,7 +341,8 @@
                 mNotificationListener,
                 configurationController,
                 mStatusBarWindowController,
-                mStatusBarWindowViewControllerBuilder);
+                mStatusBarWindowViewControllerBuilder,
+                mNotifLog);
         // TODO: we should be able to call mStatusBar.start() and have all the below values
         // initialized automatically.
         mStatusBar.mContext = mContext;
@@ -873,7 +876,7 @@
     public static class TestableNotificationEntryManager extends NotificationEntryManager {
 
         public TestableNotificationEntryManager(NotificationData notificationData) {
-            super(notificationData);
+            super(notificationData, mock(NotifLog.class));
         }
 
         public void setUpForTest(NotificationPresenter presenter,
diff --git a/core/java/android/content/pm/PackageManagerInternal.java b/services/core/java/android/content/pm/PackageManagerInternal.java
similarity index 99%
rename from core/java/android/content/pm/PackageManagerInternal.java
rename to services/core/java/android/content/pm/PackageManagerInternal.java
index 3eef92f..1d666ad 100644
--- a/core/java/android/content/pm/PackageManagerInternal.java
+++ b/services/core/java/android/content/pm/PackageManagerInternal.java
@@ -33,6 +33,8 @@
 import android.util.ArraySet;
 import android.util.SparseArray;
 
+import com.android.server.pm.PackageList;
+
 import java.io.IOException;
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index b158c32..e0f60b4 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -579,6 +579,8 @@
     // the set of network types that can only be enabled by system/sig apps
     private List mProtectedNetworks;
 
+    private Set<String> mWolSupportedInterfaces;
+
     private TelephonyManager mTelephonyManager;
 
     private KeepaliveTracker mKeepaliveTracker;
@@ -1055,6 +1057,10 @@
             }
         }
 
+        mWolSupportedInterfaces = new ArraySet(
+                mContext.getResources().getStringArray(
+                        com.android.internal.R.array.config_wakeonlan_supported_interfaces));
+
         mUserManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
 
         mTethering = deps.makeTethering(mContext, mNMS, mStatsService, mPolicyManager,
@@ -5600,6 +5606,9 @@
         } else {
             updateProxy(newLp, oldLp);
         }
+
+        updateWakeOnLan(newLp);
+
         // TODO - move this check to cover the whole function
         if (!Objects.equals(newLp, oldLp)) {
             synchronized (networkAgent) {
@@ -5770,6 +5779,10 @@
         }
     }
 
+    private void updateWakeOnLan(@NonNull LinkProperties lp) {
+        lp.setWakeOnLanSupported(mWolSupportedInterfaces.contains(lp.getInterfaceName()));
+    }
+
     private int getNetworkPermission(NetworkCapabilities nc) {
         if (!nc.hasCapability(NET_CAPABILITY_NOT_RESTRICTED)) {
             return INetd.PERMISSION_SYSTEM;
diff --git a/services/core/java/com/android/server/LocationManagerService.java b/services/core/java/com/android/server/LocationManagerService.java
index 35a06a9..09f62ff 100644
--- a/services/core/java/com/android/server/LocationManagerService.java
+++ b/services/core/java/com/android/server/LocationManagerService.java
@@ -3064,11 +3064,6 @@
             try {
                 LocationProvider oldProvider = getLocationProviderLocked(name);
                 if (oldProvider != null) {
-                    if (oldProvider.isMock()) {
-                        throw new IllegalArgumentException(
-                                "Provider \"" + name + "\" already exists");
-                    }
-
                     removeProviderLocked(oldProvider);
                 }
 
@@ -3093,7 +3088,7 @@
             try {
                 LocationProvider testProvider = getLocationProviderLocked(name);
                 if (testProvider == null || !testProvider.isMock()) {
-                    throw new IllegalArgumentException("Provider \"" + name + "\" unknown");
+                    return;
                 }
 
                 removeProviderLocked(testProvider);
diff --git a/services/core/java/com/android/server/SystemServerInitThreadPool.java b/services/core/java/com/android/server/SystemServerInitThreadPool.java
index ff6a537..19f4089 100644
--- a/services/core/java/com/android/server/SystemServerInitThreadPool.java
+++ b/services/core/java/com/android/server/SystemServerInitThreadPool.java
@@ -16,10 +16,12 @@
 
 package com.android.server;
 
+import android.annotation.NonNull;
 import android.os.Build;
 import android.os.Process;
 import android.util.Slog;
 
+import com.android.internal.annotations.GuardedBy;
 import com.android.internal.util.ConcurrentUtils;
 import com.android.internal.util.Preconditions;
 import com.android.server.am.ActivityManagerService;
@@ -32,9 +34,11 @@
 
 /**
  * Thread pool used during initialization of system server.
+ *
  * <p>System services can {@link #submit(Runnable)} tasks for execution during boot.
  * The pool will be shut down after {@link SystemService#PHASE_BOOT_COMPLETED}.
- * New tasks <em>should not</em> be submitted afterwards.
+ *
+ * <p>New tasks <em>should not</em> be submitted afterwards.
  *
  * @hide
  */
@@ -42,26 +46,46 @@
     private static final String TAG = SystemServerInitThreadPool.class.getSimpleName();
     private static final int SHUTDOWN_TIMEOUT_MILLIS = 20000;
     private static final boolean IS_DEBUGGABLE = Build.IS_DEBUGGABLE;
+    private static final Object LOCK = new Object();
 
+    @GuardedBy("LOCK")
     private static SystemServerInitThreadPool sInstance;
 
-    private ExecutorService mService = ConcurrentUtils.newFixedThreadPool(
-            Runtime.getRuntime().availableProcessors(),
-            "system-server-init-thread", Process.THREAD_PRIORITY_FOREGROUND);
+    private final ExecutorService mService;
 
-    private List<String> mPendingTasks = new ArrayList<>();
+    @GuardedBy("mPendingTasks")
+    private final List<String> mPendingTasks = new ArrayList<>();
 
-    public static synchronized SystemServerInitThreadPool get() {
-        if (sInstance == null) {
-            sInstance = new SystemServerInitThreadPool();
-        }
-        Preconditions.checkState(sInstance.mService != null, "Cannot get " + TAG
-                + " - it has been shut down");
-        return sInstance;
+    @GuardedBy("mPendingTasks")
+    private boolean mShutDown;
+
+    private SystemServerInitThreadPool() {
+        final int size = Runtime.getRuntime().availableProcessors();
+        Slog.i(TAG, "Creating instance with " + size + " threads");
+        mService = ConcurrentUtils.newFixedThreadPool(size,
+                "system-server-init-thread", Process.THREAD_PRIORITY_FOREGROUND);
     }
 
-    public Future<?> submit(Runnable runnable, String description) {
+    /**
+     * Gets the singleton.
+     *
+     * @throws IllegalStateException if it hasn't been started or has been shut down already.
+     */
+    public static SystemServerInitThreadPool get() {
+        synchronized (LOCK) {
+            Preconditions.checkState(sInstance != null, "Cannot get " + TAG
+                    + " - it has been shut down");
+            return sInstance;
+        }
+    }
+
+    /**
+     * Submits a task for execution.
+     */
+    public @NonNull Future<?> submit(@NonNull Runnable runnable, @NonNull String description) {
+        Preconditions.checkNotNull(description, "description cannot be null");
         synchronized (mPendingTasks) {
+            Preconditions.checkState(!mShutDown, TAG + " already shut down");
             mPendingTasks.add(description);
         }
         return mService.submit(() -> {
@@ -83,10 +107,36 @@
         });
     }
 
-    static synchronized void shutdown() {
-        if (sInstance != null && sInstance.mService != null) {
+    /**
+     * Starts it.
+     *
+     * <p>Note:</p> should only be called by {@link SystemServer}.
+     *
+     * @throws IllegalStateException if it has been started already without being shut down yet.
+     */
+    static void start() {
+        synchronized (LOCK) {
+            Preconditions.checkState(sInstance == null, TAG + " already started");
+            sInstance = new SystemServerInitThreadPool();
+        }
+    }
+
+    /**
+     * Shuts it down.
+     *
+     * <p>Note:</p> should only be called by {@link SystemServer}.
+     */
+    static void shutdown() {
+        synchronized (LOCK) {
+            if (sInstance == null) {
+                Slog.wtf(TAG, "Already shutdown", new Exception());
+                return;
+            }
+            synchronized (sInstance.mPendingTasks) {
+                sInstance.mShutDown = true;
+            }
             sInstance.mService.shutdown();
-            boolean terminated;
+            final boolean terminated;
             try {
                 terminated = sInstance.mService.awaitTermination(SHUTDOWN_TIMEOUT_MILLIS,
                         TimeUnit.MILLISECONDS);
@@ -100,7 +150,7 @@
                 // in the thread pool.
                 dumpStackTraces();
             }
-            List<Runnable> unstartedRunnables = sInstance.mService.shutdownNow();
+            final List<Runnable> unstartedRunnables = sInstance.mService.shutdownNow();
             if (!terminated) {
                 final List<String> copy = new ArrayList<>();
                 synchronized (sInstance.mPendingTasks) {
@@ -109,8 +159,7 @@
                 throw new IllegalStateException("Cannot shutdown. Unstarted tasks "
                         + unstartedRunnables + " Unfinished tasks " + copy);
             }
-            sInstance.mService = null; // Make mService eligible for GC
-            sInstance.mPendingTasks = null;
+            sInstance = null; // Make eligible for GC
             Slog.d(TAG, "Shutdown successful");
         }
     }
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 7cbd1fc..4bca7a1 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -5015,7 +5015,9 @@
             if (preBindAgent != null) {
                 thread.attachAgent(preBindAgent);
             }
-
+            if ((app.info.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0) {
+                thread.attachStartupAgents(app.info.dataDir);
+            }
 
             // Figure out whether the app needs to run in autofill compat mode.
             AutofillOptions autofillOptions = null;
@@ -18353,7 +18355,7 @@
 
         @Override
         public int getCurrentUserId() {
-            return mUserController.getCurrentUserIdLU();
+            return mUserController.getCurrentUserId();
         }
 
         @Override
diff --git a/services/core/java/com/android/server/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java
index 798185a..6c4cc2d 100644
--- a/services/core/java/com/android/server/appop/AppOpsService.java
+++ b/services/core/java/com/android/server/appop/AppOpsService.java
@@ -65,7 +65,6 @@
 import android.content.pm.UserInfo;
 import android.database.ContentObserver;
 import android.hardware.camera2.CameraDevice.CAMERA_AUDIO_RESTRICTION;
-import android.media.AudioAttributes;
 import android.net.Uri;
 import android.os.AsyncTask;
 import android.os.Binder;
@@ -109,6 +108,7 @@
 import com.android.internal.app.IAppOpsService;
 import com.android.internal.os.Zygote;
 import com.android.internal.util.ArrayUtils;
+import com.android.internal.util.CollectionUtils;
 import com.android.internal.util.DumpUtils;
 import com.android.internal.util.FastXmlSerializer;
 import com.android.internal.util.Preconditions;
@@ -257,6 +257,9 @@
     @GuardedBy("this")
     private CheckOpsDelegate mCheckOpsDelegate;
 
+    @GuardedBy("this")
+    private SparseArray<List<Integer>> mSwitchOpToOps;
+
     /**
      * All times are in milliseconds. These constants are kept synchronized with the system
      * global Settings. Any access to this class or its fields should be done while
@@ -1291,6 +1294,8 @@
         verifyIncomingOp(code);
         code = AppOpsManager.opToSwitch(code);
 
+        updatePermissionRevokedCompat(uid, code, mode);
+
         synchronized (this) {
             final int defaultMode = AppOpsManager.opToDefaultMode(code);
 
@@ -1392,6 +1397,86 @@
         notifyOpChangedSync(code, uid, null, mode);
     }
 
+    private void updatePermissionRevokedCompat(int uid, int switchCode, int mode) {
+        PackageManager packageManager = mContext.getPackageManager();
+        String[] packageNames = packageManager.getPackagesForUid(uid);
+        if (ArrayUtils.isEmpty(packageNames)) {
+            return;
+        }
+        String packageName = packageNames[0];
+
+        List<Integer> ops = getSwitchOpToOps().get(switchCode);
+        int opsSize = CollectionUtils.size(ops);
+        for (int i = 0; i < opsSize; i++) {
+            int code = ops.get(i);
+
+            String permissionName = AppOpsManager.opToPermission(code);
+            if (permissionName == null) {
+                continue;
+            }
+
+            PermissionInfo permissionInfo;
+            try {
+                permissionInfo = packageManager.getPermissionInfo(permissionName, 0);
+            } catch (PackageManager.NameNotFoundException e) {
+                e.printStackTrace();
+                continue;
+            }
+
+            if (!permissionInfo.isRuntime()) {
+                continue;
+            }
+
+            UserHandle user = UserHandle.getUserHandleForUid(uid);
+            boolean isRevokedCompat;
+            if (permissionInfo.backgroundPermission != null) {
+                boolean isBackgroundRevokedCompat = mode != AppOpsManager.MODE_ALLOWED;
+                long identity = Binder.clearCallingIdentity();
+                try {
+                    packageManager.updatePermissionFlags(permissionInfo.backgroundPermission,
+                            packageName, PackageManager.FLAG_PERMISSION_REVOKED_COMPAT,
+                            isBackgroundRevokedCompat
+                                    ? PackageManager.FLAG_PERMISSION_REVOKED_COMPAT : 0, user);
+                } finally {
+                    Binder.restoreCallingIdentity(identity);
+                }
+
+                isRevokedCompat = mode != AppOpsManager.MODE_ALLOWED
+                        && mode != AppOpsManager.MODE_FOREGROUND;
+            } else {
+                isRevokedCompat = mode != AppOpsManager.MODE_ALLOWED;
+            }
+
+            long identity = Binder.clearCallingIdentity();
+            try {
+                packageManager.updatePermissionFlags(permissionName, packageName,
+                        PackageManager.FLAG_PERMISSION_REVOKED_COMPAT, isRevokedCompat
+                                ? PackageManager.FLAG_PERMISSION_REVOKED_COMPAT : 0, user);
+            } finally {
+                Binder.restoreCallingIdentity(identity);
+            }
+        }
+    }
+
+    @NonNull
+    private SparseArray<List<Integer>> getSwitchOpToOps() {
+        synchronized (this) {
+            if (mSwitchOpToOps == null) {
+                mSwitchOpToOps = new SparseArray<>();
+                for (int op = 0; op < _NUM_OP; op++) {
+                    int switchOp = AppOpsManager.opToSwitch(op);
+                    List<Integer> ops = mSwitchOpToOps.get(switchOp);
+                    if (ops == null) {
+                        ops = new ArrayList<>();
+                        mSwitchOpToOps.put(switchOp, ops);
+                    }
+                    ops.add(op);
+                }
+            }
+            return mSwitchOpToOps;
+        }
+    }
+
     private void notifyOpChangedSync(int code, int uid, @NonNull String packageName, int mode) {
         final StorageManagerInternal storageManagerInternal =
                 LocalServices.getService(StorageManagerInternal.class);
diff --git a/services/core/java/com/android/server/display/ColorFade.java b/services/core/java/com/android/server/display/ColorFade.java
index c46fc20..29026e8 100644
--- a/services/core/java/com/android/server/display/ColorFade.java
+++ b/services/core/java/com/android/server/display/ColorFade.java
@@ -114,6 +114,8 @@
     private final FloatBuffer mVertexBuffer = createNativeFloatBuffer(8);
     private final FloatBuffer mTexCoordBuffer = createNativeFloatBuffer(8);
 
+    private final Transaction mTransaction = new Transaction();
+
     /**
      * Animates an color fade warming up.
      */
@@ -659,14 +661,10 @@
 
     private boolean showSurface(float alpha) {
         if (!mSurfaceVisible || mSurfaceAlpha != alpha) {
-            SurfaceControl.openTransaction();
-            try {
-                mSurfaceControl.setLayer(COLOR_FADE_LAYER);
-                mSurfaceControl.setAlpha(alpha);
-                mSurfaceControl.show();
-            } finally {
-                SurfaceControl.closeTransaction();
-            }
+            mTransaction.setLayer(mSurfaceControl, COLOR_FADE_LAYER)
+                    .setAlpha(mSurfaceControl, alpha)
+                    .show(mSurfaceControl)
+                    .apply();
             mSurfaceVisible = true;
             mSurfaceAlpha = alpha;
         }
diff --git a/services/core/java/com/android/server/integrity/model/EvaluationOutcome.java b/services/core/java/com/android/server/integrity/model/EvaluationOutcome.java
new file mode 100644
index 0000000..dc30dc3
--- /dev/null
+++ b/services/core/java/com/android/server/integrity/model/EvaluationOutcome.java
@@ -0,0 +1,67 @@
+/*
+ * 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.integrity.model;
+
+/**
+ * A class encapsulating the result from the evaluation engine after evaluating rules against app
+ * install metadata.
+ *
+ * <p>It contains the outcome effect (whether to allow or block the install), and the rule causing
+ * that effect.
+ */
+public final class EvaluationOutcome {
+
+    public enum Effect {
+        ALLOW,
+        DENY
+    }
+
+    private final Effect mEffect;
+    private final Rule mRule;
+
+    private EvaluationOutcome(Effect effect, Rule rule) {
+        this.mEffect = effect;
+        this.mRule = rule;
+    }
+
+    public Effect getEffect() {
+        return mEffect;
+    }
+
+    public Rule getRule() {
+        return mRule;
+    }
+
+    /**
+     * Create an ALLOW evaluation outcome.
+     *
+     * @return An evaluation outcome with ALLOW effect and empty rule.
+     */
+    public static EvaluationOutcome allow() {
+        return new EvaluationOutcome(Effect.ALLOW, Rule.EMPTY);
+    }
+
+    /**
+     * Create a DENY evaluation outcome.
+     *
+     * @param rule Rule causing the DENY effect.
+     * @return An evaluation outcome with DENY effect and rule causing that effect.
+     */
+    public static EvaluationOutcome deny(Rule rule) {
+        return new EvaluationOutcome(Effect.DENY, rule);
+    }
+}
diff --git a/services/core/java/com/android/server/integrity/model/OpenFormula.java b/services/core/java/com/android/server/integrity/model/OpenFormula.java
index 218cdc9..e9fa1a3 100644
--- a/services/core/java/com/android/server/integrity/model/OpenFormula.java
+++ b/services/core/java/com/android/server/integrity/model/OpenFormula.java
@@ -16,9 +16,11 @@
 
 package com.android.server.integrity.model;
 
+import static com.android.internal.util.Preconditions.checkArgument;
 import static com.android.internal.util.Preconditions.checkNotNull;
 
-import android.annotation.Nullable;
+import java.util.Collections;
+import java.util.List;
 
 /**
  * Represents a complex formula consisting of other simple and complex formulas.
@@ -34,46 +36,33 @@
     }
 
     private final Connector mConnector;
-    private final Formula mMainFormula;
-    private final Formula mAuxiliaryFormula;
+    private final List<Formula> mFormulas;
 
-    public OpenFormula(Connector connector, Formula mainFormula,
-            @Nullable Formula auxiliaryFormula) {
-        validateAuxiliaryFormula(connector, auxiliaryFormula);
+    public OpenFormula(Connector connector, List<Formula> formulas) {
+        validateFormulas(connector, formulas);
         this.mConnector = checkNotNull(connector);
-        this.mMainFormula = checkNotNull(mainFormula);
-        // TODO: Add validators on auxiliary formula
-        this.mAuxiliaryFormula = auxiliaryFormula;
+        this.mFormulas = Collections.unmodifiableList(checkNotNull(formulas));
     }
 
     public Connector getConnector() {
         return mConnector;
     }
 
-    public Formula getMainFormula() {
-        return mMainFormula;
+    public List<Formula> getFormulas() {
+        return mFormulas;
     }
 
-    public Formula getAuxiliaryFormula() {
-        return mAuxiliaryFormula;
-    }
-
-    private void validateAuxiliaryFormula(Connector connector, Formula auxiliaryFormula) {
-        boolean validAuxiliaryFormula;
+    private void validateFormulas(Connector connector, List<Formula> formulas) {
         switch (connector) {
             case AND:
             case OR:
-                validAuxiliaryFormula = (auxiliaryFormula != null);
+                checkArgument(formulas.size() >= 2,
+                        String.format("Connector %s must have at least 2 formulas", connector));
                 break;
             case NOT:
-                validAuxiliaryFormula = (auxiliaryFormula == null);
+                checkArgument(formulas.size() == 1,
+                        String.format("Connector %s must have 1 formula only", connector));
                 break;
-            default:
-                validAuxiliaryFormula = false;
-        }
-        if (!validAuxiliaryFormula) {
-            throw new IllegalArgumentException(
-                    String.format("Invalid formulas used for connector %s", connector));
         }
     }
 }
diff --git a/services/core/java/com/android/server/integrity/model/Rule.java b/services/core/java/com/android/server/integrity/model/Rule.java
index 4fd40c1..3d233ab 100644
--- a/services/core/java/com/android/server/integrity/model/Rule.java
+++ b/services/core/java/com/android/server/integrity/model/Rule.java
@@ -25,7 +25,7 @@
  */
 public final class Rule {
 
-    enum Effect {
+    public enum Effect {
         DENY
     }
 
diff --git a/services/core/java/com/android/server/pm/ComponentResolver.java b/services/core/java/com/android/server/pm/ComponentResolver.java
index 8facce1..976cdfb 100644
--- a/services/core/java/com/android/server/pm/ComponentResolver.java
+++ b/services/core/java/com/android/server/pm/ComponentResolver.java
@@ -23,6 +23,7 @@
 import static com.android.server.pm.PackageManagerService.DEBUG_REMOVE;
 import static com.android.server.pm.PackageManagerService.fixProcessName;
 
+import android.annotation.Nullable;
 import android.content.ComponentName;
 import android.content.Intent;
 import android.content.IntentFilter;
@@ -219,12 +220,14 @@
         }
     }
 
+    @Nullable
     List<ResolveInfo> queryActivities(Intent intent, String resolvedType, int flags, int userId) {
         synchronized (mLock) {
             return mActivities.queryIntent(intent, resolvedType, flags, userId);
         }
     }
 
+    @Nullable
     List<ResolveInfo> queryActivities(Intent intent, String resolvedType, int flags,
             List<PackageParser.Activity> activities, int userId) {
         synchronized (mLock) {
@@ -233,12 +236,14 @@
         }
     }
 
+    @Nullable
     List<ResolveInfo> queryProviders(Intent intent, String resolvedType, int flags, int userId) {
         synchronized (mLock) {
             return mProviders.queryIntent(intent, resolvedType, flags, userId);
         }
     }
 
+    @Nullable
     List<ResolveInfo> queryProviders(Intent intent, String resolvedType, int flags,
             List<PackageParser.Provider> providers, int userId) {
         synchronized (mLock) {
@@ -246,6 +251,7 @@
         }
     }
 
+    @Nullable
     List<ProviderInfo> queryProviders(String processName, String metaDataKey, int uid, int flags,
             int userId) {
         if (!sUserManager.exists(userId)) {
@@ -285,6 +291,7 @@
         return providerList;
     }
 
+    @Nullable
     ProviderInfo queryProvider(String authority, int flags, int userId) {
         synchronized (mLock) {
             final PackageParser.Provider p = mProvidersByAuthority.get(authority);
@@ -326,12 +333,14 @@
         }
     }
 
+    @Nullable
     List<ResolveInfo> queryReceivers(Intent intent, String resolvedType, int flags, int userId) {
         synchronized (mLock) {
             return mReceivers.queryIntent(intent, resolvedType, flags, userId);
         }
     }
 
+    @Nullable
     List<ResolveInfo> queryReceivers(Intent intent, String resolvedType, int flags,
             List<PackageParser.Activity> receivers, int userId) {
         synchronized (mLock) {
@@ -339,12 +348,14 @@
         }
     }
 
+    @Nullable
     List<ResolveInfo> queryServices(Intent intent, String resolvedType, int flags, int userId) {
         synchronized (mLock) {
             return mServices.queryIntent(intent, resolvedType, flags, userId);
         }
     }
 
+    @Nullable
     List<ResolveInfo> queryServices(Intent intent, String resolvedType, int flags,
             List<PackageParser.Service> services, int userId) {
         synchronized (mLock) {
@@ -1355,6 +1366,7 @@
             return super.queryIntent(intent, resolvedType, defaultOnly, userId);
         }
 
+        @Nullable
         List<ResolveInfo> queryIntent(Intent intent, String resolvedType, int flags,
                 int userId) {
             if (!sUserManager.exists(userId)) {
@@ -1366,6 +1378,7 @@
                     userId);
         }
 
+        @Nullable
         List<ResolveInfo> queryIntentForPackage(Intent intent, String resolvedType,
                 int flags, List<PackageParser.Provider> packageProviders, int userId) {
             if (!sUserManager.exists(userId)) {
diff --git a/core/java/android/content/pm/PackageList.java b/services/core/java/com/android/server/pm/PackageList.java
similarity index 96%
rename from core/java/android/content/pm/PackageList.java
rename to services/core/java/com/android/server/pm/PackageList.java
index e3eb2c5..60bc8a8 100644
--- a/core/java/android/content/pm/PackageList.java
+++ b/services/core/java/com/android/server/pm/PackageList.java
@@ -14,10 +14,11 @@
  * limitations under the License.
  */
 
-package android.content.pm;
+package com.android.server.pm;
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.content.pm.PackageManagerInternal;
 import android.content.pm.PackageManagerInternal.PackageListObserver;
 
 import com.android.server.LocalServices;
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index e0697a6..67339c8 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -162,7 +162,6 @@
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageInfoLite;
 import android.content.pm.PackageInstaller;
-import android.content.pm.PackageList;
 import android.content.pm.PackageManager;
 import android.content.pm.PackageManager.LegacyPackageDeleteObserver;
 import android.content.pm.PackageManager.ModuleInfoFlags;
@@ -3048,7 +3047,7 @@
 
             // Resolve protected action filters. Only the setup wizard is allowed to
             // have a high priority filter for these actions.
-            mSetupWizardPackage = getSetupWizardPackageName();
+            mSetupWizardPackage = getSetupWizardPackageNameImpl();
             mComponentResolver.fixProtectedFilterPriorities();
 
             mSystemTextClassifierPackage = getSystemTextClassifierPackageName();
@@ -6976,7 +6975,7 @@
      * @param intent
      * @return A filtered list of resolved activities.
      */
-    private List<ResolveInfo> applyPostResolutionFilter(List<ResolveInfo> resolveInfos,
+    private List<ResolveInfo> applyPostResolutionFilter(@NonNull List<ResolveInfo> resolveInfos,
             String ephemeralPkgName, boolean allowDynamicSplits, int filterCallingUid,
             boolean resolveForStart, int userId, Intent intent) {
         final boolean blockInstant = intent.isWebIntent() && areWebInstantAppsDisabled(userId);
@@ -7633,6 +7632,9 @@
             if (pkgName == null) {
                 final List<ResolveInfo> result =
                         mComponentResolver.queryReceivers(intent, resolvedType, flags, userId);
+                if (result == null) {
+                    return Collections.emptyList();
+                }
                 return applyPostResolutionFilter(
                         result, instantAppPkgName, allowDynamicSplits, callingUid, false, userId,
                         intent);
@@ -7641,6 +7643,9 @@
             if (pkg != null) {
                 final List<ResolveInfo> result = mComponentResolver.queryReceivers(
                         intent, resolvedType, flags, pkg.receivers, userId);
+                if (result == null) {
+                    return Collections.emptyList();
+                }
                 return applyPostResolutionFilter(
                         result, instantAppPkgName, allowDynamicSplits, callingUid, false, userId,
                         intent);
@@ -7735,15 +7740,25 @@
         synchronized (mLock) {
             String pkgName = intent.getPackage();
             if (pkgName == null) {
+                final List<ResolveInfo> resolveInfos = mComponentResolver.queryServices(intent,
+                        resolvedType, flags, userId);
+                if (resolveInfos == null) {
+                    return Collections.emptyList();
+                }
                 return applyPostServiceResolutionFilter(
-                        mComponentResolver.queryServices(intent, resolvedType, flags, userId),
+                        resolveInfos,
                         instantAppPkgName);
             }
             final PackageParser.Package pkg = mPackages.get(pkgName);
             if (pkg != null) {
+                final List<ResolveInfo> resolveInfos = mComponentResolver.queryServices(intent,
+                        resolvedType, flags, pkg.services,
+                        userId);
+                if (resolveInfos == null) {
+                    return Collections.emptyList();
+                }
                 return applyPostServiceResolutionFilter(
-                        mComponentResolver.queryServices(intent, resolvedType, flags, pkg.services,
-                                userId),
+                        resolveInfos,
                         instantAppPkgName);
             }
             return Collections.emptyList();
@@ -7853,15 +7868,25 @@
         synchronized (mLock) {
             String pkgName = intent.getPackage();
             if (pkgName == null) {
+                final List<ResolveInfo> resolveInfos = mComponentResolver.queryProviders(intent,
+                        resolvedType, flags, userId);
+                if (resolveInfos == null) {
+                    return Collections.emptyList();
+                }
                 return applyPostContentProviderResolutionFilter(
-                        mComponentResolver.queryProviders(intent, resolvedType, flags, userId),
+                        resolveInfos,
                         instantAppPkgName);
             }
             final PackageParser.Package pkg = mPackages.get(pkgName);
             if (pkg != null) {
+                final List<ResolveInfo> resolveInfos = mComponentResolver.queryProviders(intent,
+                        resolvedType, flags,
+                        pkg.providers, userId);
+                if (resolveInfos == null) {
+                    return Collections.emptyList();
+                }
                 return applyPostContentProviderResolutionFilter(
-                        mComponentResolver.queryProviders(intent, resolvedType, flags,
-                                pkg.providers, userId),
+                        resolveInfos,
                         instantAppPkgName);
             }
             return Collections.emptyList();
@@ -19652,7 +19677,7 @@
                 set, comp, userId);
     }
 
-    private @Nullable String getSetupWizardPackageName() {
+    private @Nullable String getSetupWizardPackageNameImpl() {
         final Intent intent = new Intent(Intent.ACTION_MAIN);
         intent.addCategory(Intent.CATEGORY_SETUP_WIZARD);
 
@@ -19759,6 +19784,14 @@
         return systemCaptionsServiceComponentName.getPackageName();
     }
 
+    @Override
+    public String getSetupWizardPackageName() {
+        if (Binder.getCallingUid() != Process.SYSTEM_UID) {
+            throw new SecurityException("Non-system caller");
+        }
+        return mPmInternal.getSetupWizardPackageName();
+    }
+
     public String getIncidentReportApproverPackageName() {
         return mContext.getString(R.string.config_incidentReportApproverPackage);
     }
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 237a771..c851cc6 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -4543,6 +4543,12 @@
     private void wakeUpFromPowerKey(long eventTime) {
         wakeUp(eventTime, mAllowTheaterModeWakeFromPowerKey,
                 PowerManager.WAKE_REASON_POWER_BUTTON, "android.policy:POWER");
+
+        // Turn on the connected TV and switch HDMI input if we're a HDMI playback device.
+        final HdmiControl hdmiControl = getHdmiControl();
+        if (hdmiControl != null) {
+            hdmiControl.turnOnTv();
+        }
     }
 
     private boolean wakeUp(long wakeTime, boolean wakeInTheaterMode, @WakeReason int reason,
@@ -4575,7 +4581,7 @@
         // ... eventually calls finishWindowsDrawn which will finalize our screen turn on
         // as well as enabling the orientation change logic/sensor.
         mWindowManagerInternal.waitForAllWindowsDrawn(mWindowManagerDrawCallback,
-                WAITING_FOR_DRAWN_TIMEOUT);
+                WAITING_FOR_DRAWN_TIMEOUT, INVALID_DISPLAY);
     }
 
     // Called on the DisplayManager's DisplayPowerController thread.
diff --git a/services/core/java/com/android/server/wm/AccessibilityController.java b/services/core/java/com/android/server/wm/AccessibilityController.java
index 59f051b..10415f5 100644
--- a/services/core/java/com/android/server/wm/AccessibilityController.java
+++ b/services/core/java/com/android/server/wm/AccessibilityController.java
@@ -271,11 +271,11 @@
         }
     }
 
-    /** NOTE: This has to be called within a surface transaction. */
-    public void drawMagnifiedRegionBorderIfNeededLocked(int displayId) {
+    public void drawMagnifiedRegionBorderIfNeededLocked(int displayId,
+            SurfaceControl.Transaction t) {
         final DisplayMagnifier displayMagnifier = mDisplayMagnifiers.get(displayId);
         if (displayMagnifier != null) {
-            displayMagnifier.drawMagnifiedRegionBorderIfNeededLocked();
+            displayMagnifier.drawMagnifiedRegionBorderIfNeededLocked(t);
         }
         // Not relevant for the window observer.
     }
@@ -431,7 +431,7 @@
                 Slog.i(LOG_TAG, "Rotation: " + Surface.rotationToString(rotation)
                         + " displayId: " + displayContent.getDisplayId());
             }
-            mMagnifedViewport.onRotationChangedLocked();
+            mMagnifedViewport.onRotationChangedLocked(displayContent.getPendingTransaction());
             mHandler.sendEmptyMessage(MyHandler.MESSAGE_NOTIFY_ROTATION_CHANGED);
         }
 
@@ -536,9 +536,8 @@
                     .sendToTarget();
         }
 
-        /** NOTE: This has to be called within a surface transaction. */
-        public void drawMagnifiedRegionBorderIfNeededLocked() {
-            mMagnifedViewport.drawWindowIfNeededLocked();
+        public void drawMagnifiedRegionBorderIfNeededLocked(SurfaceControl.Transaction t) {
+            mMagnifedViewport.drawWindowIfNeededLocked(t);
         }
 
         private final class MagnifiedViewport {
@@ -744,7 +743,7 @@
                 return letterboxBounds;
             }
 
-            public void onRotationChangedLocked() {
+            public void onRotationChangedLocked(SurfaceControl.Transaction t) {
                 // If we are showing the magnification border, hide it immediately so
                 // the user does not see strange artifacts during rotation. The screenshot
                 // used for rotation already has the border. After the rotation is complete
@@ -758,7 +757,7 @@
                     mHandler.sendMessageDelayed(message, delay);
                 }
                 recomputeBoundsLocked();
-                mWindow.updateSize();
+                mWindow.updateSize(t);
             }
 
             public void setMagnifiedRegionBorderShownLocked(boolean shown, boolean animate) {
@@ -784,10 +783,9 @@
                 return mMagnificationSpec;
             }
 
-            /** NOTE: This has to be called within a surface transaction. */
-            public void drawWindowIfNeededLocked() {
+            public void drawWindowIfNeededLocked(SurfaceControl.Transaction t) {
                 recomputeBoundsLocked();
-                mWindow.drawIfNeeded();
+                mWindow.drawIfNeeded(t);
             }
 
             public void destroyWindow() {
@@ -837,10 +835,11 @@
                         /* ignore */
                     }
                     mSurfaceControl = surfaceControl;
-                    mSurfaceControl.setLayer(mService.mPolicy.getWindowLayerFromTypeLw(
-                            TYPE_MAGNIFICATION_OVERLAY)
-                            * WindowManagerService.TYPE_LAYER_MULTIPLIER);
-                    mSurfaceControl.setPosition(0, 0);
+                    mService.mTransactionFactory.get().setLayer(mSurfaceControl,
+                            mService.mPolicy.getWindowLayerFromTypeLw(TYPE_MAGNIFICATION_OVERLAY)
+                                    * WindowManagerService.TYPE_LAYER_MULTIPLIER)
+                            .setPosition(mSurfaceControl, 0, 0)
+                            .apply();
                     mSurface.copyFrom(mSurfaceControl);
 
                     mAnimationController = new AnimationController(context,
@@ -905,10 +904,10 @@
                     }
                 }
 
-                public void updateSize() {
+                public void updateSize(SurfaceControl.Transaction t) {
                     synchronized (mService.mGlobalLock) {
                         mWindowManager.getDefaultDisplay().getRealSize(mTempPoint);
-                        mSurfaceControl.setBufferSize(mTempPoint.x, mTempPoint.y);
+                        t.setBufferSize(mSurfaceControl, mTempPoint.x, mTempPoint.y);
                         invalidate(mDirtyRect);
                     }
                 }
@@ -923,8 +922,7 @@
                     mService.scheduleAnimationLocked();
                 }
 
-                /** NOTE: This has to be called within a surface transaction. */
-                public void drawIfNeeded() {
+                public void drawIfNeeded(SurfaceControl.Transaction t) {
                     synchronized (mService.mGlobalLock) {
                         if (!mInvalidated) {
                             return;
@@ -959,9 +957,9 @@
                             canvas.drawPath(path, mPaint);
 
                             mSurface.unlockCanvasAndPost(canvas);
-                            mSurfaceControl.show();
+                            t.show(mSurfaceControl);
                         } else {
-                            mSurfaceControl.hide();
+                            t.hide(mSurfaceControl);
                         }
                     }
                 }
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 976fd52..4f52d9d 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -1723,25 +1723,27 @@
         // implied that the current finishing activity should be added into stopping list rather
         // than destroy immediately.
         final boolean isNextNotYetVisible = next != null && (!next.nowVisible || !next.visible);
-        final ActivityStack stack = getActivityStack();
-        final boolean notFocusedStack = stack != mRootActivityContainer.getTopDisplayFocusedStack();
+        final boolean notGlobalFocusedStack =
+                getActivityStack() != mRootActivityContainer.getTopDisplayFocusedStack();
         if (isVisible && isNextNotYetVisible) {
+            // Add this activity to the list of stopping activities. It will be processed and
+            // destroyed when the next activity reports idle.
             addToStopping(false /* scheduleIdle */, false /* idleDelayed */,
                     "completeFinishing");
-            if (DEBUG_STATES) {
-                Slog.v(TAG_STATES, "Moving to STOPPING: " + this + " (finish requested)");
-            }
             setState(STOPPING, "completeFinishing");
-            if (notFocusedStack) {
+            if (notGlobalFocusedStack) {
+                // Ensuring visibility and configuration only for non-focused stacks since this
+                // method call is relatively expensive and not necessary for focused stacks.
                 mRootActivityContainer.ensureVisibilityAndConfig(next, getDisplayId(),
                         false /* markFrozenIfConfigChanged */, true /* deferResume */);
             }
-        } else if (isVisible && isState(PAUSED) && getActivityStack().isFocusedStackOnDisplay()
-                && !inPinnedWindowingMode()) {
-            // TODO(b/137329632): Currently non-focused stack is handled differently.
-            addToFinishingAndWaitForIdle();
+        } else if (addToFinishingAndWaitForIdle()) {
+            // We added this activity to the finishing list and something else is becoming resumed.
+            // The activity will complete finishing when the next activity reports idle. No need to
+            // do anything else here.
         } else {
-            // Not waiting for the next one to become visible - finish right away.
+            // Not waiting for the next one to become visible, and nothing else will be resumed in
+            // place of this activity - requesting destruction right away.
             activityRemoved = destroyIfPossible(reason);
         }
 
@@ -1798,13 +1800,20 @@
         return activityRemoved;
     }
 
+    /**
+     * Add this activity to the list of finishing and trigger resuming of activities in focused
+     * stacks.
+     * @return {@code true} if some other activity is being resumed as a result of this call.
+     */
     @VisibleForTesting
-    void addToFinishingAndWaitForIdle() {
+    boolean addToFinishingAndWaitForIdle() {
         if (DEBUG_STATES) Slog.v(TAG, "Enqueueing pending finish: " + this);
         setState(FINISHING, "addToFinishingAndWaitForIdle");
-        mStackSupervisor.mFinishingActivities.add(this);
+        if (!mStackSupervisor.mFinishingActivities.contains(this)) {
+            mStackSupervisor.mFinishingActivities.add(this);
+        }
         resumeKeyDispatchingLocked();
-        mRootActivityContainer.resumeFocusedStacksTopActivities();
+        return mRootActivityContainer.resumeFocusedStacksTopActivities();
     }
 
     /**
diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java
index b1ef601..a261341 100644
--- a/services/core/java/com/android/server/wm/AppWindowToken.java
+++ b/services/core/java/com/android/server/wm/AppWindowToken.java
@@ -2930,7 +2930,7 @@
             layer += Z_BOOST_BASE;
         }
         if (!mNeedsAnimationBoundsLayer) {
-            leash.setLayer(layer);
+            t.setLayer(leash, layer);
         }
 
         final DisplayContent dc = getDisplayContent();
diff --git a/services/core/java/com/android/server/wm/CircularDisplayMask.java b/services/core/java/com/android/server/wm/CircularDisplayMask.java
index c1ca816..b73b481 100644
--- a/services/core/java/com/android/server/wm/CircularDisplayMask.java
+++ b/services/core/java/com/android/server/wm/CircularDisplayMask.java
@@ -56,7 +56,7 @@
     private int mMaskThickness;
 
     CircularDisplayMask(Supplier<Surface> surfaceFactory, DisplayContent dc, int zOrder,
-            int screenOffset, int maskThickness) {
+            int screenOffset, int maskThickness, SurfaceControl.Transaction t) {
         final Display display = dc.getDisplay();
         mSurface = surfaceFactory.get();
         mScreenSize = new Point();
@@ -75,10 +75,10 @@
                     .setFormat(PixelFormat.TRANSLUCENT)
                     .build();
 
-            ctrl.setLayerStack(display.getLayerStack());
-            ctrl.setLayer(zOrder);
-            ctrl.setPosition(0, 0);
-            ctrl.show();
+            t.setLayerStack(ctrl, display.getLayerStack());
+            t.setLayer(ctrl, zOrder);
+            t.setPosition(ctrl, 0, 0);
+            t.show(ctrl);
             mSurface.copyFrom(ctrl);
         } catch (OutOfResourcesException e) {
         }
@@ -91,7 +91,7 @@
         mMaskThickness = maskThickness;
     }
 
-    private void drawIfNeeded() {
+    private void drawIfNeeded(SurfaceControl.Transaction t) {
         if (!mDrawNeeded || !mVisible || mDimensionsUnequal) {
             return;
         }
@@ -108,45 +108,46 @@
             return;
         }
         switch (mRotation) {
-        case Surface.ROTATION_0:
-        case Surface.ROTATION_90:
-            // chin bottom or right
-            mSurfaceControl.setPosition(0, 0);
-            break;
-        case Surface.ROTATION_180:
-            // chin top
-            mSurfaceControl.setPosition(0, -mScreenOffset);
-            break;
-        case Surface.ROTATION_270:
-            // chin left
-            mSurfaceControl.setPosition(-mScreenOffset, 0);
-            break;
+            case Surface.ROTATION_0:
+            case Surface.ROTATION_90:
+                // chin bottom or right
+                t.setPosition(mSurfaceControl, 0, 0);
+                break;
+            case Surface.ROTATION_180:
+                // chin top
+                t.setPosition(mSurfaceControl, 0, -mScreenOffset);
+                break;
+            case Surface.ROTATION_270:
+                // chin left
+                t.setPosition(mSurfaceControl, -mScreenOffset, 0);
+                break;
         }
 
         int circleRadius = mScreenSize.x / 2;
         c.drawColor(Color.BLACK);
 
-        // The radius is reduced by mMaskThickness to provide an anti aliasing effect on the display edges.
+        // The radius is reduced by mMaskThickness to provide an anti aliasing effect on the
+        // display edges.
         c.drawCircle(circleRadius, circleRadius, circleRadius - mMaskThickness, mPaint);
         mSurface.unlockCanvasAndPost(c);
     }
 
     // Note: caller responsible for being inside
     // Surface.openTransaction() / closeTransaction()
-    public void setVisibility(boolean on) {
+    public void setVisibility(boolean on, SurfaceControl.Transaction t) {
         if (mSurfaceControl == null) {
             return;
         }
         mVisible = on;
-        drawIfNeeded();
+        drawIfNeeded(t);
         if (on) {
-            mSurfaceControl.show();
+            t.show(mSurfaceControl);
         } else {
-            mSurfaceControl.hide();
+            t.hide(mSurfaceControl);
         }
     }
 
-    void positionSurface(int dw, int dh, int rotation) {
+    void positionSurface(int dw, int dh, int rotation, SurfaceControl.Transaction t) {
         if (mLastDW == dw && mLastDH == dh && mRotation == rotation) {
             return;
         }
@@ -154,7 +155,7 @@
         mLastDH = dh;
         mDrawNeeded = true;
         mRotation = rotation;
-        drawIfNeeded();
+        drawIfNeeded(t);
     }
 
 }
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 0844323..ac910cd 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -132,7 +132,6 @@
 import static com.android.server.wm.WindowState.EXCLUSION_LEFT;
 import static com.android.server.wm.WindowState.EXCLUSION_RIGHT;
 import static com.android.server.wm.WindowState.RESIZE_HANDLE_WIDTH_IN_DP;
-import static com.android.server.wm.WindowStateAnimator.DRAW_PENDING;
 import static com.android.server.wm.WindowStateAnimator.READY_TO_SHOW;
 import static com.android.server.wm.utils.RegionUtils.forEachRectReverse;
 import static com.android.server.wm.utils.RegionUtils.rectListToRegion;
@@ -332,7 +331,7 @@
 
     /**
      * For default display it contains real metrics, empty for others.
-     * @see WindowManagerService#createWatermarkInTransaction()
+     * @see WindowManagerService#createWatermark()
      */
     final DisplayMetrics mRealDisplayMetrics = new DisplayMetrics();
 
@@ -3519,19 +3518,6 @@
         mWmService.mWindowPlacerLocked.performSurfacePlacement();
     }
 
-    void waitForAllWindowsDrawn() {
-        final WindowManagerPolicy policy = mWmService.mPolicy;
-        forAllWindows(w -> {
-            final boolean keyguard = policy.isKeyguardHostWindow(w.mAttrs);
-            if (w.isVisibleLw() && (w.mAppToken != null || keyguard)) {
-                w.mWinAnimator.mDrawState = DRAW_PENDING;
-                // Force add to mResizingWindows.
-                w.resetLastContentInsets();
-                mWmService.mWaitingForDrawn.add(w);
-            }
-        }, true /* traverseTopToBottom */);
-    }
-
     // TODO: Super crazy long method that should be broken down...
     void applySurfaceChangesTransaction(boolean recoveringMemory) {
         final WindowSurfacePlacer surfacePlacer = mWmService.mWindowPlacerLocked;
diff --git a/services/core/java/com/android/server/wm/DockedStackDividerController.java b/services/core/java/com/android/server/wm/DockedStackDividerController.java
index ae3b5f2..d3e42901 100644
--- a/services/core/java/com/android/server/wm/DockedStackDividerController.java
+++ b/services/core/java/com/android/server/wm/DockedStackDividerController.java
@@ -819,9 +819,12 @@
 
         // We put all tasks into drag resizing mode - wait until all of them have completed the
         // drag resizing switch.
-        if (!mService.mWaitingForDrawn.isEmpty()) {
-            mService.mH.removeMessages(H.WAITING_FOR_DRAWN_TIMEOUT);
-            mService.mH.sendEmptyMessageDelayed(H.WAITING_FOR_DRAWN_TIMEOUT,
+        final Runnable existingWaitingForDrwanCallback =
+                mService.mWaitingForDrawnCallbacks.get(mService.mRoot);
+        if (existingWaitingForDrwanCallback != null) {
+            mService.mH.removeMessages(H.WAITING_FOR_DRAWN_TIMEOUT, mService.mRoot);
+            mService.mH.sendMessageDelayed(mService.mH.obtainMessage(H.WAITING_FOR_DRAWN_TIMEOUT,
+                    mService.mRoot),
                     IME_ADJUST_DRAWN_TIMEOUT);
             mAnimationStartDelayed = true;
             if (imeWin != null) {
@@ -838,10 +841,8 @@
             // still gets executed.
             // TODO: Have a real system where we can wait on different windows to be drawn with
             // different callbacks.
-            if (mService.mWaitingForDrawnCallback != null) {
-                mService.mWaitingForDrawnCallback.run();
-            }
-            mService.mWaitingForDrawnCallback = () -> {
+            existingWaitingForDrwanCallback.run();
+            mService.mWaitingForDrawnCallbacks.put(mService.mRoot, () -> {
                 synchronized (mService.mGlobalLock) {
                     mAnimationStartDelayed = false;
                     if (mDelayedImeWin != null) {
@@ -863,7 +864,7 @@
                     notifyAdjustedForImeChanged(
                             mAdjustedForIme || mAdjustedForDivider, duration);
                 }
-            };
+            });
         } else {
             notifyAdjustedForImeChanged(
                     adjustedForIme || adjustedForDivider, IME_ADJUST_ANIM_DURATION);
diff --git a/services/core/java/com/android/server/wm/EmulatorDisplayOverlay.java b/services/core/java/com/android/server/wm/EmulatorDisplayOverlay.java
index f64592f..2165b0e 100644
--- a/services/core/java/com/android/server/wm/EmulatorDisplayOverlay.java
+++ b/services/core/java/com/android/server/wm/EmulatorDisplayOverlay.java
@@ -50,7 +50,7 @@
     private boolean mVisible;
 
     EmulatorDisplayOverlay(Supplier<Surface> surfaceFactory, Context context, DisplayContent dc,
-            int zOrder) {
+            int zOrder, SurfaceControl.Transaction t) {
         mSurface = surfaceFactory.get();
         final Display display = dc.getDisplay();
         mScreenSize = new Point();
@@ -63,9 +63,9 @@
                     .setBufferSize(mScreenSize.x, mScreenSize.y)
                     .setFormat(PixelFormat.TRANSLUCENT)
                     .build();
-            ctrl.setLayer(zOrder);
-            ctrl.setPosition(0, 0);
-            ctrl.show();
+            t.setLayer(ctrl, zOrder);
+            t.setPosition(ctrl, 0, 0);
+            t.show(ctrl);
             mSurface.copyFrom(ctrl);
         } catch (OutOfResourcesException e) {
         }
@@ -75,7 +75,7 @@
                 com.android.internal.R.drawable.emulator_circular_window_overlay);
     }
 
-    private void drawIfNeeded() {
+    private void drawIfNeeded(SurfaceControl.Transaction t) {
         if (!mDrawNeeded || !mVisible) {
             return;
         }
@@ -92,7 +92,7 @@
             return;
         }
         c.drawColor(Color.TRANSPARENT, PorterDuff.Mode.SRC);
-        mSurfaceControl.setPosition(0, 0);
+        t.setPosition(mSurfaceControl, 0, 0);
         // Always draw the overlay with square dimensions
         int size = Math.max(mScreenSize.x, mScreenSize.y);
         mOverlay.setBounds(0, 0, size, size);
@@ -102,20 +102,20 @@
 
     // Note: caller responsible for being inside
     // Surface.openTransaction() / closeTransaction()
-    public void setVisibility(boolean on) {
+    public void setVisibility(boolean on, SurfaceControl.Transaction t) {
         if (mSurfaceControl == null) {
             return;
         }
         mVisible = on;
-        drawIfNeeded();
+        drawIfNeeded(t);
         if (on) {
-            mSurfaceControl.show();
+            t.show(mSurfaceControl);
         } else {
-            mSurfaceControl.hide();
+            t.hide(mSurfaceControl);
         }
     }
 
-    void positionSurface(int dw, int dh, int rotation) {
+    void positionSurface(int dw, int dh, int rotation, SurfaceControl.Transaction t) {
         if (mLastDW == dw && mLastDH == dh && mRotation == rotation) {
             return;
         }
@@ -123,7 +123,7 @@
         mLastDH = dh;
         mDrawNeeded = true;
         mRotation = rotation;
-        drawIfNeeded();
+        drawIfNeeded(t);
     }
 
 }
diff --git a/services/core/java/com/android/server/wm/LaunchParamsPersister.java b/services/core/java/com/android/server/wm/LaunchParamsPersister.java
index 013607e..5d27390 100644
--- a/services/core/java/com/android/server/wm/LaunchParamsPersister.java
+++ b/services/core/java/com/android/server/wm/LaunchParamsPersister.java
@@ -17,7 +17,6 @@
 package com.android.server.wm;
 
 import android.content.ComponentName;
-import android.content.pm.PackageList;
 import android.content.pm.PackageManagerInternal;
 import android.graphics.Rect;
 import android.os.Environment;
@@ -32,6 +31,7 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.FastXmlSerializer;
 import com.android.server.LocalServices;
+import com.android.server.pm.PackageList;
 import com.android.server.wm.LaunchParamsController.LaunchParams;
 
 import libcore.io.IoUtils;
diff --git a/services/core/java/com/android/server/wm/Letterbox.java b/services/core/java/com/android/server/wm/Letterbox.java
index 1bd2493..94d010e 100644
--- a/services/core/java/com/android/server/wm/Letterbox.java
+++ b/services/core/java/com/android/server/wm/Letterbox.java
@@ -247,12 +247,12 @@
             mLayoutFrameRelative.offset(-surfaceOrigin.x, -surfaceOrigin.y);
         }
 
-        private void createSurface() {
+        private void createSurface(SurfaceControl.Transaction t) {
             mSurface = mSurfaceControlFactory.get().setName("Letterbox - " + mType)
                     .setFlags(HIDDEN).setColorLayer().build();
-            mSurface.setLayer(-1);
-            mSurface.setColor(new float[]{0, 0, 0});
-            mSurface.setColorSpaceAgnostic(true);
+            t.setLayer(mSurface, -1)
+                    .setColor(mSurface, new float[]{0, 0, 0})
+                    .setColorSpaceAgnostic(mSurface, true);
         }
 
         void attachInput(WindowState win) {
@@ -300,7 +300,7 @@
             mSurfaceFrameRelative.set(mLayoutFrameRelative);
             if (!mSurfaceFrameRelative.isEmpty()) {
                 if (mSurface == null) {
-                    createSurface();
+                    createSurface(t);
                 }
                 t.setPosition(mSurface, mSurfaceFrameRelative.left, mSurfaceFrameRelative.top);
                 t.setWindowCrop(mSurface, mSurfaceFrameRelative.width(),
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index 0c8cd43..b6a05d1 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -720,7 +720,7 @@
             mUpdateRotation = updateRotationUnchecked();
         }
 
-        if (mWmService.mWaitingForDrawnCallback != null
+        if (!mWmService.mWaitingForDrawnCallbacks.isEmpty()
                 || (mOrientationChangeComplete && !isLayoutNeeded()
                 && !mUpdateRotation)) {
             mWmService.checkDrawnWindowsLocked();
@@ -813,18 +813,18 @@
         final int defaultDw = defaultInfo.logicalWidth;
         final int defaultDh = defaultInfo.logicalHeight;
         if (mWmService.mWatermark != null) {
-            mWmService.mWatermark.positionSurface(defaultDw, defaultDh);
+            mWmService.mWatermark.positionSurface(defaultDw, defaultDh, mDisplayTransaction);
         }
         if (mWmService.mStrictModeFlash != null) {
-            mWmService.mStrictModeFlash.positionSurface(defaultDw, defaultDh);
+            mWmService.mStrictModeFlash.positionSurface(defaultDw, defaultDh, mDisplayTransaction);
         }
         if (mWmService.mCircularDisplayMask != null) {
             mWmService.mCircularDisplayMask.positionSurface(defaultDw, defaultDh,
-                    mWmService.getDefaultDisplayRotation());
+                    mWmService.getDefaultDisplayRotation(), mDisplayTransaction);
         }
         if (mWmService.mEmulatorDisplayOverlay != null) {
             mWmService.mEmulatorDisplayOverlay.positionSurface(defaultDw, defaultDh,
-                    mWmService.getDefaultDisplayRotation());
+                    mWmService.getDefaultDisplayRotation(), mDisplayTransaction);
         }
 
         final int count = mChildren.size();
diff --git a/services/core/java/com/android/server/wm/SeamlessRotator.java b/services/core/java/com/android/server/wm/SeamlessRotator.java
index bcd90a1..ba31818 100644
--- a/services/core/java/com/android/server/wm/SeamlessRotator.java
+++ b/services/core/java/com/android/server/wm/SeamlessRotator.java
@@ -20,9 +20,10 @@
 import static android.view.Surface.ROTATION_90;
 
 import android.graphics.Matrix;
+import android.os.IBinder;
 import android.view.DisplayInfo;
-import android.view.Surface;
 import android.view.Surface.Rotation;
+import android.view.SurfaceControl;
 import android.view.SurfaceControl.Transaction;
 
 import com.android.server.wm.utils.CoordinateTransforms;
@@ -35,7 +36,7 @@
  *
  * Works by transforming the {@link WindowState} back into the old display rotation.
  *
- * Uses {@link android.view.SurfaceControl#deferTransactionUntil(Surface, long)} instead of
+ * Uses {@link Transaction#deferTransactionUntil(SurfaceControl, IBinder, long)} instead of
  * latching on the buffer size to allow for seamless 180 degree rotations.
  */
 public class SeamlessRotator {
diff --git a/services/core/java/com/android/server/wm/StrictModeFlash.java b/services/core/java/com/android/server/wm/StrictModeFlash.java
index 9e5d9ca..f537005 100644
--- a/services/core/java/com/android/server/wm/StrictModeFlash.java
+++ b/services/core/java/com/android/server/wm/StrictModeFlash.java
@@ -39,7 +39,8 @@
     private boolean mDrawNeeded;
     private final int mThickness = 20;
 
-    StrictModeFlash(Supplier<Surface> surfaceFactory, DisplayContent dc) {
+    StrictModeFlash(Supplier<Surface> surfaceFactory, DisplayContent dc,
+            SurfaceControl.Transaction t) {
         mSurface = surfaceFactory.get();
         SurfaceControl ctrl = null;
         try {
@@ -48,9 +49,11 @@
                     .setBufferSize(1, 1)
                     .setFormat(PixelFormat.TRANSLUCENT)
                     .build();
-            ctrl.setLayer(WindowManagerService.TYPE_LAYER_MULTIPLIER * 101);  // one more than Watermark? arbitrary.
-            ctrl.setPosition(0, 0);
-            ctrl.show();
+
+            // one more than Watermark? arbitrary.
+            t.setLayer(ctrl, WindowManagerService.TYPE_LAYER_MULTIPLIER * 101);
+            t.setPosition(ctrl, 0, 0);
+            t.show(ctrl);
             mSurface.copyFrom(ctrl);
         } catch (OutOfResourcesException e) {
         }
@@ -103,25 +106,25 @@
 
     // Note: caller responsible for being inside
     // Surface.openTransaction() / closeTransaction()
-    public void setVisibility(boolean on) {
+    public void setVisibility(boolean on, SurfaceControl.Transaction t) {
         if (mSurfaceControl == null) {
             return;
         }
         drawIfNeeded();
         if (on) {
-            mSurfaceControl.show();
+            t.show(mSurfaceControl);
         } else {
-            mSurfaceControl.hide();
+            t.hide(mSurfaceControl);
         }
     }
 
-    void positionSurface(int dw, int dh) {
+    void positionSurface(int dw, int dh, SurfaceControl.Transaction t) {
         if (mLastDW == dw && mLastDH == dh) {
             return;
         }
         mLastDW = dw;
         mLastDH = dh;
-        mSurfaceControl.setBufferSize(dw, dh);
+        t.setBufferSize(mSurfaceControl, dw, dh);
         mDrawNeeded = true;
     }
 
diff --git a/services/core/java/com/android/server/wm/TaskSnapshotSurface.java b/services/core/java/com/android/server/wm/TaskSnapshotSurface.java
index d070850..172ebce 100644
--- a/services/core/java/com/android/server/wm/TaskSnapshotSurface.java
+++ b/services/core/java/com/android/server/wm/TaskSnapshotSurface.java
@@ -134,6 +134,7 @@
     private final int mStatusBarColor;
     @VisibleForTesting final SystemBarBackgroundPainter mSystemBarBackgroundPainter;
     private final int mOrientationOnCreation;
+    private final SurfaceControl.Transaction mTransaction;
 
     static TaskSnapshotSurface create(WindowManagerService service, AppWindowToken token,
             TaskSnapshot snapshot) {
@@ -252,6 +253,7 @@
                 windowPrivateFlags, sysUiVis, taskDescription, 1f);
         mStatusBarColor = taskDescription.getStatusBarColor();
         mOrientationOnCreation = currentOrientation;
+        mTransaction = mService.mTransactionFactory.get();
     }
 
     @Override
@@ -336,27 +338,23 @@
         surface.copyFrom(mChildSurfaceControl);
 
         final Rect frame;
-        SurfaceControl.openTransaction();
-        try {
-            // We can just show the surface here as it will still be hidden as the parent is
-            // still hidden.
-            mChildSurfaceControl.show();
-            if (aspectRatioMismatch) {
-                // Clip off ugly navigation bar.
-                final Rect crop = calculateSnapshotCrop();
-                frame = calculateSnapshotFrame(crop);
-                mChildSurfaceControl.setWindowCrop(crop);
-                mChildSurfaceControl.setPosition(frame.left, frame.top);
-            } else {
-                frame = null;
-            }
-
-            // Scale the mismatch dimensions to fill the task bounds
-            final float scale = 1 / mSnapshot.getScale();
-            mChildSurfaceControl.setMatrix(scale, 0, 0, scale);
-        } finally {
-            SurfaceControl.closeTransaction();
+        // We can just show the surface here as it will still be hidden as the parent is
+        // still hidden.
+        mTransaction.show(mChildSurfaceControl);
+        if (aspectRatioMismatch) {
+            // Clip off ugly navigation bar.
+            final Rect crop = calculateSnapshotCrop();
+            frame = calculateSnapshotFrame(crop);
+            mTransaction.setWindowCrop(mChildSurfaceControl, crop);
+            mTransaction.setPosition(mChildSurfaceControl, frame.left, frame.top);
+        } else {
+            frame = null;
         }
+
+        // Scale the mismatch dimensions to fill the task bounds
+        final float scale = 1 / mSnapshot.getScale();
+        mTransaction.setMatrix(mChildSurfaceControl, scale, 0, 0, scale);
+        mTransaction.apply();
         surface.attachAndQueueBufferWithColorSpace(buffer, mSnapshot.getColorSpace());
         surface.release();
 
diff --git a/services/core/java/com/android/server/wm/Watermark.java b/services/core/java/com/android/server/wm/Watermark.java
index 729cfc0..725aaa48 100644
--- a/services/core/java/com/android/server/wm/Watermark.java
+++ b/services/core/java/com/android/server/wm/Watermark.java
@@ -55,7 +55,7 @@
     private boolean mDrawNeeded;
 
     Watermark(Supplier<Surface> surfaceFactory, DisplayContent dc, DisplayMetrics dm,
-            String[] tokens) {
+            String[] tokens, SurfaceControl.Transaction t) {
         if (false) {
             Log.i(TAG_WM, "*********************** WATERMARK");
             for (int i=0; i<tokens.length; i++) {
@@ -121,21 +121,21 @@
                     .setBufferSize(1, 1)
                     .setFormat(PixelFormat.TRANSLUCENT)
                     .build();
-            ctrl.setLayerStack(mDisplay.getLayerStack());
-            ctrl.setLayer(WindowManagerService.TYPE_LAYER_MULTIPLIER*100);
-            ctrl.setPosition(0, 0);
-            ctrl.show();
+            t.setLayerStack(ctrl, mDisplay.getLayerStack())
+                    .setLayer(ctrl, WindowManagerService.TYPE_LAYER_MULTIPLIER * 100)
+                    .setPosition(ctrl, 0, 0)
+                    .show(ctrl);
             mSurface.copyFrom(ctrl);
         } catch (OutOfResourcesException e) {
         }
         mSurfaceControl = ctrl;
     }
 
-    void positionSurface(int dw, int dh) {
+    void positionSurface(int dw, int dh, SurfaceControl.Transaction t) {
         if (mLastDW != dw || mLastDH != dh) {
             mLastDW = dw;
             mLastDH = dh;
-            mSurfaceControl.setBufferSize(dw, dh);
+            t.setBufferSize(mSurfaceControl, dw, dh);
             mDrawNeeded = true;
         }
     }
diff --git a/services/core/java/com/android/server/wm/WindowAnimator.java b/services/core/java/com/android/server/wm/WindowAnimator.java
index f437b28..3a1d6e0 100644
--- a/services/core/java/com/android/server/wm/WindowAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowAnimator.java
@@ -161,7 +161,8 @@
                     dc.checkAppWindowsReadyToShow();
                     orAnimating(dc.getDockedDividerController().animate(mCurrentTime));
                     if (accessibilityController != null) {
-                        accessibilityController.drawMagnifiedRegionBorderIfNeededLocked(displayId);
+                        accessibilityController.drawMagnifiedRegionBorderIfNeededLocked(displayId,
+                                mTransaction);
                     }
                 }
 
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index 19fcb1b..a4ab66a 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -33,6 +33,7 @@
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ANIM;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
+import static com.android.server.wm.WindowStateAnimator.DRAW_PENDING;
 
 import android.annotation.CallSuper;
 import android.annotation.IntDef;
@@ -54,9 +55,11 @@
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.ToBooleanFunction;
+import com.android.server.policy.WindowManagerPolicy;
 import com.android.server.wm.SurfaceAnimator.Animatable;
 
 import java.io.PrintWriter;
+import java.util.ArrayList;
 import java.util.Comparator;
 import java.util.LinkedList;
 import java.util.function.Consumer;
@@ -124,6 +127,11 @@
     private final Transaction mPendingTransaction;
 
     /**
+     * Windows that clients are waiting to have drawn.
+     */
+    final ArrayList<WindowState> mWaitingForDrawn = new ArrayList<>();
+
+    /**
      * Applied as part of the animation pass in "prepareSurfaces".
      */
     protected final SurfaceAnimator mSurfaceAnimator;
@@ -1434,6 +1442,19 @@
         }
     }
 
+    void waitForAllWindowsDrawn() {
+        final WindowManagerPolicy policy = mWmService.mPolicy;
+        forAllWindows(w -> {
+            final boolean keyguard = policy.isKeyguardHostWindow(w.mAttrs);
+            if (w.isVisibleLw() && (w.mAppToken != null || keyguard)) {
+                w.mWinAnimator.mDrawState = DRAW_PENDING;
+                // Force add to mResizingWindows.
+                w.resetLastContentInsets();
+                mWaitingForDrawn.add(w);
+            }
+        }, true /* traverseTopToBottom */);
+    }
+
     Dimmer getDimmer() {
         if (mParent == null) {
             return null;
diff --git a/services/core/java/com/android/server/wm/WindowManagerInternal.java b/services/core/java/com/android/server/wm/WindowManagerInternal.java
index bb3caa9..f4b7672 100644
--- a/services/core/java/com/android/server/wm/WindowManagerInternal.java
+++ b/services/core/java/com/android/server/wm/WindowManagerInternal.java
@@ -313,10 +313,15 @@
     public abstract void showGlobalActions();
 
     /**
-     * Invalidate all visible windows. Then report back on the callback once all windows have
-     * redrawn.
+     * Invalidate all visible windows on a given display, and report back on the callback when all
+     * windows have redrawn.
+     *
+     * @param callback reporting callback to be called when all windows have redrawn.
+     * @param timeout calls the callback anyway after the timeout.
+     * @param displayId waits for the windows on the given display, INVALID_DISPLAY to wait for all
+     *                  windows on all displays.
      */
-    public abstract void waitForAllWindowsDrawn(Runnable callback, long timeout);
+    public abstract void waitForAllWindowsDrawn(Runnable callback, long timeout, int displayId);
 
     /**
      * Overrides the display size.
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 20812e9..6f9f2c0 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -290,6 +290,7 @@
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.Date;
+import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.function.Function;
@@ -568,13 +569,10 @@
     final ArrayList<WindowState> mForceRemoves = new ArrayList<>();
 
     /**
-     * Windows that clients are waiting to have drawn.
+     * The callbacks to make when the windows all have been drawn for a given
+     * {@link WindowContainer}.
      */
-    ArrayList<WindowState> mWaitingForDrawn = new ArrayList<>();
-    /**
-     * And the callback to make when they've all been drawn.
-     */
-    Runnable mWaitingForDrawnCallback;
+    final HashMap<WindowContainer, Runnable> mWaitingForDrawnCallbacks = new HashMap<>();
 
     /** List of window currently causing non-system overlay windows to be hidden. */
     private ArrayList<WindowState> mHidingNonSystemOverlayWindows = new ArrayList<>();
@@ -1271,14 +1269,7 @@
 
         // Add ourself to the Watchdog monitors.
         Watchdog.getInstance().addMonitor(this);
-
-        openSurfaceTransaction();
-        try {
-            createWatermarkInTransaction();
-        } finally {
-            closeSurfaceTransaction("createWatermarkInTransaction");
-        }
-
+        createWatermark();
         showEmulatorDisplayOverlayIfNeeded();
     }
 
@@ -3437,60 +3428,45 @@
 
     public void showCircularMask(boolean visible) {
         synchronized (mGlobalLock) {
+            if (visible) {
+                // TODO(multi-display): support multiple displays
+                if (mCircularDisplayMask == null) {
+                    int screenOffset = mContext.getResources().getInteger(
+                            com.android.internal.R.integer.config_windowOutsetBottom);
+                    int maskThickness = mContext.getResources().getDimensionPixelSize(
+                            com.android.internal.R.dimen.circular_display_mask_thickness);
 
-            if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG_WM,
-                    ">>> OPEN TRANSACTION showCircularMask(visible=" + visible + ")");
-            openSurfaceTransaction();
-            try {
-                if (visible) {
-                    // TODO(multi-display): support multiple displays
-                    if (mCircularDisplayMask == null) {
-                        int screenOffset = mContext.getResources().getInteger(
-                                com.android.internal.R.integer.config_windowOutsetBottom);
-                        int maskThickness = mContext.getResources().getDimensionPixelSize(
-                                com.android.internal.R.dimen.circular_display_mask_thickness);
 
-                        mCircularDisplayMask = new CircularDisplayMask(mSurfaceFactory,
-                                getDefaultDisplayContentLocked(),
-                                mPolicy.getWindowLayerFromTypeLw(
-                                        WindowManager.LayoutParams.TYPE_POINTER)
-                                        * TYPE_LAYER_MULTIPLIER + 10, screenOffset, maskThickness);
+                    if (SHOW_LIGHT_TRANSACTIONS) {
+                        Slog.i(TAG_WM,
+                                ">>> showCircularMask(visible=" + visible + ")");
                     }
-                    mCircularDisplayMask.setVisibility(true);
-                } else if (mCircularDisplayMask != null) {
-                    mCircularDisplayMask.setVisibility(false);
-                    mCircularDisplayMask = null;
+                    mCircularDisplayMask = new CircularDisplayMask(mSurfaceFactory,
+                            getDefaultDisplayContentLocked(), mPolicy.getWindowLayerFromTypeLw(
+                            WindowManager.LayoutParams.TYPE_POINTER) * TYPE_LAYER_MULTIPLIER
+                            + 10, screenOffset, maskThickness, mTransaction);
                 }
-            } finally {
-                closeSurfaceTransaction("showCircularMask");
-                if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG_WM,
-                        "<<< CLOSE TRANSACTION showCircularMask(visible=" + visible + ")");
+                mCircularDisplayMask.setVisibility(true, mTransaction);
+            } else if (mCircularDisplayMask != null) {
+                mCircularDisplayMask.setVisibility(false, mTransaction);
+                mCircularDisplayMask = null;
             }
+            mTransaction.apply();
         }
     }
 
     public void showEmulatorDisplayOverlay() {
         synchronized (mGlobalLock) {
 
-            if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG_WM,
-                    ">>> OPEN TRANSACTION showEmulatorDisplayOverlay");
-            openSurfaceTransaction();
-            try {
-                if (mEmulatorDisplayOverlay == null) {
-                    mEmulatorDisplayOverlay = new EmulatorDisplayOverlay(
-                            mSurfaceFactory,
-                            mContext,
-                            getDefaultDisplayContentLocked(),
-                            mPolicy.getWindowLayerFromTypeLw(
-                                    WindowManager.LayoutParams.TYPE_POINTER)
-                                    * TYPE_LAYER_MULTIPLIER + 10);
-                }
-                mEmulatorDisplayOverlay.setVisibility(true);
-            } finally {
-                closeSurfaceTransaction("showEmulatorDisplayOverlay");
-                if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG_WM,
-                        "<<< CLOSE TRANSACTION showEmulatorDisplayOverlay");
+            if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG_WM, ">>> showEmulatorDisplayOverlay");
+            if (mEmulatorDisplayOverlay == null) {
+                mEmulatorDisplayOverlay = new EmulatorDisplayOverlay(mSurfaceFactory, mContext,
+                        getDefaultDisplayContentLocked(),
+                        mPolicy.getWindowLayerFromTypeLw(WindowManager.LayoutParams.TYPE_POINTER)
+                                * TYPE_LAYER_MULTIPLIER + 10, mTransaction);
             }
+            mEmulatorDisplayOverlay.setVisibility(true, mTransaction);
+            mTransaction.apply();
         }
     }
 
@@ -3519,23 +3495,16 @@
                 return;
             }
 
-            if (SHOW_VERBOSE_TRANSACTIONS) Slog.i(TAG_WM,
-                    ">>> OPEN TRANSACTION showStrictModeViolation");
+            if (SHOW_VERBOSE_TRANSACTIONS) Slog.i(TAG_WM, ">>> showStrictModeViolation");
             // TODO: Modify this to use the surface trace once it is not going crazy.
             // b/31532461
-            SurfaceControl.openTransaction();
-            try {
-                // TODO(multi-display): support multiple displays
-                if (mStrictModeFlash == null) {
-                    mStrictModeFlash = new StrictModeFlash(mSurfaceFactory,
-                            getDefaultDisplayContentLocked());
-                }
-                mStrictModeFlash.setVisibility(on);
-            } finally {
-                SurfaceControl.closeTransaction();
-                if (SHOW_VERBOSE_TRANSACTIONS) Slog.i(TAG_WM,
-                        "<<< CLOSE TRANSACTION showStrictModeViolation");
+            // TODO(multi-display): support multiple displays
+            if (mStrictModeFlash == null) {
+                mStrictModeFlash = new StrictModeFlash(mSurfaceFactory,
+                        getDefaultDisplayContentLocked(), mTransaction);
             }
+            mStrictModeFlash.setVisibility(on, mTransaction);
+            mTransaction.apply();
         }
     }
 
@@ -4736,12 +4705,12 @@
 
                 case WAITING_FOR_DRAWN_TIMEOUT: {
                     Runnable callback = null;
+                    final WindowContainer container = (WindowContainer) msg.obj;
                     synchronized (mGlobalLock) {
                         ProtoLog.w(WM_ERROR, "Timeout waiting for drawn: undrawn=%s",
-                                mWaitingForDrawn);
-                        mWaitingForDrawn.clear();
-                        callback = mWaitingForDrawnCallback;
-                        mWaitingForDrawnCallback = null;
+                                container.mWaitingForDrawn);
+                        container.mWaitingForDrawn.clear();
+                        callback = mWaitingForDrawnCallbacks.remove(container);
                     }
                     if (callback != null) {
                         callback.run();
@@ -4773,9 +4742,9 @@
                 }
                 case ALL_WINDOWS_DRAWN: {
                     Runnable callback;
+                    final WindowContainer container = (WindowContainer) msg.obj;
                     synchronized (mGlobalLock) {
-                        callback = mWaitingForDrawnCallback;
-                        mWaitingForDrawnCallback = null;
+                        callback = mWaitingForDrawnCallbacks.remove(container);
                     }
                     if (callback != null) {
                         callback.run();
@@ -5265,30 +5234,32 @@
     }
 
     void checkDrawnWindowsLocked() {
-        if (mWaitingForDrawn.isEmpty() || mWaitingForDrawnCallback == null) {
+        if (mWaitingForDrawnCallbacks.isEmpty()) {
             return;
         }
-        for (int j = mWaitingForDrawn.size() - 1; j >= 0; j--) {
-            WindowState win = mWaitingForDrawn.get(j);
-            ProtoLog.i(WM_DEBUG_SCREEN_ON,
+        mWaitingForDrawnCallbacks.forEach((container, callback) -> {
+            for (int j = container.mWaitingForDrawn.size() - 1; j >= 0; j--) {
+                final WindowState win = (WindowState) container.mWaitingForDrawn.get(j);
+                ProtoLog.i(WM_DEBUG_SCREEN_ON,
                         "Waiting for drawn %s: removed=%b visible=%b mHasSurface=%b drawState=%d",
                         win, win.mRemoved, win.isVisibleLw(), win.mHasSurface,
                         win.mWinAnimator.mDrawState);
-            if (win.mRemoved || !win.mHasSurface || !win.isVisibleByPolicy()) {
-                // Window has been removed or hidden; no draw will now happen, so stop waiting.
-                ProtoLog.w(WM_DEBUG_SCREEN_ON, "Aborted waiting for drawn: %s", win);
-                mWaitingForDrawn.remove(win);
-            } else if (win.hasDrawnLw()) {
-                // Window is now drawn (and shown).
-                ProtoLog.d(WM_DEBUG_SCREEN_ON, "Window drawn win=%s", win);
-                mWaitingForDrawn.remove(win);
+                if (win.mRemoved || !win.mHasSurface || !win.isVisibleByPolicy()) {
+                    // Window has been removed or hidden; no draw will now happen, so stop waiting.
+                    ProtoLog.w(WM_DEBUG_SCREEN_ON, "Aborted waiting for drawn: %s", win);
+                    container.mWaitingForDrawn.remove(win);
+                } else if (win.hasDrawnLw()) {
+                    // Window is now drawn (and shown).
+                    ProtoLog.d(WM_DEBUG_SCREEN_ON, "Window drawn win=%s", win);
+                    container.mWaitingForDrawn.remove(win);
+                }
             }
-        }
-        if (mWaitingForDrawn.isEmpty()) {
-            ProtoLog.d(WM_DEBUG_SCREEN_ON, "All windows drawn!");
-            mH.removeMessages(H.WAITING_FOR_DRAWN_TIMEOUT);
-            mH.sendEmptyMessage(H.ALL_WINDOWS_DRAWN);
-        }
+            if (container.mWaitingForDrawn.isEmpty()) {
+                ProtoLog.d(WM_DEBUG_SCREEN_ON, "All windows drawn!");
+                mH.removeMessages(H.WAITING_FOR_DRAWN_TIMEOUT, container);
+                mH.sendMessage(mH.obtainMessage(H.ALL_WINDOWS_DRAWN, container));
+            }
+        });
     }
 
     void setHoldScreenLocked(final Session newHoldScreen) {
@@ -5520,7 +5491,7 @@
         return val;
     }
 
-    void createWatermarkInTransaction() {
+    void createWatermark() {
         if (mWatermark != null) {
             return;
         }
@@ -5538,8 +5509,8 @@
                     // TODO(multi-display): Show watermarks on secondary displays.
                     final DisplayContent displayContent = getDefaultDisplayContentLocked();
                     mWatermark = new Watermark(mSurfaceFactory, displayContent,
-                            displayContent.mRealDisplayMetrics,
-                            toks);
+                            displayContent.mRealDisplayMetrics, toks, mTransaction);
+                    mTransaction.apply();
                 }
             }
         } catch (FileNotFoundException e) {
@@ -5934,13 +5905,18 @@
                 }
             }
         }
-        if (mWaitingForDrawn.size() > 0) {
+        if (!mWaitingForDrawnCallbacks.isEmpty()) {
             pw.println();
             pw.println("  Clients waiting for these windows to be drawn:");
-            for (int i=mWaitingForDrawn.size()-1; i>=0; i--) {
-                WindowState win = mWaitingForDrawn.get(i);
-                pw.print("  Waiting #"); pw.print(i); pw.print(' '); pw.print(win);
-            }
+            mWaitingForDrawnCallbacks.forEach((wc, callback) -> {
+                pw.print("  WindowContainer ");
+                pw.println(wc.getName());
+                for (int i = wc.mWaitingForDrawn.size() - 1; i >= 0; i--) {
+                    final WindowState win = (WindowState) wc.mWaitingForDrawn.get(i);
+                    pw.print("  Waiting #"); pw.print(i); pw.print(' '); pw.print(win);
+                }
+            });
+
         }
         pw.println();
         pw.print("  mGlobalConfiguration="); pw.println(mRoot.getConfiguration());
@@ -7096,17 +7072,25 @@
         }
 
         @Override
-        public void waitForAllWindowsDrawn(Runnable callback, long timeout) {
+        public void waitForAllWindowsDrawn(Runnable callback, long timeout, int displayId) {
+            final WindowContainer container = displayId == INVALID_DISPLAY
+                    ? mRoot : mRoot.getDisplayContent(displayId);
+            if (container == null) {
+                // The waiting container doesn't exist, no need to wait to run the callback. Run and
+                // return;
+                callback.run();
+                return;
+            }
             boolean allWindowsDrawn = false;
             synchronized (mGlobalLock) {
-                mWaitingForDrawnCallback = callback;
-                getDefaultDisplayContentLocked().waitForAllWindowsDrawn();
+                container.waitForAllWindowsDrawn();
                 mWindowPlacerLocked.requestTraversal();
-                mH.removeMessages(H.WAITING_FOR_DRAWN_TIMEOUT);
-                if (mWaitingForDrawn.isEmpty()) {
+                mH.removeMessages(H.WAITING_FOR_DRAWN_TIMEOUT, container);
+                if (container.mWaitingForDrawn.isEmpty()) {
                     allWindowsDrawn = true;
                 } else {
-                    mH.sendEmptyMessageDelayed(H.WAITING_FOR_DRAWN_TIMEOUT, timeout);
+                    mWaitingForDrawnCallbacks.put(container, callback);
+                    mH.sendNewMessageDelayed(H.WAITING_FOR_DRAWN_TIMEOUT, container, timeout);
                     checkDrawnWindowsLocked();
                 }
             }
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 82b9d46..56e08b2 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -3497,7 +3497,7 @@
     @Override
     void setWaitingForDrawnIfResizingChanged() {
         if (isDragResizeChanged()) {
-            mWmService.mWaitingForDrawn.add(this);
+            mWmService.mRoot.mWaitingForDrawn.add(this);
         }
         super.setWaitingForDrawnIfResizingChanged();
     }
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index 3dcf6ec..1328273 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -506,9 +506,8 @@
                 flags |= SurfaceControl.OPAQUE;
             }
 
-            mSurfaceController = new WindowSurfaceController(mSession.mSurfaceSession,
-                    attrs.getTitle().toString(), width, height, format, flags, this,
-                    windowType, ownerUid);
+            mSurfaceController = new WindowSurfaceController(attrs.getTitle().toString(), width,
+                    height, format, flags, this, windowType, ownerUid);
             mSurfaceController.setColorSpaceAgnostic((attrs.privateFlags
                     & WindowManager.LayoutParams.PRIVATE_FLAG_COLOR_SPACE_AGNOSTIC) != 0);
 
diff --git a/services/core/java/com/android/server/wm/WindowSurfaceController.java b/services/core/java/com/android/server/wm/WindowSurfaceController.java
index 49f27a1..7f68c48 100644
--- a/services/core/java/com/android/server/wm/WindowSurfaceController.java
+++ b/services/core/java/com/android/server/wm/WindowSurfaceController.java
@@ -37,7 +37,6 @@
 import android.util.Slog;
 import android.util.proto.ProtoOutputStream;
 import android.view.SurfaceControl;
-import android.view.SurfaceSession;
 import android.view.WindowContentFrameStats;
 
 import com.android.server.protolog.common.ProtoLog;
@@ -85,7 +84,7 @@
 
     private final SurfaceControl.Transaction mTmpTransaction;
 
-    public WindowSurfaceController(SurfaceSession s, String name, int w, int h, int format,
+    WindowSurfaceController(String name, int w, int h, int format,
             int flags, WindowStateAnimator animator, int windowType, int ownerUid) {
         mAnimator = animator;
 
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index de65002..a453164 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -501,7 +501,7 @@
                     mRuntimeStartElapsedTime, mRuntimeStartUptime);
             LocalServices.addService(SystemServiceManager.class, mSystemServiceManager);
             // Prepare the thread pool for init tasks that can be parallelized
-            SystemServerInitThreadPool.get();
+            SystemServerInitThreadPool.start();
         } finally {
             t.traceEnd();  // InitBeforeStartServices
         }
diff --git a/services/tests/servicestests/src/com/android/server/integrity/model/OpenFormulaTest.java b/services/tests/servicestests/src/com/android/server/integrity/model/OpenFormulaTest.java
index 1a3dde0..2133a7d 100644
--- a/services/tests/servicestests/src/com/android/server/integrity/model/OpenFormulaTest.java
+++ b/services/tests/servicestests/src/com/android/server/integrity/model/OpenFormulaTest.java
@@ -24,6 +24,9 @@
 import org.junit.runner.RunWith;
 import org.junit.runners.JUnit4;
 
+import java.util.Arrays;
+import java.util.Collections;
+
 @RunWith(JUnit4.class)
 public class OpenFormulaTest {
 
@@ -34,12 +37,11 @@
 
     @Test
     public void testValidOpenFormula() {
-        OpenFormula openFormula = new OpenFormula(OpenFormula.Connector.AND, ATOMIC_FORMULA_1,
-                ATOMIC_FORMULA_2);
+        OpenFormula openFormula = new OpenFormula(OpenFormula.Connector.AND,
+                Arrays.asList(ATOMIC_FORMULA_1, ATOMIC_FORMULA_2));
 
         assertEquals(OpenFormula.Connector.AND, openFormula.getConnector());
-        assertEquals(ATOMIC_FORMULA_1, openFormula.getMainFormula());
-        assertEquals(ATOMIC_FORMULA_2, openFormula.getAuxiliaryFormula());
+        assertEquals(Arrays.asList(ATOMIC_FORMULA_1, ATOMIC_FORMULA_2), openFormula.getFormulas());
     }
 
     @Test
@@ -47,9 +49,10 @@
         assertExpectException(
                 IllegalArgumentException.class,
                 /* expectedExceptionMessageRegex */
-                String.format("Invalid formulas used for connector %s", OpenFormula.Connector.AND),
-                () -> new OpenFormula(OpenFormula.Connector.AND, ATOMIC_FORMULA_1,
-                        null));
+                String.format("Connector %s must have at least 2 formulas",
+                        OpenFormula.Connector.AND),
+                () -> new OpenFormula(OpenFormula.Connector.AND,
+                        Collections.singletonList(ATOMIC_FORMULA_1)));
     }
 
     @Test
@@ -57,8 +60,8 @@
         assertExpectException(
                 IllegalArgumentException.class,
                 /* expectedExceptionMessageRegex */
-                String.format("Invalid formulas used for connector %s", OpenFormula.Connector.NOT),
-                () -> new OpenFormula(OpenFormula.Connector.NOT, ATOMIC_FORMULA_1,
-                        ATOMIC_FORMULA_2));
+                String.format("Connector %s must have 1 formula only", OpenFormula.Connector.NOT),
+                () -> new OpenFormula(OpenFormula.Connector.NOT,
+                        Arrays.asList(ATOMIC_FORMULA_1, ATOMIC_FORMULA_2)));
     }
 }
diff --git a/services/tests/wmtests/src/com/android/server/wm/DimmerTests.java b/services/tests/wmtests/src/com/android/server/wm/DimmerTests.java
index a98f79c..73420a0 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DimmerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DimmerTests.java
@@ -46,7 +46,7 @@
 
     private static class TestWindowContainer extends WindowContainer<TestWindowContainer> {
         final SurfaceControl mControl = mock(SurfaceControl.class);
-        final SurfaceControl.Transaction mTransaction = mock(SurfaceControl.Transaction.class);
+        final SurfaceControl.Transaction mTransaction = spy(StubTransaction.class);
 
         TestWindowContainer(WindowManagerService wm) {
             super(wm);
@@ -66,7 +66,7 @@
     private static class MockSurfaceBuildingContainer extends WindowContainer<TestWindowContainer> {
         final SurfaceSession mSession = new SurfaceSession();
         final SurfaceControl mHostControl = mock(SurfaceControl.class);
-        final SurfaceControl.Transaction mHostTransaction = mock(SurfaceControl.Transaction.class);
+        final SurfaceControl.Transaction mHostTransaction = spy(StubTransaction.class);
 
         MockSurfaceBuildingContainer(WindowManagerService wm) {
             super(wm);
@@ -118,7 +118,7 @@
     public void setUp() throws Exception {
         mHost = new MockSurfaceBuildingContainer(mWm);
         mSurfaceAnimatorStarter = spy(new SurfaceAnimatorStarterImpl());
-        mTransaction = mock(SurfaceControl.Transaction.class);
+        mTransaction = spy(StubTransaction.class);
         mDimmer = new Dimmer(mHost, mSurfaceAnimatorStarter);
     }
 
diff --git a/services/tests/wmtests/src/com/android/server/wm/LaunchParamsPersisterTests.java b/services/tests/wmtests/src/com/android/server/wm/LaunchParamsPersisterTests.java
index 7115af9f..b9fef4b 100644
--- a/services/tests/wmtests/src/com/android/server/wm/LaunchParamsPersisterTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/LaunchParamsPersisterTests.java
@@ -32,7 +32,6 @@
 import static org.mockito.Matchers.any;
 
 import android.content.ComponentName;
-import android.content.pm.PackageList;
 import android.content.pm.PackageManagerInternal;
 import android.graphics.Rect;
 import android.os.UserHandle;
@@ -43,6 +42,7 @@
 import androidx.test.filters.MediumTest;
 
 import com.android.server.LocalServices;
+import com.android.server.pm.PackageList;
 import com.android.server.wm.LaunchParamsController.LaunchParams;
 
 import org.junit.Before;
diff --git a/services/tests/wmtests/src/com/android/server/wm/LetterboxTest.java b/services/tests/wmtests/src/com/android/server/wm/LetterboxTest.java
index 2d0416d..15417d7 100644
--- a/services/tests/wmtests/src/com/android/server/wm/LetterboxTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/LetterboxTest.java
@@ -21,6 +21,7 @@
 import static org.mockito.Mockito.clearInvocations;
 import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
@@ -48,8 +49,8 @@
     @Before
     public void setUp() throws Exception {
         mSurfaces = new SurfaceControlMocker();
-        mLetterbox = new Letterbox(mSurfaces, () -> mock(SurfaceControl.Transaction.class));
-        mTransaction = mock(SurfaceControl.Transaction.class);
+        mLetterbox = new Letterbox(mSurfaces, StubTransaction::new);
+        mTransaction = spy(StubTransaction.class);
     }
 
     @Test
diff --git a/services/tests/wmtests/src/com/android/server/wm/StubTransaction.java b/services/tests/wmtests/src/com/android/server/wm/StubTransaction.java
index 2ad40f2..f5d08dc 100644
--- a/services/tests/wmtests/src/com/android/server/wm/StubTransaction.java
+++ b/services/tests/wmtests/src/com/android/server/wm/StubTransaction.java
@@ -239,4 +239,15 @@
     public SurfaceControl.Transaction remove(SurfaceControl sc) {
         return this;
     }
+
+    @Override
+    public SurfaceControl.Transaction syncInputWindows() {
+        return this;
+    }
+
+    @Override
+    public SurfaceControl.Transaction setColorSpaceAgnostic(SurfaceControl sc, boolean agnostic) {
+        return this;
+    }
+
 }
diff --git a/services/tests/wmtests/src/com/android/server/wm/TestActivityDisplay.java b/services/tests/wmtests/src/com/android/server/wm/TestActivityDisplay.java
index 778f0ca..9c3ff65 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TestActivityDisplay.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TestActivityDisplay.java
@@ -22,8 +22,11 @@
 
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyBoolean;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
 
+import static org.mockito.ArgumentMatchers.any;
+
 import android.hardware.display.DisplayManagerGlobal;
 import android.view.Display;
 import android.view.DisplayInfo;
@@ -85,6 +88,10 @@
             displayRotation.setRotation(rotation);
             return true;
         }).when(displayRotation).updateRotationUnchecked(anyBoolean());
+
+        final InputMonitor inputMonitor = mDisplayContent.getInputMonitor();
+        spyOn(inputMonitor);
+        doNothing().when(inputMonitor).resumeDispatchingLw(any());
     }
 
     @SuppressWarnings("TypeParameterUnusedInFormals")
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowAnimationSpecTest.java b/services/tests/wmtests/src/com/android/server/wm/WindowAnimationSpecTest.java
index 0330de8..bfc0741 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowAnimationSpecTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowAnimationSpecTest.java
@@ -27,6 +27,7 @@
 import static org.mockito.ArgumentMatchers.argThat;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.when;
 
 import android.graphics.Point;
@@ -50,7 +51,7 @@
 @Presubmit
 public class WindowAnimationSpecTest {
     private final SurfaceControl mSurfaceControl = mock(SurfaceControl.class);
-    private final SurfaceControl.Transaction mTransaction = mock(SurfaceControl.Transaction.class);
+    private final SurfaceControl.Transaction mTransaction = spy(StubTransaction.class);
     private final Animation mAnimation = mock(Animation.class);
     private final Rect mStackBounds = new Rect(0, 0, 10, 10);
 
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 e5fb28d..a09253a 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
@@ -452,7 +452,7 @@
     @Test
     public void testSeamlesslyRotateWindow() {
         final WindowState app = createWindow(null, TYPE_APPLICATION, "app");
-        final SurfaceControl.Transaction t = mock(SurfaceControl.Transaction.class);
+        final SurfaceControl.Transaction t = spy(StubTransaction.class);
 
         app.mHasSurface = true;
         app.mSurfaceControl = mock(SurfaceControl.class);
@@ -536,7 +536,7 @@
 
         final float[] values = new float[9];
         final Matrix matrix = new Matrix();
-        final SurfaceControl.Transaction t = mock(SurfaceControl.Transaction.class);
+        final SurfaceControl.Transaction t = spy(StubTransaction.class);
         final WindowState win1 = createWindow(null, TYPE_APPLICATION, dc, "win1");
         win1.mHasSurface = true;
         win1.mSurfaceControl = mock(SurfaceControl.class);
diff --git a/startop/scripts/iorap/common b/startop/scripts/iorap/common
index 031dabf..387e45d 100755
--- a/startop/scripts/iorap/common
+++ b/startop/scripts/iorap/common
@@ -248,6 +248,6 @@
   local remote_path="$(_iorapd_path_to_data_file "$package" "$activity" "compiled_trace.pb")"
 
   # See 'read_ahead.cc' LOG(INFO).
-  local pattern="ReadAhead completed ($remote_path)"
+  local pattern="Description = $remote_path"
   logcat_wait_for_pattern "$timeout" "$timestamp" "$pattern"
 }
diff --git a/telephony/java/com/android/internal/telephony/SmsApplication.java b/telephony/common/com/android/internal/telephony/SmsApplication.java
similarity index 100%
rename from telephony/java/com/android/internal/telephony/SmsApplication.java
rename to telephony/common/com/android/internal/telephony/SmsApplication.java
diff --git a/telephony/java/android/telephony/CellIdentity.java b/telephony/java/android/telephony/CellIdentity.java
index 432978d..b7dab16 100644
--- a/telephony/java/android/telephony/CellIdentity.java
+++ b/telephony/java/android/telephony/CellIdentity.java
@@ -35,6 +35,15 @@
     /** @hide */
     public static final int INVALID_CHANNEL_NUMBER = -1;
 
+    /**
+     * parameters for validation
+     * @hide
+     */
+    public static final int MCC_LENGTH = 3;
+
+    private static final int MNC_MIN_LENGTH = 2;
+    private static final int MNC_MAX_LENGTH = 3;
+
     // Log tag
     /** @hide */
     protected final String mTag;
@@ -207,6 +216,17 @@
         dest.writeString(mAlphaShort);
     }
 
+    /** Used by phone interface manager to verify if a given string is valid MccMnc
+     * @hide
+     */
+    public static boolean isValidPlmn(@NonNull String plmn) {
+        if (plmn.length() < MCC_LENGTH + MNC_MIN_LENGTH
+                || plmn.length() > MCC_LENGTH + MNC_MAX_LENGTH) {
+            return false;
+        }
+        return (isMcc(plmn.substring(0, MCC_LENGTH)) && isMnc(plmn.substring(MCC_LENGTH)));
+    }
+
     /**
      * Construct from Parcel
      * @hide
@@ -267,10 +287,10 @@
     /** @hide */
     private static boolean isMcc(@NonNull String mcc) {
         // ensure no out of bounds indexing
-        if (mcc.length() != 3) return false;
+        if (mcc.length() != MCC_LENGTH) return false;
 
         // Character.isDigit allows all unicode digits, not just [0-9]
-        for (int i = 0; i < 3; i++) {
+        for (int i = 0; i < MCC_LENGTH; i++) {
             if (mcc.charAt(i) < '0' || mcc.charAt(i) > '9') return false;
         }
 
@@ -280,7 +300,7 @@
     /** @hide */
     private static boolean isMnc(@NonNull String mnc) {
         // ensure no out of bounds indexing
-        if (mnc.length() < 2 || mnc.length() > 3) return false;
+        if (mnc.length() < MNC_MIN_LENGTH || mnc.length() > MNC_MAX_LENGTH) return false;
 
         // Character.isDigit allows all unicode digits, not just [0-9]
         for (int i = 0; i < mnc.length(); i++) {
@@ -289,4 +309,5 @@
 
         return true;
     }
+
 }
diff --git a/telephony/java/android/telephony/ModemActivityInfo.java b/telephony/java/android/telephony/ModemActivityInfo.java
index 43bc85c..d105fe3 100644
--- a/telephony/java/android/telephony/ModemActivityInfo.java
+++ b/telephony/java/android/telephony/ModemActivityInfo.java
@@ -20,11 +20,10 @@
 import android.annotation.SystemApi;
 import android.os.Parcel;
 import android.os.Parcelable;
-
 import android.os.SystemClock;
 import android.util.Range;
+
 import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.List;
 
 /**
@@ -64,17 +63,20 @@
         mTimestamp = timestamp;
         mSleepTimeMs = sleepTimeMs;
         mIdleTimeMs = idleTimeMs;
-        if (txTimeMs != null) {
-            populateTransmitPowerRange(txTimeMs);
-        }
+        populateTransmitPowerRange(txTimeMs);
         mRxTimeMs = rxTimeMs;
     }
 
     /** helper API to populate tx power range for each bucket **/
     private void populateTransmitPowerRange(@NonNull int[] transmitPowerMs) {
-        for (int i = 0; i < Math.min(transmitPowerMs.length, TX_POWER_LEVELS); i++) {
+        int i = 0;
+        for ( ; i < Math.min(transmitPowerMs.length, TX_POWER_LEVELS); i++) {
             mTransmitPowerInfo.add(i, new TransmitPower(TX_POWER_RANGES[i], transmitPowerMs[i]));
         }
+        // Make sure that mTransmitPowerInfo is fully initialized.
+        for ( ; i < TX_POWER_LEVELS; i++) {
+            mTransmitPowerInfo.add(i, new TransmitPower(TX_POWER_RANGES[i], 0));
+        }
     }
 
     @Override
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 66571e3..2442023 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -6852,6 +6852,40 @@
     }
 
     /**
+     * Replace the contents of the forbidden PLMN SIM file with the provided values.
+     * Passing an empty list will clear the contents of the EFfplmn file.
+     * If the provided list is shorter than the size of EFfplmn, then the list will be padded
+     * up to the file size with 'FFFFFF'. (required by 3GPP TS 31.102 spec 4.2.16)
+     * If the list is longer than the size of EFfplmn, then the file will be written from the
+     * beginning of the list up to the file size.
+     *
+     * <p>Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
+     * or that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
+     *
+     * @param fplmns a list of PLMNs to be forbidden.
+     *
+     * @return number of PLMNs that were successfully written to the SIM FPLMN list.
+     * This may be less than the number of PLMNs passed in where the SIM file does not have enough
+     * room for all of the values passed in. Return -1 in the event of an unexpected failure
+     */
+    @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
+    @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+    public int setForbiddenPlmns(@NonNull List<String> fplmns) {
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony == null) return 0;
+            return telephony.setForbiddenPlmns(
+                    getSubId(), APPTYPE_USIM, fplmns, getOpPackageName());
+        } catch (RemoteException ex) {
+            Rlog.e(TAG, "setForbiddenPlmns RemoteException: " + ex.getMessage());
+        } catch (NullPointerException ex) {
+            // This could happen before phone starts
+            Rlog.e(TAG, "setForbiddenPlmns NullPointerException: " + ex.getMessage());
+        }
+        return 0;
+    }
+
+    /**
      * Get P-CSCF address from PCO after data connection is established or modified.
      * @param apnType the apnType, "ims" for IMS APN, "emergency" for EMERGENCY APN
      * @return array of P-CSCF address
diff --git a/telephony/java/android/telephony/TelephonyScanManager.java b/telephony/java/android/telephony/TelephonyScanManager.java
index 28747da..9ff8515 100644
--- a/telephony/java/android/telephony/TelephonyScanManager.java
+++ b/telephony/java/android/telephony/TelephonyScanManager.java
@@ -104,7 +104,7 @@
 
     private final Looper mLooper;
     private final Messenger mMessenger;
-    private SparseArray<NetworkScanInfo> mScanInfo = new SparseArray<NetworkScanInfo>();
+    private final SparseArray<NetworkScanInfo> mScanInfo = new SparseArray<NetworkScanInfo>();
 
     public TelephonyScanManager() {
         HandlerThread thread = new HandlerThread(TAG);
@@ -204,14 +204,16 @@
         try {
             ITelephony telephony = getITelephony();
             if (telephony != null) {
-                int scanId = telephony.requestNetworkScan(
-                        subId, request, mMessenger, new Binder(), callingPackage);
-                if (scanId == INVALID_SCAN_ID) {
-                    Rlog.e(TAG, "Failed to initiate network scan");
-                    return null;
+                synchronized (mScanInfo) {
+                    int scanId = telephony.requestNetworkScan(
+                            subId, request, mMessenger, new Binder(), callingPackage);
+                    if (scanId == INVALID_SCAN_ID) {
+                        Rlog.e(TAG, "Failed to initiate network scan");
+                        return null;
+                    }
+                    saveScanInfo(scanId, request, executor, callback);
+                    return new NetworkScan(scanId, subId);
                 }
-                saveScanInfo(scanId, request, executor, callback);
-                return new NetworkScan(scanId, subId);
             }
         } catch (RemoteException ex) {
             Rlog.e(TAG, "requestNetworkScan RemoteException", ex);
@@ -223,9 +225,7 @@
 
     private void saveScanInfo(
             int id, NetworkScanRequest request, Executor executor, NetworkScanCallback callback) {
-        synchronized (mScanInfo) {
-            mScanInfo.put(id, new NetworkScanInfo(request, executor, callback));
-        }
+        mScanInfo.put(id, new NetworkScanInfo(request, executor, callback));
     }
 
     private ITelephony getITelephony() {
diff --git a/telephony/java/android/telephony/euicc/EuiccManager.java b/telephony/java/android/telephony/euicc/EuiccManager.java
index cabd4df..5a90cb1 100644
--- a/telephony/java/android/telephony/euicc/EuiccManager.java
+++ b/telephony/java/android/telephony/euicc/EuiccManager.java
@@ -32,6 +32,7 @@
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.telephony.TelephonyManager;
+import android.telephony.euicc.EuiccCardManager.ResetOption;
 
 import com.android.internal.telephony.euicc.IEuiccController;
 
@@ -821,17 +822,22 @@
     }
 
     /**
-     * Erase all subscriptions and reset the eUICC.
+     * Erase all operational subscriptions and reset the eUICC.
      *
      * <p>Requires that the calling app has the
      * {@code android.Manifest.permission#WRITE_EMBEDDED_SUBSCRIPTIONS} permission.
      *
      * @param callbackIntent a PendingIntent to launch when the operation completes.
+     *
+     * @deprecated From R, callers should specify a flag for specific set of subscriptions to erase
+     * and use @link{eraseSubscriptionsWithOptions} instead
+     *
      * @hide
      */
     @SystemApi
     @RequiresPermission(Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS)
-    public void eraseSubscriptions(PendingIntent callbackIntent) {
+    @Deprecated
+    public void eraseSubscriptions(@NonNull PendingIntent callbackIntent) {
         if (!isEnabled()) {
             sendUnavailableError(callbackIntent);
             return;
@@ -844,6 +850,32 @@
     }
 
     /**
+     * Erase all specific subscriptions and reset the eUICC.
+     *
+     * <p>Requires that the calling app has the
+     * {@code android.Manifest.permission#WRITE_EMBEDDED_SUBSCRIPTIONS} permission.
+     *
+     * @param options flag indicating specific set of subscriptions to erase
+     * @param callbackIntent a PendingIntent to launch when the operation completes.
+     *
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS)
+    public void eraseSubscriptionsWithOptions(
+            @ResetOption int options, @NonNull PendingIntent callbackIntent) {
+        if (!isEnabled()) {
+            sendUnavailableError(callbackIntent);
+            return;
+        }
+        try {
+            getIEuiccController().eraseSubscriptionsWithOptions(mCardId, options, callbackIntent);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
      * Ensure that subscriptions will be retained on the next factory reset.
      *
      * <p>By default, all subscriptions on the eUICC are erased the first time a device boots (ever
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index 4d90579..fd7ec56 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -1612,6 +1612,18 @@
     String[] getForbiddenPlmns(int subId, int appType, String callingPackage);
 
     /**
+     * Set the forbidden PLMN list from the givven app type (ex APPTYPE_USIM) on a particular
+     * subscription.
+     *
+     * @param subId subId the id of the subscription
+     * @param appType appType the uicc app type, must be USIM or SIM.
+     * @param fplmns plmns the Forbiden plmns list that needed to be written to the SIM.
+     * @param content callingPackage the op Package name.
+     * @return number of fplmns that is successfully written to the SIM
+     */
+    int setForbiddenPlmns(int subId, int appType, in List<String> fplmns, String callingPackage);
+
+    /**
      * Check if phone is in emergency callback mode
      * @return true if phone is in emergency callback mode
      * @param subId the subscription ID that this action applies to.
diff --git a/telephony/java/com/android/internal/telephony/PhoneConstants.java b/telephony/java/com/android/internal/telephony/PhoneConstants.java
index c9ec0f8..c19ae7b 100644
--- a/telephony/java/com/android/internal/telephony/PhoneConstants.java
+++ b/telephony/java/com/android/internal/telephony/PhoneConstants.java
@@ -170,7 +170,7 @@
 
     public static final int RIL_CARD_MAX_APPS    = 8;
 
-    public static final int DEFAULT_CARD_INDEX   = 0;
+    public static final int DEFAULT_SLOT_INDEX   = 0;
 
     public static final int MAX_PHONE_COUNT_SINGLE_SIM = 1;
 
diff --git a/telephony/java/com/android/internal/telephony/euicc/IEuiccController.aidl b/telephony/java/com/android/internal/telephony/euicc/IEuiccController.aidl
index 2016915..7422863 100644
--- a/telephony/java/com/android/internal/telephony/euicc/IEuiccController.aidl
+++ b/telephony/java/com/android/internal/telephony/euicc/IEuiccController.aidl
@@ -44,5 +44,7 @@
     oneway void updateSubscriptionNickname(int cardId, int subscriptionId, String nickname,
         String callingPackage, in PendingIntent callbackIntent);
     oneway void eraseSubscriptions(int cardId, in PendingIntent callbackIntent);
+    oneway void eraseSubscriptionsWithOptions(
+        int cardId, int options, in PendingIntent callbackIntent);
     oneway void retainSubscriptionsForFactoryReset(int cardId, in PendingIntent callbackIntent);
 }
diff --git a/telephony/java/com/android/internal/telephony/uicc/IccUtils.java b/telephony/java/com/android/internal/telephony/uicc/IccUtils.java
index 9c69e2d..f2d4624 100644
--- a/telephony/java/com/android/internal/telephony/uicc/IccUtils.java
+++ b/telephony/java/com/android/internal/telephony/uicc/IccUtils.java
@@ -22,11 +22,13 @@
 import android.graphics.Color;
 import android.telephony.Rlog;
 
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.telephony.GsmAlphabet;
 
 import dalvik.annotation.compat.UnsupportedAppUsage;
 
 import java.io.UnsupportedEncodingException;
+import java.util.List;
 
 /**
  * Various methods, useful for dealing with SIM data.
@@ -34,6 +36,11 @@
 public class IccUtils {
     static final String LOG_TAG="IccUtils";
 
+    // 3GPP specification constants
+    // Spec reference TS 31.102 section 4.2.16
+    @VisibleForTesting
+    static final int FPLMN_BYTE_SIZE = 3;
+
     // A table mapping from a number to a hex character for fast encoding hex strings.
     private static final char[] HEX_CHARS = {
             '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'
@@ -896,4 +903,27 @@
         }
         return 0;
     }
+
+    /**
+     * Encode the Fplmns into byte array to write to EF.
+     *
+     * @param fplmns Array of fplmns to be serialized.
+     * @param dataLength the size of the EF file.
+     * @return the encoded byte array in the correct format for FPLMN file.
+     */
+    public static byte[] encodeFplmns(List<String> fplmns, int dataLength) {
+        byte[] serializedFplmns = new byte[dataLength];
+        int offset = 0;
+        for (String fplmn : fplmns) {
+            if (offset >= dataLength) break;
+            stringToBcdPlmn(fplmn, serializedFplmns, offset);
+            offset += FPLMN_BYTE_SIZE;
+        }
+        //pads to the length of the EF file.
+        while (offset < dataLength) {
+            // required by 3GPP TS 31.102 spec 4.2.16
+            serializedFplmns[offset++] = (byte) 0xff;
+        }
+        return serializedFplmns;
+    }
 }
diff --git a/tests/net/common/java/android/net/LinkPropertiesTest.java b/tests/net/common/java/android/net/LinkPropertiesTest.java
index b0464d9..ae8285b 100644
--- a/tests/net/common/java/android/net/LinkPropertiesTest.java
+++ b/tests/net/common/java/android/net/LinkPropertiesTest.java
@@ -99,6 +99,7 @@
         assertFalse(lp.isIpv4Provisioned());
         assertFalse(lp.isIpv6Provisioned());
         assertFalse(lp.isPrivateDnsActive());
+        assertFalse(lp.isWakeOnLanSupported());
     }
 
     private LinkProperties makeTestObject() {
@@ -120,6 +121,7 @@
         lp.setMtu(MTU);
         lp.setTcpBufferSizes(TCP_BUFFER_SIZES);
         lp.setNat64Prefix(new IpPrefix("2001:db8:0:64::/96"));
+        lp.setWakeOnLanSupported(true);
         return lp;
     }
 
@@ -158,6 +160,9 @@
         assertTrue(source.isIdenticalTcpBufferSizes(target));
         assertTrue(target.isIdenticalTcpBufferSizes(source));
 
+        assertTrue(source.isIdenticalWakeOnLan(target));
+        assertTrue(target.isIdenticalWakeOnLan(source));
+
         // Check result of equals().
         assertTrue(source.equals(target));
         assertTrue(target.equals(source));
@@ -1057,4 +1062,13 @@
         lp.clear();
         assertFalse(lp.isPrivateDnsActive());
     }
+
+    @Test
+    public void testWakeOnLanSupported() {
+        final LinkProperties lp = makeTestObject();
+        assertTrue(lp.isWakeOnLanSupported());
+
+        lp.clear();
+        assertFalse(lp.isWakeOnLanSupported());
+    }
 }
diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java
index 41440ab..bffbbfd 100644
--- a/tests/net/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java
@@ -274,6 +274,7 @@
     private static final String CLAT_PREFIX = "v4-";
     private static final String MOBILE_IFNAME = "test_rmnet_data0";
     private static final String WIFI_IFNAME = "test_wlan0";
+    private static final String WIFI_WOL_IFNAME = "test_wlan_wol";
     private static final String[] EMPTY_STRING_ARRAY = new String[0];
 
     private MockContext mServiceContext;
@@ -343,6 +344,12 @@
                             "mobile_mms,2,0,2,60000,true",
                     });
 
+            when(mResources.getStringArray(
+                    com.android.internal.R.array.config_wakeonlan_supported_interfaces))
+                    .thenReturn(new String[]{
+                            WIFI_WOL_IFNAME,
+                    });
+
             mContentResolver = new MockContentResolver();
             mContentResolver.addProvider(Settings.AUTHORITY, settingsProvider);
         }
@@ -5947,6 +5954,24 @@
         assertContainsExactly(uidCaptor.getValue(), APP2_UID);
     }
 
+    @Test
+    public void testLinkPropertiesWithWakeOnLanForActiveNetwork() throws Exception {
+        mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
+
+        LinkProperties wifiLp = new LinkProperties();
+        wifiLp.setInterfaceName(WIFI_WOL_IFNAME);
+        wifiLp.setWakeOnLanSupported(false);
+
+        // Default network switch should update ifaces.
+        mWiFiNetworkAgent.connect(false);
+        mWiFiNetworkAgent.sendLinkProperties(wifiLp);
+        waitForIdle();
+
+        // ConnectivityService should have changed the WakeOnLanSupported to true
+        wifiLp.setWakeOnLanSupported(true);
+        assertEquals(wifiLp, mService.getActiveLinkProperties());
+    }
+
 
     private TestNetworkAgentWrapper establishVpn(LinkProperties lp, int establishingUid,
             Set<UidRange> vpnRange) throws Exception {
diff --git a/tests/net/java/com/android/server/connectivity/PermissionMonitorTest.java b/tests/net/java/com/android/server/connectivity/PermissionMonitorTest.java
index 7029218..3fdba6e 100644
--- a/tests/net/java/com/android/server/connectivity/PermissionMonitorTest.java
+++ b/tests/net/java/com/android/server/connectivity/PermissionMonitorTest.java
@@ -54,7 +54,6 @@
 import android.content.Context;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageInfo;
-import android.content.pm.PackageList;
 import android.content.pm.PackageManager;
 import android.content.pm.PackageManagerInternal;
 import android.content.pm.UserInfo;
@@ -70,6 +69,7 @@
 import androidx.test.runner.AndroidJUnit4;
 
 import com.android.server.LocalServices;
+import com.android.server.pm.PackageList;
 
 import org.junit.Before;
 import org.junit.Test;