Merge "Rename and publicize getMaxPhoneCount; and deprecate getPhoneCount."
diff --git a/.mailmap b/.mailmap
index b061ccf..40c295e 100644
--- a/.mailmap
+++ b/.mailmap
@@ -1 +1 @@
-Ember Rose <emberr@google.com> <ashleyrose@google.com>
+Ember Rose <emberrose@google.com> <ashleyrose@google.com>
diff --git a/Android.bp b/Android.bp
index a106168..8853694 100644
--- a/Android.bp
+++ b/Android.bp
@@ -452,7 +452,9 @@
     srcs: [
         "core/java/android/annotation/IntDef.java",
         "core/java/android/annotation/UnsupportedAppUsage.java",
-        ":unsupportedappusage_annotation_files",
+    ],
+    static_libs: [
+        "art.module.api.annotations",
     ],
 
     sdk_version: "core_current",
@@ -953,6 +955,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 +1020,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 +1574,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/framework/java/com/android/server/DeviceIdleInternal.java b/apex/jobscheduler/framework/java/com/android/server/DeviceIdleInternal.java
index 1273249..6475f57 100644
--- a/apex/jobscheduler/framework/java/com/android/server/DeviceIdleInternal.java
+++ b/apex/jobscheduler/framework/java/com/android/server/DeviceIdleInternal.java
@@ -49,4 +49,24 @@
     int[] getPowerSaveWhitelistUserAppIds();
 
     int[] getPowerSaveTempWhitelistAppIds();
+
+    /**
+     * Listener to be notified when DeviceIdleController determines that the device has moved or is
+     * stationary.
+     */
+    interface StationaryListener {
+        void onDeviceStationaryChanged(boolean isStationary);
+    }
+
+    /**
+     * Registers a listener that will be notified when the system has detected that the device is
+     * stationary or in motion.
+     */
+    void registerStationaryListener(StationaryListener listener);
+
+    /**
+     * Unregisters a registered stationary listener from being notified when the system has detected
+     * that the device is stationary or in motion.
+     */
+    void unregisterStationaryListener(StationaryListener listener);
 }
diff --git a/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java b/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java
index d6661c2..20ee064 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;
@@ -275,6 +274,7 @@
     private IBatteryStats mBatteryStats;
     private ActivityManagerInternal mLocalActivityManager;
     private ActivityTaskManagerInternal mLocalActivityTaskManager;
+    private DeviceIdleInternal mLocalService;
     private PowerManagerInternal mLocalPowerManager;
     private PowerManager mPowerManager;
     private INetworkPolicyManager mNetworkPolicyManager;
@@ -289,6 +289,7 @@
     private boolean mLightEnabled;
     private boolean mDeepEnabled;
     private boolean mQuickDozeActivated;
+    private boolean mQuickDozeActivatedWhileIdling;
     private boolean mForceIdle;
     private boolean mNetworkConnected;
     private boolean mScreenOn;
@@ -300,6 +301,10 @@
     private boolean mHasNetworkLocation;
     private Location mLastGenericLocation;
     private Location mLastGpsLocation;
+
+    /** Time in the elapsed realtime timebase when this listener last received a motion event. */
+    private long mLastMotionEventElapsed;
+
     // Current locked state of the screen
     private boolean mScreenLocked;
     private int mNumBlockingConstraints = 0;
@@ -549,6 +554,9 @@
      */
     private ArrayMap<String, Integer> mRemovedFromSystemWhitelistApps = new ArrayMap<>();
 
+    private final ArraySet<DeviceIdleInternal.StationaryListener> mStationaryListeners =
+            new ArraySet<>();
+
     private static final int EVENT_NULL = 0;
     private static final int EVENT_NORMAL = 1;
     private static final int EVENT_LIGHT_IDLE = 2;
@@ -607,6 +615,22 @@
         }
     };
 
+
+    private final AlarmManager.OnAlarmListener mMotionTimeoutAlarmListener = () -> {
+        synchronized (DeviceIdleController.this) {
+            if (!isStationaryLocked()) {
+                // If the device keeps registering motion, then the alarm should be
+                // rescheduled, so this shouldn't go off until the device is stationary.
+                // This case may happen in a race condition (alarm goes off right before
+                // motion is detected, but handleMotionDetectedLocked is called before
+                // we enter this block).
+                Slog.w(TAG, "motion timeout went off and device isn't stationary");
+                return;
+            }
+        }
+        postStationaryStatusUpdated();
+    };
+
     private final AlarmManager.OnAlarmListener mSensingTimeoutAlarmListener
             = new AlarmManager.OnAlarmListener() {
         @Override
@@ -656,12 +680,70 @@
         }
     };
 
+    /** Post stationary status only to this listener. */
+    private void postStationaryStatus(DeviceIdleInternal.StationaryListener listener) {
+        mHandler.obtainMessage(MSG_REPORT_STATIONARY_STATUS, listener).sendToTarget();
+    }
+
+    /** Post stationary status to all registered listeners. */
+    private void postStationaryStatusUpdated() {
+        mHandler.sendEmptyMessage(MSG_REPORT_STATIONARY_STATUS);
+    }
+
+    private boolean isStationaryLocked() {
+        final long now = mInjector.getElapsedRealtime();
+        return mMotionListener.active
+                // Listening for motion for long enough and last motion was long enough ago.
+                && now - Math.max(mMotionListener.activatedTimeElapsed, mLastMotionEventElapsed)
+                        >= mConstants.MOTION_INACTIVE_TIMEOUT;
+    }
+
+    @VisibleForTesting
+    void registerStationaryListener(DeviceIdleInternal.StationaryListener listener) {
+        synchronized (this) {
+            if (!mStationaryListeners.add(listener)) {
+                // Listener already registered.
+                return;
+            }
+            postStationaryStatus(listener);
+            if (mMotionListener.active) {
+                if (!isStationaryLocked() && mStationaryListeners.size() == 1) {
+                    // First listener to be registered and the device isn't stationary, so we
+                    // need to register the alarm to report the device is stationary.
+                    scheduleMotionTimeoutAlarmLocked();
+                }
+            } else {
+                startMonitoringMotionLocked();
+                scheduleMotionTimeoutAlarmLocked();
+            }
+        }
+    }
+
+    private void unregisterStationaryListener(DeviceIdleInternal.StationaryListener listener) {
+        synchronized (this) {
+            if (mStationaryListeners.remove(listener) && mStationaryListeners.size() == 0
+                    // Motion detection is started when transitioning from INACTIVE to IDLE_PENDING
+                    // and so doesn't need to be on for ACTIVE or INACTIVE states.
+                    // Motion detection isn't needed when idling due to Quick Doze.
+                    && (mState == STATE_ACTIVE || mState == STATE_INACTIVE
+                            || mQuickDozeActivated)) {
+                maybeStopMonitoringMotionLocked();
+            }
+        }
+    }
+
     @VisibleForTesting
     final class MotionListener extends TriggerEventListener
             implements SensorEventListener {
 
         boolean active = false;
 
+        /**
+         * Time in the elapsed realtime timebase when this listener was activated. Only valid if
+         * {@link #active} is true.
+         */
+        long activatedTimeElapsed;
+
         public boolean isActive() {
             return active;
         }
@@ -669,7 +751,6 @@
         @Override
         public void onTrigger(TriggerEvent event) {
             synchronized (DeviceIdleController.this) {
-                active = false;
                 motionLocked();
             }
         }
@@ -677,8 +758,6 @@
         @Override
         public void onSensorChanged(SensorEvent event) {
             synchronized (DeviceIdleController.this) {
-                mSensorManager.unregisterListener(this, mMotionSensor);
-                active = false;
                 motionLocked();
             }
         }
@@ -696,6 +775,7 @@
             }
             if (success) {
                 active = true;
+                activatedTimeElapsed = mInjector.getElapsedRealtime();
             } else {
                 Slog.e(TAG, "Unable to register for " + mMotionSensor);
             }
@@ -1303,6 +1383,8 @@
     private static final int MSG_REPORT_IDLE_OFF = 4;
     private static final int MSG_REPORT_ACTIVE = 5;
     private static final int MSG_TEMP_APP_WHITELIST_TIMEOUT = 6;
+    @VisibleForTesting
+    static final int MSG_REPORT_STATIONARY_STATUS = 7;
     private static final int MSG_FINISH_IDLE_OP = 8;
     private static final int MSG_REPORT_TEMP_APP_WHITELIST_CHANGED = 9;
     private static final int MSG_SEND_CONSTRAINT_MONITORING = 10;
@@ -1428,6 +1510,32 @@
                     updatePreIdleFactor();
                     maybeDoImmediateMaintenance();
                 } break;
+                case MSG_REPORT_STATIONARY_STATUS: {
+                    final DeviceIdleInternal.StationaryListener newListener =
+                            (DeviceIdleInternal.StationaryListener) msg.obj;
+                    final DeviceIdleInternal.StationaryListener[] listeners;
+                    final boolean isStationary;
+                    synchronized (DeviceIdleController.this) {
+                        isStationary = isStationaryLocked();
+                        if (newListener == null) {
+                            // Only notify all listeners if we aren't directing to one listener.
+                            listeners = mStationaryListeners.toArray(
+                                    new DeviceIdleInternal.StationaryListener[
+                                            mStationaryListeners.size()]);
+                        } else {
+                            listeners = null;
+                        }
+                    }
+                    if (listeners != null) {
+                        for (DeviceIdleInternal.StationaryListener listener : listeners) {
+                            listener.onDeviceStationaryChanged(isStationary);
+                        }
+                    }
+                    if (newListener != null) {
+                        newListener.onDeviceStationaryChanged(isStationary);
+                    }
+                }
+                break;
             }
         }
     }
@@ -1680,6 +1788,16 @@
         public int[] getPowerSaveTempWhitelistAppIds() {
             return DeviceIdleController.this.getAppIdTempWhitelistInternal();
         }
+
+        @Override
+        public void registerStationaryListener(StationaryListener listener) {
+            DeviceIdleController.this.registerStationaryListener(listener);
+        }
+
+        @Override
+        public void unregisterStationaryListener(StationaryListener listener) {
+            DeviceIdleController.this.unregisterStationaryListener(listener);
+        }
     }
 
     static class Injector {
@@ -1863,7 +1981,8 @@
 
         mBinderService = new BinderService();
         publishBinderService(Context.DEVICE_IDLE_CONTROLLER, mBinderService);
-        publishLocalService(DeviceIdleInternal.class, new LocalService());
+        mLocalService = new LocalService();
+        publishLocalService(DeviceIdleInternal.class, mLocalService);
     }
 
     @Override
@@ -2594,6 +2713,8 @@
     void updateQuickDozeFlagLocked(boolean enabled) {
         if (DEBUG) Slog.i(TAG, "updateQuickDozeFlagLocked: enabled=" + enabled);
         mQuickDozeActivated = enabled;
+        mQuickDozeActivatedWhileIdling =
+                mQuickDozeActivated && (mState == STATE_IDLE || mState == STATE_IDLE_MAINTENANCE);
         if (enabled) {
             // If Quick Doze is enabled, see if we should go straight into it.
             becomeInactiveIfAppropriateLocked();
@@ -2778,10 +2899,11 @@
         mNextIdlePendingDelay = 0;
         mNextIdleDelay = 0;
         mIdleStartTime = 0;
+        mQuickDozeActivatedWhileIdling = false;
         cancelAlarmLocked();
         cancelSensingTimeoutAlarmLocked();
         cancelLocatingLocked();
-        stopMonitoringMotionLocked();
+        maybeStopMonitoringMotionLocked();
         mAnyMotionDetector.stop();
         updateActiveConstraintsLocked();
     }
@@ -3268,11 +3390,23 @@
 
     void motionLocked() {
         if (DEBUG) Slog.d(TAG, "motionLocked()");
-        // The motion sensor will have been disabled at this point
+        mLastMotionEventElapsed = mInjector.getElapsedRealtime();
         handleMotionDetectedLocked(mConstants.MOTION_INACTIVE_TIMEOUT, "motion");
     }
 
     void handleMotionDetectedLocked(long timeout, String type) {
+        if (mStationaryListeners.size() > 0) {
+            postStationaryStatusUpdated();
+            scheduleMotionTimeoutAlarmLocked();
+        }
+        if (mQuickDozeActivated && !mQuickDozeActivatedWhileIdling) {
+            // Don't exit idle due to motion if quick doze is enabled.
+            // However, if the device started idling due to the normal progression (going through
+            // all the states) and then had quick doze activated, come out briefly on motion so the
+            // user can get slightly fresher content.
+            return;
+        }
+        maybeStopMonitoringMotionLocked();
         // The device is not yet active, so we want to go back to the pending idle
         // state to wait again for no motion.  Note that we only monitor for motion
         // after moving out of the inactive state, so no need to worry about that.
@@ -3324,10 +3458,15 @@
         }
     }
 
-    void stopMonitoringMotionLocked() {
-        if (DEBUG) Slog.d(TAG, "stopMonitoringMotionLocked()");
-        if (mMotionSensor != null && mMotionListener.active) {
+    /**
+     * Stops motion monitoring. Will not stop monitoring if there are registered stationary
+     * listeners.
+     */
+    private void maybeStopMonitoringMotionLocked() {
+        if (DEBUG) Slog.d(TAG, "maybeStopMonitoringMotionLocked()");
+        if (mMotionSensor != null && mMotionListener.active && mStationaryListeners.size() == 0) {
             mMotionListener.unregisterLocked();
+            cancelMotionTimeoutAlarmLocked();
         }
     }
 
@@ -3354,6 +3493,10 @@
         }
     }
 
+    private void cancelMotionTimeoutAlarmLocked() {
+        mAlarmManager.cancel(mMotionTimeoutAlarmListener);
+    }
+
     void cancelSensingTimeoutAlarmLocked() {
         if (mNextSensingTimeoutAlarmTime != 0) {
             mNextSensingTimeoutAlarmTime = 0;
@@ -3394,6 +3537,14 @@
                 mNextLightAlarmTime, "DeviceIdleController.light", mLightAlarmListener, mHandler);
     }
 
+    private void scheduleMotionTimeoutAlarmLocked() {
+        if (DEBUG) Slog.d(TAG, "scheduleMotionAlarmLocked");
+        long nextMotionTimeoutAlarmTime =
+                mInjector.getElapsedRealtime() + mConstants.MOTION_INACTIVE_TIMEOUT;
+        mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, nextMotionTimeoutAlarmTime,
+                "DeviceIdleController.motion", mMotionTimeoutAlarmListener, mHandler);
+    }
+
     void scheduleSensingTimeoutAlarmLocked(long delay) {
         if (DEBUG) Slog.d(TAG, "scheduleSensingAlarmLocked(" + delay + ")");
         mNextSensingTimeoutAlarmTime = SystemClock.elapsedRealtime() + delay;
@@ -3597,9 +3748,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);
@@ -4314,9 +4462,14 @@
                 }
                 pw.println("  }");
             }
-            if (mUseMotionSensor) {
+            if (mUseMotionSensor || mStationaryListeners.size() > 0) {
                 pw.print("  mMotionActive="); pw.println(mMotionListener.active);
                 pw.print("  mNotMoving="); pw.println(mNotMoving);
+                pw.print("  mMotionListener.activatedTimeElapsed=");
+                pw.println(mMotionListener.activatedTimeElapsed);
+                pw.print("  mLastMotionEventElapsed="); pw.println(mLastMotionEventElapsed);
+                pw.print("  "); pw.print(mStationaryListeners.size());
+                pw.println(" stationary listeners registered");
             }
             pw.print("  mLocating="); pw.print(mLocating); pw.print(" mHasGps=");
                     pw.print(mHasGps); pw.print(" mHasNetwork=");
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..14d5a68 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();
@@ -979,17 +1025,18 @@
             // This may throw a SecurityException.
             jobStatus.prepareLocked();
 
-            if (work != null) {
-                // If work has been supplied, enqueue it into the new job.
-                jobStatus.enqueueWorkLocked(work);
-            }
-
             if (toCancel != null) {
                 // Implicitly replaces the existing job record with the new instance
                 cancelJobImplLocked(toCancel, jobStatus, "job rescheduled by app");
             } else {
                 startTrackingJobLocked(jobStatus, null);
             }
+
+            if (work != null) {
+                // If work has been supplied, enqueue it into the new job.
+                jobStatus.enqueueWorkLocked(work);
+            }
+
             StatsLog.write_non_chained(StatsLog.SCHEDULED_JOB_STATE_CHANGED,
                     uId, null, jobStatus.getBatteryName(),
                     StatsLog.SCHEDULED_JOB_STATE_CHANGED__STATE__SCHEDULED,
@@ -2126,7 +2173,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/JobServiceContext.java b/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java
index 782e646..26db4a3 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java
@@ -251,7 +251,7 @@
                 binding = mContext.bindServiceAsUser(intent, this,
                         Context.BIND_AUTO_CREATE | Context.BIND_NOT_FOREGROUND
                         | Context.BIND_NOT_PERCEPTIBLE,
-                        new UserHandle(job.getUserId()));
+                        UserHandle.of(job.getUserId()));
             } catch (SecurityException e) {
                 // Some permission policy, for example INTERACT_ACROSS_USERS and
                 // android:singleUser, can result in a SecurityException being thrown from
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/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java b/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java
index fe7b4ae..7c472a9 100644
--- a/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java
+++ b/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java
@@ -1851,16 +1851,6 @@
      * Observe settings changes for {@link Global#APP_IDLE_CONSTANTS}.
      */
     private class SettingsObserver extends ContentObserver {
-        /**
-         * This flag has been used to disable app idle on older builds with bug b/26355386.
-         */
-        @Deprecated
-        private static final String KEY_IDLE_DURATION_OLD = "idle_duration";
-        @Deprecated
-        private static final String KEY_IDLE_DURATION = "idle_duration2";
-        @Deprecated
-        private static final String KEY_WALLCLOCK_THRESHOLD = "wallclock_threshold";
-
         private static final String KEY_PAROLE_INTERVAL = "parole_interval";
         private static final String KEY_PAROLE_WINDOW = "parole_window";
         private static final String KEY_PAROLE_DURATION = "parole_duration";
@@ -1995,7 +1985,7 @@
                                         : DEFAULT_EXEMPTED_SYNC_START_TIMEOUT);
 
                 mUnexemptedSyncScheduledTimeoutMillis = mParser.getDurationMillis(
-                        KEY_EXEMPTED_SYNC_SCHEDULED_DOZE_HOLD_DURATION,
+                        KEY_UNEXEMPTED_SYNC_SCHEDULED_HOLD_DURATION,
                                 COMPRESS_TIME ? ONE_MINUTE
                                         : DEFAULT_UNEXEMPTED_SYNC_SCHEDULED_TIMEOUT); // TODO
 
diff --git a/api/current.txt b/api/current.txt
index a208e2d..2598dee 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -24356,6 +24356,7 @@
     field public static final int INFO_OUTPUT_FORMAT_CHANGED = -2; // 0xfffffffe
     field public static final int INFO_TRY_AGAIN_LATER = -1; // 0xffffffff
     field public static final String PARAMETER_KEY_HDR10_PLUS_INFO = "hdr10-plus-info";
+    field public static final String PARAMETER_KEY_LOW_LATENCY = "low-latency";
     field public static final String PARAMETER_KEY_OFFSET_TIME = "time-offset-us";
     field public static final String PARAMETER_KEY_REQUEST_SYNC_FRAME = "request-sync";
     field public static final String PARAMETER_KEY_SUSPEND = "drop-input-frames";
@@ -24529,6 +24530,7 @@
     field public static final String FEATURE_DynamicTimestamp = "dynamic-timestamp";
     field public static final String FEATURE_FrameParsing = "frame-parsing";
     field public static final String FEATURE_IntraRefresh = "intra-refresh";
+    field public static final String FEATURE_LowLatency = "low-latency";
     field public static final String FEATURE_MultipleFrames = "multiple-frames";
     field public static final String FEATURE_PartialFrame = "partial-frame";
     field public static final String FEATURE_SecurePlayback = "secure-playback";
@@ -25239,6 +25241,7 @@
     field public static final String KEY_LANGUAGE = "language";
     field public static final String KEY_LATENCY = "latency";
     field public static final String KEY_LEVEL = "level";
+    field public static final String KEY_LOW_LATENCY = "low-latency";
     field public static final String KEY_MAX_B_FRAMES = "max-bframes";
     field public static final String KEY_MAX_FPS_TO_ENCODER = "max-fps-to-encoder";
     field public static final String KEY_MAX_HEIGHT = "max-height";
@@ -25387,6 +25390,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 +25400,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 +28814,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);
@@ -45171,6 +45177,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);
@@ -47918,6 +47925,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..447ba30 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -226,6 +226,7 @@
     field public static final int isVrOnly = 16844152; // 0x1010578
     field public static final int requiredSystemPropertyName = 16844133; // 0x1010565
     field public static final int requiredSystemPropertyValue = 16844134; // 0x1010566
+    field public static final int resourcesMap = 16844297; // 0x1010609
     field public static final int supportsAmbientMode = 16844173; // 0x101058d
     field public static final int userRestriction = 16844164; // 0x1010584
   }
@@ -1358,8 +1359,9 @@
 
   public abstract class Context {
     method @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS) public boolean bindServiceAsUser(@RequiresPermission android.content.Intent, android.content.ServiceConnection, int, android.os.UserHandle);
+    method @NonNull public android.content.Context createContextAsUser(@NonNull android.os.UserHandle);
     method public abstract android.content.Context createCredentialProtectedStorageContext();
-    method public android.content.Context createPackageContextAsUser(String, int, android.os.UserHandle) throws android.content.pm.PackageManager.NameNotFoundException;
+    method @NonNull public android.content.Context createPackageContextAsUser(@NonNull String, int, @NonNull android.os.UserHandle) throws android.content.pm.PackageManager.NameNotFoundException;
     method @Nullable public abstract java.io.File getPreloadsFileCache();
     method public abstract boolean isCredentialProtectedStorage();
     method public abstract void sendBroadcast(android.content.Intent, @Nullable String, @Nullable android.os.Bundle);
@@ -5875,6 +5877,7 @@
     method @RequiresPermission(android.Manifest.permission.READ_DEVICE_CONFIG) public static float getFloat(@NonNull String, @NonNull String, float);
     method @RequiresPermission(android.Manifest.permission.READ_DEVICE_CONFIG) public static int getInt(@NonNull String, @NonNull String, int);
     method @RequiresPermission(android.Manifest.permission.READ_DEVICE_CONFIG) public static long getLong(@NonNull String, @NonNull String, long);
+    method @NonNull @RequiresPermission(android.Manifest.permission.READ_DEVICE_CONFIG) public static android.provider.DeviceConfig.Properties getProperties(@NonNull String, @NonNull java.lang.String...);
     method @RequiresPermission(android.Manifest.permission.READ_DEVICE_CONFIG) public static String getProperty(@NonNull String, @NonNull String);
     method @RequiresPermission(android.Manifest.permission.READ_DEVICE_CONFIG) public static String getString(@NonNull String, @NonNull String, @Nullable String);
     method public static void removeOnPropertiesChangedListener(@NonNull android.provider.DeviceConfig.OnPropertiesChangedListener);
@@ -6557,7 +6560,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 +8624,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/api/test-current.txt b/api/test-current.txt
index 70837a8..700be90 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -131,7 +131,7 @@
     method public void startActivity(@NonNull android.content.Intent);
     method public void startActivity(@NonNull android.content.Intent, android.os.UserHandle);
     method public void startActivity(@NonNull android.app.PendingIntent);
-    method public void startActivity(@NonNull android.app.PendingIntent, @NonNull android.app.ActivityOptions);
+    method public void startActivity(@NonNull android.app.PendingIntent, @Nullable android.content.Intent, @NonNull android.app.ActivityOptions);
   }
 
   public abstract static class ActivityView.StateCallback {
@@ -659,7 +659,8 @@
   }
 
   public abstract class Context {
-    method public android.content.Context createPackageContextAsUser(String, int, android.os.UserHandle) throws android.content.pm.PackageManager.NameNotFoundException;
+    method @NonNull public android.content.Context createContextAsUser(@NonNull android.os.UserHandle);
+    method @NonNull public android.content.Context createPackageContextAsUser(@NonNull String, int, @NonNull android.os.UserHandle) throws android.content.pm.PackageManager.NameNotFoundException;
     method public abstract android.view.Display getDisplay();
     method public abstract int getDisplayId();
     method public android.os.UserHandle getUser();
diff --git a/cmds/idmap2/Android.bp b/cmds/idmap2/Android.bp
index 4c77ba4..41a1706 100644
--- a/cmds/idmap2/Android.bp
+++ b/cmds/idmap2/Android.bp
@@ -43,9 +43,10 @@
         "libidmap2/Policies.cpp",
         "libidmap2/PrettyPrintVisitor.cpp",
         "libidmap2/RawPrintVisitor.cpp",
+        "libidmap2/ResourceMapping.cpp",
         "libidmap2/ResourceUtils.cpp",
         "libidmap2/Result.cpp",
-        "libidmap2/Xml.cpp",
+        "libidmap2/XmlParser.cpp",
         "libidmap2/ZipFile.cpp",
     ],
     export_include_dirs: ["include"],
@@ -97,9 +98,10 @@
         "tests/PoliciesTests.cpp",
         "tests/PrettyPrintVisitorTests.cpp",
         "tests/RawPrintVisitorTests.cpp",
+        "tests/ResourceMappingTests.cpp",
         "tests/ResourceUtilsTests.cpp",
         "tests/ResultTests.cpp",
-        "tests/XmlTests.cpp",
+        "tests/XmlParserTests.cpp",
         "tests/ZipFileTests.cpp",
     ],
     required: [
diff --git a/cmds/idmap2/idmap2/Create.cpp b/cmds/idmap2/idmap2/Create.cpp
index f482191..3ff6d35 100644
--- a/cmds/idmap2/idmap2/Create.cpp
+++ b/cmds/idmap2/idmap2/Create.cpp
@@ -99,8 +99,8 @@
     return Error("failed to load apk %s", overlay_apk_path.c_str());
   }
 
-  const auto idmap = Idmap::FromApkAssets(target_apk_path, *target_apk, overlay_apk_path,
-                                          *overlay_apk, fulfilled_policies, !ignore_overlayable);
+  const auto idmap =
+      Idmap::FromApkAssets(*target_apk, *overlay_apk, fulfilled_policies, !ignore_overlayable);
   if (!idmap) {
     return Error(idmap.GetError(), "failed to create idmap");
   }
diff --git a/cmds/idmap2/idmap2/Lookup.cpp b/cmds/idmap2/idmap2/Lookup.cpp
index b7ae9d0..c5cf980 100644
--- a/cmds/idmap2/idmap2/Lookup.cpp
+++ b/cmds/idmap2/idmap2/Lookup.cpp
@@ -33,9 +33,10 @@
 #include "androidfw/Util.h"
 #include "idmap2/CommandLineOptions.h"
 #include "idmap2/Idmap.h"
+#include "idmap2/ResourceUtils.h"
 #include "idmap2/Result.h"
 #include "idmap2/SysTrace.h"
-#include "idmap2/Xml.h"
+#include "idmap2/XmlParser.h"
 #include "idmap2/ZipFile.h"
 #include "utils/String16.h"
 #include "utils/String8.h"
@@ -57,8 +58,7 @@
 using android::idmap2::ResourceId;
 using android::idmap2::Result;
 using android::idmap2::Unit;
-using android::idmap2::Xml;
-using android::idmap2::ZipFile;
+using android::idmap2::utils::ExtractOverlayManifestInfo;
 using android::util::Utf16ToUtf8;
 
 namespace {
@@ -132,29 +132,6 @@
   return out;
 }
 
-Result<std::string> GetTargetPackageNameFromManifest(const std::string& apk_path) {
-  const auto zip = ZipFile::Open(apk_path);
-  if (!zip) {
-    return Error("failed to open %s as zip", apk_path.c_str());
-  }
-  const auto entry = zip->Uncompress("AndroidManifest.xml");
-  if (!entry) {
-    return Error("failed to uncompress AndroidManifest.xml in %s", apk_path.c_str());
-  }
-  const auto xml = Xml::Create(entry->buf, entry->size);
-  if (!xml) {
-    return Error("failed to create XML buffer");
-  }
-  const auto tag = xml->FindTag("overlay");
-  if (!tag) {
-    return Error("failed to find <overlay> tag");
-  }
-  const auto iter = tag->find("targetPackage");
-  if (iter == tag->end()) {
-    return Error("failed to find targetPackage attribute");
-  }
-  return iter->second;
-}
 }  // namespace
 
 Result<Unit> Lookup(const std::vector<std::string>& args) {
@@ -202,12 +179,12 @@
       }
       apk_assets.push_back(std::move(target_apk));
 
-      const Result<std::string> package_name =
-          GetTargetPackageNameFromManifest(idmap_header->GetOverlayPath().to_string());
-      if (!package_name) {
-        return Error("failed to parse android:targetPackage from overlay manifest");
+      auto manifest_info = ExtractOverlayManifestInfo(idmap_header->GetOverlayPath().to_string(),
+                                                      true /* assert_overlay */);
+      if (!manifest_info) {
+        return manifest_info.GetError();
       }
-      target_package_name = *package_name;
+      target_package_name = (*manifest_info).target_package;
     } else if (target_path != idmap_header->GetTargetPath()) {
       return Error("different target APKs (expected target APK %s but %s has target APK %s)",
                    target_path.c_str(), idmap_path.c_str(),
diff --git a/cmds/idmap2/idmap2/Scan.cpp b/cmds/idmap2/idmap2/Scan.cpp
index d0530f2..e643ab5 100644
--- a/cmds/idmap2/idmap2/Scan.cpp
+++ b/cmds/idmap2/idmap2/Scan.cpp
@@ -33,7 +33,7 @@
 #include "idmap2/ResourceUtils.h"
 #include "idmap2/Result.h"
 #include "idmap2/SysTrace.h"
-#include "idmap2/Xml.h"
+#include "idmap2/XmlParser.h"
 #include "idmap2/ZipFile.h"
 
 using android::idmap2::CommandLineOptions;
diff --git a/cmds/idmap2/idmap2d/Idmap2Service.cpp b/cmds/idmap2/idmap2d/Idmap2Service.cpp
index 8ee79f6..4aabf83 100644
--- a/cmds/idmap2/idmap2d/Idmap2Service.cpp
+++ b/cmds/idmap2/idmap2d/Idmap2Service.cpp
@@ -137,8 +137,8 @@
     return error("failed to load apk " + overlay_apk_path);
   }
 
-  const auto idmap = Idmap::FromApkAssets(target_apk_path, *target_apk, overlay_apk_path,
-                                          *overlay_apk, policy_bitmask, enforce_overlayable);
+  const auto idmap =
+      Idmap::FromApkAssets(*target_apk, *overlay_apk, policy_bitmask, enforce_overlayable);
   if (!idmap) {
     return error(idmap.GetErrorMessage());
   }
diff --git a/cmds/idmap2/include/idmap2/Idmap.h b/cmds/idmap2/include/idmap2/Idmap.h
index ebbb5ff..f2cae58 100644
--- a/cmds/idmap2/include/idmap2/Idmap.h
+++ b/cmds/idmap2/include/idmap2/Idmap.h
@@ -56,20 +56,14 @@
 #include "androidfw/ResourceTypes.h"
 #include "androidfw/StringPiece.h"
 #include "idmap2/Policies.h"
+#include "idmap2/ResourceMapping.h"
 
 namespace android::idmap2 {
 
 class Idmap;
 class Visitor;
 
-// use typedefs to let the compiler warn us about implicit casts
-typedef uint32_t ResourceId;  // 0xpptteeee
-typedef uint8_t PackageId;    // pp in 0xpptteeee
-typedef uint8_t TypeId;       // tt in 0xpptteeee
-typedef uint16_t EntryId;     // eeee in 0xpptteeee
-
 static constexpr const ResourceId kPadding = 0xffffffffu;
-
 static constexpr const EntryId kNoEntry = 0xffffu;
 
 // magic number: all idmap files start with this
@@ -155,7 +149,7 @@
     PackageId target_package_id_;
     uint16_t type_count_;
 
-    friend Idmap;
+    friend IdmapData;
     DISALLOW_COPY_AND_ASSIGN(Header);
   };
 
@@ -194,12 +188,15 @@
     uint16_t entry_offset_;
     std::vector<EntryId> entries_;
 
-    friend Idmap;
+    friend IdmapData;
     DISALLOW_COPY_AND_ASSIGN(TypeEntry);
   };
 
   static std::unique_ptr<const IdmapData> FromBinaryStream(std::istream& stream);
 
+  static Result<std::unique_ptr<const IdmapData>> FromResourceMapping(
+      const ResourceMapping& resource_mapping);
+
   inline const std::unique_ptr<const Header>& GetHeader() const {
     return header_;
   }
@@ -232,9 +229,7 @@
   // file is used; change this in the next version of idmap to use a named
   // package instead; also update FromApkAssets to take additional parameters:
   // the target and overlay package names
-  static Result<std::unique_ptr<const Idmap>> FromApkAssets(const std::string& target_apk_path,
-                                                            const ApkAssets& target_apk_assets,
-                                                            const std::string& overlay_apk_path,
+  static Result<std::unique_ptr<const Idmap>> FromApkAssets(const ApkAssets& target_apk_assets,
                                                             const ApkAssets& overlay_apk_assets,
                                                             const PolicyBitmask& fulfilled_policies,
                                                             bool enforce_overlayable);
diff --git a/cmds/idmap2/include/idmap2/ResourceMapping.h b/cmds/idmap2/include/idmap2/ResourceMapping.h
new file mode 100644
index 0000000..c3e1ef0
--- /dev/null
+++ b/cmds/idmap2/include/idmap2/ResourceMapping.h
@@ -0,0 +1,132 @@
+/*
+ * 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.
+ */
+
+#ifndef IDMAP2_INCLUDE_IDMAP2_RESOURCEMAPPING_H_
+#define IDMAP2_INCLUDE_IDMAP2_RESOURCEMAPPING_H_
+
+#include <map>
+#include <memory>
+#include <utility>
+
+#include "androidfw/ApkAssets.h"
+#include "idmap2/Policies.h"
+#include "idmap2/ResourceUtils.h"
+#include "idmap2/Result.h"
+#include "idmap2/XmlParser.h"
+
+using android::idmap2::utils::OverlayManifestInfo;
+
+namespace android::idmap2 {
+
+struct TargetValue {
+  typedef uint8_t DataType;
+  typedef uint32_t DataValue;
+  DataType data_type;
+  DataValue data_value;
+};
+
+using TargetResourceMap = std::map<ResourceId, TargetValue>;
+using OverlayResourceMap = std::map<ResourceId, ResourceId>;
+
+class ResourceMapping {
+ public:
+  // Creates a ResourceMapping using the target and overlay APKs. Setting enforce_overlayable to
+  // `false` disables all overlayable and policy enforcement: this is intended for backwards
+  // compatibility pre-Q and unit tests.
+  static Result<ResourceMapping> FromApkAssets(const ApkAssets& target_apk_assets,
+                                               const ApkAssets& overlay_apk_assets,
+                                               const OverlayManifestInfo& overlay_info,
+                                               const PolicyBitmask& fulfilled_policies,
+                                               bool enforce_overlayable);
+
+  // Retrieves the mapping of target resource id to overlay value.
+  inline TargetResourceMap GetTargetToOverlayMap() const {
+    return target_map_;
+  }
+
+  // Retrieves the mapping of overlay resource id to target resource id. This allows a reference to
+  // an overlay resource to appear as a reference to its corresponding target resource at runtime.
+  OverlayResourceMap GetOverlayToTargetMap() const;
+
+  // Retrieves the build-time package id of the target package.
+  inline uint32_t GetTargetPackageId() const {
+    return target_package_id_;
+  }
+
+  // Retrieves the build-time package id of the overlay package.
+  inline uint32_t GetOverlayPackageId() const {
+    return overlay_package_id_;
+  }
+
+  // Retrieves the offset that was added to the index of inline string overlay values so the indices
+  // do not collide with the indices of the overlay resource table string pool.
+  inline uint32_t GetStringPoolOffset() const {
+    return string_pool_offset_;
+  }
+
+  // Retrieves the raw string pool data from the xml referenced in android:resourcesMap.
+  inline const std::pair<const uint8_t*, uint32_t> GetStringPoolData() const {
+    return std::make_pair(string_pool_data_.get(), string_pool_data_length_);
+  }
+
+ private:
+  ResourceMapping() = default;
+
+  // Apps a mapping of target resource id to the type and value of the data that overlays the
+  // target resource. The data_type is the runtime format of the data value (see
+  // Res_value::dataType). If rewrite_overlay_reference is `true` then references to an overlay
+  // resource should appear as a reference to its corresponding target resource at runtime.
+  Result<Unit> AddMapping(ResourceId target_resource, TargetValue::DataType data_type,
+                          TargetValue::DataValue data_value, bool rewrite_overlay_reference);
+
+  // Removes the overlay value mapping for the target resource.
+  void RemoveMapping(ResourceId target_resource);
+
+  // Parses the mapping of target resources to overlay resources to generate a ResourceMapping.
+  static Result<ResourceMapping> CreateResourceMapping(const AssetManager2* target_am,
+                                                       const LoadedPackage* target_package,
+                                                       const LoadedPackage* overlay_package,
+                                                       size_t string_pool_offset,
+                                                       const XmlParser& overlay_parser);
+
+  // Generates a ResourceMapping that maps target resources to overlay resources by name. To overlay
+  // a target resource, a resource must exist in the overlay with the same type and entry name as
+  // the target resource.
+  static Result<ResourceMapping> CreateResourceMappingLegacy(const AssetManager2* target_am,
+                                                             const AssetManager2* overlay_am,
+                                                             const LoadedPackage* target_package,
+                                                             const LoadedPackage* overlay_package);
+
+  // Removes resources that do not pass policy or overlayable checks of the target package.
+  void FilterOverlayableResources(const AssetManager2* target_am,
+                                  const LoadedPackage* target_package,
+                                  const LoadedPackage* overlay_package,
+                                  const OverlayManifestInfo& overlay_info,
+                                  const PolicyBitmask& fulfilled_policies);
+
+  TargetResourceMap target_map_;
+  std::multimap<ResourceId, ResourceId> overlay_map_;
+
+  uint32_t target_package_id_ = 0;
+  uint32_t overlay_package_id_ = 0;
+  uint32_t string_pool_offset_ = 0;
+  uint32_t string_pool_data_length_ = 0;
+  std::unique_ptr<uint8_t[]> string_pool_data_ = nullptr;
+};
+
+}  // namespace android::idmap2
+
+#endif  // IDMAP2_INCLUDE_IDMAP2_RESOURCEMAPPING_H_
diff --git a/cmds/idmap2/include/idmap2/ResourceUtils.h b/cmds/idmap2/include/idmap2/ResourceUtils.h
index 9a0c2ab..abc2df1 100644
--- a/cmds/idmap2/include/idmap2/ResourceUtils.h
+++ b/cmds/idmap2/include/idmap2/ResourceUtils.h
@@ -21,17 +21,28 @@
 #include <string>
 
 #include "androidfw/AssetManager2.h"
-#include "idmap2/Idmap.h"
 #include "idmap2/Result.h"
 #include "idmap2/ZipFile.h"
 
-namespace android::idmap2::utils {
+namespace android::idmap2 {
+
+// use typedefs to let the compiler warn us about implicit casts
+typedef uint32_t ResourceId;  // 0xpptteeee
+typedef uint8_t PackageId;    // pp in 0xpptteeee
+typedef uint8_t TypeId;       // tt in 0xpptteeee
+typedef uint16_t EntryId;     // eeee in 0xpptteeee
+
+#define EXTRACT_TYPE(resid) ((0x00ff0000 & (resid)) >> 16)
+#define EXTRACT_ENTRY(resid) (0x0000ffff & (resid))
+
+namespace utils {
 
 struct OverlayManifestInfo {
   std::string target_package;  // NOLINT(misc-non-private-member-variables-in-classes)
   std::string target_name;     // NOLINT(misc-non-private-member-variables-in-classes)
   std::string requiredSystemPropertyName;  // NOLINT(misc-non-private-member-variables-in-classes)
   std::string requiredSystemPropertyValue;  // NOLINT(misc-non-private-member-variables-in-classes)
+  uint32_t resource_mapping;   // NOLINT(misc-non-private-member-variables-in-classes)
   bool is_static;              // NOLINT(misc-non-private-member-variables-in-classes)
   int priority = -1;           // NOLINT(misc-non-private-member-variables-in-classes)
 };
@@ -41,6 +52,8 @@
 
 Result<std::string> ResToTypeEntryName(const AssetManager2& am, ResourceId resid);
 
-}  // namespace android::idmap2::utils
+}  // namespace utils
+
+}  // namespace android::idmap2
 
 #endif  // IDMAP2_INCLUDE_IDMAP2_RESOURCEUTILS_H_
diff --git a/cmds/idmap2/include/idmap2/Xml.h b/cmds/idmap2/include/idmap2/Xml.h
deleted file mode 100644
index dd89dee..0000000
--- a/cmds/idmap2/include/idmap2/Xml.h
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef IDMAP2_INCLUDE_IDMAP2_XML_H_
-#define IDMAP2_INCLUDE_IDMAP2_XML_H_
-
-#include <map>
-#include <memory>
-#include <string>
-
-#include "android-base/macros.h"
-#include "androidfw/ResourceTypes.h"
-#include "utils/String16.h"
-
-namespace android::idmap2 {
-
-class Xml {
- public:
-  static std::unique_ptr<const Xml> Create(const uint8_t* data, size_t size, bool copyData = false);
-
-  std::unique_ptr<std::map<std::string, std::string>> FindTag(const std::string& name) const;
-
-  ~Xml();
-
- private:
-  Xml() {
-  }
-
-  mutable ResXMLTree xml_;
-
-  DISALLOW_COPY_AND_ASSIGN(Xml);
-};
-
-}  // namespace android::idmap2
-
-#endif  // IDMAP2_INCLUDE_IDMAP2_XML_H_
diff --git a/cmds/idmap2/include/idmap2/XmlParser.h b/cmds/idmap2/include/idmap2/XmlParser.h
new file mode 100644
index 0000000..972a6d7
--- /dev/null
+++ b/cmds/idmap2/include/idmap2/XmlParser.h
@@ -0,0 +1,147 @@
+/*
+ * 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.
+ */
+
+#ifndef IDMAP2_INCLUDE_IDMAP2_XMLPARSER_H_
+#define IDMAP2_INCLUDE_IDMAP2_XMLPARSER_H_
+
+#include <iostream>
+#include <map>
+#include <memory>
+#include <string>
+
+#include "Result.h"
+#include "android-base/macros.h"
+#include "androidfw/ResourceTypes.h"
+#include "utils/String16.h"
+
+namespace android::idmap2 {
+
+class XmlParser {
+ public:
+  using Event = ResXMLParser::event_code_t;
+  class iterator;
+
+  class Node {
+   public:
+    Event event() const;
+    std::string name() const;
+
+    Result<std::string> GetAttributeStringValue(const std::string& name) const;
+    Result<Res_value> GetAttributeValue(const std::string& name) const;
+
+    bool operator==(const Node& rhs) const;
+    bool operator!=(const Node& rhs) const;
+
+   private:
+    explicit Node(const ResXMLTree& tree);
+    Node(const ResXMLTree& tree, const ResXMLParser::ResXMLPosition& pos);
+
+    // Retrieves/Sets the position of the position of the xml parser in the xml tree.
+    ResXMLParser::ResXMLPosition get_position() const;
+    void set_position(const ResXMLParser::ResXMLPosition& pos);
+
+    // If `inner_child` is true, seek advances the parser to the first inner child of the current
+    // node. Otherwise, seek advances the parser to the following node. Returns false if there is
+    // no node to seek to.
+    bool Seek(bool inner_child);
+
+    ResXMLParser parser_;
+    friend iterator;
+  };
+
+  class iterator {
+   public:
+    iterator(const iterator& other) : iterator(other.tree_, other.iter_) {
+    }
+
+    inline iterator& operator=(const iterator& rhs) {
+      iter_.set_position(rhs.iter_.get_position());
+      return *this;
+    }
+
+    inline bool operator==(const iterator& rhs) const {
+      return iter_ == rhs.iter_;
+    }
+
+    inline bool operator!=(const iterator& rhs) const {
+      return !(*this == rhs);
+    }
+
+    inline iterator operator++() {
+      // Seek to the following xml node.
+      iter_.Seek(false /* inner_child */);
+      return *this;
+    }
+
+    iterator begin() const {
+      iterator child_it(*this);
+      // Seek to the first inner child of the current node.
+      child_it.iter_.Seek(true /* inner_child */);
+      return child_it;
+    }
+
+    iterator end() const {
+      iterator child_it = begin();
+      while (child_it.iter_.Seek(false /* inner_child */)) {
+        // Continue iterating until the end tag is found.
+      }
+
+      return child_it;
+    }
+
+    inline const Node operator*() {
+      return Node(tree_, iter_.get_position());
+    }
+
+    inline const Node* operator->() {
+      return &iter_;
+    }
+
+   private:
+    explicit iterator(const ResXMLTree& tree) : tree_(tree), iter_(Node(tree)) {
+    }
+    iterator(const ResXMLTree& tree, const Node& node)
+        : tree_(tree), iter_(Node(tree, node.get_position())) {
+    }
+
+    const ResXMLTree& tree_;
+    Node iter_;
+    friend XmlParser;
+  };
+
+  // Creates a new xml parser beginning at the first tag.
+  static Result<std::unique_ptr<const XmlParser>> Create(const void* data, size_t size,
+                                                         bool copy_data = false);
+  ~XmlParser();
+
+  inline iterator tree_iterator() const {
+    return iterator(tree_);
+  }
+
+  inline const ResStringPool& get_strings() const {
+    return tree_.getStrings();
+  }
+
+ private:
+  XmlParser() = default;
+  mutable ResXMLTree tree_;
+
+  DISALLOW_COPY_AND_ASSIGN(XmlParser);
+};
+
+}  // namespace android::idmap2
+
+#endif  // IDMAP2_INCLUDE_IDMAP2_XMLPARSER_H_
diff --git a/cmds/idmap2/libidmap2/Idmap.cpp b/cmds/idmap2/libidmap2/Idmap.cpp
index 4649675..389ade5 100644
--- a/cmds/idmap2/libidmap2/Idmap.cpp
+++ b/cmds/idmap2/libidmap2/Idmap.cpp
@@ -30,6 +30,7 @@
 #include "android-base/macros.h"
 #include "android-base/stringprintf.h"
 #include "androidfw/AssetManager2.h"
+#include "idmap2/ResourceMapping.h"
 #include "idmap2/ResourceUtils.h"
 #include "idmap2/Result.h"
 #include "idmap2/SysTrace.h"
@@ -41,10 +42,6 @@
 
 namespace {
 
-#define EXTRACT_TYPE(resid) ((0x00ff0000 & (resid)) >> 16)
-
-#define EXTRACT_ENTRY(resid) (0x0000ffff & (resid))
-
 class MatchingResources {
  public:
   void Add(ResourceId target_resid, ResourceId overlay_resid) {
@@ -97,28 +94,6 @@
   return true;
 }
 
-ResourceId NameToResid(const AssetManager2& am, const std::string& name) {
-  return am.GetResourceId(name);
-}
-
-// TODO(martenkongstad): scan for package name instead of assuming package at index 0
-//
-// idmap version 0x01 naively assumes that the package to use is always the first ResTable_package
-// in the resources.arsc blob. In most cases, there is only a single ResTable_package anyway, so
-// this assumption tends to work out. That said, the correct thing to do is to scan
-// resources.arsc for a package with a given name as read from the package manifest instead of
-// relying on a hard-coded index. This however requires storing the package name in the idmap
-// header, which in turn requires incrementing the idmap version. Because the initial version of
-// idmap2 is compatible with idmap, this will have to wait for now.
-const LoadedPackage* GetPackageAtIndex0(const LoadedArsc& loaded_arsc) {
-  const std::vector<std::unique_ptr<const LoadedPackage>>& packages = loaded_arsc.GetPackages();
-  if (packages.empty()) {
-    return nullptr;
-  }
-  int id = packages[0]->GetPackageId();
-  return loaded_arsc.GetPackageById(id);
-}
-
 Result<uint32_t> GetCrc(const ZipFile& zip) {
   const Result<uint32_t> a = zip.Crc("resources.arsc");
   const Result<uint32_t> b = zip.Crc("AndroidManifest.xml");
@@ -266,95 +241,57 @@
   return {std::move(idmap)};
 }
 
-std::string ConcatPolicies(const std::vector<std::string>& policies) {
-  std::string message;
-  for (const std::string& policy : policies) {
-    if (!message.empty()) {
-      message.append("|");
+Result<std::unique_ptr<const IdmapData>> IdmapData::FromResourceMapping(
+    const ResourceMapping& resource_mapping) {
+  if (resource_mapping.GetTargetToOverlayMap().empty()) {
+    return Error("no resources were overlaid");
+  }
+
+  MatchingResources matching_resources;
+  for (const auto mapping : resource_mapping.GetTargetToOverlayMap()) {
+    if (mapping.second.data_type != Res_value::TYPE_REFERENCE) {
+      // The idmap format must change to support non-references.
+      continue;
     }
-    message.append(policy);
+
+    matching_resources.Add(mapping.first, mapping.second.data_value);
   }
 
-  return message;
+  // encode idmap data
+  std::unique_ptr<IdmapData> data(new IdmapData());
+  const auto types_end = matching_resources.Map().cend();
+  for (auto ti = matching_resources.Map().cbegin(); ti != types_end; ++ti) {
+    auto ei = ti->second.cbegin();
+    std::unique_ptr<IdmapData::TypeEntry> type(new IdmapData::TypeEntry());
+    type->target_type_id_ = EXTRACT_TYPE(ei->first);
+    type->overlay_type_id_ = EXTRACT_TYPE(ei->second);
+    type->entry_offset_ = EXTRACT_ENTRY(ei->first);
+    EntryId last_target_entry = kNoEntry;
+    for (; ei != ti->second.cend(); ++ei) {
+      if (last_target_entry != kNoEntry) {
+        int count = EXTRACT_ENTRY(ei->first) - last_target_entry - 1;
+        type->entries_.insert(type->entries_.end(), count, kNoEntry);
+      }
+      type->entries_.push_back(EXTRACT_ENTRY(ei->second));
+      last_target_entry = EXTRACT_ENTRY(ei->first);
+    }
+    data->type_entries_.push_back(std::move(type));
+  }
+
+  std::unique_ptr<IdmapData::Header> data_header(new IdmapData::Header());
+  data_header->target_package_id_ = resource_mapping.GetTargetPackageId();
+  data_header->type_count_ = data->type_entries_.size();
+  data->header_ = std::move(data_header);
+  return {std::move(data)};
 }
 
-Result<Unit> CheckOverlayable(const LoadedPackage& target_package,
-                              const utils::OverlayManifestInfo& overlay_info,
-                              const PolicyBitmask& fulfilled_policies, const ResourceId& resid) {
-  static constexpr const PolicyBitmask sDefaultPolicies =
-      PolicyFlags::POLICY_ODM_PARTITION | PolicyFlags::POLICY_OEM_PARTITION |
-      PolicyFlags::POLICY_SYSTEM_PARTITION | PolicyFlags::POLICY_VENDOR_PARTITION |
-      PolicyFlags::POLICY_PRODUCT_PARTITION | PolicyFlags::POLICY_SIGNATURE;
-
-  // If the resource does not have an overlayable definition, allow the resource to be overlaid if
-  // the overlay is preinstalled or signed with the same signature as the target.
-  if (!target_package.DefinesOverlayable()) {
-    return (sDefaultPolicies & fulfilled_policies) != 0
-               ? Result<Unit>({})
-               : Error(
-                     "overlay must be preinstalled or signed with the same signature as the "
-                     "target");
-  }
-
-  const OverlayableInfo* overlayable_info = target_package.GetOverlayableInfo(resid);
-  if (overlayable_info == nullptr) {
-    // Do not allow non-overlayable resources to be overlaid.
-    return Error("resource has no overlayable declaration");
-  }
-
-  if (overlay_info.target_name != overlayable_info->name) {
-    // If the overlay supplies a target overlayable name, the resource must belong to the
-    // overlayable defined with the specified name to be overlaid.
-    return Error("<overlay> android:targetName '%s' does not match overlayable name '%s'",
-                 overlay_info.target_name.c_str(), overlayable_info->name.c_str());
-  }
-
-  // Enforce policy restrictions if the resource is declared as overlayable.
-  if ((overlayable_info->policy_flags & fulfilled_policies) == 0) {
-    return Error("overlay with policies '%s' does not fulfill any overlayable policies '%s'",
-                 ConcatPolicies(BitmaskToPolicies(fulfilled_policies)).c_str(),
-                 ConcatPolicies(BitmaskToPolicies(overlayable_info->policy_flags)).c_str());
-  }
-
-  return Result<Unit>({});
-}
-
-Result<std::unique_ptr<const Idmap>> Idmap::FromApkAssets(const std::string& target_apk_path,
-                                                          const ApkAssets& target_apk_assets,
-                                                          const std::string& overlay_apk_path,
+Result<std::unique_ptr<const Idmap>> Idmap::FromApkAssets(const ApkAssets& target_apk_assets,
                                                           const ApkAssets& overlay_apk_assets,
                                                           const PolicyBitmask& fulfilled_policies,
                                                           bool enforce_overlayable) {
   SYSTRACE << "Idmap::FromApkAssets";
-  AssetManager2 target_asset_manager;
-  if (!target_asset_manager.SetApkAssets({&target_apk_assets}, true, false)) {
-    return Error("failed to create target asset manager");
-  }
-
-  AssetManager2 overlay_asset_manager;
-  if (!overlay_asset_manager.SetApkAssets({&overlay_apk_assets}, true, false)) {
-    return Error("failed to create overlay asset manager");
-  }
-
-  const LoadedArsc* target_arsc = target_apk_assets.GetLoadedArsc();
-  if (target_arsc == nullptr) {
-    return Error("failed to load target resources.arsc");
-  }
-
-  const LoadedArsc* overlay_arsc = overlay_apk_assets.GetLoadedArsc();
-  if (overlay_arsc == nullptr) {
-    return Error("failed to load overlay resources.arsc");
-  }
-
-  const LoadedPackage* target_pkg = GetPackageAtIndex0(*target_arsc);
-  if (target_pkg == nullptr) {
-    return Error("failed to load target package from resources.arsc");
-  }
-
-  const LoadedPackage* overlay_pkg = GetPackageAtIndex0(*overlay_arsc);
-  if (overlay_pkg == nullptr) {
-    return Error("failed to load overlay package from resources.arsc");
-  }
+  const std::string& target_apk_path = target_apk_assets.GetPath();
+  const std::string& overlay_apk_path = overlay_apk_assets.GetPath();
 
   const std::unique_ptr<const ZipFile> target_zip = ZipFile::Open(target_apk_path);
   if (!target_zip) {
@@ -366,11 +303,6 @@
     return Error("failed to open overlay as zip");
   }
 
-  auto overlay_info = utils::ExtractOverlayManifestInfo(overlay_apk_path);
-  if (!overlay_info) {
-    return overlay_info.GetError();
-  }
-
   std::unique_ptr<IdmapHeader> header(new IdmapHeader());
   header->magic_ = kIdmapMagic;
   header->version_ = kIdmapCurrentVersion;
@@ -395,7 +327,7 @@
   memcpy(header->target_path_, target_apk_path.data(), target_apk_path.size());
 
   if (overlay_apk_path.size() > sizeof(header->overlay_path_)) {
-    return Error("overlay apk path \"%s\" longer than maximum size %zu", target_apk_path.c_str(),
+    return Error("overlay apk path \"%s\" longer than maximum size %zu", overlay_apk_path.c_str(),
                  sizeof(header->target_path_));
   }
   memset(header->overlay_path_, 0, sizeof(header->overlay_path_));
@@ -404,70 +336,24 @@
   std::unique_ptr<Idmap> idmap(new Idmap());
   idmap->header_ = std::move(header);
 
-  // find the resources that exist in both packages
-  MatchingResources matching_resources;
-  const auto end = overlay_pkg->end();
-  for (auto iter = overlay_pkg->begin(); iter != end; ++iter) {
-    const ResourceId overlay_resid = *iter;
-    Result<std::string> name = utils::ResToTypeEntryName(overlay_asset_manager, overlay_resid);
-    if (!name) {
-      continue;
-    }
-    // prepend "<package>:" to turn name into "<package>:<type>/<name>"
-    const std::string full_name =
-        base::StringPrintf("%s:%s", target_pkg->GetPackageName().c_str(), name->c_str());
-    const ResourceId target_resid = NameToResid(target_asset_manager, full_name);
-    if (target_resid == 0) {
-      continue;
-    }
-
-    if (enforce_overlayable) {
-      Result<Unit> success =
-          CheckOverlayable(*target_pkg, *overlay_info, fulfilled_policies, target_resid);
-      if (!success) {
-        LOG(WARNING) << "overlay \"" << overlay_apk_path
-                     << "\" is not allowed to overlay resource \"" << full_name
-                     << "\": " << success.GetErrorMessage();
-        continue;
-      }
-    }
-
-    matching_resources.Add(target_resid, overlay_resid);
+  auto overlay_info = utils::ExtractOverlayManifestInfo(overlay_apk_path);
+  if (!overlay_info) {
+    return overlay_info.GetError();
   }
 
-  if (matching_resources.Map().empty()) {
-    return Error("overlay \"%s\" does not successfully overlay any resource",
-                 overlay_apk_path.c_str());
+  auto resource_mapping =
+      ResourceMapping::FromApkAssets(target_apk_assets, overlay_apk_assets, *overlay_info,
+                                     fulfilled_policies, enforce_overlayable);
+  if (!resource_mapping) {
+    return resource_mapping.GetError();
   }
 
-  // encode idmap data
-  std::unique_ptr<IdmapData> data(new IdmapData());
-  const auto types_end = matching_resources.Map().cend();
-  for (auto ti = matching_resources.Map().cbegin(); ti != types_end; ++ti) {
-    auto ei = ti->second.cbegin();
-    std::unique_ptr<IdmapData::TypeEntry> type(new IdmapData::TypeEntry());
-    type->target_type_id_ = EXTRACT_TYPE(ei->first);
-    type->overlay_type_id_ = EXTRACT_TYPE(ei->second);
-    type->entry_offset_ = EXTRACT_ENTRY(ei->first);
-    EntryId last_target_entry = kNoEntry;
-    for (; ei != ti->second.cend(); ++ei) {
-      if (last_target_entry != kNoEntry) {
-        int count = EXTRACT_ENTRY(ei->first) - last_target_entry - 1;
-        type->entries_.insert(type->entries_.end(), count, kNoEntry);
-      }
-      type->entries_.push_back(EXTRACT_ENTRY(ei->second));
-      last_target_entry = EXTRACT_ENTRY(ei->first);
-    }
-    data->type_entries_.push_back(std::move(type));
+  auto idmap_data = IdmapData::FromResourceMapping(*resource_mapping);
+  if (!idmap_data) {
+    return idmap_data.GetError();
   }
 
-  std::unique_ptr<IdmapData::Header> data_header(new IdmapData::Header());
-  data_header->target_package_id_ = target_pkg->GetPackageId();
-  data_header->type_count_ = data->type_entries_.size();
-  data->header_ = std::move(data_header);
-
-  idmap->data_.push_back(std::move(data));
-
+  idmap->data_.push_back(std::move(*idmap_data));
   return {std::move(idmap)};
 }
 
diff --git a/cmds/idmap2/libidmap2/ResourceMapping.cpp b/cmds/idmap2/libidmap2/ResourceMapping.cpp
new file mode 100644
index 0000000..95ae626
--- /dev/null
+++ b/cmds/idmap2/libidmap2/ResourceMapping.cpp
@@ -0,0 +1,411 @@
+/*
+ * 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.
+ */
+
+#include "idmap2/ResourceMapping.h"
+
+#include <map>
+#include <memory>
+#include <set>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "android-base/stringprintf.h"
+#include "idmap2/ResourceUtils.h"
+
+using android::base::StringPrintf;
+using android::idmap2::utils::ResToTypeEntryName;
+
+namespace android::idmap2 {
+
+namespace {
+
+#define EXTRACT_PACKAGE(resid) ((0xff000000 & (resid)) >> 24)
+
+std::string ConcatPolicies(const std::vector<std::string>& policies) {
+  std::string message;
+  for (const std::string& policy : policies) {
+    if (!message.empty()) {
+      message.append("|");
+    }
+    message.append(policy);
+  }
+
+  return message;
+}
+
+Result<Unit> CheckOverlayable(const LoadedPackage& target_package,
+                              const OverlayManifestInfo& overlay_info,
+                              const PolicyBitmask& fulfilled_policies,
+                              const ResourceId& target_resource) {
+  static constexpr const PolicyBitmask sDefaultPolicies =
+      PolicyFlags::POLICY_ODM_PARTITION | PolicyFlags::POLICY_OEM_PARTITION |
+      PolicyFlags::POLICY_SYSTEM_PARTITION | PolicyFlags::POLICY_VENDOR_PARTITION |
+      PolicyFlags::POLICY_PRODUCT_PARTITION | PolicyFlags::POLICY_SIGNATURE;
+
+  // If the resource does not have an overlayable definition, allow the resource to be overlaid if
+  // the overlay is preinstalled or signed with the same signature as the target.
+  if (!target_package.DefinesOverlayable()) {
+    return (sDefaultPolicies & fulfilled_policies) != 0
+               ? Result<Unit>({})
+               : Error(
+                     "overlay must be preinstalled or signed with the same signature as the "
+                     "target");
+  }
+
+  const OverlayableInfo* overlayable_info = target_package.GetOverlayableInfo(target_resource);
+  if (overlayable_info == nullptr) {
+    // Do not allow non-overlayable resources to be overlaid.
+    return Error("target resource has no overlayable declaration");
+  }
+
+  if (overlay_info.target_name != overlayable_info->name) {
+    // If the overlay supplies a target overlayable name, the resource must belong to the
+    // overlayable defined with the specified name to be overlaid.
+    return Error(R"(<overlay> android:targetName "%s" does not match overlayable name "%s")",
+                 overlay_info.target_name.c_str(), overlayable_info->name.c_str());
+  }
+
+  // Enforce policy restrictions if the resource is declared as overlayable.
+  if ((overlayable_info->policy_flags & fulfilled_policies) == 0) {
+    return Error(R"(overlay with policies "%s" does not fulfill any overlayable policies "%s")",
+                 ConcatPolicies(BitmaskToPolicies(fulfilled_policies)).c_str(),
+                 ConcatPolicies(BitmaskToPolicies(overlayable_info->policy_flags)).c_str());
+  }
+
+  return Result<Unit>({});
+}
+
+// TODO(martenkongstad): scan for package name instead of assuming package at index 0
+//
+// idmap version 0x01 naively assumes that the package to use is always the first ResTable_package
+// in the resources.arsc blob. In most cases, there is only a single ResTable_package anyway, so
+// this assumption tends to work out. That said, the correct thing to do is to scan
+// resources.arsc for a package with a given name as read from the package manifest instead of
+// relying on a hard-coded index. This however requires storing the package name in the idmap
+// header, which in turn requires incrementing the idmap version. Because the initial version of
+// idmap2 is compatible with idmap, this will have to wait for now.
+const LoadedPackage* GetPackageAtIndex0(const LoadedArsc& loaded_arsc) {
+  const std::vector<std::unique_ptr<const LoadedPackage>>& packages = loaded_arsc.GetPackages();
+  if (packages.empty()) {
+    return nullptr;
+  }
+  int id = packages[0]->GetPackageId();
+  return loaded_arsc.GetPackageById(id);
+}
+
+Result<std::unique_ptr<Asset>> OpenNonAssetFromResource(const ResourceId& resource_id,
+                                                        const AssetManager2& asset_manager) {
+  Res_value value{};
+  ResTable_config selected_config{};
+  uint32_t flags;
+  auto cookie =
+      asset_manager.GetResource(resource_id, /* may_be_bag */ false,
+                                /* density_override */ 0U, &value, &selected_config, &flags);
+  if (cookie == kInvalidCookie) {
+    return Error("failed to find resource for id 0x%08x", resource_id);
+  }
+
+  if (value.dataType != Res_value::TYPE_STRING) {
+    return Error("resource for is 0x%08x is not a file", resource_id);
+  }
+
+  auto string_pool = asset_manager.GetStringPoolForCookie(cookie);
+  size_t len;
+  auto file_path16 = string_pool->stringAt(value.data, &len);
+  if (file_path16 == nullptr) {
+    return Error("failed to find string for index %d", value.data);
+  }
+
+  // Load the overlay resource mappings from the file specified using android:resourcesMap.
+  auto file_path = String8(String16(file_path16));
+  auto asset = asset_manager.OpenNonAsset(file_path.c_str(), Asset::AccessMode::ACCESS_BUFFER);
+  if (asset == nullptr) {
+    return Error("file \"%s\" not found", file_path.c_str());
+  }
+
+  return asset;
+}
+
+}  // namespace
+
+Result<ResourceMapping> ResourceMapping::CreateResourceMapping(const AssetManager2* target_am,
+                                                               const LoadedPackage* target_package,
+                                                               const LoadedPackage* overlay_package,
+                                                               size_t string_pool_offset,
+                                                               const XmlParser& overlay_parser) {
+  ResourceMapping resource_mapping;
+  auto root_it = overlay_parser.tree_iterator();
+  if (root_it->event() != XmlParser::Event::START_TAG || root_it->name() != "overlay") {
+    return Error("root element is not <overlay> tag");
+  }
+
+  const uint8_t overlay_package_id = overlay_package->GetPackageId();
+  auto overlay_it_end = root_it.end();
+  for (auto overlay_it = root_it.begin(); overlay_it != overlay_it_end; ++overlay_it) {
+    if (overlay_it->event() == XmlParser::Event::BAD_DOCUMENT) {
+      return Error("failed to parse overlay xml document");
+    }
+
+    if (overlay_it->event() != XmlParser::Event::START_TAG) {
+      continue;
+    }
+
+    if (overlay_it->name() != "item") {
+      return Error("unexpected tag <%s> in <overlay>", overlay_it->name().c_str());
+    }
+
+    Result<std::string> target_resource = overlay_it->GetAttributeStringValue("target");
+    if (!target_resource) {
+      return Error(R"(<item> tag missing expected attribute "target")");
+    }
+
+    Result<android::Res_value> overlay_resource = overlay_it->GetAttributeValue("value");
+    if (!overlay_resource) {
+      return Error(R"(<item> tag missing expected attribute "value")");
+    }
+
+    ResourceId target_id =
+        target_am->GetResourceId(*target_resource, "", target_package->GetPackageName());
+    if (target_id == 0U) {
+      LOG(WARNING) << "failed to find resource \"" << *target_resource << "\" in target resources";
+      continue;
+    }
+
+    if (overlay_resource->dataType == Res_value::TYPE_STRING) {
+      overlay_resource->data += string_pool_offset;
+    }
+
+    // Only rewrite resources defined within the overlay package to their corresponding target
+    // resource ids at runtime.
+    bool rewrite_overlay_reference =
+        (overlay_resource->dataType == Res_value::TYPE_REFERENCE)
+            ? overlay_package_id == EXTRACT_PACKAGE(overlay_resource->data)
+            : false;
+
+    resource_mapping.AddMapping(target_id, overlay_resource->dataType, overlay_resource->data,
+                                rewrite_overlay_reference);
+  }
+
+  return resource_mapping;
+}
+
+Result<ResourceMapping> ResourceMapping::CreateResourceMappingLegacy(
+    const AssetManager2* target_am, const AssetManager2* overlay_am,
+    const LoadedPackage* target_package, const LoadedPackage* overlay_package) {
+  ResourceMapping resource_mapping;
+  const auto end = overlay_package->end();
+  for (auto iter = overlay_package->begin(); iter != end; ++iter) {
+    const ResourceId overlay_resid = *iter;
+    Result<std::string> name = utils::ResToTypeEntryName(*overlay_am, overlay_resid);
+    if (!name) {
+      continue;
+    }
+
+    // Find the resource with the same type and entry name within the target package.
+    const std::string full_name =
+        base::StringPrintf("%s:%s", target_package->GetPackageName().c_str(), name->c_str());
+    const ResourceId target_resource = target_am->GetResourceId(full_name);
+    if (target_resource == 0U) {
+      continue;
+    }
+
+    resource_mapping.AddMapping(target_resource, Res_value::TYPE_REFERENCE, overlay_resid,
+                                /* rewrite_overlay_reference */ true);
+  }
+
+  return resource_mapping;
+}
+
+void ResourceMapping::FilterOverlayableResources(const AssetManager2* target_am,
+                                                 const LoadedPackage* target_package,
+                                                 const LoadedPackage* overlay_package,
+                                                 const OverlayManifestInfo& overlay_info,
+                                                 const PolicyBitmask& fulfilled_policies) {
+  std::set<ResourceId> remove_ids;
+  for (const auto& target_map : target_map_) {
+    const ResourceId target_resid = target_map.first;
+    Result<Unit> success =
+        CheckOverlayable(*target_package, overlay_info, fulfilled_policies, target_resid);
+    if (success) {
+      continue;
+    }
+
+    // Attempting to overlay a resource that is not allowed to be overlaid is treated as a
+    // warning.
+    Result<std::string> name = utils::ResToTypeEntryName(*target_am, target_resid);
+    if (!name) {
+      name = StringPrintf("0x%08x", target_resid);
+    }
+
+    LOG(WARNING) << "overlay \"" << overlay_package->GetPackageName()
+                 << "\" is not allowed to overlay resource \"" << *name
+                 << "\" in target: " << success.GetErrorMessage();
+
+    remove_ids.insert(target_resid);
+  }
+
+  for (const ResourceId target_resid : remove_ids) {
+    RemoveMapping(target_resid);
+  }
+}
+
+Result<ResourceMapping> ResourceMapping::FromApkAssets(const ApkAssets& target_apk_assets,
+                                                       const ApkAssets& overlay_apk_assets,
+                                                       const OverlayManifestInfo& overlay_info,
+                                                       const PolicyBitmask& fulfilled_policies,
+                                                       bool enforce_overlayable) {
+  AssetManager2 target_asset_manager;
+  if (!target_asset_manager.SetApkAssets({&target_apk_assets}, true /* invalidate_caches */,
+                                         false /* filter_incompatible_configs*/)) {
+    return Error("failed to create target asset manager");
+  }
+
+  AssetManager2 overlay_asset_manager;
+  if (!overlay_asset_manager.SetApkAssets({&overlay_apk_assets}, true /* invalidate_caches */,
+                                          false /* filter_incompatible_configs */)) {
+    return Error("failed to create overlay asset manager");
+  }
+
+  const LoadedArsc* target_arsc = target_apk_assets.GetLoadedArsc();
+  if (target_arsc == nullptr) {
+    return Error("failed to load target resources.arsc");
+  }
+
+  const LoadedArsc* overlay_arsc = overlay_apk_assets.GetLoadedArsc();
+  if (overlay_arsc == nullptr) {
+    return Error("failed to load overlay resources.arsc");
+  }
+
+  const LoadedPackage* target_pkg = GetPackageAtIndex0(*target_arsc);
+  if (target_pkg == nullptr) {
+    return Error("failed to load target package from resources.arsc");
+  }
+
+  const LoadedPackage* overlay_pkg = GetPackageAtIndex0(*overlay_arsc);
+  if (overlay_pkg == nullptr) {
+    return Error("failed to load overlay package from resources.arsc");
+  }
+
+  size_t string_pool_data_length = 0U;
+  size_t string_pool_offset = 0U;
+  std::unique_ptr<uint8_t[]> string_pool_data;
+  Result<ResourceMapping> resource_mapping = {{}};
+  if (overlay_info.resource_mapping != 0U) {
+    // Load the overlay resource mappings from the file specified using android:resourcesMap.
+    auto asset = OpenNonAssetFromResource(overlay_info.resource_mapping, overlay_asset_manager);
+    if (!asset) {
+      return Error("failed opening xml for android:resourcesMap: %s",
+                   asset.GetErrorMessage().c_str());
+    }
+
+    auto parser =
+        XmlParser::Create((*asset)->getBuffer(true /* wordAligned*/), (*asset)->getLength());
+    if (!parser) {
+      return Error("failed opening ResXMLTree");
+    }
+
+    // Copy the xml string pool data before the parse goes out of scope.
+    auto& string_pool = (*parser)->get_strings();
+    string_pool_data_length = string_pool.bytes();
+    string_pool_data.reset(new uint8_t[string_pool_data_length]);
+    memcpy(string_pool_data.get(), string_pool.data(), string_pool_data_length);
+
+    // Offset string indices by the size of the overlay resource table string pool.
+    string_pool_offset = overlay_arsc->GetStringPool()->size();
+
+    resource_mapping = CreateResourceMapping(&target_asset_manager, target_pkg, overlay_pkg,
+                                             string_pool_offset, *(*parser));
+  } else {
+    // If no file is specified using android:resourcesMap, it is assumed that the overlay only
+    // defines resources intended to override target resources of the same type and name.
+    resource_mapping = CreateResourceMappingLegacy(&target_asset_manager, &overlay_asset_manager,
+                                                   target_pkg, overlay_pkg);
+  }
+
+  if (!resource_mapping) {
+    return resource_mapping.GetError();
+  }
+
+  if (enforce_overlayable) {
+    // Filter out resources the overlay is not allowed to override.
+    (*resource_mapping)
+        .FilterOverlayableResources(&target_asset_manager, target_pkg, overlay_pkg, overlay_info,
+                                    fulfilled_policies);
+  }
+
+  resource_mapping->target_package_id_ = target_pkg->GetPackageId();
+  resource_mapping->overlay_package_id_ = overlay_pkg->GetPackageId();
+  resource_mapping->string_pool_offset_ = string_pool_offset;
+  resource_mapping->string_pool_data_ = std::move(string_pool_data);
+  resource_mapping->string_pool_data_length_ = string_pool_data_length;
+  return std::move(*resource_mapping);
+}
+
+OverlayResourceMap ResourceMapping::GetOverlayToTargetMap() const {
+  // An overlay resource can override multiple target resources at once. Rewrite the overlay
+  // resource as the first target resource it overrides.
+  OverlayResourceMap map;
+  for (const auto& mappings : overlay_map_) {
+    map.insert(std::make_pair(mappings.first, mappings.second));
+  }
+  return map;
+}
+
+Result<Unit> ResourceMapping::AddMapping(ResourceId target_resource,
+                                         TargetValue::DataType data_type,
+                                         TargetValue::DataValue data_value,
+                                         bool rewrite_overlay_reference) {
+  if (target_map_.find(target_resource) != target_map_.end()) {
+    return Error(R"(target resource id "0x%08x" mapped to multiple values)", target_resource);
+  }
+
+  // TODO(141485591): Ensure that the overlay type is compatible with the target type. If the
+  // runtime types are not compatible, it could cause runtime crashes when the resource is resolved.
+
+  target_map_.insert(std::make_pair(target_resource, TargetValue{data_type, data_value}));
+
+  if (rewrite_overlay_reference && data_type == Res_value::TYPE_REFERENCE) {
+    overlay_map_.insert(std::make_pair(data_value, target_resource));
+  }
+
+  return Result<Unit>({});
+}
+
+void ResourceMapping::RemoveMapping(ResourceId target_resource) {
+  auto target_iter = target_map_.find(target_resource);
+  if (target_iter == target_map_.end()) {
+    return;
+  }
+
+  const TargetValue value = target_iter->second;
+  target_map_.erase(target_iter);
+
+  // Remove rewriting of overlay resource id to target resource id.
+  if (value.data_type != Res_value::TYPE_REFERENCE) {
+    return;
+  }
+
+  auto overlay_iter = overlay_map_.equal_range(value.data_value);
+  for (auto i = overlay_iter.first; i != overlay_iter.second; ++i) {
+    if (i->second == target_resource) {
+      overlay_map_.erase(i);
+      return;
+    }
+  }
+}
+
+}  // namespace android::idmap2
diff --git a/cmds/idmap2/libidmap2/ResourceUtils.cpp b/cmds/idmap2/libidmap2/ResourceUtils.cpp
index dce83e3..9d32692 100644
--- a/cmds/idmap2/libidmap2/ResourceUtils.cpp
+++ b/cmds/idmap2/libidmap2/ResourceUtils.cpp
@@ -22,18 +22,18 @@
 #include "androidfw/StringPiece.h"
 #include "androidfw/Util.h"
 #include "idmap2/Result.h"
-#include "idmap2/Xml.h"
+#include "idmap2/XmlParser.h"
 #include "idmap2/ZipFile.h"
 
 using android::StringPiece16;
 using android::idmap2::Result;
-using android::idmap2::Xml;
+using android::idmap2::XmlParser;
 using android::idmap2::ZipFile;
 using android::util::Utf16ToUtf8;
 
 namespace android::idmap2::utils {
 
-Result<std::string> ResToTypeEntryName(const AssetManager2& am, ResourceId resid) {
+Result<std::string> ResToTypeEntryName(const AssetManager2& am, uint32_t resid) {
   AssetManager2::ResourceName name;
   if (!am.GetResourceName(resid, &name)) {
     return Error("no resource 0x%08x in asset manager", resid);
@@ -65,52 +65,72 @@
     return Error("failed to uncompress AndroidManifest.xml from %s", path.c_str());
   }
 
-  std::unique_ptr<const Xml> xml = Xml::Create(entry->buf, entry->size);
+  Result<std::unique_ptr<const XmlParser>> xml = XmlParser::Create(entry->buf, entry->size);
   if (!xml) {
     return Error("failed to parse AndroidManifest.xml from %s", path.c_str());
   }
 
+  auto manifest_it = (*xml)->tree_iterator();
+  if (manifest_it->event() != XmlParser::Event::START_TAG || manifest_it->name() != "manifest") {
+    return Error("root element tag is not <manifest> in AndroidManifest.xml of %s", path.c_str());
+  }
+
+  auto overlay_it = std::find_if(manifest_it.begin(), manifest_it.end(), [](const auto& it) {
+    return it.event() == XmlParser::Event::START_TAG && it.name() == "overlay";
+  });
+
   OverlayManifestInfo info{};
-  const auto tag = xml->FindTag("overlay");
-  if (!tag) {
-    if (assert_overlay) {
-      return Error("<overlay> missing from AndroidManifest.xml of %s", path.c_str());
+  if (overlay_it == manifest_it.end()) {
+    if (!assert_overlay) {
+      return info;
     }
-    return info;
+    return Error("<overlay> missing from AndroidManifest.xml of %s", path.c_str());
   }
 
-  auto iter = tag->find("targetPackage");
-  if (iter == tag->end()) {
-    if (assert_overlay) {
-      return Error("android:targetPackage missing from <overlay> of %s", path.c_str());
-    }
+  if (auto result_str = overlay_it->GetAttributeStringValue("targetPackage")) {
+    info.target_package = *result_str;
   } else {
-    info.target_package = iter->second;
+    return Error("android:targetPackage missing from <overlay> of %s: %s", path.c_str(),
+                 result_str.GetErrorMessage().c_str());
   }
 
-  iter = tag->find("targetName");
-  if (iter != tag->end()) {
-    info.target_name = iter->second;
+  if (auto result_str = overlay_it->GetAttributeStringValue("targetName")) {
+    info.target_name = *result_str;
   }
 
-  iter = tag->find("isStatic");
-  if (iter != tag->end()) {
-    info.is_static = std::stoul(iter->second) != 0U;
+  if (auto result_value = overlay_it->GetAttributeValue("resourcesMap")) {
+    if ((*result_value).dataType == Res_value::TYPE_REFERENCE) {
+      info.resource_mapping = (*result_value).data;
+    } else {
+      return Error("android:resourcesMap is not a reference in AndroidManifest.xml of %s",
+                   path.c_str());
+    }
   }
 
-  iter = tag->find("priority");
-  if (iter != tag->end()) {
-    info.priority = std::stoi(iter->second);
+  if (auto result_value = overlay_it->GetAttributeValue("isStatic")) {
+    if ((*result_value).dataType >= Res_value::TYPE_FIRST_INT &&
+        (*result_value).dataType <= Res_value::TYPE_LAST_INT) {
+      info.is_static = (*result_value).data != 0U;
+    } else {
+      return Error("android:isStatic is not a boolean in AndroidManifest.xml of %s", path.c_str());
+    }
   }
 
-  iter = tag->find("requiredSystemPropertyName");
-  if (iter != tag->end()) {
-    info.requiredSystemPropertyName = iter->second;
+  if (auto result_value = overlay_it->GetAttributeValue("priority")) {
+    if ((*result_value).dataType >= Res_value::TYPE_FIRST_INT &&
+        (*result_value).dataType <= Res_value::TYPE_LAST_INT) {
+      info.priority = (*result_value).data;
+    } else {
+      return Error("android:priority is not an integer in AndroidManifest.xml of %s", path.c_str());
+    }
   }
 
-  iter = tag->find("requiredSystemPropertyValue");
-  if (iter != tag->end()) {
-    info.requiredSystemPropertyValue = iter->second;
+  if (auto result_str = overlay_it->GetAttributeStringValue("requiredSystemPropertyName")) {
+    info.requiredSystemPropertyName = *result_str;
+  }
+
+   if (auto result_str = overlay_it->GetAttributeStringValue("requiredSystemPropertyValue")) {
+    info.requiredSystemPropertyValue = *result_str;
   }
 
   return info;
diff --git a/cmds/idmap2/libidmap2/Xml.cpp b/cmds/idmap2/libidmap2/Xml.cpp
deleted file mode 100644
index 2645868..0000000
--- a/cmds/idmap2/libidmap2/Xml.cpp
+++ /dev/null
@@ -1,80 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "idmap2/Xml.h"
-
-#include <map>
-#include <memory>
-#include <string>
-#include <utility>
-
-namespace android::idmap2 {
-
-std::unique_ptr<const Xml> Xml::Create(const uint8_t* data, size_t size, bool copyData) {
-  std::unique_ptr<Xml> xml(new Xml());
-  if (xml->xml_.setTo(data, size, copyData) != NO_ERROR) {
-    return nullptr;
-  }
-  return xml;
-}
-
-std::unique_ptr<std::map<std::string, std::string>> Xml::FindTag(const std::string& name) const {
-  const String16 tag_to_find(name.c_str(), name.size());
-  xml_.restart();
-  ResXMLParser::event_code_t type;
-  do {
-    type = xml_.next();
-    if (type == ResXMLParser::START_TAG) {
-      size_t len;
-      const String16 tag(xml_.getElementName(&len));
-      if (tag == tag_to_find) {
-        std::unique_ptr<std::map<std::string, std::string>> map(
-            new std::map<std::string, std::string>());
-        for (size_t i = 0; i < xml_.getAttributeCount(); i++) {
-          const String16 key16(xml_.getAttributeName(i, &len));
-          std::string key = String8(key16).c_str();
-
-          std::string value;
-          switch (xml_.getAttributeDataType(i)) {
-            case Res_value::TYPE_STRING: {
-              const String16 value16(xml_.getAttributeStringValue(i, &len));
-              value = String8(value16).c_str();
-            } break;
-            case Res_value::TYPE_INT_DEC:
-            case Res_value::TYPE_INT_HEX:
-            case Res_value::TYPE_INT_BOOLEAN: {
-              Res_value resValue;
-              xml_.getAttributeValue(i, &resValue);
-              value = std::to_string(resValue.data);
-            } break;
-            default:
-              return nullptr;
-          }
-
-          map->emplace(std::make_pair(key, value));
-        }
-        return map;
-      }
-    }
-  } while (type != ResXMLParser::BAD_DOCUMENT && type != ResXMLParser::END_DOCUMENT);
-  return nullptr;
-}
-
-Xml::~Xml() {
-  xml_.uninit();
-}
-
-}  // namespace android::idmap2
diff --git a/cmds/idmap2/libidmap2/XmlParser.cpp b/cmds/idmap2/libidmap2/XmlParser.cpp
new file mode 100644
index 0000000..526a560
--- /dev/null
+++ b/cmds/idmap2/libidmap2/XmlParser.cpp
@@ -0,0 +1,163 @@
+/*
+ * 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.
+ */
+
+#include "idmap2/XmlParser.h"
+
+#include <iostream>
+#include <map>
+#include <memory>
+#include <string>
+#include <utility>
+
+namespace android::idmap2 {
+
+template <typename T>
+ResXMLParser::ResXMLPosition get_tree_position(const T& tree) {
+  ResXMLParser::ResXMLPosition pos{};
+  tree.getPosition(&pos);
+  return pos;
+}
+
+XmlParser::Node::Node(const ResXMLTree& tree) : Node(tree, get_tree_position(tree)) {
+}
+XmlParser::Node::Node(const ResXMLTree& tree, const ResXMLParser::ResXMLPosition& pos)
+    : parser_(tree) {
+  set_position(pos);
+}
+
+bool XmlParser::Node::operator==(const XmlParser::Node& rhs) const {
+  ResXMLParser::ResXMLPosition pos = get_position();
+  ResXMLParser::ResXMLPosition rhs_pos = rhs.get_position();
+  return pos.curExt == rhs_pos.curExt && pos.curNode == rhs_pos.curNode &&
+         pos.eventCode == rhs_pos.eventCode;
+}
+
+bool XmlParser::Node::operator!=(const XmlParser::Node& rhs) const {
+  return !(*this == rhs);
+}
+
+ResXMLParser::ResXMLPosition XmlParser::Node::get_position() const {
+  return get_tree_position(parser_);
+}
+
+void XmlParser::Node::set_position(const ResXMLParser::ResXMLPosition& pos) {
+  parser_.setPosition(pos);
+}
+
+bool XmlParser::Node::Seek(bool inner_child) {
+  if (parser_.getEventType() == XmlParser::Event::END_TAG) {
+    return false;
+  }
+
+  ssize_t depth = 0;
+  XmlParser::Event code;
+  while ((code = parser_.next()) != XmlParser::Event::BAD_DOCUMENT &&
+         code != XmlParser::Event::END_DOCUMENT) {
+    if (code == XmlParser::Event::START_TAG) {
+      if (++depth == (inner_child ? 1 : 0)) {
+        return true;
+      }
+    } else if (code == XmlParser::Event::END_TAG) {
+      if (--depth == (inner_child ? -1 : -2)) {
+        return false;
+      }
+    }
+  }
+
+  return false;
+}
+
+XmlParser::Event XmlParser::Node::event() const {
+  return parser_.getEventType();
+}
+
+std::string XmlParser::Node::name() const {
+  size_t len;
+  const String16 key16(parser_.getElementName(&len));
+  return String8(key16).c_str();
+}
+
+Result<std::string> XmlParser::Node::GetAttributeStringValue(const std::string& name) const {
+  auto value = GetAttributeValue(name);
+  if (!value) {
+    return value.GetError();
+  }
+
+  switch ((*value).dataType) {
+    case Res_value::TYPE_STRING: {
+      size_t len;
+      const String16 value16(parser_.getStrings().stringAt((*value).data, &len));
+      return std::string(String8(value16).c_str());
+    }
+    case Res_value::TYPE_INT_DEC:
+    case Res_value::TYPE_INT_HEX:
+    case Res_value::TYPE_INT_BOOLEAN: {
+      return std::to_string((*value).data);
+    }
+    default:
+      return Error(R"(Failed to convert attribute "%s" value to a string)", name.c_str());
+  }
+}
+
+Result<Res_value> XmlParser::Node::GetAttributeValue(const std::string& name) const {
+  size_t len;
+  for (size_t i = 0; i < parser_.getAttributeCount(); i++) {
+    const String16 key16(parser_.getAttributeName(i, &len));
+    std::string key = String8(key16).c_str();
+    if (key != name) {
+      continue;
+    }
+
+    Res_value res_value{};
+    if (parser_.getAttributeValue(i, &res_value) == BAD_TYPE) {
+      return Error(R"(Bad value for attribute "%s")", name.c_str());
+    }
+
+    return res_value;
+  }
+
+  return Error(R"(Failed to find attribute "%s")", name.c_str());
+}
+
+Result<std::unique_ptr<const XmlParser>> XmlParser::Create(const void* data, size_t size,
+                                                           bool copy_data) {
+  auto parser = std::unique_ptr<const XmlParser>(new XmlParser());
+  if (parser->tree_.setTo(data, size, copy_data) != NO_ERROR) {
+    return Error("Malformed xml block");
+  }
+
+  // Find the beginning of the first tag.
+  XmlParser::Event event;
+  while ((event = parser->tree_.next()) != XmlParser::Event::BAD_DOCUMENT &&
+         event != XmlParser::Event::END_DOCUMENT && event != XmlParser::Event::START_TAG) {
+  }
+
+  if (event == XmlParser::Event::END_DOCUMENT) {
+    return Error("Root tag was not be found");
+  }
+
+  if (event == XmlParser::Event::BAD_DOCUMENT) {
+    return Error("Bad xml document");
+  }
+
+  return parser;
+}
+
+XmlParser::~XmlParser() {
+  tree_.uninit();
+}
+
+}  // namespace android::idmap2
diff --git a/cmds/idmap2/tests/BinaryStreamVisitorTests.cpp b/cmds/idmap2/tests/BinaryStreamVisitorTests.cpp
index 9348ab7..43fdc9a 100644
--- a/cmds/idmap2/tests/BinaryStreamVisitorTests.cpp
+++ b/cmds/idmap2/tests/BinaryStreamVisitorTests.cpp
@@ -75,9 +75,8 @@
   std::unique_ptr<const ApkAssets> overlay_apk = ApkAssets::Load(overlay_apk_path);
   ASSERT_THAT(overlay_apk, NotNull());
 
-  const auto idmap =
-      Idmap::FromApkAssets(target_apk_path, *target_apk, overlay_apk_path, *overlay_apk,
-                           PolicyFlags::POLICY_PUBLIC, /* enforce_overlayable */ true);
+  const auto idmap = Idmap::FromApkAssets(*target_apk, *overlay_apk, PolicyFlags::POLICY_PUBLIC,
+                                          /* enforce_overlayable */ true);
   ASSERT_TRUE(idmap);
 
   std::stringstream stream;
diff --git a/cmds/idmap2/tests/IdmapTests.cpp b/cmds/idmap2/tests/IdmapTests.cpp
index 0f47f1e..47e5b17 100644
--- a/cmds/idmap2/tests/IdmapTests.cpp
+++ b/cmds/idmap2/tests/IdmapTests.cpp
@@ -179,8 +179,7 @@
   ASSERT_THAT(overlay_apk, NotNull());
 
   auto result =
-      Idmap::FromApkAssets(target_apk_path.to_string(), *target_apk, overlay_apk_path.to_string(),
-                           *overlay_apk, fulfilled_policies, enforce_overlayable);
+      Idmap::FromApkAssets(*target_apk, *overlay_apk, fulfilled_policies, enforce_overlayable);
   *out_idmap = result ? std::move(*result) : nullptr;
 }
 
@@ -195,7 +194,7 @@
   ASSERT_EQ(idmap->GetHeader()->GetMagic(), 0x504d4449U);
   ASSERT_EQ(idmap->GetHeader()->GetVersion(), 0x01U);
   ASSERT_EQ(idmap->GetHeader()->GetTargetCrc(), 0x76a20829);
-  ASSERT_EQ(idmap->GetHeader()->GetOverlayCrc(), 0x8635c2ed);
+  ASSERT_EQ(idmap->GetHeader()->GetOverlayCrc(), 0xc054fb26);
   ASSERT_EQ(idmap->GetHeader()->GetTargetPath().to_string(), target_apk_path);
   ASSERT_EQ(idmap->GetHeader()->GetOverlayPath(), overlay_apk_path);
   ASSERT_EQ(idmap->GetHeader()->GetOverlayPath(), overlay_apk_path);
@@ -480,9 +479,8 @@
   std::unique_ptr<const ApkAssets> overlay_apk = ApkAssets::Load(overlay_apk_path);
   ASSERT_THAT(overlay_apk, NotNull());
 
-  const auto result =
-      Idmap::FromApkAssets(target_apk_path, *target_apk, overlay_apk_path, *overlay_apk,
-                           PolicyFlags::POLICY_PUBLIC, /* enforce_overlayable */ true);
+  const auto result = Idmap::FromApkAssets(*target_apk, *overlay_apk, PolicyFlags::POLICY_PUBLIC,
+                                           /* enforce_overlayable */ true);
   ASSERT_FALSE(result);
 }
 
@@ -497,8 +495,7 @@
   std::unique_ptr<const ApkAssets> overlay_apk = ApkAssets::Load(overlay_apk_path);
   ASSERT_THAT(overlay_apk, NotNull());
 
-  auto result = Idmap::FromApkAssets(target_apk_path, *target_apk, overlay_apk_path, *overlay_apk,
-                                     PolicyFlags::POLICY_PUBLIC,
+  auto result = Idmap::FromApkAssets(*target_apk, *overlay_apk, PolicyFlags::POLICY_PUBLIC,
                                      /* enforce_overlayable */ true);
   ASSERT_TRUE(result);
   const auto idmap = std::move(*result);
diff --git a/cmds/idmap2/tests/PrettyPrintVisitorTests.cpp b/cmds/idmap2/tests/PrettyPrintVisitorTests.cpp
index c412504..1d34e42 100644
--- a/cmds/idmap2/tests/PrettyPrintVisitorTests.cpp
+++ b/cmds/idmap2/tests/PrettyPrintVisitorTests.cpp
@@ -43,9 +43,8 @@
   std::unique_ptr<const ApkAssets> overlay_apk = ApkAssets::Load(overlay_apk_path);
   ASSERT_THAT(overlay_apk, NotNull());
 
-  const auto idmap =
-      Idmap::FromApkAssets(target_apk_path, *target_apk, overlay_apk_path, *overlay_apk,
-                           PolicyFlags::POLICY_PUBLIC, /* enforce_overlayable */ true);
+  const auto idmap = Idmap::FromApkAssets(*target_apk, *overlay_apk, PolicyFlags::POLICY_PUBLIC,
+                                          /* enforce_overlayable */ true);
   ASSERT_TRUE(idmap);
 
   std::stringstream stream;
diff --git a/cmds/idmap2/tests/RawPrintVisitorTests.cpp b/cmds/idmap2/tests/RawPrintVisitorTests.cpp
index 2695176..c243d74 100644
--- a/cmds/idmap2/tests/RawPrintVisitorTests.cpp
+++ b/cmds/idmap2/tests/RawPrintVisitorTests.cpp
@@ -38,9 +38,8 @@
   std::unique_ptr<const ApkAssets> overlay_apk = ApkAssets::Load(overlay_apk_path);
   ASSERT_THAT(overlay_apk, NotNull());
 
-  const auto idmap =
-      Idmap::FromApkAssets(target_apk_path, *target_apk, overlay_apk_path, *overlay_apk,
-                           PolicyFlags::POLICY_PUBLIC, /* enforce_overlayable */ true);
+  const auto idmap = Idmap::FromApkAssets(*target_apk, *overlay_apk, PolicyFlags::POLICY_PUBLIC,
+                                          /* enforce_overlayable */ true);
   ASSERT_TRUE(idmap);
 
   std::stringstream stream;
@@ -50,7 +49,7 @@
   ASSERT_NE(stream.str().find("00000000: 504d4449  magic\n"), std::string::npos);
   ASSERT_NE(stream.str().find("00000004: 00000001  version\n"), std::string::npos);
   ASSERT_NE(stream.str().find("00000008: 76a20829  target crc\n"), std::string::npos);
-  ASSERT_NE(stream.str().find("0000000c: 8635c2ed  overlay crc\n"), std::string::npos);
+  ASSERT_NE(stream.str().find("0000000c: c054fb26  overlay crc\n"), std::string::npos);
   ASSERT_NE(stream.str().find("0000021c: 00000000  0x7f010000 -> 0x7f010000 integer/int1\n"),
             std::string::npos);
 }
diff --git a/cmds/idmap2/tests/ResourceMappingTests.cpp b/cmds/idmap2/tests/ResourceMappingTests.cpp
new file mode 100644
index 0000000..1ef41de
--- /dev/null
+++ b/cmds/idmap2/tests/ResourceMappingTests.cpp
@@ -0,0 +1,322 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <cstdio>  // fclose
+#include <fstream>
+#include <memory>
+#include <sstream>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "TestHelpers.h"
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+#include "idmap2/ResourceMapping.h"
+
+using android::idmap2::utils::ExtractOverlayManifestInfo;
+
+namespace android::idmap2 {
+
+#define ASSERT_RESULT(r)                             \
+  do {                                               \
+    auto result = r;                                 \
+    ASSERT_TRUE(result) << result.GetErrorMessage(); \
+  } while (0)
+
+Result<ResourceMapping> TestGetResourceMapping(const android::StringPiece& local_target_apk_path,
+                                               const android::StringPiece& local_overlay_apk_path,
+                                               const OverlayManifestInfo& overlay_info,
+                                               const PolicyBitmask& fulfilled_policies,
+                                               bool enforce_overlayable) {
+  const std::string target_apk_path(GetTestDataPath() + local_target_apk_path.data());
+  std::unique_ptr<const ApkAssets> target_apk = ApkAssets::Load(target_apk_path);
+  if (!target_apk) {
+    return Error(R"(Failed to load target apk "%s")", target_apk_path.data());
+  }
+
+  const std::string overlay_apk_path(GetTestDataPath() + local_overlay_apk_path.data());
+  std::unique_ptr<const ApkAssets> overlay_apk = ApkAssets::Load(overlay_apk_path);
+  if (!overlay_apk) {
+    return Error(R"(Failed to load overlay apk "%s")", overlay_apk_path.data());
+  }
+
+  return ResourceMapping::FromApkAssets(*target_apk, *overlay_apk, overlay_info, fulfilled_policies,
+                                        enforce_overlayable);
+}
+
+Result<ResourceMapping> TestGetResourceMapping(const android::StringPiece& local_target_apk_path,
+                                               const android::StringPiece& local_overlay_apk_path,
+                                               const PolicyBitmask& fulfilled_policies,
+                                               bool enforce_overlayable) {
+  auto overlay_info = ExtractOverlayManifestInfo(GetTestDataPath() + local_overlay_apk_path.data());
+  if (!overlay_info) {
+    return overlay_info.GetError();
+  }
+  return TestGetResourceMapping(local_target_apk_path, local_overlay_apk_path, *overlay_info,
+                                fulfilled_policies, enforce_overlayable);
+}
+
+Result<Unit> MappingExists(const ResourceMapping& mapping, const ResourceId& target_resource,
+                           const uint8_t type, const uint32_t value, bool rewrite) {
+  auto target_map = mapping.GetTargetToOverlayMap();
+  auto entry_map = target_map.find(target_resource);
+  if (entry_map == target_map.end()) {
+    return Error("Failed to find mapping for target resource");
+  }
+
+  if (entry_map->second.data_type != type) {
+    return Error(R"(Expected type: "0x%02x" Actual type: "0x%02x")", type,
+                 entry_map->second.data_type);
+  }
+
+  if (entry_map->second.data_value != value) {
+    return Error(R"(Expected value: "0x%08x" Actual value: "0x%08x")", type,
+                 entry_map->second.data_value);
+  }
+
+  auto overlay_map = mapping.GetOverlayToTargetMap();
+  auto overlay_iter = overlay_map.find(entry_map->second.data_value);
+  if ((overlay_iter != overlay_map.end()) != rewrite) {
+    return Error(R"(Expected rewriting: "%s")", rewrite ? "true" : "false");
+  }
+
+  return Result<Unit>({});
+}
+
+TEST(ResourceMappingTests, ResourcesFromApkAssetsLegacy) {
+  OverlayManifestInfo info{};
+  info.target_package = "test.target";
+  info.target_name = "TestResources";
+  info.resource_mapping = 0U;  // no xml
+  auto resources = TestGetResourceMapping("/target/target.apk", "/overlay/overlay.apk", info,
+                                          PolicyFlags::POLICY_PUBLIC,
+                                          /* enforce_overlayable */ false);
+
+  ASSERT_TRUE(resources) << resources.GetErrorMessage();
+  auto& res = *resources;
+  ASSERT_EQ(res.GetTargetToOverlayMap().size(), 4U);
+  ASSERT_RESULT(MappingExists(res, 0x7f010000, 0x01 /* Res_value::TYPE_REFERENCE */, 0x7f010000,
+                              true /* rewrite */));  // integer/int1
+  ASSERT_RESULT(MappingExists(res, 0x7f02000c, 0x01 /* Res_value::TYPE_REFERENCE */, 0x7f020000,
+                              true /* rewrite */));  // string/str1
+  ASSERT_RESULT(MappingExists(res, 0x7f02000e, 0x01 /* Res_value::TYPE_REFERENCE */, 0x7f020001,
+                              true /* rewrite */));  // string/str3
+  ASSERT_RESULT(MappingExists(res, 0x7f02000f, 0x01 /* Res_value::TYPE_REFERENCE */, 0x7f020002,
+                              true /* rewrite */));  // string/str4
+}
+
+TEST(ResourceMappingTests, ResourcesFromApkAssetsNonMatchingNames) {
+  OverlayManifestInfo info{};
+  info.target_package = "test.target";
+  info.target_name = "TestResources";
+  info.resource_mapping = 0x7f030003;  // xml/overlays_swap
+  auto resources = TestGetResourceMapping("/target/target.apk", "/overlay/overlay.apk", info,
+                                          PolicyFlags::POLICY_PUBLIC,
+                                          /* enforce_overlayable */ false);
+
+  ASSERT_TRUE(resources) << resources.GetErrorMessage();
+  auto& res = *resources;
+  ASSERT_EQ(res.GetTargetToOverlayMap().size(), 3U);
+  ASSERT_RESULT(MappingExists(res, 0x7f02000c, 0x01 /* Res_value::TYPE_REFERENCE */, 0x7f020002,
+                              true /* rewrite */));  // string/str1 -> string/str4
+  ASSERT_RESULT(MappingExists(res, 0x7f02000e, 0x01 /* Res_value::TYPE_REFERENCE */, 0x7f020000,
+                              true /* rewrite */));  // string/str3 -> string/str1
+  ASSERT_RESULT(MappingExists(res, 0x7f02000f, 0x01 /* Res_value::TYPE_REFERENCE */, 0x7f020001,
+                              true /* rewrite */));  // string/str4 -> string/str3
+}
+
+TEST(ResourceMappingTests, DoNotRewriteNonResourceMapping) {
+  OverlayManifestInfo info{};
+  info.target_package = "test.target";
+  info.target_name = "TestResources";
+  info.resource_mapping = 0x7f030001;  // xml/overlays_different_packages
+  auto resources = TestGetResourceMapping("/target/target.apk", "/overlay/overlay.apk", info,
+                                          PolicyFlags::POLICY_PUBLIC,
+                                          /* enforce_overlayable */ false);
+
+  ASSERT_TRUE(resources) << resources.GetErrorMessage();
+  auto& res = *resources;
+  ASSERT_EQ(res.GetTargetToOverlayMap().size(), 2U);
+  ASSERT_EQ(res.GetOverlayToTargetMap().size(), 1U);
+  ASSERT_RESULT(MappingExists(res, 0x7f02000c, 0x01 /* Res_value::TYPE_REFERENCE */, 0x0104000a,
+                              false /* rewrite */));  // string/str1 -> android:string/ok
+  ASSERT_RESULT(MappingExists(res, 0x7f02000e, 0x01 /* Res_value::TYPE_REFERENCE */, 0x7f020001,
+                              true /* rewrite */));  // string/str3 -> string/str4
+}
+
+TEST(ResourceMappingTests, InlineResources) {
+  OverlayManifestInfo info{};
+  info.target_package = "test.target";
+  info.target_name = "TestResources";
+  info.resource_mapping = 0x7f030002;  // xml/overlays_inline
+  auto resources = TestGetResourceMapping("/target/target.apk", "/overlay/overlay.apk", info,
+                                          PolicyFlags::POLICY_PUBLIC,
+                                          /* enforce_overlayable */ false);
+
+  constexpr size_t overlay_string_pool_size = 8U;
+  ASSERT_TRUE(resources) << resources.GetErrorMessage();
+  auto& res = *resources;
+  ASSERT_EQ(res.GetTargetToOverlayMap().size(), 2U);
+  ASSERT_EQ(res.GetOverlayToTargetMap().size(), 0U);
+  ASSERT_RESULT(MappingExists(res, 0x7f02000c, 0x03 /* Res_value::TYPE_STRING */,
+                              overlay_string_pool_size + 0U,
+                              false /* rewrite */));  // string/str1 -> "Hello World"
+  ASSERT_RESULT(MappingExists(res, 0x7f010000, 0x10 /* Res_value::TYPE_INT_DEC */, 73U,
+                              false /* rewrite */));  // string/str1 -> "Hello World"
+}
+
+TEST(ResourceMappingTests, CreateIdmapFromApkAssetsPolicySystemPublic) {
+  auto resources =
+      TestGetResourceMapping("/target/target.apk", "/system-overlay/system-overlay.apk",
+                             PolicyFlags::POLICY_SYSTEM_PARTITION | PolicyFlags::POLICY_PUBLIC,
+                             /* enforce_overlayable */ true);
+
+  ASSERT_TRUE(resources) << resources.GetErrorMessage();
+  auto& res = *resources;
+  ASSERT_EQ(res.GetTargetToOverlayMap().size(), 3U);
+  ASSERT_RESULT(MappingExists(res, 0x7f020008, 0x01 /* Res_value::TYPE_REFERENCE */, 0x7f010000,
+                              true /* rewrite */));  // string/policy_public
+  ASSERT_RESULT(MappingExists(res, 0x7f02000a, 0x01 /* Res_value::TYPE_REFERENCE */, 0x7f010001,
+                              true /* rewrite */));  // string/policy_system
+  ASSERT_RESULT(MappingExists(res, 0x7f02000b, 0x01 /* Res_value::TYPE_REFERENCE */, 0x7f010002,
+                              true /* rewrite */));  // string/policy_system_vendor
+}
+
+// Resources that are not declared as overlayable and resources that a protected by policies the
+// overlay does not fulfill must not map to overlay resources.
+TEST(ResourceMappingTests, CreateIdmapFromApkAssetsPolicySystemPublicInvalid) {
+  auto resources = TestGetResourceMapping(
+      "/target/target.apk", "/system-overlay-invalid/system-overlay-invalid.apk",
+      PolicyFlags::POLICY_SYSTEM_PARTITION | PolicyFlags::POLICY_PUBLIC,
+      /* enforce_overlayable */ true);
+
+  ASSERT_TRUE(resources) << resources.GetErrorMessage();
+  auto& res = *resources;
+  ASSERT_EQ(res.GetTargetToOverlayMap().size(), 3U);
+  ASSERT_RESULT(MappingExists(res, 0x7f020008, 0x01 /* Res_value::TYPE_REFERENCE */, 0x7f010005,
+                              true /* rewrite */));  // string/policy_public
+  ASSERT_RESULT(MappingExists(res, 0x7f02000a, 0x01 /* Res_value::TYPE_REFERENCE */, 0x7f010007,
+                              true /* rewrite */));  // string/policy_system
+  ASSERT_RESULT(MappingExists(res, 0x7f02000b, 0x01 /* Res_value::TYPE_REFERENCE */, 0x7f010008,
+                              true /* rewrite */));  // string/policy_system_vendor
+}
+
+// Resources that are not declared as overlayable and resources that a protected by policies the
+// overlay does not fulfilled can map to overlay resources when overlayable enforcement is turned
+// off.
+TEST(ResourceMappingTests, ResourcesFromApkAssetsPolicySystemPublicInvalidIgnoreOverlayable) {
+  auto resources = TestGetResourceMapping(
+      "/target/target.apk", "/system-overlay-invalid/system-overlay-invalid.apk",
+      PolicyFlags::POLICY_SYSTEM_PARTITION | PolicyFlags::POLICY_PUBLIC,
+      /* enforce_overlayable */ false);
+
+  ASSERT_TRUE(resources) << resources.GetErrorMessage();
+  auto& res = *resources;
+  ASSERT_EQ(res.GetTargetToOverlayMap().size(), 9U);
+  ASSERT_RESULT(MappingExists(res, 0x7f020003, 0x01 /* Res_value::TYPE_REFERENCE */, 0x7f010000,
+                              true /* rewrite */));  // string/not_overlayable
+  ASSERT_RESULT(MappingExists(res, 0x7f020004, 0x01 /* Res_value::TYPE_REFERENCE */, 0x7f010001,
+                              true /* rewrite */));  // string/other
+  ASSERT_RESULT(MappingExists(res, 0x7f020005, 0x01 /* Res_value::TYPE_REFERENCE */, 0x7f010002,
+                              true /* rewrite */));  // string/policy_odm
+  ASSERT_RESULT(MappingExists(res, 0x7f020006, 0x01 /* Res_value::TYPE_REFERENCE */, 0x7f010003,
+                              true /* rewrite */));  // string/policy_oem
+  ASSERT_RESULT(MappingExists(res, 0x7f020007, 0x01 /* Res_value::TYPE_REFERENCE */, 0x7f010004,
+                              true /* rewrite */));  // string/policy_product
+  ASSERT_RESULT(MappingExists(res, 0x7f020008, 0x01 /* Res_value::TYPE_REFERENCE */, 0x7f010005,
+                              true /* rewrite */));  // string/policy_public
+  ASSERT_RESULT(MappingExists(res, 0x7f020009, 0x01 /* Res_value::TYPE_REFERENCE */, 0x7f010006,
+                              true /* rewrite */));  // string/policy_signature
+  ASSERT_RESULT(MappingExists(res, 0x7f02000a, 0x01 /* Res_value::TYPE_REFERENCE */, 0x7f010007,
+                              true /* rewrite */));  // string/policy_system
+  ASSERT_RESULT(MappingExists(res, 0x7f02000b, 0x01 /* Res_value::TYPE_REFERENCE */, 0x7f010008,
+                              true /* rewrite */));  // string/policy_system_vendor
+}
+
+// Overlays that do not target an <overlayable> tag can overlay resources defined within any
+// <overlayable> tag.
+TEST(ResourceMappingTests, ResourcesFromApkAssetsNoDefinedOverlayableAndNoTargetName) {
+  auto resources = TestGetResourceMapping("/target/target.apk", "/overlay/overlay-no-name.apk",
+                                          PolicyFlags::POLICY_PUBLIC,
+                                          /* enforce_overlayable */ false);
+
+  ASSERT_TRUE(resources) << resources.GetErrorMessage();
+  auto& res = *resources;
+  ASSERT_EQ(res.GetTargetToOverlayMap().size(), 4U);
+  ASSERT_RESULT(MappingExists(res, 0x7f010000, 0x01 /* Res_value::TYPE_REFERENCE */, 0x7f010000,
+                              true /* rewrite */));  // integer/int1
+  ASSERT_RESULT(MappingExists(res, 0x7f02000c, 0x01 /* Res_value::TYPE_REFERENCE */, 0x7f020000,
+                              true /* rewrite */));  // string/str1
+  ASSERT_RESULT(MappingExists(res, 0x7f02000e, 0x01 /* Res_value::TYPE_REFERENCE */, 0x7f020001,
+                              true /* rewrite */));  // string/str3
+  ASSERT_RESULT(MappingExists(res, 0x7f02000f, 0x01 /* Res_value::TYPE_REFERENCE */, 0x7f020002,
+                              true /* rewrite */));  // string/str4
+}
+
+// Overlays that are neither pre-installed nor signed with the same signature as the target cannot
+// overlay packages that have not defined overlayable resources.
+TEST(ResourceMappingTests, ResourcesFromApkAssetsDefaultPoliciesPublicFail) {
+  auto resources =
+      TestGetResourceMapping("/target/target-no-overlayable.apk", "/overlay/overlay-no-name.apk",
+                             PolicyFlags::POLICY_PUBLIC,
+                             /* enforce_overlayable */ true);
+
+  ASSERT_TRUE(resources) << resources.GetErrorMessage();
+  ASSERT_EQ(resources->GetTargetToOverlayMap().size(), 0U);
+}
+
+// Overlays that are pre-installed or are signed with the same signature as the target can overlay
+// packages that have not defined overlayable resources.
+TEST(ResourceMappingTests, ResourcesFromApkAssetsDefaultPolicies) {
+  auto CheckEntries = [&](const PolicyBitmask& fulfilled_policies) -> void {
+    auto resources = TestGetResourceMapping("/target/target-no-overlayable.apk",
+                                            "/system-overlay-invalid/system-overlay-invalid.apk",
+                                            fulfilled_policies,
+                                            /* enforce_overlayable */ true);
+
+    ASSERT_TRUE(resources) << resources.GetErrorMessage();
+    auto& res = *resources;
+    ASSERT_EQ(resources->GetTargetToOverlayMap().size(), 9U);
+    ASSERT_RESULT(MappingExists(res, 0x7f020003, 0x01 /* Res_value::TYPE_REFERENCE */, 0x7f010000,
+                                true /* rewrite */));  // string/not_overlayable
+    ASSERT_RESULT(MappingExists(res, 0x7f020004, 0x01 /* Res_value::TYPE_REFERENCE */, 0x7f010001,
+                                true /* rewrite */));  // string/other
+    ASSERT_RESULT(MappingExists(res, 0x7f020005, 0x01 /* Res_value::TYPE_REFERENCE */, 0x7f010002,
+                                true /* rewrite */));  // string/policy_odm
+    ASSERT_RESULT(MappingExists(res, 0x7f020006, 0x01 /* Res_value::TYPE_REFERENCE */, 0x7f010003,
+                                true /* rewrite */));  // string/policy_oem
+    ASSERT_RESULT(MappingExists(res, 0x7f020007, 0x01 /* Res_value::TYPE_REFERENCE */, 0x7f010004,
+                                true /* rewrite */));  // string/policy_product
+    ASSERT_RESULT(MappingExists(res, 0x7f020008, 0x01 /* Res_value::TYPE_REFERENCE */, 0x7f010005,
+                                true /* rewrite */));  // string/policy_public
+    ASSERT_RESULT(MappingExists(res, 0x7f020009, 0x01 /* Res_value::TYPE_REFERENCE */, 0x7f010006,
+                                true /* rewrite */));  // string/policy_signature
+    ASSERT_RESULT(MappingExists(res, 0x7f02000a, 0x01 /* Res_value::TYPE_REFERENCE */, 0x7f010007,
+                                true /* rewrite */));  // string/policy_system
+    ASSERT_RESULT(MappingExists(res, 0x7f02000b, 0x01 /* Res_value::TYPE_REFERENCE */, 0x7f010008,
+                                true /* rewrite */));  // string/policy_system_vendor
+  };
+
+  CheckEntries(PolicyFlags::POLICY_SIGNATURE);
+  CheckEntries(PolicyFlags::POLICY_PRODUCT_PARTITION);
+  CheckEntries(PolicyFlags::POLICY_SYSTEM_PARTITION);
+  CheckEntries(PolicyFlags::POLICY_VENDOR_PARTITION);
+  CheckEntries(PolicyFlags::POLICY_ODM_PARTITION);
+  CheckEntries(PolicyFlags::POLICY_OEM_PARTITION);
+}
+
+}  // namespace android::idmap2
diff --git a/cmds/idmap2/tests/XmlParserTests.cpp b/cmds/idmap2/tests/XmlParserTests.cpp
new file mode 100644
index 0000000..1a7eaca
--- /dev/null
+++ b/cmds/idmap2/tests/XmlParserTests.cpp
@@ -0,0 +1,174 @@
+/*
+ * 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.
+ */
+
+#include <cstdio>  // fclose
+#include <memory>
+#include <string>
+
+#include "TestHelpers.h"
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+#include "idmap2/XmlParser.h"
+#include "idmap2/ZipFile.h"
+
+namespace android::idmap2 {
+
+Result<std::unique_ptr<const XmlParser>> CreateTestParser(const std::string& test_file) {
+  auto zip = ZipFile::Open(GetTestDataPath() + "/target/target.apk");
+  if (zip == nullptr) {
+    return Error("Failed to open zip file");
+  }
+
+  auto data = zip->Uncompress(test_file);
+  if (data == nullptr) {
+    return Error("Failed to open xml file");
+  }
+
+  return XmlParser::Create(data->buf, data->size, /* copy_data */ true);
+}
+
+TEST(XmlParserTests, Create) {
+  auto xml = CreateTestParser("AndroidManifest.xml");
+  ASSERT_TRUE(xml) << xml.GetErrorMessage();
+
+  fclose(stderr);  // silence expected warnings from libandroidfw
+  const char* not_xml = "foo";
+  auto fail = XmlParser::Create(reinterpret_cast<const uint8_t*>(not_xml), strlen(not_xml));
+  ASSERT_FALSE(fail);
+}
+
+TEST(XmlParserTests, NextChild) {
+  auto xml = CreateTestParser("res/xml/test.xml");
+  ASSERT_TRUE(xml) << xml.GetErrorMessage();
+
+  auto root_iter = (*xml)->tree_iterator();
+  ASSERT_EQ(root_iter->event(), XmlParser::Event::START_TAG);
+  ASSERT_EQ(root_iter->name(), "a");
+
+  auto a_iter = root_iter.begin();
+  ASSERT_EQ(a_iter->event(), XmlParser::Event::START_TAG);
+  ASSERT_EQ(a_iter->name(), "b");
+
+  auto c_iter = a_iter.begin();
+  ASSERT_EQ(c_iter->event(), XmlParser::Event::START_TAG);
+  ASSERT_EQ(c_iter->name(), "c");
+
+  ++c_iter;
+  ASSERT_EQ(c_iter->event(), XmlParser::Event::END_TAG);
+  ASSERT_EQ(c_iter, a_iter.end());
+
+  ++a_iter;
+  ASSERT_EQ(a_iter->event(), XmlParser::Event::START_TAG);
+  ASSERT_EQ(a_iter->name(), "d");
+
+  // Skip the <e> tag.
+  ++a_iter;
+  ASSERT_EQ(a_iter->event(), XmlParser::Event::END_TAG);
+  ASSERT_EQ(a_iter, root_iter.end());
+}
+
+TEST(XmlParserTests, AttributeValues) {
+  auto xml = CreateTestParser("res/xml/test.xml");
+  ASSERT_TRUE(xml) << xml.GetErrorMessage();
+
+  // Start at the <a> tag.
+  auto root_iter = (*xml)->tree_iterator();
+
+  // Start at the <b> tag.
+  auto a_iter = root_iter.begin();
+  auto attribute_str = a_iter->GetAttributeStringValue("type_string");
+  ASSERT_TRUE(attribute_str);
+  ASSERT_EQ(*attribute_str, "fortytwo");
+
+  auto attribute_value = a_iter->GetAttributeValue("type_int_dec");
+  ASSERT_TRUE(attribute_value);
+  ASSERT_EQ(attribute_value->data, 42);
+
+  attribute_value = a_iter->GetAttributeValue("type_int_hex");
+  ASSERT_TRUE(attribute_value);
+  ASSERT_EQ(attribute_value->data, 42);
+
+  attribute_value = a_iter->GetAttributeValue("type_int_boolean");
+  ASSERT_TRUE(attribute_value);
+  ASSERT_EQ(attribute_value->data, 0xffffffff);
+}
+
+TEST(XmlParserTests, IteratorEquality) {
+  auto xml = CreateTestParser("res/xml/test.xml");
+  ASSERT_TRUE(xml) << xml.GetErrorMessage();
+
+  // Start at the <a> tag.
+  auto root_iter_1 = (*xml)->tree_iterator();
+  auto root_iter_2 = (*xml)->tree_iterator();
+  ASSERT_EQ(root_iter_1, root_iter_2);
+  ASSERT_EQ(*root_iter_1, *root_iter_2);
+
+  // Start at the <b> tag.
+  auto a_iter_1 = root_iter_1.begin();
+  auto a_iter_2 = root_iter_2.begin();
+  ASSERT_NE(a_iter_1, root_iter_1.end());
+  ASSERT_NE(a_iter_2, root_iter_2.end());
+  ASSERT_EQ(a_iter_1, a_iter_2);
+  ASSERT_EQ(*a_iter_1, *a_iter_2);
+
+  // Move to the <d> tag.
+  ++a_iter_1;
+  ++a_iter_2;
+  ASSERT_NE(a_iter_1, root_iter_1.end());
+  ASSERT_NE(a_iter_2, root_iter_2.end());
+  ASSERT_EQ(a_iter_1, a_iter_2);
+  ASSERT_EQ(*a_iter_1, *a_iter_2);
+
+  // Move to the end of the <a> tag.
+  ++a_iter_1;
+  ++a_iter_2;
+  ASSERT_EQ(a_iter_1, root_iter_1.end());
+  ASSERT_EQ(a_iter_2, root_iter_2.end());
+  ASSERT_EQ(a_iter_1, a_iter_2);
+  ASSERT_EQ(*a_iter_1, *a_iter_2);
+}
+
+TEST(XmlParserTests, Backtracking) {
+  auto xml = CreateTestParser("res/xml/test.xml");
+  ASSERT_TRUE(xml) << xml.GetErrorMessage();
+
+  // Start at the <a> tag.
+  auto root_iter_1 = (*xml)->tree_iterator();
+
+  // Start at the <b> tag.
+  auto a_iter_1 = root_iter_1.begin();
+
+  // Start a second iterator at the <a> tag.
+  auto root_iter_2 = root_iter_1;
+  ASSERT_EQ(root_iter_1, root_iter_2);
+  ASSERT_EQ(*root_iter_1, *root_iter_2);
+
+  // Move the first iterator to the end of the <a> tag.
+  auto root_iter_end_1 = root_iter_1.end();
+  ++root_iter_1;
+  ASSERT_NE(root_iter_1, root_iter_2);
+  ASSERT_NE(*root_iter_1, *root_iter_2);
+
+  // Move to the <d> tag.
+  ++a_iter_1;
+  ASSERT_NE(a_iter_1, root_iter_end_1);
+
+  // Move to the end of the <a> tag.
+  ++a_iter_1;
+  ASSERT_EQ(a_iter_1, root_iter_end_1);
+}
+
+}  // namespace android::idmap2
diff --git a/cmds/idmap2/tests/XmlTests.cpp b/cmds/idmap2/tests/XmlTests.cpp
deleted file mode 100644
index df63211..0000000
--- a/cmds/idmap2/tests/XmlTests.cpp
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <cstdio>  // fclose
-
-#include "TestHelpers.h"
-#include "gmock/gmock.h"
-#include "gtest/gtest.h"
-#include "idmap2/Xml.h"
-#include "idmap2/ZipFile.h"
-
-using ::testing::IsNull;
-using ::testing::NotNull;
-
-namespace android::idmap2 {
-
-TEST(XmlTests, Create) {
-  auto zip = ZipFile::Open(GetTestDataPath() + "/target/target.apk");
-  ASSERT_THAT(zip, NotNull());
-
-  auto data = zip->Uncompress("AndroidManifest.xml");
-  ASSERT_THAT(data, NotNull());
-
-  auto xml = Xml::Create(data->buf, data->size);
-  ASSERT_THAT(xml, NotNull());
-
-  fclose(stderr);  // silence expected warnings from libandroidfw
-  const char* not_xml = "foo";
-  auto fail = Xml::Create(reinterpret_cast<const uint8_t*>(not_xml), strlen(not_xml));
-  ASSERT_THAT(fail, IsNull());
-}
-
-TEST(XmlTests, FindTag) {
-  auto zip = ZipFile::Open(GetTestDataPath() + "/target/target.apk");
-  ASSERT_THAT(zip, NotNull());
-
-  auto data = zip->Uncompress("res/xml/test.xml");
-  ASSERT_THAT(data, NotNull());
-
-  auto xml = Xml::Create(data->buf, data->size);
-  ASSERT_THAT(xml, NotNull());
-
-  auto attrs = xml->FindTag("c");
-  ASSERT_THAT(attrs, NotNull());
-  ASSERT_EQ(attrs->size(), 4U);
-  ASSERT_EQ(attrs->at("type_string"), "fortytwo");
-  ASSERT_EQ(std::stoi(attrs->at("type_int_dec")), 42);
-  ASSERT_EQ(std::stoi(attrs->at("type_int_hex")), 42);
-  ASSERT_NE(std::stoul(attrs->at("type_int_boolean")), 0U);
-
-  auto fail = xml->FindTag("does-not-exist");
-  ASSERT_THAT(fail, IsNull());
-}
-
-}  // namespace android::idmap2
diff --git a/cmds/idmap2/tests/data/overlay/AndroidManifest.xml b/cmds/idmap2/tests/data/overlay/AndroidManifest.xml
index 619bb6c..cf3691c 100644
--- a/cmds/idmap2/tests/data/overlay/AndroidManifest.xml
+++ b/cmds/idmap2/tests/data/overlay/AndroidManifest.xml
@@ -16,8 +16,11 @@
 <manifest
     xmlns:android="http://schemas.android.com/apk/res/android"
     package="test.overlay">
+
     <application android:hasCode="false"/>
+
     <overlay
         android:targetPackage="test.target"
-        android:targetName="TestResources"/>
+        android:targetName="TestResources"
+        android:resourcesMap="@xml/overlays"/>
 </manifest>
diff --git a/cmds/idmap2/tests/data/overlay/build b/cmds/idmap2/tests/data/overlay/build
index 68b9f50..b921b0d 100755
--- a/cmds/idmap2/tests/data/overlay/build
+++ b/cmds/idmap2/tests/data/overlay/build
@@ -12,7 +12,7 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-FRAMEWORK_RES_APK=${ANDROID_BUILD_TOP}/prebuilts/sdk/current/public/android.jar
+FRAMEWORK_RES_APK=${ANDROID_PRODUCT_OUT}/system/framework/framework-res.apk
 
 aapt2 compile --dir res -o compiled.flata
 
diff --git a/cmds/idmap2/tests/data/overlay/overlay-no-name-static.apk b/cmds/idmap2/tests/data/overlay/overlay-no-name-static.apk
index 18ee43d..7c25985 100644
--- a/cmds/idmap2/tests/data/overlay/overlay-no-name-static.apk
+++ b/cmds/idmap2/tests/data/overlay/overlay-no-name-static.apk
Binary files differ
diff --git a/cmds/idmap2/tests/data/overlay/overlay-no-name.apk b/cmds/idmap2/tests/data/overlay/overlay-no-name.apk
index 6425190..c75f3e1 100644
--- a/cmds/idmap2/tests/data/overlay/overlay-no-name.apk
+++ b/cmds/idmap2/tests/data/overlay/overlay-no-name.apk
Binary files differ
diff --git a/cmds/idmap2/tests/data/overlay/overlay-static-1.apk b/cmds/idmap2/tests/data/overlay/overlay-static-1.apk
index 642ab90..5b8a6e4 100644
--- a/cmds/idmap2/tests/data/overlay/overlay-static-1.apk
+++ b/cmds/idmap2/tests/data/overlay/overlay-static-1.apk
Binary files differ
diff --git a/cmds/idmap2/tests/data/overlay/overlay-static-2.apk b/cmds/idmap2/tests/data/overlay/overlay-static-2.apk
index 2ec5602..698a1fd 100644
--- a/cmds/idmap2/tests/data/overlay/overlay-static-2.apk
+++ b/cmds/idmap2/tests/data/overlay/overlay-static-2.apk
Binary files differ
diff --git a/cmds/idmap2/tests/data/overlay/overlay.apk b/cmds/idmap2/tests/data/overlay/overlay.apk
index 5842da4..1db303f 100644
--- a/cmds/idmap2/tests/data/overlay/overlay.apk
+++ b/cmds/idmap2/tests/data/overlay/overlay.apk
Binary files differ
diff --git a/cmds/idmap2/tests/data/overlay/res/xml/overlays.xml b/cmds/idmap2/tests/data/overlay/res/xml/overlays.xml
new file mode 100644
index 0000000..edd33f7
--- /dev/null
+++ b/cmds/idmap2/tests/data/overlay/res/xml/overlays.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2019 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<overlay>
+    <item target="string/str1" value="@string/str1"/>
+    <item target="string/str3" value="@string/str3" />
+    <item target="string/str4" value="@string/str4" />
+    <item target="integer/int1" value="@integer/int1" />
+    <item target="integer/not_in_target" value="@integer/not_in_target" />
+</overlay>
+
diff --git a/cmds/idmap2/tests/data/overlay/res/xml/overlays_different_package.xml b/cmds/idmap2/tests/data/overlay/res/xml/overlays_different_package.xml
new file mode 100644
index 0000000..aa7fefa
--- /dev/null
+++ b/cmds/idmap2/tests/data/overlay/res/xml/overlays_different_package.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2019 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<overlay>
+    <item target="string/str1" value="@android:string/ok"/>
+    <item target="string/str3" value="@string/str3" />
+</overlay>
+
diff --git a/cmds/idmap2/tests/data/overlay/res/xml/overlays_inline.xml b/cmds/idmap2/tests/data/overlay/res/xml/overlays_inline.xml
new file mode 100644
index 0000000..e12b823
--- /dev/null
+++ b/cmds/idmap2/tests/data/overlay/res/xml/overlays_inline.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2019 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<overlay>
+    <item target="string/str1" value="Hello World"/>
+    <item target="integer/int1" value="73" />
+</overlay>
+
diff --git a/cmds/idmap2/tests/data/overlay/res/xml/overlays_swap.xml b/cmds/idmap2/tests/data/overlay/res/xml/overlays_swap.xml
new file mode 100644
index 0000000..5728e67
--- /dev/null
+++ b/cmds/idmap2/tests/data/overlay/res/xml/overlays_swap.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2019 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<overlay>
+    <item target="string/str1" value="@string/str4"/>
+    <item target="string/str3" value="@string/str1" />
+    <item target="string/str4" value="@string/str3" />
+    <item target="integer/int_not_in_target" value="@integer/int1" />
+</overlay>
diff --git a/cmds/idmap2/tests/data/target/res/xml/test.xml b/cmds/idmap2/tests/data/target/res/xml/test.xml
index 0fe21c6..56a3f7f 100644
--- a/cmds/idmap2/tests/data/target/res/xml/test.xml
+++ b/cmds/idmap2/tests/data/target/res/xml/test.xml
@@ -14,12 +14,15 @@
      limitations under the License.
 -->
 <a>
-    <b>
-        <c
-            type_string="fortytwo"
-            type_int_dec="42"
-            type_int_hex="0x2a"
-            type_int_boolean="true"
-            />
+    <b type_string="fortytwo"
+       type_int_dec="42"
+       type_int_hex="0x2a"
+       type_int_boolean="true">
+
+        <c />
     </b>
-</a>
+
+    <d>
+        <e />
+    </d>
+</a>
\ No newline at end of file
diff --git a/cmds/idmap2/tests/data/target/target-no-overlayable.apk b/cmds/idmap2/tests/data/target/target-no-overlayable.apk
index 033305a..2eb7c47 100644
--- a/cmds/idmap2/tests/data/target/target-no-overlayable.apk
+++ b/cmds/idmap2/tests/data/target/target-no-overlayable.apk
Binary files differ
diff --git a/cmds/idmap2/tests/data/target/target.apk b/cmds/idmap2/tests/data/target/target.apk
index 9bcd6dc..251cf46 100644
--- a/cmds/idmap2/tests/data/target/target.apk
+++ b/cmds/idmap2/tests/data/target/target.apk
Binary files differ
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/ActivityView.java b/core/java/android/app/ActivityView.java
index b56c00e..fbf1f59 100644
--- a/core/java/android/app/ActivityView.java
+++ b/core/java/android/app/ActivityView.java
@@ -22,6 +22,7 @@
 import static android.view.Display.INVALID_DISPLAY;
 
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.annotation.TestApi;
 import android.app.ActivityManager.StackInfo;
 import android.content.ComponentName;
@@ -324,16 +325,17 @@
      * this method can be called.
      *
      * @param pendingIntent Intent used to launch an activity.
+     * @param fillInIntent Additional Intent data, see {@link Intent#fillIn Intent.fillIn()}.
      * @param options options for the activity
      *
      * @see StateCallback
      * @see #startActivity(Intent)
      */
-    public void startActivity(@NonNull PendingIntent pendingIntent,
+    public void startActivity(@NonNull PendingIntent pendingIntent, @Nullable Intent fillInIntent,
             @NonNull ActivityOptions options) {
         options.setLaunchDisplayId(mVirtualDisplay.getDisplay().getDisplayId());
         try {
-            pendingIntent.send(null /* context */, 0 /* code */, null /* intent */,
+            pendingIntent.send(getContext(), 0 /* code */, fillInIntent,
                     null /* onFinished */, null /* handler */, null /* requiredPermission */,
                     options.toBundle());
         } catch (PendingIntent.CanceledException e) {
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..39fab63 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
@@ -2203,6 +2212,15 @@
     }
 
     @Override
+    public Context createContextAsUser(UserHandle user) {
+        try {
+            return createPackageContextAsUser(getPackageName(), mFlags, user);
+        } catch (NameNotFoundException e) {
+            throw new IllegalStateException("Own package not found: package=" + getPackageName());
+        }
+    }
+
+    @Override
     public Context createContextForSplit(String splitName) throws NameNotFoundException {
         if (!mPackageInfo.getApplicationInfo().requestsIsolatedSplitLoading()) {
             // All Splits are always loaded.
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/app/admin/DevicePolicyManagerInternal.java b/core/java/android/app/admin/DevicePolicyManagerInternal.java
index b873be3..713126e 100644
--- a/core/java/android/app/admin/DevicePolicyManagerInternal.java
+++ b/core/java/android/app/admin/DevicePolicyManagerInternal.java
@@ -24,6 +24,10 @@
 /**
  * Device policy manager local system service interface.
  *
+ * Maintenance note: if you need to expose information from DPMS to lower level services such as
+ * PM/UM/AM/etc, then exposing it from DevicePolicyManagerInternal is not safe because it may cause
+ * lock order inversion. Consider using {@link DevicePolicyCache} instead.
+ *
  * @hide Only for use within the system server.
  */
 public abstract class DevicePolicyManagerInternal {
@@ -81,6 +85,16 @@
     public abstract boolean isActiveAdminWithPolicy(int uid, int reqPolicy);
 
     /**
+     * Checks if an app with given uid is the active supervision admin.
+     *
+     * <p>This takes the DPMS lock. DO NOT call from PM/UM/AM with their lock held.
+     *
+     * @param uid App uid.
+     * @return true if the uid is the active supervision app.
+     */
+    public abstract boolean isActiveSupervisionApp(int uid);
+
+    /**
      * Creates an intent to show the admin support dialog to say that an action is disallowed by
      * the device/profile owner.
      *
diff --git a/core/java/android/app/usage/UsageStatsManager.java b/core/java/android/app/usage/UsageStatsManager.java
index 656f474..1f13a1e 100644
--- a/core/java/android/app/usage/UsageStatsManager.java
+++ b/core/java/android/app/usage/UsageStatsManager.java
@@ -801,8 +801,8 @@
      *                       {@link #EXTRA_TIME_USED}. Cannot be {@code null} unless the observer is
      *                       being registered with a {@code timeUsed} equal to or greater than
      *                       {@code timeLimit}.
-     * @throws SecurityException if the caller doesn't have both SUSPEND_APPS and OBSERVE_APP_USAGE
-     *                           permissions.
+     * @throws SecurityException if the caller is neither the active supervision app nor does it
+     *                           have both SUSPEND_APPS and OBSERVE_APP_USAGE permissions.
      * @hide
      */
     @SystemApi
@@ -827,8 +827,8 @@
      * an observer that was already unregistered or never registered will have no effect.
      *
      * @param observerId The id of the observer that was previously registered.
-     * @throws SecurityException if the caller doesn't have both SUSPEND_APPS and OBSERVE_APP_USAGE
-     *                           permissions.
+     * @throws SecurityException if the caller is neither the active supervision app nor does it
+     *                         have both SUSPEND_APPS and OBSERVE_APP_USAGE permissions.
      * @hide
      */
     @SystemApi
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 08817e0..2dde3ae 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -5217,8 +5217,9 @@
      */
     @SystemApi
     @TestApi
+    @NonNull
     public Context createPackageContextAsUser(
-            String packageName, @CreatePackageOptions int flags, UserHandle user)
+            @NonNull String packageName, @CreatePackageOptions int flags, @NonNull UserHandle user)
             throws PackageManager.NameNotFoundException {
         if (Build.IS_ENG) {
             throw new IllegalStateException("createPackageContextAsUser not overridden!");
@@ -5227,6 +5228,23 @@
     }
 
     /**
+     * Similar to {@link #createPackageContext(String, int)}, but for the own package with a
+     * different {@link UserHandle}. For example, {@link #getContentResolver()}
+     * will open any {@link Uri} as the given user.
+     *
+     * @hide
+     */
+    @SystemApi
+    @TestApi
+    @NonNull
+    public Context createContextAsUser(@NonNull UserHandle user) {
+        if (Build.IS_ENG) {
+            throw new IllegalStateException("createContextAsUser not overridden!");
+        }
+        return this;
+    }
+
+    /**
      * Creates a context given an {@link android.content.pm.ApplicationInfo}.
      *
      * @hide
diff --git a/core/java/android/content/ContextWrapper.java b/core/java/android/content/ContextWrapper.java
index 0859f97..f7cd51e 100644
--- a/core/java/android/content/ContextWrapper.java
+++ b/core/java/android/content/ContextWrapper.java
@@ -885,6 +885,12 @@
 
     /** @hide */
     @Override
+    public Context createContextAsUser(UserHandle user) {
+        return mBase.createContextAsUser(user);
+    }
+
+    /** @hide */
+    @Override
     @UnsupportedAppUsage
     public Context createApplicationContext(ApplicationInfo application,
             int flags) throws PackageManager.NameNotFoundException {
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/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index 111a8c4..5c65238 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -3265,42 +3265,77 @@
 
         /**
          * Called when the framework connects and has declared a new network ready for use.
-         * This callback may be called more than once if the {@link Network} that is
-         * satisfying the request changes. This will always immediately be followed by a
-         * call to {@link #onCapabilitiesChanged(Network, NetworkCapabilities)} then by a
-         * call to {@link #onLinkPropertiesChanged(Network, LinkProperties)}, and a call to
-         * {@link #onBlockedStatusChanged(Network, boolean)}.
+         *
+         * <p>For callbacks registered with {@link #registerNetworkCallback}, multiple networks may
+         * be available at the same time, and onAvailable will be called for each of these as they
+         * appear.
+         *
+         * <p>For callbacks registered with {@link #requestNetwork} and
+         * {@link #registerDefaultNetworkCallback}, this means the network passed as an argument
+         * is the new best network for this request and is now tracked by this callback ; this
+         * callback will no longer receive method calls about other networks that may have been
+         * passed to this method previously. The previously-best network may have disconnected, or
+         * it may still be around and the newly-best network may simply be better.
+         *
+         * <p>Starting with {@link android.os.Build.VERSION_CODES#O}, this will always immediately
+         * be followed by a call to {@link #onCapabilitiesChanged(Network, NetworkCapabilities)}
+         * then by a call to {@link #onLinkPropertiesChanged(Network, LinkProperties)}, and a call
+         * to {@link #onBlockedStatusChanged(Network, boolean)}.
+         *
+         * <p>Do NOT call {@link #getNetworkCapabilities(Network)} or
+         * {@link #getLinkProperties(Network)} or other synchronous ConnectivityManager methods in
+         * this callback as this is prone to race conditions (there is no guarantee the objects
+         * returned by these methods will be current). Instead, wait for a call to
+         * {@link #onCapabilitiesChanged(Network, NetworkCapabilities)} and
+         * {@link #onLinkPropertiesChanged(Network, LinkProperties)} whose arguments are guaranteed
+         * to be well-ordered with respect to other callbacks.
          *
          * @param network The {@link Network} of the satisfying network.
          */
         public void onAvailable(@NonNull Network network) {}
 
         /**
-         * Called when the network is about to be disconnected.  Often paired with an
-         * {@link NetworkCallback#onAvailable} call with the new replacement network
-         * for graceful handover.  This may not be called if we have a hard loss
-         * (loss without warning).  This may be followed by either a
-         * {@link NetworkCallback#onLost} call or a
-         * {@link NetworkCallback#onAvailable} call for this network depending
-         * on whether we lose or regain it.
+         * Called when the network is about to be lost, typically because there are no outstanding
+         * requests left for it. This may be paired with a {@link NetworkCallback#onAvailable} call
+         * with the new replacement network for graceful handover. This method is not guaranteed
+         * to be called before {@link NetworkCallback#onLost} is called, for example in case a
+         * network is suddenly disconnected.
          *
-         * @param network The {@link Network} that is about to be disconnected.
-         * @param maxMsToLive The time in ms the framework will attempt to keep the
-         *                     network connected.  Note that the network may suffer a
-         *                     hard loss at any time.
+         * <p>Do NOT call {@link #getNetworkCapabilities(Network)} or
+         * {@link #getLinkProperties(Network)} or other synchronous ConnectivityManager methods in
+         * this callback as this is prone to race conditions ; calling these methods while in a
+         * callback may return an outdated or even a null object.
+         *
+         * @param network The {@link Network} that is about to be lost.
+         * @param maxMsToLive The time in milliseconds the system intends to keep the network
+         *                    connected for graceful handover; note that the network may still
+         *                    suffer a hard loss at any time.
          */
         public void onLosing(@NonNull Network network, int maxMsToLive) {}
 
         /**
-         * Called when the framework has a hard loss of the network or when the
-         * graceful failure ends.
+         * Called when a network disconnects or otherwise no longer satisfies this request or
+         * callback.
+         *
+         * <p>If the callback was registered with requestNetwork() or
+         * registerDefaultNetworkCallback(), it will only be invoked against the last network
+         * returned by onAvailable() when that network is lost and no other network satisfies
+         * the criteria of the request.
+         *
+         * <p>If the callback was registered with registerNetworkCallback() it will be called for
+         * each network which no longer satisfies the criteria of the callback.
+         *
+         * <p>Do NOT call {@link #getNetworkCapabilities(Network)} or
+         * {@link #getLinkProperties(Network)} or other synchronous ConnectivityManager methods in
+         * this callback as this is prone to race conditions ; calling these methods while in a
+         * callback may return an outdated or even a null object.
          *
          * @param network The {@link Network} lost.
          */
         public void onLost(@NonNull Network network) {}
 
         /**
-         * Called if no network is found in the timeout time specified in
+         * Called if no network is found within the timeout time specified in
          * {@link #requestNetwork(NetworkRequest, NetworkCallback, int)} call or if the
          * requested network request cannot be fulfilled (whether or not a timeout was
          * specified). When this callback is invoked the associated
@@ -3310,8 +3345,15 @@
         public void onUnavailable() {}
 
         /**
-         * Called when the network the framework connected to for this request
-         * changes capabilities but still satisfies the stated need.
+         * Called when the network corresponding to this request changes capabilities but still
+         * satisfies the requested criteria.
+         *
+         * <p>Starting with {@link android.os.Build.VERSION_CODES#O} this method is guaranteed
+         * to be called immediately after {@link #onAvailable}.
+         *
+         * <p>Do NOT call {@link #getLinkProperties(Network)} or other synchronous
+         * ConnectivityManager methods in this callback as this is prone to race conditions :
+         * calling these methods while in a callback may return an outdated or even a null object.
          *
          * @param network The {@link Network} whose capabilities have changed.
          * @param networkCapabilities The new {@link android.net.NetworkCapabilities} for this
@@ -3321,8 +3363,14 @@
                 @NonNull NetworkCapabilities networkCapabilities) {}
 
         /**
-         * Called when the network the framework connected to for this request
-         * changes {@link LinkProperties}.
+         * Called when the network corresponding to this request changes {@link LinkProperties}.
+         *
+         * <p>Starting with {@link android.os.Build.VERSION_CODES#O} this method is guaranteed
+         * to be called immediately after {@link #onAvailable}.
+         *
+         * <p>Do NOT call {@link #getNetworkCapabilities(Network)} or other synchronous
+         * ConnectivityManager methods in this callback as this is prone to race conditions :
+         * calling these methods while in a callback may return an outdated or even a null object.
          *
          * @param network The {@link Network} whose link properties have changed.
          * @param linkProperties The new {@link LinkProperties} for this network.
@@ -3331,12 +3379,20 @@
                 @NonNull LinkProperties linkProperties) {}
 
         /**
-         * Called when the network the framework connected to for this request
-         * goes into {@link NetworkInfo.State#SUSPENDED}.
-         * This generally means that while the TCP connections are still live,
-         * temporarily network data fails to transfer.  Specifically this is used
-         * on cellular networks to mask temporary outages when driving through
-         * a tunnel, etc.
+         * Called when the network the framework connected to for this request suspends data
+         * transmission temporarily.
+         *
+         * <p>This generally means that while the TCP connections are still live temporarily
+         * network data fails to transfer. To give a specific example, this is used on cellular
+         * networks to mask temporary outages when driving through a tunnel, etc. In general this
+         * means read operations on sockets on this network will block once the buffers are
+         * drained, and write operations will block once the buffers are full.
+         *
+         * <p>Do NOT call {@link #getNetworkCapabilities(Network)} or
+         * {@link #getLinkProperties(Network)} or other synchronous ConnectivityManager methods in
+         * this callback as this is prone to race conditions (there is no guarantee the objects
+         * returned by these methods will be current).
+         *
          * @hide
          */
         public void onNetworkSuspended(@NonNull Network network) {}
@@ -3345,6 +3401,12 @@
          * Called when the network the framework connected to for this request
          * returns from a {@link NetworkInfo.State#SUSPENDED} state. This should always be
          * preceded by a matching {@link NetworkCallback#onNetworkSuspended} call.
+
+         * <p>Do NOT call {@link #getNetworkCapabilities(Network)} or
+         * {@link #getLinkProperties(Network)} or other synchronous ConnectivityManager methods in
+         * this callback as this is prone to race conditions : calling these methods while in a
+         * callback may return an outdated or even a null object.
+         *
          * @hide
          */
         public void onNetworkResumed(@NonNull Network network) {}
@@ -3352,6 +3414,11 @@
         /**
          * Called when access to the specified network is blocked or unblocked.
          *
+         * <p>Do NOT call {@link #getNetworkCapabilities(Network)} or
+         * {@link #getLinkProperties(Network)} or other synchronous ConnectivityManager methods in
+         * this callback as this is prone to race conditions : calling these methods while in a
+         * callback may return an outdated or even a null object.
+         *
          * @param network The {@link Network} whose blocked status has changed.
          * @param blocked The blocked status of this {@link Network}.
          */
@@ -3588,13 +3655,51 @@
     /**
      * Request a network to satisfy a set of {@link android.net.NetworkCapabilities}.
      *
-     * This {@link NetworkRequest} will live until released via
-     * {@link #unregisterNetworkCallback(NetworkCallback)} or the calling application exits. A
-     * version of the method which takes a timeout is
-     * {@link #requestNetwork(NetworkRequest, NetworkCallback, int)}.
-     * Status of the request can be followed by listening to the various
-     * callbacks described in {@link NetworkCallback}.  The {@link Network}
-     * can be used to direct traffic to the network.
+     * <p>This method will attempt to find the best network that matches the passed
+     * {@link NetworkRequest}, and to bring up one that does if none currently satisfies the
+     * criteria. The platform will evaluate which network is the best at its own discretion.
+     * Throughput, latency, cost per byte, policy, user preference and other considerations
+     * may be factored in the decision of what is considered the best network.
+     *
+     * <p>As long as this request is outstanding, the platform will try to maintain the best network
+     * matching this request, while always attempting to match the request to a better network if
+     * possible. If a better match is found, the platform will switch this request to the now-best
+     * network and inform the app of the newly best network by invoking
+     * {@link NetworkCallback#onAvailable(Network)} on the provided callback. Note that the platform
+     * will not try to maintain any other network than the best one currently matching the request:
+     * a network not matching any network request may be disconnected at any time.
+     *
+     * <p>For example, an application could use this method to obtain a connected cellular network
+     * even if the device currently has a data connection over Ethernet. This may cause the cellular
+     * radio to consume additional power. Or, an application could inform the system that it wants
+     * a network supporting sending MMSes and have the system let it know about the currently best
+     * MMS-supporting network through the provided {@link NetworkCallback}.
+     *
+     * <p>The status of the request can be followed by listening to the various callbacks described
+     * in {@link NetworkCallback}. The {@link Network} object passed to the callback methods can be
+     * used to direct traffic to the network (although accessing some networks may be subject to
+     * holding specific permissions). Callers will learn about the specific characteristics of the
+     * network through
+     * {@link NetworkCallback#onCapabilitiesChanged(Network, NetworkCapabilities)} and
+     * {@link NetworkCallback#onLinkPropertiesChanged(Network, LinkProperties)}. The methods of the
+     * provided {@link NetworkCallback} will only be invoked due to changes in the best network
+     * matching the request at any given time; therefore when a better network matching the request
+     * becomes available, the {@link NetworkCallback#onAvailable(Network)} method is called
+     * with the new network after which no further updates are given about the previously-best
+     * network, unless it becomes the best again at some later time. All callbacks are invoked
+     * in order on the same thread, which by default is a thread created by the framework running
+     * in the app.
+     * {@see #requestNetwork(NetworkRequest, NetworkCallback, Handler)} to change where the
+     * callbacks are invoked.
+     *
+     * <p>This{@link NetworkRequest} will live until released via
+     * {@link #unregisterNetworkCallback(NetworkCallback)} or the calling application exits, at
+     * which point the system may let go of the network at any time.
+     *
+     * <p>A version of this method which takes a timeout is
+     * {@link #requestNetwork(NetworkRequest, NetworkCallback, int)}, that an app can use to only
+     * wait for a limited amount of time for the network to become unavailable.
+     *
      * <p>It is presently unsupported to request a network with mutable
      * {@link NetworkCapabilities} such as
      * {@link NetworkCapabilities#NET_CAPABILITY_VALIDATED} or
@@ -3602,7 +3707,7 @@
      * as these {@code NetworkCapabilities} represent states that a particular
      * network may never attain, and whether a network will attain these states
      * is unknown prior to bringing up the network so the framework does not
-     * know how to go about satisfing a request with these capabilities.
+     * know how to go about satisfying a request with these capabilities.
      *
      * <p>This method requires the caller to hold either the
      * {@link android.Manifest.permission#CHANGE_NETWORK_STATE} permission
@@ -3625,34 +3730,17 @@
     /**
      * Request a network to satisfy a set of {@link android.net.NetworkCapabilities}.
      *
-     * This {@link NetworkRequest} will live until released via
-     * {@link #unregisterNetworkCallback(NetworkCallback)} or the calling application exits. A
-     * version of the method which takes a timeout is
-     * {@link #requestNetwork(NetworkRequest, NetworkCallback, int)}.
-     * Status of the request can be followed by listening to the various
-     * callbacks described in {@link NetworkCallback}.  The {@link Network}
-     * can be used to direct traffic to the network.
-     * <p>It is presently unsupported to request a network with mutable
-     * {@link NetworkCapabilities} such as
-     * {@link NetworkCapabilities#NET_CAPABILITY_VALIDATED} or
-     * {@link NetworkCapabilities#NET_CAPABILITY_CAPTIVE_PORTAL}
-     * as these {@code NetworkCapabilities} represent states that a particular
-     * network may never attain, and whether a network will attain these states
-     * is unknown prior to bringing up the network so the framework does not
-     * know how to go about satisfying a request with these capabilities.
+     * This method behaves identically to {@link #requestNetwork(NetworkRequest, NetworkCallback)}
+     * but runs all the callbacks on the passed Handler.
      *
-     * <p>This method requires the caller to hold either the
-     * {@link android.Manifest.permission#CHANGE_NETWORK_STATE} permission
-     * or the ability to modify system settings as determined by
-     * {@link android.provider.Settings.System#canWrite}.</p>
+     * <p>This method has the same permission requirements as
+     * {@link #requestNetwork(NetworkRequest, NetworkCallback)} and throws the same exceptions in
+     * the same conditions.
      *
      * @param request {@link NetworkRequest} describing this request.
      * @param networkCallback The {@link NetworkCallback} to be utilized for this request. Note
      *                        the callback must not be shared - it uniquely specifies this request.
      * @param handler {@link Handler} to specify the thread upon which the callback will be invoked.
-     * @throws IllegalArgumentException if {@code request} contains invalid network capabilities.
-     * @throws SecurityException if missing the appropriate permissions.
-     * @throws RuntimeException if request limit per UID is exceeded.
      */
     public void requestNetwork(@NonNull NetworkRequest request,
             @NonNull NetworkCallback networkCallback, @NonNull Handler handler) {
@@ -3677,10 +3765,9 @@
      * timeout) - {@link #registerNetworkCallback(NetworkRequest, NetworkCallback)} is provided
      * for that purpose. Calling this method will attempt to bring up the requested network.
      *
-     * <p>This method requires the caller to hold either the
-     * {@link android.Manifest.permission#CHANGE_NETWORK_STATE} permission
-     * or the ability to modify system settings as determined by
-     * {@link android.provider.Settings.System#canWrite}.</p>
+     * <p>This method has the same permission requirements as
+     * {@link #requestNetwork(NetworkRequest, NetworkCallback)} and throws the same exceptions in
+     * the same conditions.
      *
      * @param request {@link NetworkRequest} describing this request.
      * @param networkCallback The {@link NetworkCallback} to be utilized for this request. Note
@@ -3688,9 +3775,6 @@
      * @param timeoutMs The time in milliseconds to attempt looking for a suitable network
      *                  before {@link NetworkCallback#onUnavailable()} is called. The timeout must
      *                  be a positive value (i.e. >0).
-     * @throws IllegalArgumentException if {@code request} contains invalid network capabilities.
-     * @throws SecurityException if missing the appropriate permissions.
-     * @throws RuntimeException if request limit per UID is exceeded.
      */
     public void requestNetwork(@NonNull NetworkRequest request,
             @NonNull NetworkCallback networkCallback, int timeoutMs) {
@@ -3703,21 +3787,13 @@
      * Request a network to satisfy a set of {@link android.net.NetworkCapabilities}, limited
      * by a timeout.
      *
-     * This function behaves identically to the version without timeout, but if a suitable
-     * network is not found within the given time (in milliseconds) the
-     * {@link NetworkCallback#onUnavailable} callback is called. The request can still be
-     * released normally by calling {@link #unregisterNetworkCallback(NetworkCallback)} but does
-     * not have to be released if timed-out (it is automatically released). Unregistering a
-     * request that timed out is not an error.
+     * This method behaves identically to
+     * {@link #requestNetwork(NetworkRequest, NetworkCallback, int)} but runs all the callbacks
+     * on the passed Handler.
      *
-     * <p>Do not use this method to poll for the existence of specific networks (e.g. with a small
-     * timeout) - {@link #registerNetworkCallback(NetworkRequest, NetworkCallback)} is provided
-     * for that purpose. Calling this method will attempt to bring up the requested network.
-     *
-     * <p>This method requires the caller to hold either the
-     * {@link android.Manifest.permission#CHANGE_NETWORK_STATE} permission
-     * or the ability to modify system settings as determined by
-     * {@link android.provider.Settings.System#canWrite}.</p>
+     * <p>This method has the same permission requirements as
+     * {@link #requestNetwork(NetworkRequest, NetworkCallback, int)} and throws the same exceptions
+     * in the same conditions.
      *
      * @param request {@link NetworkRequest} describing this request.
      * @param networkCallback The {@link NetworkCallback} to be utilized for this request. Note
@@ -3725,9 +3801,6 @@
      * @param handler {@link Handler} to specify the thread upon which the callback will be invoked.
      * @param timeoutMs The time in milliseconds to attempt looking for a suitable network
      *                  before {@link NetworkCallback#onUnavailable} is called.
-     * @throws IllegalArgumentException if {@code request} contains invalid network capabilities.
-     * @throws SecurityException if missing the appropriate permissions.
-     * @throws RuntimeException if request limit per UID is exceeded.
      */
     public void requestNetwork(@NonNull NetworkRequest request,
             @NonNull NetworkCallback networkCallback, @NonNull Handler handler, int timeoutMs) {
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/GraphicsEnvironment.java b/core/java/android/os/GraphicsEnvironment.java
index 7a70e93..947b0a1 100644
--- a/core/java/android/os/GraphicsEnvironment.java
+++ b/core/java/android/os/GraphicsEnvironment.java
@@ -69,6 +69,8 @@
     private static final String METADATA_DRIVER_BUILD_TIME = "com.android.gamedriver.build_time";
     private static final String METADATA_DEVELOPER_DRIVER_ENABLE =
             "com.android.graphics.developerdriver.enable";
+    private static final String METADATA_INJECT_LAYERS_ENABLE =
+            "com.android.graphics.injectLayers.enable";
     private static final String ANGLE_RULES_FILE = "a4a_rules.json";
     private static final String ANGLE_TEMP_RULES = "debug.angle.rules";
     private static final String ACTION_ANGLE_FOR_ANDROID = "android.app.action.ANGLE_FOR_ANDROID";
@@ -100,14 +102,16 @@
     public void setup(Context context, Bundle coreSettings) {
         final PackageManager pm = context.getPackageManager();
         final String packageName = context.getPackageName();
+        final ApplicationInfo appInfoWithMetaData =
+                getAppInfoWithMetadata(context, pm, packageName);
         Trace.traceBegin(Trace.TRACE_TAG_GRAPHICS, "setupGpuLayers");
-        setupGpuLayers(context, coreSettings, pm, packageName);
+        setupGpuLayers(context, coreSettings, pm, packageName, appInfoWithMetaData);
         Trace.traceEnd(Trace.TRACE_TAG_GRAPHICS);
         Trace.traceBegin(Trace.TRACE_TAG_GRAPHICS, "setupAngle");
         setupAngle(context, coreSettings, pm, packageName);
         Trace.traceEnd(Trace.TRACE_TAG_GRAPHICS);
         Trace.traceBegin(Trace.TRACE_TAG_GRAPHICS, "chooseDriver");
-        if (!chooseDriver(context, coreSettings, pm, packageName)) {
+        if (!chooseDriver(context, coreSettings, pm, packageName, appInfoWithMetaData)) {
             setGpuStats(SYSTEM_DRIVER_NAME, SYSTEM_DRIVER_VERSION_NAME, SYSTEM_DRIVER_VERSION_CODE,
                     SystemProperties.getLong(PROPERTY_GFX_DRIVER_BUILD_TIME, 0), packageName,
                     getVulkanVersion(pm));
@@ -180,6 +184,14 @@
     }
 
     /**
+     * Check whether application is has set the manifest metadata for layer injection.
+     */
+    private static boolean canInjectLayers(ApplicationInfo ai) {
+        return (ai.metaData != null && ai.metaData.getBoolean(METADATA_INJECT_LAYERS_ENABLE)
+                && setInjectLayersPrSetDumpable());
+    }
+
+    /**
      * Store the layer paths available to the loader.
      */
     public void setLayerPaths(ClassLoader classLoader,
@@ -225,15 +237,16 @@
      * If debuggable, check for additional debug settings
      */
     private void setupGpuLayers(
-            Context context, Bundle coreSettings, PackageManager pm, String packageName) {
+            Context context, Bundle coreSettings, PackageManager pm, String packageName,
+            ApplicationInfo ai) {
         String layerPaths = "";
 
         // Only enable additional debug functionality if the following conditions are met:
-        // 1. App is debuggable or device is rooted
+        // 1. App is debuggable or device is rooted or layer injection metadata flag is true
         // 2. ENABLE_GPU_DEBUG_LAYERS is true
         // 3. Package name is equal to GPU_DEBUG_APP
 
-        if (isDebuggable(context) || (getCanLoadSystemLibraries() == 1)) {
+        if (isDebuggable(context) || (getCanLoadSystemLibraries() == 1) || canInjectLayers(ai)) {
 
             final int enable = coreSettings.getInt(Settings.Global.ENABLE_GPU_DEBUG_LAYERS, 0);
 
@@ -343,6 +356,20 @@
         return -1;
     }
 
+    private static ApplicationInfo getAppInfoWithMetadata(Context context,
+                                                          PackageManager pm, String packageName) {
+        ApplicationInfo ai;
+        try {
+            // Get the ApplicationInfo from PackageManager so that metadata fields present.
+            ai = pm.getApplicationInfo(packageName, PackageManager.GET_META_DATA);
+        } catch (PackageManager.NameNotFoundException e) {
+            // Unlikely to fail for applications, but in case of failure, fall back to use the
+            // ApplicationInfo from context directly.
+            ai = context.getApplicationInfo();
+        }
+        return ai;
+    }
+
     private static String getDriverForPkg(Context context, Bundle bundle, String packageName) {
         final String allUseAngle;
         if (bundle != null) {
@@ -693,8 +720,7 @@
     /**
      * Return the driver package name to use. Return null for system driver.
      */
-    private static String chooseDriverInternal(
-            Context context, Bundle coreSettings, PackageManager pm, String packageName) {
+    private static String chooseDriverInternal(Bundle coreSettings, ApplicationInfo ai) {
         final String gameDriver = SystemProperties.get(PROPERTY_GFX_DRIVER);
         final boolean hasGameDriver = gameDriver != null && !gameDriver.isEmpty();
 
@@ -709,15 +735,6 @@
         // To minimize risk of driver updates crippling the device beyond user repair, never use an
         // updated driver for privileged or non-updated system apps. Presumably pre-installed apps
         // were tested thoroughly with the pre-installed driver.
-        ApplicationInfo ai;
-        try {
-            // Get the ApplicationInfo from PackageManager so that metadata fields present.
-            ai = pm.getApplicationInfo(packageName, PackageManager.GET_META_DATA);
-        } catch (PackageManager.NameNotFoundException e) {
-            // Unlikely to fail for applications, but in case of failure, fall back to use the
-            // ApplicationInfo from context directly.
-            ai = context.getApplicationInfo();
-        }
         if (ai.isPrivilegedApp() || (ai.isSystemApp() && !ai.isUpdatedSystemApp())) {
             if (DEBUG) Log.v(TAG, "Ignoring driver package for privileged/non-updated system app.");
             return null;
@@ -797,9 +814,9 @@
      * Choose whether the current process should use the builtin or an updated driver.
      */
     private static boolean chooseDriver(
-            Context context, Bundle coreSettings, PackageManager pm, String packageName) {
-        final String driverPackageName = chooseDriverInternal(context, coreSettings, pm,
-                packageName);
+            Context context, Bundle coreSettings, PackageManager pm, String packageName,
+            ApplicationInfo ai) {
+        final String driverPackageName = chooseDriverInternal(coreSettings, ai);
         if (driverPackageName == null) {
             return false;
         }
@@ -911,4 +928,5 @@
     private static native void setAngleInfo(String path, String appPackage, String devOptIn,
             FileDescriptor rulesFd, long rulesOffset, long rulesLength);
     private static native boolean getShouldUseAngle(String packageName);
+    private static native boolean setInjectLayersPrSetDumpable();
 }
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/os/Process.java b/core/java/android/os/Process.java
index 76e728a..43b9c67 100644
--- a/core/java/android/os/Process.java
+++ b/core/java/android/os/Process.java
@@ -420,10 +420,9 @@
      * Background thread group - All threads in
      * this group are scheduled with a reduced share of the CPU.
      * Value is same as constant SP_BACKGROUND of enum SchedPolicy.
-     * FIXME rename to THREAD_GROUP_BACKGROUND.
      * @hide
      */
-    public static final int THREAD_GROUP_BG_NONINTERACTIVE = 0;
+    public static final int THREAD_GROUP_BACKGROUND = 0;
 
     /**
      * Foreground thread group - All threads in
@@ -809,7 +808,7 @@
      *
      * group == THREAD_GROUP_DEFAULT means to move all non-background priority
      * threads to the foreground scheduling group, but to leave background
-     * priority threads alone.  group == THREAD_GROUP_BG_NONINTERACTIVE moves all
+     * priority threads alone.  group == THREAD_GROUP_BACKGROUND moves all
      * threads, regardless of priority, to the background scheduling group.
      * group == THREAD_GROUP_FOREGROUND is not allowed.
      *
diff --git a/core/java/android/provider/DeviceConfig.java b/core/java/android/provider/DeviceConfig.java
index fd1381a..e456c8a 100644
--- a/core/java/android/provider/DeviceConfig.java
+++ b/core/java/android/provider/DeviceConfig.java
@@ -403,9 +403,37 @@
     @TestApi
     @RequiresPermission(READ_DEVICE_CONFIG)
     public static String getProperty(@NonNull String namespace, @NonNull String name) {
+        // Fetch all properties for the namespace at once and cache them in the local process, so we
+        // incur the cost of the IPC less often. Lookups happen much more frequently than updates,
+        // and we want to optimize the former.
+        return getProperties(namespace, name).getString(name, null);
+    }
+
+    /**
+     * Look up the values of multiple properties for a particular namespace. The lookup is atomic,
+     * such that the values of these properties cannot change between the time when the first is
+     * fetched and the time when the last is fetched.
+     *
+     * TODO: reference setProperties when it is added.
+     *
+     * @param namespace The namespace containing the properties to look up.
+     * @param names     The names of properties to look up, or empty to fetch all properties for the
+     *                  given namespace.
+     * @return {@link Properties} object containing the requested properties. This reflects the
+     *     state of these properties at the time of the lookup, and is not updated to reflect any
+     *     future changes. The keyset of this Properties object will contain only the intersection
+     *     of properties already set and properties requested via the names parameter. Properties
+     *     that are already set but were not requested will not be contained here. Properties that
+     *     are not set, but were requested will not be contained here either.
+     * @hide
+     */
+    @SystemApi
+    @NonNull
+    @RequiresPermission(READ_DEVICE_CONFIG)
+    public static Properties getProperties(@NonNull String namespace, @NonNull String ... names) {
         ContentResolver contentResolver = ActivityThread.currentApplication().getContentResolver();
-        String compositeName = createCompositeName(namespace, name);
-        return Settings.Config.getString(contentResolver, compositeName);
+        return new Properties(namespace,
+                Settings.Config.getStrings(contentResolver, namespace, Arrays.asList(names)));
     }
 
     /**
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index e4e8bf7..457dcc0 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -84,8 +84,10 @@
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.net.URISyntaxException;
+import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.HashSet;
+import java.util.List;
 import java.util.Locale;
 import java.util.Map;
 import java.util.Set;
@@ -2248,7 +2250,7 @@
         private static final String NAME_EQ_PLACEHOLDER = "name=?";
 
         // Must synchronize on 'this' to access mValues and mValuesVersion.
-        private final HashMap<String, String> mValues = new HashMap<>();
+        private final ArrayMap<String, String> mValues = new ArrayMap<>();
 
         private final Uri mUri;
         @UnsupportedAppUsage
@@ -2258,15 +2260,22 @@
         // for the fast path of retrieving settings.
         private final String mCallGetCommand;
         private final String mCallSetCommand;
+        private final String mCallListCommand;
 
         @GuardedBy("this")
         private GenerationTracker mGenerationTracker;
 
         public NameValueCache(Uri uri, String getCommand, String setCommand,
                 ContentProviderHolder providerHolder) {
+            this(uri, getCommand, setCommand, null, providerHolder);
+        }
+
+        NameValueCache(Uri uri, String getCommand, String setCommand, String listCommand,
+                ContentProviderHolder providerHolder) {
             mUri = uri;
             mCallGetCommand = getCommand;
             mCallSetCommand = setCommand;
+            mCallListCommand = listCommand;
             mProviderHolder = providerHolder;
         }
 
@@ -2448,8 +2457,8 @@
 
                 String value = c.moveToNext() ? c.getString(0) : null;
                 synchronized (NameValueCache.this) {
-                    if(mGenerationTracker != null &&
-                            currentGeneration == mGenerationTracker.getCurrentGeneration()) {
+                    if (mGenerationTracker != null
+                            && currentGeneration == mGenerationTracker.getCurrentGeneration()) {
                         mValues.put(name, value);
                     }
                 }
@@ -2466,6 +2475,141 @@
             }
         }
 
+        public ArrayMap<String, String> getStringsForPrefix(ContentResolver cr, String prefix,
+                List<String> names) {
+            ArrayMap<String, String> keyValues = new ArrayMap<>();
+            int currentGeneration = -1;
+
+            synchronized (NameValueCache.this) {
+                if (mGenerationTracker != null) {
+                    if (mGenerationTracker.isGenerationChanged()) {
+                        if (DEBUG) {
+                            Log.i(TAG, "Generation changed for type:" + mUri.getPath()
+                                    + " in package:" + cr.getPackageName());
+                        }
+                        mValues.clear();
+                    } else {
+                        boolean prefixCached = false;
+                        int size = mValues.size();
+                        for (int i = 0; i < size; ++i) {
+                            if (mValues.keyAt(i).startsWith(prefix + "/")) {
+                                prefixCached = true;
+                                break;
+                            }
+                        }
+                        if (prefixCached) {
+                            if (!names.isEmpty()) {
+                                for (String name : names) {
+                                    if (mValues.containsKey(name)) {
+                                        keyValues.put(name, mValues.get(name));
+                                    }
+                                }
+                            } else {
+                                for (int i = 0; i < size; ++i) {
+                                    String key = mValues.keyAt(i);
+                                    if (key.startsWith(prefix + "/")) {
+                                        keyValues.put(key, mValues.get(key));
+                                    }
+                                }
+                            }
+                            return keyValues;
+                        }
+                    }
+                    if (mGenerationTracker != null) {
+                        currentGeneration = mGenerationTracker.getCurrentGeneration();
+                    }
+                }
+            }
+
+            if (mCallListCommand == null) {
+                // No list command specified, return empty map
+                return keyValues;
+            }
+            IContentProvider cp = mProviderHolder.getProvider(cr);
+
+            try {
+                Bundle args = new Bundle();
+                args.putString(Settings.CALL_METHOD_PREFIX_KEY, prefix);
+                boolean needsGenerationTracker = false;
+                synchronized (NameValueCache.this) {
+                    if (mGenerationTracker == null) {
+                        needsGenerationTracker = true;
+                        args.putString(CALL_METHOD_TRACK_GENERATION_KEY, null);
+                        if (DEBUG) {
+                            Log.i(TAG, "Requested generation tracker for type: "
+                                    + mUri.getPath() + " in package:" + cr.getPackageName());
+                        }
+                    }
+                }
+
+                // Fetch all flags for the namespace at once for caching purposes
+                Bundle b = cp.call(cr.getPackageName(), mProviderHolder.mUri.getAuthority(),
+                        mCallListCommand, null, args);
+                if (b == null) {
+                    // Invalid response, return an empty map
+                    return keyValues;
+                }
+
+                // All flags for the namespace
+                Map<String, String> flagsToValues =
+                        (HashMap) b.getSerializable(Settings.NameValueTable.VALUE);
+                // Only the flags requested by the caller
+                if (!names.isEmpty()) {
+                    for (Map.Entry<String, String> flag : flagsToValues.entrySet()) {
+                        if (names.contains(flag.getKey())) {
+                            keyValues.put(flag.getKey(), flag.getValue());
+                        }
+                    }
+                } else {
+                    keyValues.putAll(flagsToValues);
+                }
+
+                synchronized (NameValueCache.this) {
+                    if (needsGenerationTracker) {
+                        MemoryIntArray array = b.getParcelable(
+                                CALL_METHOD_TRACK_GENERATION_KEY);
+                        final int index = b.getInt(
+                                CALL_METHOD_GENERATION_INDEX_KEY, -1);
+                        if (array != null && index >= 0) {
+                            final int generation = b.getInt(
+                                    CALL_METHOD_GENERATION_KEY, 0);
+                            if (DEBUG) {
+                                Log.i(TAG, "Received generation tracker for type:"
+                                        + mUri.getPath() + " in package:"
+                                        + cr.getPackageName() + " with index:" + index);
+                            }
+                            if (mGenerationTracker != null) {
+                                mGenerationTracker.destroy();
+                            }
+                            mGenerationTracker = new GenerationTracker(array, index,
+                                    generation, () -> {
+                                synchronized (NameValueCache.this) {
+                                    Log.e(TAG, "Error accessing generation tracker"
+                                            + " - removing");
+                                    if (mGenerationTracker != null) {
+                                        GenerationTracker generationTracker =
+                                                mGenerationTracker;
+                                        mGenerationTracker = null;
+                                        generationTracker.destroy();
+                                        mValues.clear();
+                                    }
+                                }
+                            });
+                        }
+                    }
+                    if (mGenerationTracker != null && currentGeneration
+                            == mGenerationTracker.getCurrentGeneration()) {
+                        // cache the complete list of flags for the namespace
+                        mValues.putAll(flagsToValues);
+                    }
+                }
+                return keyValues;
+            } catch (RemoteException e) {
+                // Not supported by the remote side, return an empty map
+                return keyValues;
+            }
+        }
+
         public void clearGenerationTrackerForTest() {
             synchronized (NameValueCache.this) {
                 if (mGenerationTracker != null) {
@@ -13499,6 +13643,7 @@
                 DeviceConfig.CONTENT_URI,
                 CALL_METHOD_GET_CONFIG,
                 CALL_METHOD_PUT_CONFIG,
+                CALL_METHOD_LIST_CONFIG,
                 sProviderHolder);
 
         /**
@@ -13515,6 +13660,37 @@
         }
 
         /**
+         * Look up a list of names in the database, based on a common prefix.
+         *
+         * @param resolver to access the database with
+         * @param prefix to apply to all of the names which will be fetched
+         * @param names to look up in the table
+         * @return a non null, but possibly empty, map from name to value for any of the names that
+         *         were found during lookup.
+         *
+         * @hide
+         */
+        @RequiresPermission(Manifest.permission.READ_DEVICE_CONFIG)
+        static Map<String, String> getStrings(@NonNull ContentResolver resolver,
+                @NonNull String prefix, @NonNull List<String> names) {
+            List<String> concatenatedNames = new ArrayList<>(names.size());
+            for (String name : names) {
+                concatenatedNames.add(prefix + "/" + name);
+            }
+
+            ArrayMap<String, String> rawKeyValues = sNameValueCache.getStringsForPrefix(
+                    resolver, prefix, concatenatedNames);
+            int size = rawKeyValues.size();
+            int substringLength = prefix.length() + 1;
+            ArrayMap<String, String> keyValues = new ArrayMap<>(size);
+            for (int i = 0; i < size; ++i) {
+                keyValues.put(rawKeyValues.keyAt(i).substring(substringLength),
+                        rawKeyValues.valueAt(i));
+            }
+            return keyValues;
+        }
+
+        /**
          * Store a name/value pair into the database.
          * <p>
          * Also the method takes an argument whether to make the value the default for this setting.
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..8dd475e 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);
 
@@ -200,7 +199,10 @@
 
     private final CloseGuard mCloseGuard = CloseGuard.get();
     private String mName;
-    long mNativeObject; // package visibility only for Surface.java access
+    /**
+     * @hide
+     */
+    public long mNativeObject;
 
     // TODO: Move this to native.
     private final Object mSizeLock = new Object();
@@ -303,8 +305,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
      */
@@ -319,6 +321,11 @@
     public static final int FX_SURFACE_CONTAINER = 0x00080000;
 
     /**
+     * @hide
+     */
+    public static final int FX_SURFACE_BLAST = 0x00040000;
+
+    /**
      * Mask used for FX values above.
      *
      * @hide
@@ -694,6 +701,14 @@
         }
 
         /**
+         * @hide
+         */
+        public Builder setBLASTLayer() {
+            unsetBufferSize();
+            return setFlags(FX_SURFACE_BLAST, FX_SURFACE_MASK);
+        }
+
+        /**
          * Indicates whether a 'ContainerLayer' is to be constructed.
          *
          * Container layers will not be rendered in any fashion and instead are used
@@ -740,20 +755,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 +1029,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 +1038,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 +1057,6 @@
     /**
      * @hide
      */
-    public static void setAnimationTransaction() {
-        synchronized (SurfaceControl.class) {
-            sGlobalTransaction.setAnimationTransaction();
-        }
-    }
-
-    /**
-     * @hide
-     */
     @UnsupportedAppUsage
     public void setLayer(int zorder) {
         checkNotReleased();
@@ -1080,16 +1068,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 +1161,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 +1169,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 +1198,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) {
@@ -2066,7 +1967,10 @@
         public static final NativeAllocationRegistry sRegistry = new NativeAllocationRegistry(
                 Transaction.class.getClassLoader(),
                 nativeGetNativeTransactionFinalizer(), 512);
-        private long mNativeObject;
+        /**
+         * @hide
+         */
+        public long mNativeObject;
 
         private final ArrayMap<SurfaceControl, Point> mResizedSurfaces = new ArrayMap<>();
         Runnable mFreeNativeResources;
@@ -2302,6 +2206,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 +2225,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 +2251,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 +2274,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..1599afb 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -75,6 +75,7 @@
 import android.graphics.Shader;
 import android.graphics.drawable.ColorDrawable;
 import android.graphics.drawable.Drawable;
+import android.graphics.drawable.GradientDrawable;
 import android.hardware.display.DisplayManagerGlobal;
 import android.net.Uri;
 import android.os.Build;
@@ -4496,8 +4497,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;
@@ -5228,6 +5230,8 @@
 
             sBrokenWindowBackground = targetSdkVersion < Build.VERSION_CODES.Q;
 
+            GradientDrawable.sWrapNegativeAngleMeasurements =
+                    targetSdkVersion >= Build.VERSION_CODES.Q;
             sCompatibilityDone = true;
         }
     }
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index fedd6fb..9ddd84f 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -45,6 +45,7 @@
 import android.content.res.Configuration;
 import android.content.res.Resources;
 import android.content.res.TypedArray;
+import android.graphics.BLASTBufferQueue;
 import android.graphics.Canvas;
 import android.graphics.Color;
 import android.graphics.FrameInfo;
@@ -170,6 +171,8 @@
      */
     private static final boolean MT_RENDERER_AVAILABLE = true;
 
+    private static final boolean USE_BLAST_BUFFERQUEUE = false;
+
     /**
      * If set to 2, the view system will switch from using rectangles retrieved from window to
      * dispatch to the view hierarchy to using {@link InsetsController}, that derives the insets
@@ -475,6 +478,9 @@
     @UnsupportedAppUsage
     public final Surface mSurface = new Surface();
     private final SurfaceControl mSurfaceControl = new SurfaceControl();
+    private SurfaceControl mBlastSurfaceControl;
+
+    private BLASTBufferQueue mBlastBufferQueue;
 
     /**
      * Transaction object that can be used to synchronize child SurfaceControl changes with
@@ -1282,6 +1288,11 @@
             }
             mWindowAttributes.privateFlags |= compatibleWindowFlag;
 
+            if (USE_BLAST_BUFFERQUEUE) {
+                mWindowAttributes.privateFlags =
+                    WindowManager.LayoutParams.PRIVATE_FLAG_USE_BLAST;
+            }
+
             if (mWindowAttributes.preservePreviousSurfaceInsets) {
                 // Restore old surface insets.
                 mWindowAttributes.surfaceInsets.set(
@@ -1629,6 +1640,29 @@
         return mBoundsLayer;
     }
 
+    Surface getOrCreateBLASTSurface(int width, int height) {
+        if (mSurfaceControl == null || !mSurfaceControl.isValid()) {
+            return null;
+        }
+        if (mBlastSurfaceControl == null) {
+            mBlastSurfaceControl = new SurfaceControl.Builder(mSurfaceSession)
+            .setParent(mSurfaceControl)
+            .setName("BLAST")
+            .setBLASTLayer()
+            .build();
+            mBlastBufferQueue = new BLASTBufferQueue(
+                mBlastSurfaceControl, width, height);
+
+        }
+        mBlastBufferQueue.update(mSurfaceControl, width, height);
+
+        mTransaction.show(mBlastSurfaceControl)
+            .reparent(mBlastSurfaceControl, mSurfaceControl)
+            .apply();
+
+        return mBlastBufferQueue.getSurface();
+    }
+    
     private void setBoundsLayerCrop() {
         // mWinFrame is already adjusted for surface insets. So offset it and use it as
         // the cropping bounds.
@@ -1658,6 +1692,13 @@
         }
         mSurface.release();
         mSurfaceControl.release();
+
+        if (mBlastBufferQueue != null) {
+            mTransaction.remove(mBlastSurfaceControl).apply();
+            mBlastSurfaceControl = null;
+            // We should probably add an explicit dispose.
+            mBlastBufferQueue = null;
+        }
     }
 
     /**
@@ -2413,10 +2454,9 @@
                     // will be transparent
                     if (mAttachInfo.mThreadedRenderer != null) {
                         try {
-                            hwInitialized = mAttachInfo.mThreadedRenderer.initialize(
-                                    mSurface);
+                            hwInitialized = mAttachInfo.mThreadedRenderer.initialize(mSurface);
                             if (hwInitialized && (host.mPrivateFlags
-                                    & View.PFLAG_REQUEST_TRANSPARENT_REGIONS) == 0) {
+                                            & View.PFLAG_REQUEST_TRANSPARENT_REGIONS) == 0) {
                                 // Don't pre-allocate if transparent regions
                                 // are requested as they may not be needed
                                 mAttachInfo.mThreadedRenderer.allocateBuffers();
@@ -7139,7 +7179,13 @@
                 mPendingStableInsets, mPendingOutsets, mPendingBackDropFrame, mPendingDisplayCutout,
                 mPendingMergedConfiguration, mSurfaceControl, mTempInsets);
         if (mSurfaceControl.isValid()) {
-            mSurface.copyFrom(mSurfaceControl);
+            if (USE_BLAST_BUFFERQUEUE == false) {
+                mSurface.copyFrom(mSurfaceControl);
+            } else { 
+                mSurface.transferFrom(getOrCreateBLASTSurface(
+                    (int) (mView.getMeasuredWidth() * appScale + 0.5f),
+                    (int) (mView.getMeasuredHeight() * appScale + 0.5f)));
+            }
         } else {
             destroySurface();
         }
@@ -7297,26 +7343,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/WindowManager.java b/core/java/android/view/WindowManager.java
index 001ab66..4a6ef98 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -1834,6 +1834,13 @@
         public static final int PRIVATE_FLAG_COLOR_SPACE_AGNOSTIC = 0x01000000;
 
         /**
+         * Flag to request creation of a BLAST (Buffer as LayerState) Layer.
+         * If not specified the client will receive a BufferQueue layer.
+         * @hide
+         */
+        public static final int PRIVATE_FLAG_USE_BLAST = 0x02000000;
+
+        /**
          * An internal annotation for flags that can be specified to {@link #softInputMode}.
          *
          * @hide
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/android/view/inspector/OWNERS b/core/java/android/view/inspector/OWNERS
index 4554fdc..c2827cc 100644
--- a/core/java/android/view/inspector/OWNERS
+++ b/core/java/android/view/inspector/OWNERS
@@ -1,3 +1,3 @@
 alanv@google.com
 aurimas@google.com
-emberr@google.com
+emberrose@google.com
diff --git a/core/java/com/android/internal/app/procstats/AssociationState.java b/core/java/com/android/internal/app/procstats/AssociationState.java
index 2df5158..0ed2524 100644
--- a/core/java/com/android/internal/app/procstats/AssociationState.java
+++ b/core/java/com/android/internal/app/procstats/AssociationState.java
@@ -16,7 +16,6 @@
 
 package com.android.internal.app.procstats;
 
-
 import android.annotation.Nullable;
 import android.os.Parcel;
 import android.os.SystemClock;
@@ -24,23 +23,37 @@
 import android.service.procstats.PackageAssociationProcessStatsProto;
 import android.service.procstats.PackageAssociationSourceProcessStatsProto;
 import android.util.ArrayMap;
+import android.util.Pair;
 import android.util.Slog;
 import android.util.TimeUtils;
 import android.util.proto.ProtoOutputStream;
 
 import java.io.PrintWriter;
 import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
 import java.util.Objects;
 
 public final class AssociationState {
     private static final String TAG = "ProcessStats";
     private static final boolean DEBUG = false;
 
+    private static final boolean VALIDATE_TIMES = false;
+
     private final ProcessStats mProcessStats;
     private final ProcessStats.PackageState mPackageState;
     private final String mProcessName;
     private final String mName;
 
+    private int mTotalNesting;
+    private long mTotalStartUptime;
+    private int mTotalCount;
+    private long mTotalDuration;
+    private int mTotalActiveNesting;
+    private long mTotalActiveStartUptime;
+    private int mTotalActiveCount;
+    private long mTotalActiveDuration;
+
     public final class SourceState {
         final SourceKey mKey;
         int mProcStateSeq = -1;
@@ -55,7 +68,7 @@
         int mActiveProcState = ProcessStats.STATE_NOTHING;
         long mActiveStartUptime;
         long mActiveDuration;
-        DurationsTable mDurations;
+        DurationsTable mActiveDurations;
 
         SourceState(SourceKey key) {
             mKey = key;
@@ -97,9 +110,9 @@
         public void stop() {
             mNesting--;
             if (mNesting == 0) {
-                mDuration += SystemClock.uptimeMillis() - mStartUptime;
-                mNumActive--;
-                stopTracking(SystemClock.uptimeMillis());
+                final long now = SystemClock.uptimeMillis();
+                mDuration += now - mStartUptime;
+                stopTracking(now);
             }
         }
 
@@ -108,20 +121,25 @@
                 if (mActiveStartUptime == 0) {
                     mActiveStartUptime = now;
                     mActiveCount++;
+                    AssociationState.this.mTotalActiveNesting++;
+                    if (AssociationState.this.mTotalActiveNesting == 1) {
+                        AssociationState.this.mTotalActiveCount++;
+                        AssociationState.this.mTotalActiveStartUptime = now;
+                    }
                 }
                 if (mActiveProcState != mProcState) {
                     if (mActiveProcState != ProcessStats.STATE_NOTHING) {
                         // Currently active proc state changed, need to store the duration
                         // so far and switch tracking to the new proc state.
-                        final long duration = mActiveDuration + now - mActiveStartUptime;
-                        if (duration != 0) {
-                            if (mDurations == null) {
+                        final long addedDuration = mActiveDuration + now - mActiveStartUptime;
+                        mActiveStartUptime = now;
+                        if (addedDuration != 0) {
+                            if (mActiveDurations == null) {
                                 makeDurations();
                             }
-                            mDurations.addDuration(mActiveProcState, duration);
+                            mActiveDurations.addDuration(mActiveProcState, addedDuration);
                             mActiveDuration = 0;
                         }
-                        mActiveStartUptime = now;
                     }
                     mActiveProcState = mProcState;
                 }
@@ -135,21 +153,44 @@
                 if (!mInTrackingList) {
                     Slog.wtf(TAG, "stopActive while not tracking: " + this);
                 }
-                final long duration = mActiveDuration + now - mActiveStartUptime;
-                if (mDurations != null) {
-                    mDurations.addDuration(mActiveProcState, duration);
-                } else {
-                    mActiveDuration = duration;
-                }
+                final long addedDuration = now - mActiveStartUptime;
                 mActiveStartUptime = 0;
+                if (mActiveDurations != null) {
+                    mActiveDurations.addDuration(mActiveProcState, addedDuration);
+                } else {
+                    mActiveDuration += addedDuration;
+                }
+                AssociationState.this.mTotalActiveNesting--;
+                if (AssociationState.this.mTotalActiveNesting == 0) {
+                    AssociationState.this.mTotalActiveDuration += now
+                            - AssociationState.this.mTotalActiveStartUptime;
+                    AssociationState.this.mTotalActiveStartUptime = 0;
+                    if (VALIDATE_TIMES) {
+                        if (mActiveDuration > AssociationState.this.mTotalActiveDuration) {
+                            RuntimeException ex = new RuntimeException();
+                            ex.fillInStackTrace();
+                            Slog.w(TAG, "Source act duration " + mActiveDurations
+                                    + " exceeds total " + AssociationState.this.mTotalActiveDuration
+                                    + " in procstate " + mActiveProcState + " in source "
+                                    + mKey.mProcess + " to assoc "
+                                    + AssociationState.this.mName, ex);
+                        }
+
+                    }
+                }
             }
         }
 
         void makeDurations() {
-            mDurations = new DurationsTable(mProcessStats.mTableData);
+            mActiveDurations = new DurationsTable(mProcessStats.mTableData);
         }
 
         void stopTracking(long now) {
+            AssociationState.this.mTotalNesting--;
+            if (AssociationState.this.mTotalNesting == 0) {
+                AssociationState.this.mTotalDuration += now
+                        - AssociationState.this.mTotalStartUptime;
+            }
             stopActive(now);
             if (mInTrackingList) {
                 mInTrackingList = false;
@@ -181,7 +222,17 @@
         }
     }
 
-    private final static class SourceKey {
+    public final class SourceDumpContainer {
+        public final SourceState mState;
+        public long mTotalTime;
+        public long mActiveTime;
+
+        public SourceDumpContainer(SourceState state) {
+            mState = state;
+        }
+    }
+
+    public static final class SourceKey {
         /**
          * UID, consider this final.  Not final just to avoid a temporary object during lookup.
          */
@@ -239,12 +290,10 @@
      */
     private final ArrayMap<SourceKey, SourceState> mSources = new ArrayMap<>();
 
-    private final SourceKey mTmpSourceKey = new SourceKey(0, null, null);
+    private static final SourceKey sTmpSourceKey = new SourceKey(0, null, null);
 
     private ProcessState mProc;
 
-    private int mNumActive;
-
     public AssociationState(ProcessStats processStats, ProcessStats.PackageState packageState,
             String name, String processName, ProcessState proc) {
         mProcessStats = processStats;
@@ -278,11 +327,24 @@
         mProc = proc;
     }
 
+    public long getTotalDuration(long now) {
+        return mTotalDuration
+                + (mTotalNesting > 0 ? (now - mTotalStartUptime) : 0);
+    }
+
+    public long getActiveDuration(long now) {
+        return mTotalActiveDuration
+                + (mTotalActiveNesting > 0 ? (now - mTotalActiveStartUptime) : 0);
+    }
+
     public SourceState startSource(int uid, String processName, String packageName) {
-        mTmpSourceKey.mUid = uid;
-        mTmpSourceKey.mProcess = processName;
-        mTmpSourceKey.mPackage = packageName;
-        SourceState src = mSources.get(mTmpSourceKey);
+        SourceState src;
+        synchronized (sTmpSourceKey) {
+            sTmpSourceKey.mUid = uid;
+            sTmpSourceKey.mProcess = processName;
+            sTmpSourceKey.mPackage = packageName;
+            src = mSources.get(sTmpSourceKey);
+        }
         if (src == null) {
             SourceKey key = new SourceKey(uid, processName, packageName);
             src = new SourceState(key);
@@ -290,43 +352,88 @@
         }
         src.mNesting++;
         if (src.mNesting == 1) {
+            final long now = SystemClock.uptimeMillis();
             src.mCount++;
-            src.mStartUptime = SystemClock.uptimeMillis();
-            mNumActive++;
+            src.mStartUptime = now;
+            mTotalNesting++;
+            if (mTotalNesting == 1) {
+                mTotalCount++;
+                mTotalStartUptime = now;
+            }
         }
         return src;
     }
 
     public void add(AssociationState other) {
+        mTotalCount += other.mTotalCount;
+        final long origDuration = mTotalDuration;
+        mTotalDuration += other.mTotalDuration;
+        mTotalActiveCount += other.mTotalActiveCount;
+        mTotalActiveDuration += other.mTotalActiveDuration;
         for (int isrc = other.mSources.size() - 1; isrc >= 0; isrc--) {
             final SourceKey key = other.mSources.keyAt(isrc);
             final SourceState otherSrc = other.mSources.valueAt(isrc);
             SourceState mySrc = mSources.get(key);
+            boolean newSrc = false;
             if (mySrc == null) {
                 mySrc = new SourceState(key);
                 mSources.put(key, mySrc);
+                newSrc = true;
+            }
+            if (VALIDATE_TIMES) {
+                Slog.w(TAG, "Adding tot duration " + mySrc.mDuration + "+"
+                        + otherSrc.mDuration
+                        + (newSrc ? " (new)" : " (old)") + " (total "
+                        + origDuration + "+" + other.mTotalDuration + ") in source "
+                        + mySrc.mKey.mProcess + " to assoc " + mName);
+                if ((mySrc.mDuration + otherSrc.mDuration) > mTotalDuration) {
+                    RuntimeException ex = new RuntimeException();
+                    ex.fillInStackTrace();
+                    Slog.w(TAG, "Source tot duration " + mySrc.mDuration + "+"
+                            + otherSrc.mDuration
+                            + (newSrc ? " (new)" : " (old)") + " exceeds total "
+                            + origDuration + "+" + other.mTotalDuration + " in source "
+                            + mySrc.mKey.mProcess + " to assoc " + mName, ex);
+                }
+                if (mySrc.mActiveDurations == null && otherSrc.mActiveDurations == null) {
+                    Slog.w(TAG, "Adding act duration " + mySrc.mActiveDuration
+                            + "+" + otherSrc.mActiveDuration
+                            + (newSrc ? " (new)" : " (old)") + " (total "
+                            + origDuration + "+" + other.mTotalDuration + ") in source "
+                            + mySrc.mKey.mProcess + " to assoc " + mName);
+                    if ((mySrc.mActiveDuration + otherSrc.mActiveDuration) > mTotalDuration) {
+                        RuntimeException ex = new RuntimeException();
+                        ex.fillInStackTrace();
+                        Slog.w(TAG, "Source act duration " + mySrc.mActiveDuration + "+"
+                                + otherSrc.mActiveDuration
+                                + (newSrc ? " (new)" : " (old)") + " exceeds total "
+                                + origDuration + "+" + other.mTotalDuration + " in source "
+                                + mySrc.mKey.mProcess + " to assoc " + mName, ex);
+                    }
+                }
             }
             mySrc.mCount += otherSrc.mCount;
             mySrc.mDuration += otherSrc.mDuration;
             mySrc.mActiveCount += otherSrc.mActiveCount;
-            if (otherSrc.mActiveDuration != 0 || otherSrc.mDurations != null) {
+            if (otherSrc.mActiveDuration != 0 || otherSrc.mActiveDurations != null) {
                 // Only need to do anything if the other one has some duration data.
-                if (mySrc.mDurations != null) {
+                if (mySrc.mActiveDurations != null) {
                     // If the target already has multiple durations, just add in whatever
                     // we have in the other.
-                    if (otherSrc.mDurations != null) {
-                        mySrc.mDurations.addDurations(otherSrc.mDurations);
+                    if (otherSrc.mActiveDurations != null) {
+                        mySrc.mActiveDurations.addDurations(otherSrc.mActiveDurations);
                     } else {
-                        mySrc.mDurations.addDuration(otherSrc.mActiveProcState,
+                        mySrc.mActiveDurations.addDuration(otherSrc.mActiveProcState,
                                 otherSrc.mActiveDuration);
                     }
-                } else if (otherSrc.mDurations != null) {
+                } else if (otherSrc.mActiveDurations != null) {
                     // The other one has multiple durations, but we don't.  Expand to
                     // multiple durations and copy over.
                     mySrc.makeDurations();
-                    mySrc.mDurations.addDurations(otherSrc.mDurations);
+                    mySrc.mActiveDurations.addDurations(otherSrc.mActiveDurations);
                     if (mySrc.mActiveDuration != 0) {
-                        mySrc.mDurations.addDuration(mySrc.mActiveProcState, mySrc.mActiveDuration);
+                        mySrc.mActiveDurations.addDuration(mySrc.mActiveProcState,
+                                mySrc.mActiveDuration);
                         mySrc.mActiveDuration = 0;
                         mySrc.mActiveProcState = ProcessStats.STATE_NOTHING;
                     }
@@ -334,13 +441,14 @@
                     // Both have a single inline duration...  we can either add them together,
                     // or need to expand to multiple durations.
                     if (mySrc.mActiveProcState == otherSrc.mActiveProcState) {
-                        mySrc.mDuration += otherSrc.mDuration;
+                        mySrc.mActiveDuration += otherSrc.mActiveDuration;
                     } else {
                         // The two have durations with different proc states, need to turn
                         // in to multiple durations.
                         mySrc.makeDurations();
-                        mySrc.mDurations.addDuration(mySrc.mActiveProcState, mySrc.mActiveDuration);
-                        mySrc.mDurations.addDuration(otherSrc.mActiveProcState,
+                        mySrc.mActiveDurations.addDuration(mySrc.mActiveProcState,
+                                mySrc.mActiveDuration);
+                        mySrc.mActiveDurations.addDuration(otherSrc.mActiveProcState,
                                 otherSrc.mActiveDuration);
                         mySrc.mActiveDuration = 0;
                         mySrc.mActiveProcState = ProcessStats.STATE_NOTHING;
@@ -355,12 +463,13 @@
     }
 
     public boolean isInUse() {
-        return mNumActive > 0;
+        return mTotalNesting > 0;
     }
 
     public void resetSafely(long now) {
         if (!isInUse()) {
             mSources.clear();
+            mTotalCount = mTotalActiveCount = 0;
         } else {
             // We have some active sources...  clear out everything but those.
             for (int isrc = mSources.size() - 1; isrc >= 0; isrc--) {
@@ -376,15 +485,28 @@
                         src.mActiveCount = 0;
                     }
                     src.mActiveDuration = 0;
-                    src.mDurations = null;
+                    src.mActiveDurations = null;
                 } else {
                     mSources.removeAt(isrc);
                 }
             }
+            mTotalCount = 1;
+            mTotalStartUptime = now;
+            if (mTotalActiveNesting > 0) {
+                mTotalActiveCount = 1;
+                mTotalActiveStartUptime = now;
+            } else {
+                mTotalActiveCount = 0;
+            }
         }
+        mTotalDuration = mTotalActiveDuration = 0;
     }
 
     public void writeToParcel(ProcessStats stats, Parcel out, long nowUptime) {
+        out.writeInt(mTotalCount);
+        out.writeLong(mTotalDuration);
+        out.writeInt(mTotalActiveCount);
+        out.writeLong(mTotalActiveDuration);
         final int NSRC = mSources.size();
         out.writeInt(NSRC);
         for (int isrc = 0; isrc < NSRC; isrc++) {
@@ -396,9 +518,9 @@
             out.writeInt(src.mCount);
             out.writeLong(src.mDuration);
             out.writeInt(src.mActiveCount);
-            if (src.mDurations != null) {
+            if (src.mActiveDurations != null) {
                 out.writeInt(1);
-                src.mDurations.writeToParcel(out);
+                src.mActiveDurations.writeToParcel(out);
             } else {
                 out.writeInt(0);
                 out.writeInt(src.mActiveProcState);
@@ -412,6 +534,10 @@
      * caused it to fail.
      */
     public String readFromParcel(ProcessStats stats, Parcel in, int parcelVersion) {
+        mTotalCount = in.readInt();
+        mTotalDuration = in.readLong();
+        mTotalActiveCount = in.readInt();
+        mTotalActiveDuration = in.readLong();
         final int NSRC = in.readInt();
         if (NSRC < 0 || NSRC > 100000) {
             return "Association with bad src count: " + NSRC;
@@ -427,13 +553,29 @@
             src.mActiveCount = in.readInt();
             if (in.readInt() != 0) {
                 src.makeDurations();
-                if (!src.mDurations.readFromParcel(in)) {
+                if (!src.mActiveDurations.readFromParcel(in)) {
                     return "Duration table corrupt: " + key + " <- " + src;
                 }
             } else {
                 src.mActiveProcState = in.readInt();
                 src.mActiveDuration = in.readLong();
             }
+            if (VALIDATE_TIMES) {
+                if (src.mDuration > mTotalDuration) {
+                    RuntimeException ex = new RuntimeException();
+                    ex.fillInStackTrace();
+                    Slog.w(TAG, "Reading tot duration " + src.mDuration
+                            + " exceeds total " + mTotalDuration + " in source "
+                            + src.mKey.mProcess + " to assoc " + mName, ex);
+                }
+                if (src.mActiveDurations == null && src.mActiveDuration > mTotalDuration) {
+                    RuntimeException ex = new RuntimeException();
+                    ex.fillInStackTrace();
+                    Slog.w(TAG, "Reading act duration " + src.mActiveDuration
+                            + " exceeds total " + mTotalDuration + " in source "
+                            + src.mKey.mProcess + " to assoc " + mName, ex);
+                }
+            }
             mSources.put(key, src);
         }
         return null;
@@ -448,19 +590,30 @@
                     src.mStartUptime = nowUptime;
                 }
                 if (src.mActiveStartUptime > 0) {
-                    final long duration = src.mActiveDuration + nowUptime - src.mActiveStartUptime;
-                    if (src.mDurations != null) {
-                        src.mDurations.addDuration(src.mActiveProcState, duration);
-                    } else {
-                        src.mActiveDuration = duration;
-                    }
+                    final long addedDuration = nowUptime - src.mActiveStartUptime;
                     src.mActiveStartUptime = nowUptime;
+                    if (src.mActiveDurations != null) {
+                        src.mActiveDurations.addDuration(src.mActiveProcState, addedDuration);
+                    } else {
+                        src.mActiveDuration += addedDuration;
+                    }
                 }
             }
+            if (mTotalNesting > 0) {
+                mTotalDuration += nowUptime - mTotalStartUptime;
+                mTotalStartUptime = nowUptime;
+            }
+            if (mTotalActiveNesting > 0) {
+                mTotalActiveDuration += nowUptime - mTotalActiveStartUptime;
+                mTotalActiveStartUptime = nowUptime;
+            }
         }
     }
 
     public boolean hasProcessOrPackage(String procName) {
+        if (mProcessName.equals(procName)) {
+            return true;
+        }
         final int NSRC = mSources.size();
         for (int isrc = 0; isrc < NSRC; isrc++) {
             final SourceKey key = mSources.keyAt(isrc);
@@ -471,22 +624,110 @@
         return false;
     }
 
-    public void dumpStats(PrintWriter pw, String prefix, String prefixInner, String headerPrefix,
-            long now, long totalTime, String reqPackage, boolean dumpDetails, boolean dumpAll) {
-        if (dumpAll) {
-            pw.print(prefix);
-            pw.print("mNumActive=");
-            pw.println(mNumActive);
+    static final Comparator<Pair<SourceKey, SourceDumpContainer>> ASSOCIATION_COMPARATOR =
+            (o1, o2) -> {
+        if (o1.second.mActiveTime != o2.second.mActiveTime) {
+            return o1.second.mActiveTime > o2.second.mActiveTime ? -1 : 1;
         }
-        final int NSRC = mSources.size();
-        for (int isrc = 0; isrc < NSRC; isrc++) {
-            final SourceKey key = mSources.keyAt(isrc);
-            final SourceState src = mSources.valueAt(isrc);
-            if (reqPackage != null && !reqPackage.equals(key.mProcess)
-                    && !reqPackage.equals(key.mPackage)) {
-                continue;
+        if (o1.second.mTotalTime != o2.second.mTotalTime) {
+            return o1.second.mTotalTime > o2.second.mTotalTime ? -1 : 1;
+        }
+        if (o1.first.mUid != o2.first.mUid) {
+            return o1.first.mUid < o2.first.mUid ? -1 : 1;
+        }
+        if (o1.first.mProcess != o2.first.mProcess) {
+            int diff = o1.first.mProcess.compareTo(o2.first.mProcess);
+            if (diff != 0) {
+                return diff;
             }
-            pw.print(prefixInner);
+        }
+        return 0;
+    };
+
+    public ArrayList<Pair<SourceKey, SourceDumpContainer>> createSortedAssociations(long now,
+            long totalTime) {
+        final int NSRC = mSources.size();
+        ArrayList<Pair<SourceKey, SourceDumpContainer>> sources = new ArrayList<>(NSRC);
+        for (int isrc = 0; isrc < NSRC; isrc++) {
+            final SourceState src = mSources.valueAt(isrc);
+            final SourceDumpContainer cont = new SourceDumpContainer(src);
+            long duration = src.mDuration;
+            if (src.mNesting > 0) {
+                duration += now - src.mStartUptime;
+            }
+            cont.mTotalTime = duration;
+            cont.mActiveTime = dumpTime(null, null, src, totalTime, now, false, false);
+            if (cont.mActiveTime < 0) {
+                cont.mActiveTime = -cont.mActiveTime;
+            }
+            sources.add(new Pair<>(mSources.keyAt(isrc), cont));
+        }
+        Collections.sort(sources, ASSOCIATION_COMPARATOR);
+        return sources;
+    }
+
+    public void dumpStats(PrintWriter pw, String prefix, String prefixInner, String headerPrefix,
+            ArrayList<Pair<SourceKey, SourceDumpContainer>> sources, long now, long totalTime,
+            String reqPackage, boolean dumpDetails, boolean dumpAll) {
+        final String prefixInnerInner = prefixInner + "     ";
+        long totalDuration = mTotalActiveDuration;
+        if (mTotalActiveNesting > 0) {
+            totalDuration += now - mTotalActiveStartUptime;
+        }
+        if (totalDuration > 0 || mTotalActiveCount != 0) {
+            pw.print(prefix);
+            pw.print("Active count ");
+            pw.print(mTotalActiveCount);
+            if (dumpAll) {
+                pw.print(": ");
+                TimeUtils.formatDuration(totalDuration, pw);
+                pw.print(" / ");
+            } else {
+                pw.print(": time ");
+            }
+            DumpUtils.printPercent(pw, (double) totalDuration / (double) totalTime);
+            pw.println();
+        }
+        if (dumpAll && mTotalActiveNesting != 0) {
+            pw.print(prefix);
+            pw.print("mTotalActiveNesting=");
+            pw.print(mTotalActiveNesting);
+            pw.print(" mTotalActiveStartUptime=");
+            TimeUtils.formatDuration(mTotalActiveStartUptime, now, pw);
+            pw.println();
+        }
+        totalDuration = mTotalDuration;
+        if (mTotalNesting > 0) {
+            totalDuration += now - mTotalStartUptime;
+        }
+        if (totalDuration > 0 || mTotalCount != 0) {
+            pw.print(prefix);
+            pw.print("Total count ");
+            pw.print(mTotalCount);
+            if (dumpAll) {
+                pw.print(": ");
+                TimeUtils.formatDuration(totalDuration, pw);
+                pw.print(" / ");
+            } else {
+                pw.print(": time ");
+            }
+            DumpUtils.printPercent(pw, (double) totalDuration / (double) totalTime);
+            pw.println();
+        }
+        if (dumpAll && mTotalNesting != 0) {
+            pw.print(prefix);
+            pw.print("mTotalNesting=");
+            pw.print(mTotalNesting);
+            pw.print(" mTotalStartUptime=");
+            TimeUtils.formatDuration(mTotalStartUptime, now, pw);
+            pw.println();
+        }
+        final int NSRC = sources.size();
+        for (int isrc = 0; isrc < NSRC; isrc++) {
+            final SourceKey key = sources.get(isrc).first;
+            final SourceDumpContainer cont = sources.get(isrc).second;
+            final SourceState src = cont.mState;
+            pw.print(prefix);
             pw.print("<- ");
             pw.print(key.mProcess);
             pw.print("/");
@@ -496,24 +737,69 @@
                 pw.print(key.mPackage);
                 pw.print(")");
             }
+            // If we are skipping this one, we still print the first line just to give
+            // context for the others (so it is clear the total times for the overall
+            // association come from other sources whose times are not shown).
+            if (reqPackage != null && !reqPackage.equals(key.mProcess)
+                    && !reqPackage.equals(key.mPackage)) {
+                pw.println();
+                continue;
+            }
             pw.println(":");
+            if (src.mActiveCount != 0 || src.mActiveDurations != null || src.mActiveDuration != 0
+                    || src.mActiveStartUptime != 0) {
+                pw.print(prefixInner);
+                pw.print("   Active count ");
+                pw.print(src.mActiveCount);
+                if (dumpDetails) {
+                    if (dumpAll) {
+                        if (src.mActiveDurations != null) {
+                            pw.print(" (multi-state)");
+                        } else if (src.mActiveProcState >= ProcessStats.STATE_PERSISTENT) {
+                            pw.print(" (");
+                            pw.print(DumpUtils.STATE_NAMES[src.mActiveProcState]);
+                            pw.print(")");
+                        } else {
+                            pw.print(" (*UNKNOWN STATE*)");
+                        }
+                    }
+                    if (dumpAll) {
+                        pw.print(": ");
+                        TimeUtils.formatDuration(cont.mActiveTime, pw);
+                        pw.print(" / ");
+                    } else {
+                        pw.print(": time ");
+                    }
+                    DumpUtils.printPercent(pw, (double) cont.mActiveTime / (double) totalTime);
+                    if (src.mActiveStartUptime != 0) {
+                        pw.print(" (running)");
+                    }
+                    pw.println();
+                    if (src.mActiveDurations != null) {
+                        dumpTime(pw, prefixInnerInner, src, totalTime, now, dumpDetails, dumpAll);
+                    }
+                } else {
+                    pw.print(": ");
+                    dumpActiveDurationSummary(pw, src, totalTime, now, dumpAll);
+                }
+            }
             pw.print(prefixInner);
             pw.print("   Total count ");
             pw.print(src.mCount);
-            long duration = src.mDuration;
-            if (src.mNesting > 0) {
-                duration += now - src.mStartUptime;
-            }
             if (dumpAll) {
-                pw.print(": Duration ");
-                TimeUtils.formatDuration(duration, pw);
+                pw.print(": ");
+                TimeUtils.formatDuration(cont.mTotalTime, pw);
                 pw.print(" / ");
             } else {
                 pw.print(": time ");
             }
-            DumpUtils.printPercent(pw, (double)duration/(double)totalTime);
+            DumpUtils.printPercent(pw, (double) cont.mTotalTime / (double) totalTime);
             if (src.mNesting > 0) {
                 pw.print(" (running");
+                if (dumpAll) {
+                    pw.print(" nest=");
+                    pw.print(src.mNesting);
+                }
                 if (src.mProcState != ProcessStats.STATE_NOTHING) {
                     pw.print(" / ");
                     pw.print(DumpUtils.STATE_NAMES[src.mProcState]);
@@ -523,23 +809,6 @@
                 pw.print(")");
             }
             pw.println();
-            if (src.mActiveCount > 0 || src.mDurations != null || src.mActiveDuration != 0
-                    || src.mActiveStartUptime != 0) {
-                pw.print(prefixInner);
-                pw.print("   Active count ");
-                pw.print(src.mActiveCount);
-                if (dumpDetails) {
-                    if (dumpAll) {
-                        pw.print(src.mDurations != null ? " (multi-field)" : " (inline)");
-                    }
-                    pw.println(":");
-                    dumpTime(pw, prefixInner, src, totalTime, now, dumpDetails, dumpAll);
-                } else {
-                    pw.print(": ");
-                    dumpActiveDurationSummary(pw, src, totalTime, now, dumpAll);
-                    pw.println();
-                }
-            }
             if (dumpAll) {
                 if (src.mInTrackingList) {
                     pw.print(prefixInner);
@@ -565,7 +834,6 @@
             duration = -duration;
         }
         if (dumpAll) {
-            pw.print("Duration ");
             TimeUtils.formatDuration(duration, pw);
             pw.print(" / ");
         } else {
@@ -584,10 +852,10 @@
         boolean isRunning = false;
         for (int iprocstate = 0; iprocstate < ProcessStats.STATE_COUNT; iprocstate++) {
             long time;
-            if (src.mDurations != null) {
-                time = src.mDurations.getValueForId((byte)iprocstate);
+            if (src.mActiveDurations != null) {
+                time = src.mActiveDurations.getValueForId((byte) iprocstate);
             } else {
-                time = src.mActiveProcState == iprocstate ? src.mDuration : 0;
+                time = src.mActiveProcState == iprocstate ? src.mActiveDuration : 0;
             }
             final String running;
             if (src.mActiveStartUptime != 0 && src.mActiveProcState == iprocstate) {
@@ -600,11 +868,9 @@
             if (time != 0) {
                 if (pw != null) {
                     pw.print(prefix);
-                    pw.print("  ");
                     pw.print(DumpUtils.STATE_LABELS[iprocstate]);
                     pw.print(": ");
                     if (dumpAll) {
-                        pw.print("Duration ");
                         TimeUtils.formatDuration(time, pw);
                         pw.print(" / ");
                     } else {
@@ -619,21 +885,6 @@
                 totalTime += time;
             }
         }
-        if (totalTime != 0 && pw != null) {
-            pw.print(prefix);
-            pw.print("  ");
-            pw.print(DumpUtils.STATE_LABEL_TOTAL);
-            pw.print(": ");
-            if (dumpAll) {
-                pw.print("Duration ");
-                TimeUtils.formatDuration(totalTime, pw);
-                pw.print(" / ");
-            } else {
-                pw.print("time ");
-            }
-            DumpUtils.printPercent(pw, (double) totalTime / (double) overallTime);
-            pw.println();
-        }
         return isRunning ? -totalTime : totalTime;
     }
 
@@ -667,11 +918,11 @@
             pw.print(",");
             pw.print(src.mActiveCount);
             final long timeNow = src.mActiveStartUptime != 0 ? (now-src.mActiveStartUptime) : 0;
-            if (src.mDurations != null) {
-                final int N = src.mDurations.getKeyCount();
+            if (src.mActiveDurations != null) {
+                final int N = src.mActiveDurations.getKeyCount();
                 for (int i=0; i<N; i++) {
-                    final int dkey = src.mDurations.getKeyAt(i);
-                    duration = src.mDurations.getValue(dkey);
+                    final int dkey = src.mActiveDurations.getKeyAt(i);
+                    duration = src.mActiveDurations.getValue(dkey);
                     if (dkey == src.mActiveProcState) {
                         duration += timeNow;
                     }
@@ -718,11 +969,11 @@
                         src.mActiveCount);
             }
             final long timeNow = src.mActiveStartUptime != 0 ? (now-src.mActiveStartUptime) : 0;
-            if (src.mDurations != null) {
-                final int N = src.mDurations.getKeyCount();
+            if (src.mActiveDurations != null) {
+                final int N = src.mActiveDurations.getKeyCount();
                 for (int i=0; i<N; i++) {
-                    final int dkey = src.mDurations.getKeyAt(i);
-                    duration = src.mDurations.getValue(dkey);
+                    final int dkey = src.mActiveDurations.getKeyAt(i);
+                    duration = src.mActiveDurations.getValue(dkey);
                     if (dkey == src.mActiveProcState) {
                         duration += timeNow;
                     }
diff --git a/core/java/com/android/internal/app/procstats/ProcessStats.java b/core/java/com/android/internal/app/procstats/ProcessStats.java
index 8e88c51..875cff8 100644
--- a/core/java/com/android/internal/app/procstats/ProcessStats.java
+++ b/core/java/com/android/internal/app/procstats/ProcessStats.java
@@ -31,6 +31,7 @@
 import android.util.ArraySet;
 import android.util.DebugUtils;
 import android.util.LongSparseArray;
+import android.util.Pair;
 import android.util.Slog;
 import android.util.SparseArray;
 import android.util.TimeUtils;
@@ -48,6 +49,7 @@
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
+import java.util.Comparator;
 import java.util.Objects;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
@@ -179,7 +181,7 @@
             {"proc", "pkg-proc", "pkg-svc", "pkg-asc", "pkg-all", "all"};
 
     // Current version of the parcel format.
-    private static final int PARCEL_VERSION = 36;
+    private static final int PARCEL_VERSION = 38;
     // In-memory Parcel magic number, used to detect attempts to unmarshall bad data
     private static final int MAGIC = 0x50535454;
 
@@ -196,6 +198,9 @@
     public int mMemFactor = STATE_NOTHING;
     public long mStartTime;
 
+    // Number of individual stats that have been aggregated to create this one.
+    public int mNumAggregated = 1;
+
     public long mTimePeriodStartClock;
     public long mTimePeriodStartRealtime;
     public long mTimePeriodEndRealtime;
@@ -348,6 +353,8 @@
 
         mSysMemUsage.mergeStats(other.mSysMemUsage);
 
+        mNumAggregated += other.mNumAggregated;
+
         if (other.mTimePeriodStartClock < mTimePeriodStartClock) {
             mTimePeriodStartClock = other.mTimePeriodStartClock;
             mTimePeriodStartClockStr = other.mTimePeriodStartClockStr;
@@ -569,6 +576,7 @@
     }
 
     private void resetCommon() {
+        mNumAggregated = 1;
         mTimePeriodStartClock = System.currentTimeMillis();
         buildTimePeriodStartClockStr();
         mTimePeriodStartRealtime = mTimePeriodEndRealtime = SystemClock.elapsedRealtime();
@@ -845,6 +853,7 @@
             }
         }
 
+        out.writeInt(mNumAggregated);
         out.writeLong(mTimePeriodStartClock);
         out.writeLong(mTimePeriodStartRealtime);
         out.writeLong(mTimePeriodEndRealtime);
@@ -1031,6 +1040,7 @@
 
         mIndexToCommonString = new ArrayList<String>();
 
+        mNumAggregated = in.readInt();
         mTimePeriodStartClock = in.readLong();
         buildTimePeriodStartClockStr();
         mTimePeriodStartRealtime = in.readLong();
@@ -1457,15 +1467,79 @@
         }
     }
 
+    final class AssociationDumpContainer {
+        final AssociationState mState;
+        ArrayList<Pair<AssociationState.SourceKey, AssociationState.SourceDumpContainer>> mSources;
+        long mTotalTime;
+        long mActiveTime;
+
+        AssociationDumpContainer(AssociationState state) {
+            mState = state;
+        }
+    }
+
+    static final Comparator<AssociationDumpContainer> ASSOCIATION_COMPARATOR = (o1, o2) -> {
+        int diff = o1.mState.getProcessName().compareTo(o2.mState.getProcessName());
+        if (diff != 0) {
+            return diff;
+        }
+        if (o1.mActiveTime != o2.mActiveTime) {
+            return o1.mActiveTime > o2.mActiveTime ? -1 : 1;
+        }
+        if (o1.mTotalTime != o2.mTotalTime) {
+            return o1.mTotalTime > o2.mTotalTime ? -1 : 1;
+        }
+        diff = o1.mState.getName().compareTo(o2.mState.getName());
+        if (diff != 0) {
+            return diff;
+        }
+        return 0;
+    };
+
     public void dumpLocked(PrintWriter pw, String reqPackage, long now, boolean dumpSummary,
             boolean dumpDetails, boolean dumpAll, boolean activeOnly, int section) {
         long totalTime = DumpUtils.dumpSingleTime(null, null, mMemFactorDurations, mMemFactor,
                 mStartTime, now);
-        boolean sepNeeded = false;
+        pw.print("          Start time: ");
+        pw.print(DateFormat.format("yyyy-MM-dd HH:mm:ss", mTimePeriodStartClock));
+        pw.println();
+        pw.print("        Total uptime: ");
+        TimeUtils.formatDuration(
+                (mRunning ? SystemClock.uptimeMillis() : mTimePeriodEndUptime)
+                        - mTimePeriodStartUptime, pw);
+        pw.println();
+        pw.print("  Total elapsed time: ");
+        TimeUtils.formatDuration(
+                (mRunning ? SystemClock.elapsedRealtime() : mTimePeriodEndRealtime)
+                        - mTimePeriodStartRealtime, pw);
+        boolean partial = true;
+        if ((mFlags & FLAG_SHUTDOWN) != 0) {
+            pw.print(" (shutdown)");
+            partial = false;
+        }
+        if ((mFlags & FLAG_SYSPROPS) != 0) {
+            pw.print(" (sysprops)");
+            partial = false;
+        }
+        if ((mFlags & FLAG_COMPLETE) != 0) {
+            pw.print(" (complete)");
+            partial = false;
+        }
+        if (partial) {
+            pw.print(" (partial)");
+        }
+        if (mHasSwappedOutPss) {
+            pw.print(" (swapped-out-pss)");
+        }
+        pw.print(' ');
+        pw.print(mRuntime);
+        pw.println();
+        pw.print("     Aggregated over: ");
+        pw.println(mNumAggregated);
         if (mSysMemUsage.getKeyCount() > 0) {
+            pw.println();
             pw.println("System memory usage:");
             mSysMemUsage.dump(pw, "  ", ALL_SCREEN_ADJ, ALL_MEM_ADJ);
-            sepNeeded = true;
         }
         boolean printedHeader = false;
         if ((section & REPORT_PKG_STATS) != 0) {
@@ -1485,8 +1559,8 @@
                         final int NASCS = pkgState.mAssociations.size();
                         final boolean pkgMatch = reqPackage == null || reqPackage.equals(pkgName);
                         boolean onlyAssociations = false;
+                        boolean procMatch = false;
                         if (!pkgMatch) {
-                            boolean procMatch = false;
                             for (int iproc = 0; iproc < NPROCS; iproc++) {
                                 ProcessState proc = pkgState.mProcesses.valueAt(iproc);
                                 if (reqPackage.equals(proc.getName())) {
@@ -1511,10 +1585,9 @@
                         }
                         if (NPROCS > 0 || NSRVS > 0 || NASCS > 0) {
                             if (!printedHeader) {
-                                if (sepNeeded) pw.println();
+                                pw.println();
                                 pw.println("Per-Package Stats:");
                                 printedHeader = true;
-                                sepNeeded = true;
                             }
                             pw.print("  * ");
                             pw.print(pkgName);
@@ -1597,6 +1670,8 @@
                             }
                         }
                         if ((section & REPORT_PKG_ASC_STATS) != 0) {
+                            ArrayList<AssociationDumpContainer> associations =
+                                    new ArrayList<>(NASCS);
                             for (int iasc = 0; iasc < NASCS; iasc++) {
                                 AssociationState asc = pkgState.mAssociations.valueAt(iasc);
                                 if (!pkgMatch && !reqPackage.equals(asc.getProcessName())) {
@@ -1604,6 +1679,18 @@
                                         continue;
                                     }
                                 }
+                                final AssociationDumpContainer cont =
+                                        new AssociationDumpContainer(asc);
+                                cont.mSources = asc.createSortedAssociations(now, totalTime);
+                                cont.mTotalTime = asc.getTotalDuration(now);
+                                cont.mActiveTime = asc.getActiveDuration(now);
+                                associations.add(cont);
+                            }
+                            Collections.sort(associations, ASSOCIATION_COMPARATOR);
+                            final int NCONT = associations.size();
+                            for (int iasc = 0; iasc < NCONT; iasc++) {
+                                final AssociationDumpContainer cont = associations.get(iasc);
+                                final AssociationState asc = cont.mState;
                                 if (activeOnly && !asc.isInUse()) {
                                     pw.print("      (Not active association: ");
                                     pw.print(pkgState.mAssociations.keyAt(iasc));
@@ -1615,13 +1702,15 @@
                                 } else {
                                     pw.print("      * Asc ");
                                 }
-                                pw.print(pkgState.mAssociations.keyAt(iasc));
+                                pw.print(cont.mState.getName());
                                 pw.println(":");
                                 pw.print("        Process: ");
                                 pw.println(asc.getProcessName());
                                 asc.dumpStats(pw, "        ", "          ", "    ",
-                                        now, totalTime, onlyAssociations ? reqPackage : null,
-                                        dumpDetails, dumpAll);
+                                        cont.mSources, now, totalTime,
+                                        onlyAssociations && !pkgMatch && !procMatch
+                                                && !asc.getProcessName().equals(reqPackage)
+                                                ? reqPackage : null, dumpDetails, dumpAll);
                             }
                         }
                     }
@@ -1651,10 +1740,7 @@
                         continue;
                     }
                     numShownProcs++;
-                    if (sepNeeded) {
-                        pw.println();
-                    }
-                    sepNeeded = true;
+                    pw.println();
                     if (!printedHeader) {
                         pw.println("Multi-Package Common Processes:");
                         printedHeader = true;
@@ -1684,11 +1770,7 @@
         }
 
         if (dumpAll) {
-            if (sepNeeded) {
-                pw.println();
-            }
-            sepNeeded = true;
-
+            pw.println();
             if (mTrackingAssociations.size() > 0) {
                 pw.println();
                 pw.println("Tracking associations:");
@@ -1734,9 +1816,7 @@
             }
         }
 
-        if (sepNeeded) {
-            pw.println();
-        }
+        pw.println();
         if (dumpSummary) {
             pw.println("Process summary:");
             dumpSummaryLocked(pw, reqPackage, now, activeOnly);
@@ -1861,41 +1941,6 @@
         pw.print("x over ");
         TimeUtils.formatDuration(mExternalSlowPssTime, pw);
         pw.println();
-        pw.println();
-        pw.print("          Start time: ");
-        pw.print(DateFormat.format("yyyy-MM-dd HH:mm:ss", mTimePeriodStartClock));
-        pw.println();
-        pw.print("        Total uptime: ");
-        TimeUtils.formatDuration(
-                (mRunning ? SystemClock.uptimeMillis() : mTimePeriodEndUptime)
-                        - mTimePeriodStartUptime, pw);
-        pw.println();
-        pw.print("  Total elapsed time: ");
-        TimeUtils.formatDuration(
-                (mRunning ? SystemClock.elapsedRealtime() : mTimePeriodEndRealtime)
-                        - mTimePeriodStartRealtime, pw);
-        boolean partial = true;
-        if ((mFlags&FLAG_SHUTDOWN) != 0) {
-            pw.print(" (shutdown)");
-            partial = false;
-        }
-        if ((mFlags&FLAG_SYSPROPS) != 0) {
-            pw.print(" (sysprops)");
-            partial = false;
-        }
-        if ((mFlags&FLAG_COMPLETE) != 0) {
-            pw.print(" (complete)");
-            partial = false;
-        }
-        if (partial) {
-            pw.print(" (partial)");
-        }
-        if (mHasSwappedOutPss) {
-            pw.print(" (swapped-out-pss)");
-        }
-        pw.print(' ');
-        pw.print(mRuntime);
-        pw.println();
     }
 
     void dumpFilteredSummaryLocked(PrintWriter pw, String header, String prefix, String prcLabel,
diff --git a/core/java/com/android/internal/compat/ChangeReporter.java b/core/java/com/android/internal/compat/ChangeReporter.java
index 8283eb7..72b0ad7 100644
--- a/core/java/com/android/internal/compat/ChangeReporter.java
+++ b/core/java/com/android/internal/compat/ChangeReporter.java
@@ -22,7 +22,9 @@
 
 import com.android.internal.annotations.GuardedBy;
 
+import java.util.HashMap;
 import java.util.HashSet;
+import java.util.Map;
 import java.util.Objects;
 import java.util.Set;
 
@@ -36,12 +38,10 @@
     private int mSource;
 
     private final class ChangeReport {
-        int mUid;
         long mChangeId;
         int mState;
 
-        ChangeReport(int uid, long changeId, int state) {
-            mUid = uid;
+        ChangeReport(long changeId, int state) {
             mChangeId = changeId;
             mState = state;
         }
@@ -51,40 +51,62 @@
             if (this == o) return true;
             if (o == null || getClass() != o.getClass()) return false;
             ChangeReport that = (ChangeReport) o;
-            return mUid == that.mUid
-                    && mChangeId == that.mChangeId
+            return mChangeId == that.mChangeId
                     && mState == that.mState;
         }
 
         @Override
         public int hashCode() {
-            return Objects.hash(mUid, mChangeId, mState);
+            return Objects.hash(mChangeId, mState);
         }
     }
 
+    // Maps uid to a set of ChangeReports (that were reported for that uid).
     @GuardedBy("mReportedChanges")
-    private Set<ChangeReport> mReportedChanges =  new HashSet<>();
+    private final Map<Integer, Set<ChangeReport>> mReportedChanges;
 
     public ChangeReporter(int source) {
         mSource = source;
+        mReportedChanges =  new HashMap<>();
     }
 
     /**
-     * Report the change to stats log.
+     * Report the change to stats log and to the debug log if the change was not previously
+     * logged already.
      *
      * @param uid      affected by the change
      * @param changeId the reported change id
      * @param state    of the reported change - enabled/disabled/only logged
      */
     public void reportChange(int uid, long changeId, int state) {
-        ChangeReport report = new ChangeReport(uid, changeId, state);
+        ChangeReport report = new ChangeReport(changeId, state);
         synchronized (mReportedChanges) {
-            if (!mReportedChanges.contains(report)) {
+            Set<ChangeReport> reportedChangesForUid = mReportedChanges.get(uid);
+            if (reportedChangesForUid == null) {
+                mReportedChanges.put(uid, new HashSet<ChangeReport>());
+                reportedChangesForUid = mReportedChanges.get(uid);
+            }
+            if (!reportedChangesForUid.contains(report)) {
                 debugLog(uid, changeId, state);
                 StatsLog.write(StatsLog.APP_COMPATIBILITY_CHANGE_REPORTED, uid, changeId,
                         state, mSource);
-                mReportedChanges.add(report);
+                reportedChangesForUid.add(report);
             }
+
+        }
+    }
+
+    /**
+     * Clears the saved information about a given uid. Requests to report uid again will be reported
+     * regardless to the past reports.
+     *
+     * <p> Only intended to be called from PlatformCompat.
+     *
+     * @param uid to reset
+     */
+    public void resetReportedChanges(int uid) {
+        synchronized (mReportedChanges) {
+            mReportedChanges.remove(uid);
         }
     }
 
diff --git a/core/java/com/android/internal/compat/OWNERS b/core/java/com/android/internal/compat/OWNERS
new file mode 100644
index 0000000..2b7cdb0
--- /dev/null
+++ b/core/java/com/android/internal/compat/OWNERS
@@ -0,0 +1,7 @@
+# Use this reviewer by default.
+platform-compat-eng+reviews@google.com
+
+andreionea@google.com
+atrost@google.com
+mathewi@google.com
+satayev@google.com
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..ce405fe 100644
--- a/core/jni/Android.bp
+++ b/core/jni/Android.bp
@@ -117,6 +117,7 @@
                 "android_view_RenderNodeAnimator.cpp",
                 "android_view_Surface.cpp",
                 "android_view_SurfaceControl.cpp",
+                "android_graphics_BLASTBufferQueue.cpp",
                 "android_view_SurfaceSession.cpp",
                 "android_view_TextureView.cpp",
                 "android_view_VelocityTracker.cpp",
@@ -257,8 +258,6 @@
                 "libnativeloader_lazy",
                 "libmemunreachable",
                 "libhidlbase",
-                "libhidltransport",
-                "libhwbinder",
                 "libvintf",
                 "libnativewindow",
                 "libdl",
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index d476d2d..3497f92 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -115,6 +115,7 @@
 extern int register_android_content_StringBlock(JNIEnv* env);
 extern int register_android_content_XmlBlock(JNIEnv* env);
 extern int register_android_content_res_ApkAssets(JNIEnv* env);
+extern int register_android_graphics_BLASTBufferQueue(JNIEnv* env);
 extern int register_android_view_DisplayEventReceiver(JNIEnv* env);
 extern int register_android_view_InputApplicationHandle(JNIEnv* env);
 extern int register_android_view_InputWindowHandle(JNIEnv* env);
@@ -1458,10 +1459,9 @@
     REG_JNI(register_android_opengl_jni_GLES31),
     REG_JNI(register_android_opengl_jni_GLES31Ext),
     REG_JNI(register_android_opengl_jni_GLES32),
-
     REG_JNI(register_android_graphics_classes),
+    REG_JNI(register_android_graphics_BLASTBufferQueue),
     REG_JNI(register_android_graphics_GraphicBuffer),
-
     REG_JNI(register_android_database_CursorWindow),
     REG_JNI(register_android_database_SQLiteConnection),
     REG_JNI(register_android_database_SQLiteGlobal),
diff --git a/core/jni/android_graphics_BLASTBufferQueue.cpp b/core/jni/android_graphics_BLASTBufferQueue.cpp
new file mode 100644
index 0000000..185e581
--- /dev/null
+++ b/core/jni/android_graphics_BLASTBufferQueue.cpp
@@ -0,0 +1,82 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "BLASTBufferQueue"
+
+#include <nativehelper/JNIHelp.h>
+
+#include <android_runtime/AndroidRuntime.h>
+#include <android_runtime/android_view_Surface.h>
+#include <utils/Log.h>
+#include <utils/RefBase.h>
+
+#include <gui/BLASTBufferQueue.h>
+#include <gui/Surface.h>
+#include <gui/SurfaceComposerClient.h>
+
+namespace android {
+
+static jlong nativeCreate(JNIEnv* env, jclass clazz, jlong surfaceControl, jlong width, jlong height) {
+    sp<BLASTBufferQueue> queue = new BLASTBufferQueue(
+            reinterpret_cast<SurfaceControl*>(surfaceControl), width, height);
+    queue->incStrong((void*)nativeCreate);
+    return reinterpret_cast<jlong>(queue.get());
+}
+
+static void nativeDestroy(JNIEnv* env, jclass clazz, jlong ptr) {
+    sp<BLASTBufferQueue> queue = reinterpret_cast<BLASTBufferQueue*>(ptr);
+    queue->decStrong((void*)nativeCreate);
+}
+
+static jobject nativeGetSurface(JNIEnv* env, jclass clazz, jlong ptr) {
+    sp<BLASTBufferQueue> queue = reinterpret_cast<BLASTBufferQueue*>(ptr);
+    return android_view_Surface_createFromIGraphicBufferProducer(env, queue->getIGraphicBufferProducer());
+}
+
+static void nativeSetNextTransaction(JNIEnv* env, jclass clazz, jlong ptr, jlong transactionPtr) {
+    sp<BLASTBufferQueue> queue = reinterpret_cast<BLASTBufferQueue*>(ptr);
+    auto transaction = reinterpret_cast<SurfaceComposerClient::Transaction*>(transactionPtr);
+    queue->setNextTransaction(transaction);
+}
+
+static void nativeUpdate(JNIEnv*env, jclass clazz, jlong ptr, jlong surfaceControl, jlong width, jlong height) {
+    sp<BLASTBufferQueue> queue = reinterpret_cast<BLASTBufferQueue*>(ptr);
+    queue->update(reinterpret_cast<SurfaceControl*>(surfaceControl), width, height);
+}
+
+static const JNINativeMethod gMethods[] = {
+    /* name, signature, funcPtr */
+    { "nativeCreate", "(JJJ)J",
+            (void*)nativeCreate },
+    {  "nativeGetSurface", "(J)Landroid/view/Surface;",
+       (void*)nativeGetSurface },
+    { "nativeDestroy", "(J)V",
+            (void*)nativeDestroy },
+    { "nativeSetNextTransaction", "(JJ)V",
+      (void*)nativeSetNextTransaction },
+    { "nativeUpdate", "(JJJJ)V",
+      (void*)nativeUpdate }
+};
+
+int register_android_graphics_BLASTBufferQueue(JNIEnv* env) {
+    int res = jniRegisterNativeMethods(env, "android/graphics/BLASTBufferQueue",
+            gMethods, NELEM(gMethods));
+    LOG_ALWAYS_FATAL_IF(res < 0, "Unable to register native methods.");
+
+    return 0;
+}
+
+} // namespace android
diff --git a/core/jni/android_os_GraphicsEnvironment.cpp b/core/jni/android_os_GraphicsEnvironment.cpp
index be9aee4..7582cae 100644
--- a/core/jni/android_os_GraphicsEnvironment.cpp
+++ b/core/jni/android_os_GraphicsEnvironment.cpp
@@ -85,6 +85,10 @@
     }
 }
 
+bool setInjectLayersPrSetDumpable_native() {
+    return android::GraphicsEnv::getInstance().setInjectLayersPrSetDumpable();
+}
+
 void hintActivityLaunch_native(JNIEnv* env, jobject clazz) {
     android::GraphicsEnv::getInstance().hintActivityLaunch();
 }
@@ -93,6 +97,7 @@
     { "getCanLoadSystemLibraries", "()I", reinterpret_cast<void*>(getCanLoadSystemLibraries_native) },
     { "setDriverPathAndSphalLibraries", "(Ljava/lang/String;Ljava/lang/String;)V", reinterpret_cast<void*>(setDriverPathAndSphalLibraries_native) },
     { "setGpuStats", "(Ljava/lang/String;Ljava/lang/String;JJLjava/lang/String;I)V", reinterpret_cast<void*>(setGpuStats_native) },
+    { "setInjectLayersPrSetDumpable", "()Z", reinterpret_cast<void*>(setInjectLayersPrSetDumpable_native) },
     { "setAngleInfo", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/io/FileDescriptor;JJ)V", reinterpret_cast<void*>(setAngleInfo_native) },
     { "getShouldUseAngle", "(Ljava/lang/String;)Z", reinterpret_cast<void*>(shouldUseAngle_native) },
     { "setLayerPaths", "(Ljava/lang/ClassLoader;Ljava/lang/String;)V", reinterpret_cast<void*>(setLayerPaths_native) },
diff --git a/core/jni/android_util_Process.cpp b/core/jni/android_util_Process.cpp
index 3cefeea..0fada1b 100644
--- a/core/jni/android_util_Process.cpp
+++ b/core/jni/android_util_Process.cpp
@@ -35,6 +35,7 @@
 #include <limits>
 #include <memory>
 #include <string>
+#include <unordered_map>
 #include <vector>
 
 #include "core_jni_helpers.h"
@@ -81,6 +82,25 @@
 static pthread_key_t gBgKey = -1;
 #endif
 
+/*
+ *  cpuset/sched aggregate profile mappings
+ */
+static const std::unordered_map<int, std::string> kCpusetProfileMap = {
+    {SP_DEFAULT, "CPUSET_SP_DEFAULT"}, {SP_BACKGROUND, "CPUSET_SP_BACKGROUND"},
+    {SP_FOREGROUND, "CPUSET_SP_FOREGROUND"},{SP_SYSTEM, "CPUSET_SP_SYSTEM"},
+    {SP_AUDIO_APP, "CPUSET_SP_FOREGROUND"}, {SP_AUDIO_SYS, "CPUSET_SP_FOREGROUND"},
+    {SP_TOP_APP, "CPUSET_SP_TOP_APP"}, {SP_RT_APP, "CPUSET_SP_DEFAULT"},
+    {SP_RESTRICTED, "CPUSET_SP_RESTRICTED"}
+};
+
+static const std::unordered_map<int, std::string> kSchedProfileMap = {
+    {SP_DEFAULT, "SCHED_SP_DEFAULT"}, {SP_BACKGROUND, "SCHED_SP_BACKGROUND"},
+    {SP_FOREGROUND, "SCHED_SP_FOREGROUND"}, {SP_SYSTEM, "SCHED_SP_DEFAULT"},
+    {SP_AUDIO_APP, "SCHED_SP_FOREGROUND"}, {SP_AUDIO_SYS, "SCHED_SP_FOREGROUND"},
+    {SP_TOP_APP, "SCHED_SP_TOP_APP"}, {SP_RT_APP, "SCHED_SP_RT_APP"},
+    {SP_RESTRICTED, "SCHED_SP_DEFAULT"}
+};
+
 // For both of these, err should be in the errno range (positive), not a status_t (negative)
 static void signalExceptionForError(JNIEnv* env, int err, int tid) {
     switch (err) {
@@ -206,8 +226,9 @@
     if (!verifyGroup(env, grp)) {
         return;
     }
-    SchedPolicy sp = (SchedPolicy) grp;
-    int res = set_sched_policy(tid, sp);
+
+    int res = SetTaskProfiles(tid, {kSchedProfileMap.at(grp)}, true) ? 0 : -1;
+
     if (res != NO_ERROR) {
         signalExceptionForGroupError(env, -res, tid);
     }
@@ -219,14 +240,9 @@
     if (!verifyGroup(env, grp)) {
         return;
     }
-    SchedPolicy sp = (SchedPolicy) grp;
-    int res = set_sched_policy(tid, sp);
 
-    if (res != NO_ERROR) {
-        signalExceptionForGroupError(env, -res, tid);
-    }
+    int res = SetTaskProfiles(tid, {kCpusetProfileMap.at(grp)}, true) ? 0 : -1;
 
-    res = set_cpuset_policy(tid, sp);
     if (res != NO_ERROR) {
         signalExceptionForGroupError(env, -res, tid);
     }
@@ -239,7 +255,11 @@
     char proc_path[255];
     struct dirent *de;
 
-    if ((grp == SP_FOREGROUND) || (grp > SP_MAX)) {
+    if (!verifyGroup(env, grp)) {
+        return;
+    }
+
+    if (grp == SP_FOREGROUND) {
         signalExceptionForGroupError(env, EINVAL, pid);
         return;
     }
@@ -249,10 +269,6 @@
         grp = SP_FOREGROUND;
         isDefault = true;
     }
-    if (!verifyGroup(env, grp)) {
-        return;
-    }
-    SchedPolicy sp = (SchedPolicy) grp;
 
     if (kDebugPolicy) {
         char cmdline[32];
@@ -268,7 +284,7 @@
             close(fd);
         }
 
-        if (sp == SP_BACKGROUND) {
+        if (grp == SP_BACKGROUND) {
             ALOGD("setProcessGroup: vvv pid %d (%s)", pid, cmdline);
         } else {
             ALOGD("setProcessGroup: ^^^ pid %d (%s)", pid, cmdline);
@@ -286,6 +302,7 @@
     while ((de = readdir(d))) {
         int t_pid;
         int t_pri;
+        int err;
 
         if (de->d_name[0] == '.')
             continue;
@@ -311,28 +328,16 @@
             if (t_pri >= ANDROID_PRIORITY_BACKGROUND) {
                 // This task wants to stay at background
                 // update its cpuset so it doesn't only run on bg core(s)
-                if (cpusets_enabled()) {
-                    int err = set_cpuset_policy(t_pid, sp);
-                    if (err != NO_ERROR) {
-                        signalExceptionForGroupError(env, -err, t_pid);
-                        break;
-                    }
+                err = SetTaskProfiles(t_pid, {kCpusetProfileMap.at(grp)}, true) ? 0 : -1;
+                if (err != NO_ERROR) {
+                    signalExceptionForGroupError(env, -err, t_pid);
+                    break;
                 }
                 continue;
             }
         }
-        int err;
 
-        if (cpusets_enabled()) {
-            // set both cpuset and cgroup for general threads
-            err = set_cpuset_policy(t_pid, sp);
-            if (err != NO_ERROR) {
-                signalExceptionForGroupError(env, -err, t_pid);
-                break;
-            }
-        }
-
-        err = set_sched_policy(t_pid, sp);
+        err = SetTaskProfiles(t_pid, {kCpusetProfileMap.at(grp)}, true) ? 0 : -1;
         if (err != NO_ERROR) {
             signalExceptionForGroupError(env, -err, t_pid);
             break;
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/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index 381ed7f..a301702 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -2840,6 +2840,9 @@
 
         <!-- The name of the overlayable whose resources will be overlaid. -->
         <attr name="targetName" />
+
+        <!-- The xml file that defines the target id to overlay value mappings. -->
+        <attr name="resourcesMap" format="reference" />
     </declare-styleable>
 
     <!-- Declaration of an {@link android.content.Intent} object in XML.  May
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/public.xml b/core/res/res/values/public.xml
index 96c0cf3..9724c41 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -3000,6 +3000,8 @@
     <public-group type="attr" first-id="0x01010607">
       <public name="importantForContentCapture" />
       <public name="forceQueryable" />
+      <!-- @hide @SystemApi -->
+      <public name="resourcesMap" />
     </public-group>
 
     <public-group type="drawable" first-id="0x010800b5">
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/core/tests/coretests/src/android/provider/DeviceConfigTest.java b/core/tests/coretests/src/android/provider/DeviceConfigTest.java
index 23fabce..77b7f2a 100644
--- a/core/tests/coretests/src/android/provider/DeviceConfigTest.java
+++ b/core/tests/coretests/src/android/provider/DeviceConfigTest.java
@@ -17,6 +17,7 @@
 package android.provider;
 
 import static android.provider.DeviceConfig.OnPropertiesChangedListener;
+import static android.provider.DeviceConfig.Properties;
 
 import static com.google.common.truth.Truth.assertThat;
 
@@ -42,27 +43,33 @@
 @RunWith(AndroidJUnit4.class)
 @SmallTest
 public class DeviceConfigTest {
-    // TODO(b/109919982): Migrate tests to CTS
-    private static final String sNamespace = "namespace1";
-    private static final String sKey = "key1";
-    private static final String sValue = "value1";
     private static final long WAIT_FOR_PROPERTY_CHANGE_TIMEOUT_MILLIS = 2000; // 2 sec
+    private static final String DEFAULT_VALUE = "test_default_value";
+    private static final String NAMESPACE = "namespace1";
+    private static final String KEY = "key1";
+    private static final String KEY2 = "key2";
+    private static final String KEY3 = "key3";
+    private static final String VALUE = "value1";
+    private static final String VALUE2 = "value2";
+    private static final String VALUE3 = "value3";
 
     @After
     public void cleanUp() {
-        deleteViaContentProvider(sNamespace, sKey);
+        deleteViaContentProvider(NAMESPACE, KEY);
+        deleteViaContentProvider(NAMESPACE, KEY2);
+        deleteViaContentProvider(NAMESPACE, KEY3);
     }
 
     @Test
     public void getProperty_empty() {
-        String result = DeviceConfig.getProperty(sNamespace, sKey);
+        String result = DeviceConfig.getProperty(NAMESPACE, KEY);
         assertThat(result).isNull();
     }
 
     @Test
     public void getProperty_nullNamespace() {
         try {
-            DeviceConfig.getProperty(null, sKey);
+            DeviceConfig.getProperty(null, KEY);
             Assert.fail("Null namespace should have resulted in an NPE.");
         } catch (NullPointerException e) {
             // expected
@@ -72,7 +79,7 @@
     @Test
     public void getProperty_nullName() {
         try {
-            DeviceConfig.getProperty(sNamespace, null);
+            DeviceConfig.getProperty(NAMESPACE, null);
             Assert.fail("Null name should have resulted in an NPE.");
         } catch (NullPointerException e) {
             // expected
@@ -82,13 +89,13 @@
     @Test
     public void getString_empty() {
         final String default_value = "default_value";
-        final String result = DeviceConfig.getString(sNamespace, sKey, default_value);
+        final String result = DeviceConfig.getString(NAMESPACE, KEY, default_value);
         assertThat(result).isEqualTo(default_value);
     }
 
     @Test
     public void getString_nullDefault() {
-        final String result = DeviceConfig.getString(sNamespace, sKey, null);
+        final String result = DeviceConfig.getString(NAMESPACE, KEY, null);
         assertThat(result).isNull();
     }
 
@@ -96,16 +103,16 @@
     public void getString_nonEmpty() {
         final String value = "new_value";
         final String default_value = "default";
-        DeviceConfig.setProperty(sNamespace, sKey, value, false);
+        DeviceConfig.setProperty(NAMESPACE, KEY, value, false);
 
-        final String result = DeviceConfig.getString(sNamespace, sKey, default_value);
+        final String result = DeviceConfig.getString(NAMESPACE, KEY, default_value);
         assertThat(result).isEqualTo(value);
     }
 
     @Test
     public void getString_nullNamespace() {
         try {
-            DeviceConfig.getString(null, sKey, "default_value");
+            DeviceConfig.getString(null, KEY, "default_value");
             Assert.fail("Null namespace should have resulted in an NPE.");
         } catch (NullPointerException e) {
             // expected
@@ -115,7 +122,7 @@
     @Test
     public void getString_nullName() {
         try {
-            DeviceConfig.getString(sNamespace, null, "default_value");
+            DeviceConfig.getString(NAMESPACE, null, "default_value");
             Assert.fail("Null name should have resulted in an NPE.");
         } catch (NullPointerException e) {
             // expected
@@ -125,7 +132,7 @@
     @Test
     public void getBoolean_empty() {
         final boolean default_value = true;
-        final boolean result = DeviceConfig.getBoolean(sNamespace, sKey, default_value);
+        final boolean result = DeviceConfig.getBoolean(NAMESPACE, KEY, default_value);
         assertThat(result).isEqualTo(default_value);
     }
 
@@ -133,18 +140,18 @@
     public void getBoolean_valid() {
         final boolean value = true;
         final boolean default_value = false;
-        DeviceConfig.setProperty(sNamespace, sKey, String.valueOf(value), false);
+        DeviceConfig.setProperty(NAMESPACE, KEY, String.valueOf(value), false);
 
-        final boolean result = DeviceConfig.getBoolean(sNamespace, sKey, default_value);
+        final boolean result = DeviceConfig.getBoolean(NAMESPACE, KEY, default_value);
         assertThat(result).isEqualTo(value);
     }
 
     @Test
     public void getBoolean_invalid() {
         final boolean default_value = true;
-        DeviceConfig.setProperty(sNamespace, sKey, "not_a_boolean", false);
+        DeviceConfig.setProperty(NAMESPACE, KEY, "not_a_boolean", false);
 
-        final boolean result = DeviceConfig.getBoolean(sNamespace, sKey, default_value);
+        final boolean result = DeviceConfig.getBoolean(NAMESPACE, KEY, default_value);
         // Anything non-null other than case insensitive "true" parses to false.
         assertThat(result).isFalse();
     }
@@ -152,7 +159,7 @@
     @Test
     public void getBoolean_nullNamespace() {
         try {
-            DeviceConfig.getBoolean(null, sKey, false);
+            DeviceConfig.getBoolean(null, KEY, false);
             Assert.fail("Null namespace should have resulted in an NPE.");
         } catch (NullPointerException e) {
             // expected
@@ -162,7 +169,7 @@
     @Test
     public void getBoolean_nullName() {
         try {
-            DeviceConfig.getBoolean(sNamespace, null, false);
+            DeviceConfig.getBoolean(NAMESPACE, null, false);
             Assert.fail("Null name should have resulted in an NPE.");
         } catch (NullPointerException e) {
             // expected
@@ -172,7 +179,7 @@
     @Test
     public void getInt_empty() {
         final int default_value = 999;
-        final int result = DeviceConfig.getInt(sNamespace, sKey, default_value);
+        final int result = DeviceConfig.getInt(NAMESPACE, KEY, default_value);
         assertThat(result).isEqualTo(default_value);
     }
 
@@ -180,18 +187,18 @@
     public void getInt_valid() {
         final int value = 123;
         final int default_value = 999;
-        DeviceConfig.setProperty(sNamespace, sKey, String.valueOf(value), false);
+        DeviceConfig.setProperty(NAMESPACE, KEY, String.valueOf(value), false);
 
-        final int result = DeviceConfig.getInt(sNamespace, sKey, default_value);
+        final int result = DeviceConfig.getInt(NAMESPACE, KEY, default_value);
         assertThat(result).isEqualTo(value);
     }
 
     @Test
     public void getInt_invalid() {
         final int default_value = 999;
-        DeviceConfig.setProperty(sNamespace, sKey, "not_an_int", false);
+        DeviceConfig.setProperty(NAMESPACE, KEY, "not_an_int", false);
 
-        final int result = DeviceConfig.getInt(sNamespace, sKey, default_value);
+        final int result = DeviceConfig.getInt(NAMESPACE, KEY, default_value);
         // Failure to parse results in using the default value
         assertThat(result).isEqualTo(default_value);
     }
@@ -199,7 +206,7 @@
     @Test
     public void getInt_nullNamespace() {
         try {
-            DeviceConfig.getInt(null, sKey, 0);
+            DeviceConfig.getInt(null, KEY, 0);
             Assert.fail("Null namespace should have resulted in an NPE.");
         } catch (NullPointerException e) {
             // expected
@@ -209,7 +216,7 @@
     @Test
     public void getInt_nullName() {
         try {
-            DeviceConfig.getInt(sNamespace, null, 0);
+            DeviceConfig.getInt(NAMESPACE, null, 0);
             Assert.fail("Null name should have resulted in an NPE.");
         } catch (NullPointerException e) {
             // expected
@@ -219,7 +226,7 @@
     @Test
     public void getLong_empty() {
         final long default_value = 123456;
-        final long result = DeviceConfig.getLong(sNamespace, sKey, default_value);
+        final long result = DeviceConfig.getLong(NAMESPACE, KEY, default_value);
         assertThat(result).isEqualTo(default_value);
     }
 
@@ -227,18 +234,18 @@
     public void getLong_valid() {
         final long value = 456789;
         final long default_value = 123456;
-        DeviceConfig.setProperty(sNamespace, sKey, String.valueOf(value), false);
+        DeviceConfig.setProperty(NAMESPACE, KEY, String.valueOf(value), false);
 
-        final long result = DeviceConfig.getLong(sNamespace, sKey, default_value);
+        final long result = DeviceConfig.getLong(NAMESPACE, KEY, default_value);
         assertThat(result).isEqualTo(value);
     }
 
     @Test
     public void getLong_invalid() {
         final long default_value = 123456;
-        DeviceConfig.setProperty(sNamespace, sKey, "not_a_long", false);
+        DeviceConfig.setProperty(NAMESPACE, KEY, "not_a_long", false);
 
-        final long result = DeviceConfig.getLong(sNamespace, sKey, default_value);
+        final long result = DeviceConfig.getLong(NAMESPACE, KEY, default_value);
         // Failure to parse results in using the default value
         assertThat(result).isEqualTo(default_value);
     }
@@ -246,7 +253,7 @@
     @Test
     public void getLong_nullNamespace() {
         try {
-            DeviceConfig.getLong(null, sKey, 0);
+            DeviceConfig.getLong(null, KEY, 0);
             Assert.fail("Null namespace should have resulted in an NPE.");
         } catch (NullPointerException e) {
             // expected
@@ -256,7 +263,7 @@
     @Test
     public void getLong_nullName() {
         try {
-            DeviceConfig.getLong(sNamespace, null, 0);
+            DeviceConfig.getLong(NAMESPACE, null, 0);
             Assert.fail("Null name should have resulted in an NPE.");
         } catch (NullPointerException e) {
             // expected
@@ -266,7 +273,7 @@
     @Test
     public void getFloat_empty() {
         final float default_value = 123.456f;
-        final float result = DeviceConfig.getFloat(sNamespace, sKey, default_value);
+        final float result = DeviceConfig.getFloat(NAMESPACE, KEY, default_value);
         assertThat(result).isEqualTo(default_value);
     }
 
@@ -274,18 +281,18 @@
     public void getFloat_valid() {
         final float value = 456.789f;
         final float default_value = 123.456f;
-        DeviceConfig.setProperty(sNamespace, sKey, String.valueOf(value), false);
+        DeviceConfig.setProperty(NAMESPACE, KEY, String.valueOf(value), false);
 
-        final float result = DeviceConfig.getFloat(sNamespace, sKey, default_value);
+        final float result = DeviceConfig.getFloat(NAMESPACE, KEY, default_value);
         assertThat(result).isEqualTo(value);
     }
 
     @Test
     public void getFloat_invalid() {
         final float default_value = 123.456f;
-        DeviceConfig.setProperty(sNamespace, sKey, "not_a_float", false);
+        DeviceConfig.setProperty(NAMESPACE, KEY, "not_a_float", false);
 
-        final float result = DeviceConfig.getFloat(sNamespace, sKey, default_value);
+        final float result = DeviceConfig.getFloat(NAMESPACE, KEY, default_value);
         // Failure to parse results in using the default value
         assertThat(result).isEqualTo(default_value);
     }
@@ -293,7 +300,7 @@
     @Test
     public void getFloat_nullNamespace() {
         try {
-            DeviceConfig.getFloat(null, sKey, 0);
+            DeviceConfig.getFloat(null, KEY, 0);
             Assert.fail("Null namespace should have resulted in an NPE.");
         } catch (NullPointerException e) {
             // expected
@@ -303,7 +310,7 @@
     @Test
     public void getFloat_nullName() {
         try {
-            DeviceConfig.getFloat(sNamespace, null, 0);
+            DeviceConfig.getFloat(NAMESPACE, null, 0);
             Assert.fail("Null name should have resulted in an NPE.");
         } catch (NullPointerException e) {
             // expected
@@ -313,7 +320,7 @@
     @Test
     public void setProperty_nullNamespace() {
         try {
-            DeviceConfig.setProperty(null, sKey, sValue, false);
+            DeviceConfig.setProperty(null, KEY, VALUE, false);
             Assert.fail("Null namespace should have resulted in an NPE.");
         } catch (NullPointerException e) {
             // expected
@@ -323,7 +330,7 @@
     @Test
     public void setProperty_nullName() {
         try {
-            DeviceConfig.setProperty(sNamespace, null, sValue, false);
+            DeviceConfig.setProperty(NAMESPACE, null, VALUE, false);
             Assert.fail("Null name should have resulted in an NPE.");
         } catch (NullPointerException e) {
             // expected
@@ -332,16 +339,16 @@
 
     @Test
     public void setAndGetProperty_sameNamespace() {
-        DeviceConfig.setProperty(sNamespace, sKey, sValue, false);
-        String result = DeviceConfig.getProperty(sNamespace, sKey);
-        assertThat(result).isEqualTo(sValue);
+        DeviceConfig.setProperty(NAMESPACE, KEY, VALUE, false);
+        String result = DeviceConfig.getProperty(NAMESPACE, KEY);
+        assertThat(result).isEqualTo(VALUE);
     }
 
     @Test
     public void setAndGetProperty_differentNamespace() {
         String newNamespace = "namespace2";
-        DeviceConfig.setProperty(sNamespace, sKey, sValue, false);
-        String result = DeviceConfig.getProperty(newNamespace, sKey);
+        DeviceConfig.setProperty(NAMESPACE, KEY, VALUE, false);
+        String result = DeviceConfig.getProperty(newNamespace, KEY);
         assertThat(result).isNull();
     }
 
@@ -349,41 +356,147 @@
     public void setAndGetProperty_multipleNamespaces() {
         String newNamespace = "namespace2";
         String newValue = "value2";
-        DeviceConfig.setProperty(sNamespace, sKey, sValue, false);
-        DeviceConfig.setProperty(newNamespace, sKey, newValue, false);
-        String result = DeviceConfig.getProperty(sNamespace, sKey);
-        assertThat(result).isEqualTo(sValue);
-        result = DeviceConfig.getProperty(newNamespace, sKey);
+        DeviceConfig.setProperty(NAMESPACE, KEY, VALUE, false);
+        DeviceConfig.setProperty(newNamespace, KEY, newValue, false);
+        String result = DeviceConfig.getProperty(NAMESPACE, KEY);
+        assertThat(result).isEqualTo(VALUE);
+        result = DeviceConfig.getProperty(newNamespace, KEY);
         assertThat(result).isEqualTo(newValue);
 
         // clean up
-        deleteViaContentProvider(newNamespace, sKey);
+        deleteViaContentProvider(newNamespace, KEY);
     }
 
     @Test
     public void setAndGetProperty_overrideValue() {
         String newValue = "value2";
-        DeviceConfig.setProperty(sNamespace, sKey, sValue, false);
-        DeviceConfig.setProperty(sNamespace, sKey, newValue, false);
-        String result = DeviceConfig.getProperty(sNamespace, sKey);
+        DeviceConfig.setProperty(NAMESPACE, KEY, VALUE, false);
+        DeviceConfig.setProperty(NAMESPACE, KEY, newValue, false);
+        String result = DeviceConfig.getProperty(NAMESPACE, KEY);
         assertThat(result).isEqualTo(newValue);
     }
 
     @Test
+    public void getProperties_fullNamespace() {
+        Properties properties = DeviceConfig.getProperties(NAMESPACE);
+        assertThat(properties.getKeyset()).isEmpty();
+
+        DeviceConfig.setProperty(NAMESPACE, KEY, VALUE, false);
+        DeviceConfig.setProperty(NAMESPACE, KEY2, VALUE2, false);
+        properties = DeviceConfig.getProperties(NAMESPACE);
+        assertThat(properties.getKeyset()).containsExactly(KEY, KEY2);
+        assertThat(properties.getString(KEY, DEFAULT_VALUE)).isEqualTo(VALUE);
+        assertThat(properties.getString(KEY2, DEFAULT_VALUE)).isEqualTo(VALUE2);
+
+        DeviceConfig.setProperty(NAMESPACE, KEY, VALUE3, false);
+        properties = DeviceConfig.getProperties(NAMESPACE);
+        assertThat(properties.getKeyset()).containsExactly(KEY, KEY2);
+        assertThat(properties.getString(KEY, DEFAULT_VALUE)).isEqualTo(VALUE3);
+        assertThat(properties.getString(KEY2, DEFAULT_VALUE)).isEqualTo(VALUE2);
+    }
+
+    @Test
+    public void getProperties_getString() {
+        DeviceConfig.setProperty(NAMESPACE, KEY, VALUE, false);
+        DeviceConfig.setProperty(NAMESPACE, KEY2, VALUE2, false);
+
+        Properties properties = DeviceConfig.getProperties(NAMESPACE, KEY, KEY2);
+        assertThat(properties.getKeyset()).containsExactly(KEY, KEY2);
+        assertThat(properties.getString(KEY, DEFAULT_VALUE)).isEqualTo(VALUE);
+        assertThat(properties.getString(KEY2, DEFAULT_VALUE)).isEqualTo(VALUE2);
+    }
+
+    @Test
+    public void getProperties_getBoolean() {
+        DeviceConfig.setProperty(NAMESPACE, KEY, "true", false);
+        DeviceConfig.setProperty(NAMESPACE, KEY2, "false", false);
+        DeviceConfig.setProperty(NAMESPACE, KEY3, "not a valid boolean", false);
+
+        Properties properties = DeviceConfig.getProperties(NAMESPACE, KEY, KEY2, KEY3);
+        assertThat(properties.getKeyset()).containsExactly(KEY, KEY2, KEY3);
+        assertThat(properties.getBoolean(KEY, true)).isTrue();
+        assertThat(properties.getBoolean(KEY, false)).isTrue();
+        assertThat(properties.getBoolean(KEY2, true)).isFalse();
+        assertThat(properties.getBoolean(KEY2, false)).isFalse();
+        // KEY3 was set to garbage, anything nonnull but "true" will parse as false
+        assertThat(properties.getBoolean(KEY3, true)).isFalse();
+        assertThat(properties.getBoolean(KEY3, false)).isFalse();
+        // If a key was not set, it will return the default value
+        assertThat(properties.getBoolean("missing_key", true)).isTrue();
+        assertThat(properties.getBoolean("missing_key", false)).isFalse();
+    }
+
+    @Test
+    public void getProperties_getInt() {
+        final int value = 101;
+
+        DeviceConfig.setProperty(NAMESPACE, KEY, Integer.toString(value), false);
+        DeviceConfig.setProperty(NAMESPACE, KEY2, "not a valid int", false);
+
+        Properties properties = DeviceConfig.getProperties(NAMESPACE, KEY, KEY2);
+        assertThat(properties.getKeyset()).containsExactly(KEY, KEY2);
+        assertThat(properties.getInt(KEY, -1)).isEqualTo(value);
+        // KEY2 was set to garbage, the default value is returned if an int cannot be parsed
+        assertThat(properties.getInt(KEY2, -1)).isEqualTo(-1);
+    }
+
+    @Test
+    public void getProperties_getFloat() {
+        final float value = 101.010f;
+
+        DeviceConfig.setProperty(NAMESPACE, KEY, Float.toString(value), false);
+        DeviceConfig.setProperty(NAMESPACE, KEY2, "not a valid float", false);
+
+        Properties properties = DeviceConfig.getProperties(NAMESPACE, KEY, KEY2);
+        assertThat(properties.getKeyset()).containsExactly(KEY, KEY2);
+        assertThat(properties.getFloat(KEY, -1.0f)).isEqualTo(value);
+        // KEY2 was set to garbage, the default value is returned if a float cannot be parsed
+        assertThat(properties.getFloat(KEY2, -1.0f)).isEqualTo(-1.0f);
+    }
+
+    @Test
+    public void getProperties_getLong() {
+        final long value = 101;
+
+        DeviceConfig.setProperty(NAMESPACE, KEY, Long.toString(value), false);
+        DeviceConfig.setProperty(NAMESPACE, KEY2, "not a valid long", false);
+
+        Properties properties = DeviceConfig.getProperties(NAMESPACE, KEY, KEY2);
+        assertThat(properties.getKeyset()).containsExactly(KEY, KEY2);
+        assertThat(properties.getLong(KEY, -1)).isEqualTo(value);
+        // KEY2 was set to garbage, the default value is returned if a long cannot be parsed
+        assertThat(properties.getLong(KEY2, -1)).isEqualTo(-1);
+    }
+
+    @Test
+    public void getProperties_defaults() {
+        DeviceConfig.setProperty(NAMESPACE, KEY, VALUE, false);
+        DeviceConfig.setProperty(NAMESPACE, KEY3, VALUE3, false);
+
+        Properties properties = DeviceConfig.getProperties(NAMESPACE, KEY, KEY2);
+        assertThat(properties.getKeyset()).containsExactly(KEY);
+        assertThat(properties.getString(KEY, DEFAULT_VALUE)).isEqualTo(VALUE);
+        // not set in DeviceConfig, but requested in getProperties
+        assertThat(properties.getString(KEY2, DEFAULT_VALUE)).isEqualTo(DEFAULT_VALUE);
+        // set in DeviceConfig, but not requested in getProperties
+        assertThat(properties.getString(KEY3, DEFAULT_VALUE)).isEqualTo(DEFAULT_VALUE);
+    }
+
+    @Test
     public void testOnPropertiesChangedListener() throws InterruptedException {
         final CountDownLatch countDownLatch = new CountDownLatch(1);
 
         OnPropertiesChangedListener changeListener = (properties) -> {
-            assertThat(properties.getNamespace()).isEqualTo(sNamespace);
-            assertThat(properties.getKeyset()).contains(sKey);
-            assertThat(properties.getString(sKey, "default_value")).isEqualTo(sValue);
+            assertThat(properties.getNamespace()).isEqualTo(NAMESPACE);
+            assertThat(properties.getKeyset()).contains(KEY);
+            assertThat(properties.getString(KEY, "default_value")).isEqualTo(VALUE);
             countDownLatch.countDown();
         };
 
         try {
-            DeviceConfig.addOnPropertiesChangedListener(sNamespace,
+            DeviceConfig.addOnPropertiesChangedListener(NAMESPACE,
                     ActivityThread.currentApplication().getMainExecutor(), changeListener);
-            DeviceConfig.setProperty(sNamespace, sKey, sValue, false);
+            DeviceConfig.setProperty(NAMESPACE, KEY, VALUE, false);
             assertThat(countDownLatch.await(
                     WAIT_FOR_PROPERTY_CHANGE_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS)).isTrue();
         } catch (InterruptedException e) {
diff --git a/graphics/java/android/graphics/BLASTBufferQueue.java b/graphics/java/android/graphics/BLASTBufferQueue.java
new file mode 100644
index 0000000..8c6a9371
--- /dev/null
+++ b/graphics/java/android/graphics/BLASTBufferQueue.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 android.graphics;
+
+import android.view.Surface;
+import android.view.SurfaceControl;
+
+/**
+ * @hide
+ */
+public final class BLASTBufferQueue {
+    // Note: This field is accessed by native code.
+    private long mNativeObject; // BLASTBufferQueue*
+
+    private static native long nativeCreate(long surfaceControl, long width, long height);
+    private static native void nativeDestroy(long ptr);
+    private static native Surface nativeGetSurface(long ptr);
+    private static native void nativeSetNextTransaction(long ptr, long transactionPtr);
+    private static native void nativeUpdate(long ptr, long surfaceControl, long width, long height);
+
+    /** Create a new connection with the surface flinger. */
+    public BLASTBufferQueue(SurfaceControl sc, int width, int height) {
+        mNativeObject = nativeCreate(sc.mNativeObject, width, height);
+    }
+
+    public void destroy() {
+        nativeDestroy(mNativeObject);
+    }
+
+    public Surface getSurface() {
+        return nativeGetSurface(mNativeObject);
+    }
+
+    public void setNextTransaction(SurfaceControl.Transaction t) {
+        nativeSetNextTransaction(mNativeObject, t.mNativeObject);
+    }
+
+    public void update(SurfaceControl sc, int width, int height) {
+        nativeUpdate(mNativeObject, sc.mNativeObject, width, height);
+    }
+
+    @Override
+    protected void finalize() throws Throwable {
+        try {
+            if (mNativeObject != 0) {
+                nativeDestroy(mNativeObject);
+            }
+        } finally {
+            super.finalize();
+        }
+    }
+}
+
diff --git a/graphics/java/android/graphics/ImageDecoder.java b/graphics/java/android/graphics/ImageDecoder.java
index 150a941..6619dba 100644
--- a/graphics/java/android/graphics/ImageDecoder.java
+++ b/graphics/java/android/graphics/ImageDecoder.java
@@ -286,6 +286,9 @@
 
                 return createFromStream(is, true, preferAnimation, this);
             }
+            if (assetFd == null) {
+                throw new FileNotFoundException(mUri.toString());
+            }
             return createFromAssetFileDescriptor(assetFd, preferAnimation, this);
         }
     }
@@ -341,6 +344,9 @@
     @NonNull
     private static ImageDecoder createFromAssetFileDescriptor(@NonNull AssetFileDescriptor assetFd,
             boolean preferAnimation, Source source) throws IOException {
+        if (assetFd == null) {
+            throw new FileNotFoundException();
+        }
         final FileDescriptor fd = assetFd.getFileDescriptor();
         final long offset = assetFd.getStartOffset();
 
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/graphics/java/android/graphics/drawable/GradientDrawable.java b/graphics/java/android/graphics/drawable/GradientDrawable.java
index 96ac0f9..c6586ec 100644
--- a/graphics/java/android/graphics/drawable/GradientDrawable.java
+++ b/graphics/java/android/graphics/drawable/GradientDrawable.java
@@ -97,6 +97,14 @@
  * @attr ref android.R.styleable#GradientDrawablePadding_bottom
  */
 public class GradientDrawable extends Drawable {
+
+    /**
+     * Flag to determine if we should wrap negative gradient angle measurements
+     * for API levels that support it
+     * @hide
+     */
+    public static boolean sWrapNegativeAngleMeasurements = true;
+
     /**
      * Shape is a rectangle, possibly with rounded corners
      */
@@ -151,6 +159,9 @@
     /** Radius is a fraction of the bounds size. */
     private static final int RADIUS_TYPE_FRACTION_PARENT = 2;
 
+    /** Default orientation for GradientDrawable **/
+    private static final Orientation DEFAULT_ORIENTATION = Orientation.TOP_BOTTOM;
+
     /** @hide */
     @IntDef({RADIUS_TYPE_PIXELS, RADIUS_TYPE_FRACTION, RADIUS_TYPE_FRACTION_PARENT})
     @Retention(RetentionPolicy.SOURCE)
@@ -207,7 +218,7 @@
     }
 
     public GradientDrawable() {
-        this(new GradientState(Orientation.TOP_BOTTOM, null), null);
+        this(new GradientState(DEFAULT_ORIENTATION, null), null);
     }
 
     /**
@@ -1757,33 +1768,48 @@
         }
 
         int angle = (int) a.getFloat(R.styleable.GradientDrawableGradient_angle, st.mAngle);
-        st.mAngle = ((angle % 360) + 360) % 360; // offset negative angle measures
 
-        switch (st.mAngle) {
-            case 0:
-                st.mOrientation = Orientation.LEFT_RIGHT;
-                break;
-            case 45:
-                st.mOrientation = Orientation.BL_TR;
-                break;
-            case 90:
-                st.mOrientation = Orientation.BOTTOM_TOP;
-                break;
-            case 135:
-                st.mOrientation = Orientation.BR_TL;
-                break;
-            case 180:
-                st.mOrientation = Orientation.RIGHT_LEFT;
-                break;
-            case 225:
-                st.mOrientation = Orientation.TR_BL;
-                break;
-            case 270:
-                st.mOrientation = Orientation.TOP_BOTTOM;
-                break;
-            case 315:
-                st.mOrientation = Orientation.TL_BR;
-                break;
+        // GradientDrawable historically has not parsed negative angle measurements and always
+        // stays on the default orientation for API levels older than Q.
+        // Only configure the orientation if the angle is greater than zero.
+        // Otherwise fallback on Orientation.TOP_BOTTOM
+        // In Android Q and later, actually wrap the negative angle measurement to the correct
+        // value
+        if (sWrapNegativeAngleMeasurements) {
+            st.mAngle = ((angle % 360) + 360) % 360; // offset negative angle measures
+        } else {
+            st.mAngle = angle % 360;
+        }
+
+        if (st.mAngle >= 0) {
+            switch (st.mAngle) {
+                case 0:
+                    st.mOrientation = Orientation.LEFT_RIGHT;
+                    break;
+                case 45:
+                    st.mOrientation = Orientation.BL_TR;
+                    break;
+                case 90:
+                    st.mOrientation = Orientation.BOTTOM_TOP;
+                    break;
+                case 135:
+                    st.mOrientation = Orientation.BR_TL;
+                    break;
+                case 180:
+                    st.mOrientation = Orientation.RIGHT_LEFT;
+                    break;
+                case 225:
+                    st.mOrientation = Orientation.TR_BL;
+                    break;
+                case 270:
+                    st.mOrientation = Orientation.TOP_BOTTOM;
+                    break;
+                case 315:
+                    st.mOrientation = Orientation.TL_BR;
+                    break;
+            }
+        } else {
+            st.mOrientation = DEFAULT_ORIENTATION;
         }
 
         final TypedValue tv = a.peekValue(R.styleable.GradientDrawableGradient_gradientRadius);
diff --git a/libs/androidfw/ResourceTypes.cpp b/libs/androidfw/ResourceTypes.cpp
index 5353869..3fe2c5b 100644
--- a/libs/androidfw/ResourceTypes.cpp
+++ b/libs/androidfw/ResourceTypes.cpp
@@ -1063,6 +1063,11 @@
     return (mError == NO_ERROR) ? mHeader->header.size : 0;
 }
 
+const void* ResStringPool::data() const
+{
+    return mHeader;
+}
+
 bool ResStringPool::isSorted() const
 {
     return (mHeader->flags&ResStringPool_header::SORTED_FLAG)!=0;
diff --git a/libs/androidfw/include/androidfw/ResourceTypes.h b/libs/androidfw/include/androidfw/ResourceTypes.h
index fc635aa..c8ace90 100644
--- a/libs/androidfw/include/androidfw/ResourceTypes.h
+++ b/libs/androidfw/include/androidfw/ResourceTypes.h
@@ -523,6 +523,8 @@
     size_t size() const;
     size_t styleCount() const;
     size_t bytes() const;
+    const void* data() const;
+
 
     bool isSorted() const;
     bool isUTF8() const;
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/MediaCodec.java b/media/java/android/media/MediaCodec.java
index 510ee44..b7a9ffe 100644
--- a/media/java/android/media/MediaCodec.java
+++ b/media/java/android/media/MediaCodec.java
@@ -649,7 +649,7 @@
  are not consumed by the Surface in a timely manner). Or it may be configured to not drop excessive
  frames. In the latter mode if the Surface is not consuming output frames fast enough, it will
  eventually block the decoder. Prior to {@link android.os.Build.VERSION_CODES#Q} the exact behavior
- was undefined, with the exception that View surfaces (SuerfaceView or TextureView) always dropped
+ was undefined, with the exception that View surfaces (SurfaceView or TextureView) always dropped
  excessive frames. Since {@link android.os.Build.VERSION_CODES#Q} the default behavior is to drop
  excessive frames. Applications can opt out of this behavior for non-View surfaces (such as
  ImageReader or SurfaceTexture) by targeting SDK {@link android.os.Build.VERSION_CODES#Q} and
@@ -3513,6 +3513,19 @@
     public static final String PARAMETER_KEY_HDR10_PLUS_INFO = MediaFormat.KEY_HDR10_PLUS_INFO;
 
     /**
+     * Enable/disable low latency decoding mode.
+     * When enabled, the decoder doesn't hold input and output data more than
+     * required by the codec standards.
+     * The value is an Integer object containing the value 1 to enable
+     * or the value 0 to disable.
+     *
+     * @see #setParameters(Bundle)
+     * @see MediaFormat#KEY_LOW_LATENCY
+     */
+    public static final String PARAMETER_KEY_LOW_LATENCY =
+            MediaFormat.KEY_LOW_LATENCY;
+
+    /**
      * Communicate additional parameter changes to the component instance.
      * <b>Note:</b> Some of these parameter changes may silently fail to apply.
      *
diff --git a/media/java/android/media/MediaCodecInfo.java b/media/java/android/media/MediaCodecInfo.java
index f304f7c..26e7936 100644
--- a/media/java/android/media/MediaCodecInfo.java
+++ b/media/java/android/media/MediaCodecInfo.java
@@ -559,6 +559,14 @@
         public static final String FEATURE_IntraRefresh = "intra-refresh";
 
         /**
+         * <b>decoder only</b>: codec supports low latency decoding.
+         * If supported, clients can enable the low latency mode for the decoder.
+         * When the mode is enabled, the decoder doesn't hold input and output data more than
+         * required by the codec standards.
+         */
+        public static final String FEATURE_LowLatency = "low-latency";
+
+        /**
          * Query codec feature capabilities.
          * <p>
          * These features are supported to be used by the codec.  These
@@ -587,6 +595,7 @@
             new Feature(FEATURE_FrameParsing,     (1 << 4), false),
             new Feature(FEATURE_MultipleFrames,   (1 << 5), false),
             new Feature(FEATURE_DynamicTimestamp, (1 << 6), false),
+            new Feature(FEATURE_LowLatency,       (1 << 7), true),
         };
 
         private static final Feature[] encoderFeatures = {
diff --git a/media/java/android/media/MediaFormat.java b/media/java/android/media/MediaFormat.java
index 8b667f7..8080f45 100644
--- a/media/java/android/media/MediaFormat.java
+++ b/media/java/android/media/MediaFormat.java
@@ -211,6 +211,15 @@
     public static final String KEY_MIME = "mime";
 
     /**
+     * An optional key describing the low latency decoding mode. This is an optional parameter
+     * that applies only to decoders. If enabled, the decoder doesn't hold input and output
+     * data more than required by the codec standards.
+     * The associated value is an integer (0 or 1): 1 when low-latency decoding is enabled,
+     * 0 otherwise. The default value is 0.
+     */
+    public static final String KEY_LOW_LATENCY = "low-latency";
+
+    /**
      * A key describing the language of the content, using either ISO 639-1
      * or 639-2/T codes.  The associated value is a string.
      */
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/java/android/media/MediaRoute2Info.java b/media/java/android/media/MediaRoute2Info.java
index 5dcbb05..abd774d 100644
--- a/media/java/android/media/MediaRoute2Info.java
+++ b/media/java/android/media/MediaRoute2Info.java
@@ -46,6 +46,21 @@
         }
     };
 
+    /**
+     * Playback information indicating the playback volume is fixed, i&#46;e&#46; it cannot be
+     * controlled from this object. An example of fixed playback volume is a remote player,
+     * playing over HDMI where the user prefers to control the volume on the HDMI sink, rather
+     * than attenuate at the source.
+     * @see #getVolumeHandling()
+     */
+    public static final int PLAYBACK_VOLUME_FIXED = 0;
+    /**
+     * Playback information indicating the playback volume is variable and can be controlled
+     * from this object.
+     * @see #getVolumeHandling()
+     */
+    public static final int PLAYBACK_VOLUME_VARIABLE = 1;
+
     @NonNull
     final String mId;
     @Nullable
@@ -58,6 +73,9 @@
     final String mClientPackageName;
     @NonNull
     final List<String> mSupportedCategories;
+    final int mVolume;
+    final int mVolumeMax;
+    final int mVolumeHandling;
     @Nullable
     final Bundle mExtras;
 
@@ -68,6 +86,9 @@
         mDescription = builder.mDescription;
         mClientPackageName = builder.mClientPackageName;
         mSupportedCategories = builder.mSupportedCategories;
+        mVolume = builder.mVolume;
+        mVolumeMax = builder.mVolumeMax;
+        mVolumeHandling = builder.mVolumeHandling;
         mExtras = builder.mExtras;
     }
 
@@ -78,6 +99,9 @@
         mDescription = in.readString();
         mClientPackageName = in.readString();
         mSupportedCategories = in.createStringArrayList();
+        mVolume = in.readInt();
+        mVolumeMax = in.readInt();
+        mVolumeHandling = in.readInt();
         mExtras = in.readBundle();
     }
 
@@ -111,6 +135,9 @@
                 && Objects.equals(mDescription, other.mDescription)
                 && Objects.equals(mClientPackageName, other.mClientPackageName)
                 && Objects.equals(mSupportedCategories, other.mSupportedCategories)
+                && (mVolume == other.mVolume)
+                && (mVolumeMax == other.mVolumeMax)
+                && (mVolumeHandling == other.mVolumeHandling)
                 //TODO: This will be evaluated as false in most cases. Try not to.
                 && Objects.equals(mExtras, other.mExtras);
     }
@@ -162,6 +189,29 @@
         return mSupportedCategories;
     }
 
+    /**
+     * Gets the current volume of the route. This may be invalid if the route is not selected.
+     */
+    public int getVolume() {
+        return mVolume;
+    }
+
+    /**
+     * Gets the maximum volume of the route.
+     */
+    public int getVolumeMax() {
+        return mVolumeMax;
+    }
+
+    /**
+     * Gets information about how volume is handled on the route.
+     *
+     * @return {@link #PLAYBACK_VOLUME_FIXED} or {@link #PLAYBACK_VOLUME_VARIABLE}
+     */
+    public int getVolumeHandling() {
+        return mVolumeHandling;
+    }
+
     @Nullable
     public Bundle getExtras() {
         return mExtras;
@@ -199,6 +249,9 @@
         dest.writeString(mDescription);
         dest.writeString(mClientPackageName);
         dest.writeStringList(mSupportedCategories);
+        dest.writeInt(mVolume);
+        dest.writeInt(mVolumeMax);
+        dest.writeInt(mVolumeHandling);
         dest.writeBundle(mExtras);
     }
 
@@ -209,6 +262,9 @@
                 .append("id=").append(getId())
                 .append(", name=").append(getName())
                 .append(", description=").append(getDescription())
+                .append(", volume=").append(getVolume())
+                .append(", volumeMax=").append(getVolumeMax())
+                .append(", volumeHandling=").append(getVolumeHandling())
                 .append(", providerId=").append(getProviderId())
                 .append(" }");
         return result.toString();
@@ -224,6 +280,9 @@
         String mDescription;
         String mClientPackageName;
         List<String> mSupportedCategories;
+        int mVolume;
+        int mVolumeMax;
+        int mVolumeHandling;
         Bundle mExtras;
 
         public Builder(@NonNull String id, @NonNull String name) {
@@ -251,6 +310,9 @@
             mDescription = routeInfo.mDescription;
             setClientPackageName(routeInfo.mClientPackageName);
             setSupportedCategories(routeInfo.mSupportedCategories);
+            setVolume(routeInfo.mVolume);
+            setVolumeMax(routeInfo.mVolumeMax);
+            setVolumeHandling(routeInfo.mVolumeHandling);
             if (routeInfo.mExtras != null) {
                 mExtras = new Bundle(routeInfo.mExtras);
             }
@@ -345,6 +407,32 @@
         }
 
         /**
+         * Sets the route's current volume, or 0 if unknown.
+         */
+        @NonNull
+        public Builder setVolume(int volume) {
+            mVolume = volume;
+            return this;
+        }
+
+        /**
+         * Sets the route's maximum volume, or 0 if unknown.
+         */
+        @NonNull
+        public Builder setVolumeMax(int volumeMax) {
+            mVolumeMax = volumeMax;
+            return this;
+        }
+
+        /**
+         * Sets the route's volume handling.
+         */
+        @NonNull
+        public Builder setVolumeHandling(int volumeHandling) {
+            mVolumeHandling = volumeHandling;
+            return this;
+        }
+        /**
          * Sets a bundle of extras for the route.
          */
         @NonNull
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/media/tests/MediaRouteProvider/src/com/android/mediarouteprovider/example/SampleMediaRoute2ProviderService.java b/media/tests/MediaRouteProvider/src/com/android/mediarouteprovider/example/SampleMediaRoute2ProviderService.java
index 1267aa8..8d39a93 100644
--- a/media/tests/MediaRouteProvider/src/com/android/mediarouteprovider/example/SampleMediaRoute2ProviderService.java
+++ b/media/tests/MediaRouteProvider/src/com/android/mediarouteprovider/example/SampleMediaRoute2ProviderService.java
@@ -36,6 +36,12 @@
     public static final String ROUTE_ID_SPECIAL_CATEGORY = "route_special_category";
     public static final String ROUTE_NAME_SPECIAL_CATEGORY = "Special Category Route";
 
+    public static final int VOLUME_MAX = 100;
+    public static final String ROUTE_ID_FIXED_VOLUME = "route_fixed_volume";
+    public static final String ROUTE_NAME_FIXED_VOLUME = "Fixed Volume Route";
+    public static final String ROUTE_ID_VARIABLE_VOLUME = "route_variable_volume";
+    public static final String ROUTE_NAME_VARIABLE_VOLUME = "Variable Volume Route";
+
     public static final String ACTION_REMOVE_ROUTE =
             "com.android.mediarouteprovider.action_remove_route";
 
@@ -58,9 +64,23 @@
                         .addSupportedCategory(CATEGORY_SAMPLE)
                         .addSupportedCategory(CATEGORY_SPECIAL)
                         .build();
+        MediaRoute2Info fixedVolumeRoute =
+                new MediaRoute2Info.Builder(ROUTE_ID_FIXED_VOLUME, ROUTE_NAME_FIXED_VOLUME)
+                        .addSupportedCategory(CATEGORY_SAMPLE)
+                        .setVolumeHandling(MediaRoute2Info.PLAYBACK_VOLUME_FIXED)
+                        .build();
+        MediaRoute2Info variableVolumeRoute =
+                new MediaRoute2Info.Builder(ROUTE_ID_VARIABLE_VOLUME, ROUTE_NAME_VARIABLE_VOLUME)
+                        .addSupportedCategory(CATEGORY_SAMPLE)
+                        .setVolumeHandling(MediaRoute2Info.PLAYBACK_VOLUME_VARIABLE)
+                        .setVolumeMax(VOLUME_MAX)
+                        .build();
+
         mRoutes.put(route1.getId(), route1);
         mRoutes.put(route2.getId(), route2);
         mRoutes.put(routeSpecial.getId(), routeSpecial);
+        mRoutes.put(fixedVolumeRoute.getId(), fixedVolumeRoute);
+        mRoutes.put(variableVolumeRoute.getId(), variableVolumeRoute);
     }
 
     @Override
diff --git a/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouterManagerTest.java b/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouterManagerTest.java
index a3ed07a..da832ac 100644
--- a/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouterManagerTest.java
+++ b/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouterManagerTest.java
@@ -16,6 +16,9 @@
 
 package com.android.mediaroutertest;
 
+import static android.media.MediaRoute2Info.PLAYBACK_VOLUME_FIXED;
+import static android.media.MediaRoute2Info.PLAYBACK_VOLUME_VARIABLE;
+
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotEquals;
 import static org.junit.Assert.assertNotNull;
@@ -61,6 +64,12 @@
     public static final String ROUTE_ID_SPECIAL_CATEGORY = "route_special_category";
     public static final String ROUTE_NAME_SPECIAL_CATEGORY = "Special Category Route";
 
+    public static final int VOLUME_MAX = 100;
+    public static final String ROUTE_ID_FIXED_VOLUME = "route_fixed_volume";
+    public static final String ROUTE_NAME_FIXED_VOLUME = "Fixed Volume Route";
+    public static final String ROUTE_ID_VARIABLE_VOLUME = "route_variable_volume";
+    public static final String ROUTE_NAME_VARIABLE_VOLUME = "Variable Volume Route";
+
     public static final String ACTION_REMOVE_ROUTE =
             "com.android.mediarouteprovider.action_remove_route";
 
@@ -98,7 +107,7 @@
         mPackageName = mContext.getPackageName();
     }
 
-    //TODO: Move to a seperate file
+    //TODO: Move to a separate file
     @Test
     public void testMediaRoute2Info() {
         MediaRoute2Info routeInfo1 = new MediaRoute2Info.Builder("id", "name")
@@ -281,6 +290,26 @@
         mManager.unregisterCallback(managerCallback);
     }
 
+    @Test
+    public void testVolumeHandling() {
+        MediaRouter2.Callback mockCallback = mock(MediaRouter2.Callback.class);
+
+        mRouter2.setControlCategories(CONTROL_CATEGORIES_ALL);
+        mRouter2.registerCallback(mExecutor, mockCallback);
+        verify(mockCallback, timeout(TIMEOUT_MS).atLeastOnce())
+                .onRoutesChanged(argThat(routes -> routes.size() > 0));
+        Map<String, MediaRoute2Info> routes = createRouteMap(mRouter2.getRoutes());
+
+        MediaRoute2Info fixedVolumeRoute = routes.get(ROUTE_ID_FIXED_VOLUME);
+        MediaRoute2Info variableVolumeRoute = routes.get(ROUTE_ID_VARIABLE_VOLUME);
+
+        assertEquals(PLAYBACK_VOLUME_FIXED, fixedVolumeRoute.getVolumeHandling());
+        assertEquals(PLAYBACK_VOLUME_VARIABLE, variableVolumeRoute.getVolumeHandling());
+        assertEquals(VOLUME_MAX, variableVolumeRoute.getVolumeMax());
+
+        mRouter2.unregisterCallback(mockCallback);
+    }
+
     // Helper for getting routes easily
     static Map<String, MediaRoute2Info> createRouteMap(List<MediaRoute2Info> routes) {
         Map<String, MediaRoute2Info> routeMap = new HashMap<>();
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/ClearCryptoStateTask.java b/packages/BackupEncryption/src/com/android/server/backup/encryption/tasks/ClearCryptoStateTask.java
new file mode 100644
index 0000000..8f35db6
--- /dev/null
+++ b/packages/BackupEncryption/src/com/android/server/backup/encryption/tasks/ClearCryptoStateTask.java
@@ -0,0 +1,71 @@
+/*
+ * 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 android.content.Context;
+import android.util.Slog;
+
+import com.android.server.backup.encryption.CryptoSettings;
+import com.android.server.backup.encryption.chunking.ProtoStore;
+import com.android.server.backup.encryption.storage.BackupEncryptionDb;
+import com.android.server.backup.encryption.storage.EncryptionDbException;
+
+import java.io.IOException;
+
+/**
+ * Task to clear local crypto state.
+ *
+ * <p>Needs to run whenever the user changes their backup account.
+ */
+public class ClearCryptoStateTask {
+    private static final String TAG = "ClearCryptoStateTask";
+
+    private final Context mContext;
+    private final CryptoSettings mCryptoSettings;
+
+    /**
+     * A new instance.
+     *
+     * @param context for finding local storage.
+     * @param cryptoSettings to clear
+     */
+    public ClearCryptoStateTask(Context context, CryptoSettings cryptoSettings) {
+        mContext = context;
+        mCryptoSettings = cryptoSettings;
+    }
+
+    /** Deletes all local state for backup (not restore). */
+    public void run() {
+        Slog.d(TAG, "Clearing local crypto state.");
+        try {
+            BackupEncryptionDb.newInstance(mContext).clear();
+        } catch (EncryptionDbException e) {
+            Slog.e(TAG, "Error clearing encryption database", e);
+        }
+        mCryptoSettings.clearAllSettingsForBackup();
+        try {
+            ProtoStore.createChunkListingStore(mContext).deleteAllProtos();
+        } catch (IOException e) {
+            Slog.e(TAG, "Error clearing chunk listing store", e);
+        }
+        try {
+            ProtoStore.createKeyValueListingStore(mContext).deleteAllProtos();
+        } catch (IOException e) {
+            Slog.e(TAG, "Error clearing key-value store", e);
+        }
+    }
+}
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/ClearCryptoStateTaskTest.java b/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/tasks/ClearCryptoStateTaskTest.java
new file mode 100644
index 0000000..81bfce1
--- /dev/null
+++ b/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/tasks/ClearCryptoStateTaskTest.java
@@ -0,0 +1,109 @@
+/*
+ * 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.spy;
+
+import android.content.Context;
+import android.platform.test.annotations.Presubmit;
+
+import androidx.test.core.app.ApplicationProvider;
+
+import com.android.server.backup.encryption.CryptoSettings;
+import com.android.server.backup.encryption.chunking.ProtoStore;
+import com.android.server.backup.encryption.protos.nano.ChunksMetadataProto.ChunkListing;
+import com.android.server.backup.encryption.protos.nano.KeyValueListingProto.KeyValueListing;
+import com.android.server.backup.encryption.storage.BackupEncryptionDb;
+import com.android.server.backup.encryption.storage.TertiaryKey;
+import com.android.server.backup.encryption.storage.TertiaryKeysTable;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+
+@RunWith(RobolectricTestRunner.class)
+@Presubmit
+public class ClearCryptoStateTaskTest {
+    private static final String TEST_PACKAGE_NAME = "com.android.example";
+
+    private ClearCryptoStateTask mClearCryptoStateTask;
+    private CryptoSettings mCryptoSettings;
+    private Context mApplication;
+
+    @Before
+    public void setUp() {
+        mApplication = ApplicationProvider.getApplicationContext();
+        mCryptoSettings = spy(CryptoSettings.getInstanceForTesting(mApplication));
+        mClearCryptoStateTask = new ClearCryptoStateTask(mApplication, mCryptoSettings);
+    }
+
+    @Test
+    public void run_clearsChunkListingProtoState() throws Exception {
+        String packageName = TEST_PACKAGE_NAME;
+        ChunkListing chunkListing = new ChunkListing();
+        ProtoStore.createChunkListingStore(mApplication).saveProto(packageName, chunkListing);
+
+        mClearCryptoStateTask.run();
+
+        assertThat(
+                        ProtoStore.createChunkListingStore(mApplication)
+                                .loadProto(packageName)
+                                .isPresent())
+                .isFalse();
+    }
+
+    @Test
+    public void run_clearsKeyValueProtoState() throws Exception {
+        String packageName = TEST_PACKAGE_NAME;
+        KeyValueListing keyValueListing = new KeyValueListing();
+        ProtoStore.createKeyValueListingStore(mApplication).saveProto(packageName, keyValueListing);
+
+        mClearCryptoStateTask.run();
+
+        assertThat(
+                        ProtoStore.createKeyValueListingStore(mApplication)
+                                .loadProto(packageName)
+                                .isPresent())
+                .isFalse();
+    }
+
+    @Test
+    public void run_clearsTertiaryKeysTable() throws Exception {
+        String secondaryKeyAlias = "bob";
+        TertiaryKeysTable tertiaryKeysTable =
+                BackupEncryptionDb.newInstance(mApplication).getTertiaryKeysTable();
+        tertiaryKeysTable.addKey(
+                new TertiaryKey(
+                        secondaryKeyAlias, "packageName", /*wrappedKeyBytes=*/ new byte[0]));
+
+        mClearCryptoStateTask.run();
+
+        assertThat(tertiaryKeysTable.getAllKeys(secondaryKeyAlias)).isEmpty();
+    }
+
+    @Test
+    public void run_clearsSettings() {
+        mCryptoSettings.setSecondaryLastRotated(100001);
+
+        mClearCryptoStateTask.run();
+
+        assertThat(mCryptoSettings.getSecondaryLastRotated().isPresent()).isFalse();
+    }
+}
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/CarSystemUI/src/com/android/systemui/statusbar/car/UserGridRecyclerView.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/UserGridRecyclerView.java
index 743ab47..886162f 100644
--- a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/UserGridRecyclerView.java
+++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/UserGridRecyclerView.java
@@ -183,11 +183,9 @@
         filter.addAction(Intent.ACTION_USER_ADDED);
         filter.addAction(Intent.ACTION_USER_INFO_CHANGED);
         filter.addAction(Intent.ACTION_USER_SWITCHED);
-        filter.addAction(Intent.ACTION_USER_STOPPED);
-        filter.addAction(Intent.ACTION_USER_UNLOCKED);
         mContext.registerReceiverAsUser(
                 mUserUpdateReceiver,
-                UserHandle.ALL,
+                UserHandle.ALL, // Necessary because CarSystemUi lives in User 0
                 filter,
                 /* broadcastPermission= */ null,
                 /* scheduler= */ null);
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/DeviceConfigService.java b/packages/SettingsProvider/src/com/android/providers/settings/DeviceConfigService.java
index d0ffe7a..8fb879d 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/DeviceConfigService.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/DeviceConfigService.java
@@ -16,7 +16,6 @@
 
 package com.android.providers.settings;
 
-import android.annotation.Nullable;
 import android.annotation.SystemApi;
 import android.app.ActivityManager;
 import android.content.IContentProvider;
@@ -195,8 +194,15 @@
                             : "Failed to delete " + key + " from " + namespace);
                     break;
                 case LIST:
-                    for (String line : list(iprovider, namespace)) {
-                        pout.println(line);
+                    if (namespace != null) {
+                        DeviceConfig.Properties properties = DeviceConfig.getProperties(namespace);
+                        for (String name : properties.getKeyset()) {
+                            pout.println(name + "=" + properties.getString(name, null));
+                        }
+                    } else {
+                        for (String line : listAll(iprovider)) {
+                            pout.println(line);
+                        }
                     }
                     break;
                 case RESET:
@@ -251,16 +257,13 @@
             return success;
         }
 
-        private List<String> list(IContentProvider provider, @Nullable String namespace) {
+        private List<String> listAll(IContentProvider provider) {
             final ArrayList<String> lines = new ArrayList<>();
 
             try {
                 Bundle args = new Bundle();
                 args.putInt(Settings.CALL_METHOD_USER_KEY,
                         ActivityManager.getService().getCurrentUser().id);
-                if (namespace != null) {
-                    args.putString(Settings.CALL_METHOD_PREFIX_KEY, namespace);
-                }
                 Bundle b = provider.call(resolveCallingPackage(), Settings.AUTHORITY,
                         Settings.CALL_METHOD_LIST_CONFIG, null, args);
                 if (b != null) {
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index 16c96e6..a9c466e 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -439,10 +439,8 @@
 
             case Settings.CALL_METHOD_LIST_CONFIG: {
                 String prefix = getSettingPrefix(args);
-                Bundle result = new Bundle();
-                result.putSerializable(
-                        Settings.NameValueTable.VALUE, (HashMap) getAllConfigFlags(prefix));
-                return result;
+                return packageValuesForCallResult(getAllConfigFlags(prefix),
+                        isTrackingGeneration(args));
             }
 
             case Settings.CALL_METHOD_LIST_GLOBAL: {
@@ -1076,7 +1074,7 @@
         return false;
     }
 
-    private Map<String, String> getAllConfigFlags(@Nullable String prefix) {
+    private HashMap<String, String> getAllConfigFlags(@Nullable String prefix) {
         if (DEBUG) {
             Slog.v(LOG_TAG, "getAllConfigFlags() for " + prefix);
         }
@@ -1085,12 +1083,11 @@
             // Get the settings.
             SettingsState settingsState = mSettingsRegistry.getSettingsLocked(
                     SETTINGS_TYPE_CONFIG, UserHandle.USER_SYSTEM);
-
             List<String> names = getSettingsNamesLocked(SETTINGS_TYPE_CONFIG,
                     UserHandle.USER_SYSTEM);
 
             final int nameCount = names.size();
-            Map<String, String> flagsToValues = new HashMap<>(names.size());
+            HashMap<String, String> flagsToValues = new HashMap<>(names.size());
 
             for (int i = 0; i < nameCount; i++) {
                 String name = names.get(i);
@@ -2057,8 +2054,7 @@
                 "get/set setting for user", null);
     }
 
-    private Bundle packageValueForCallResult(Setting setting,
-            boolean trackingGeneration) {
+    private Bundle packageValueForCallResult(Setting setting, boolean trackingGeneration) {
         if (!trackingGeneration) {
             if (setting == null || setting.isNull()) {
                 return NULL_SETTING_BUNDLE;
@@ -2073,6 +2069,21 @@
         return result;
     }
 
+    private Bundle packageValuesForCallResult(HashMap<String, String> keyValues,
+            boolean trackingGeneration) {
+        Bundle result = new Bundle();
+        result.putSerializable(Settings.NameValueTable.VALUE, keyValues);
+        if (trackingGeneration) {
+            synchronized (mLock) {
+                mSettingsRegistry.mGenerationRegistry.addGenerationData(result,
+                        mSettingsRegistry.getSettingsLocked(
+                                SETTINGS_TYPE_CONFIG, UserHandle.USER_SYSTEM).mKey);
+            }
+        }
+
+        return result;
+    }
+
     private static int getRequestingUserId(Bundle args) {
         final int callingUserId = UserHandle.getCallingUserId();
         return (args != null) ? args.getInt(Settings.CALL_METHOD_USER_KEY, callingUserId)
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
index c05c4cd..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..9568a18 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
@@ -18,7 +18,6 @@
 
 import static android.app.Notification.FLAG_AUTOGROUP_SUMMARY;
 import static android.app.Notification.FLAG_BUBBLE;
-import static android.content.pm.ActivityInfo.DOCUMENT_LAUNCH_ALWAYS;
 import static android.service.notification.NotificationListenerService.REASON_APP_CANCEL;
 import static android.service.notification.NotificationListenerService.REASON_APP_CANCEL_ALL;
 import static android.service.notification.NotificationListenerService.REASON_CANCEL;
@@ -569,7 +568,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 +609,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 +761,8 @@
                 mStackView.setExpanded(true);
             }
 
-            mNotificationEntryManager.updateNotifications();
+            mNotificationEntryManager.updateNotifications(
+                    "BubbleData.Listener.applyUpdate");
             updateStack();
 
             if (DEBUG_BUBBLE_CONTROLLER) {
@@ -962,16 +964,6 @@
                     + intent);
             return false;
         }
-        if (info.documentLaunchMode != DOCUMENT_LAUNCH_ALWAYS) {
-            Log.w(TAG, "Unable to send as bubble -- activity is not documentLaunchMode=always "
-                    + "for intent: " + intent);
-            return false;
-        }
-        if ((info.flags & ActivityInfo.FLAG_ALLOW_EMBEDDED) == 0) {
-            Log.w(TAG, "Unable to send as bubble -- activity is not embeddable for intent: "
-                    + intent);
-            return false;
-        }
         return true;
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java
index 521ebde..6f953d5 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java
@@ -16,6 +16,8 @@
 
 package com.android.systemui.bubbles;
 
+import static android.content.Intent.FLAG_ACTIVITY_MULTIPLE_TASK;
+import static android.content.Intent.FLAG_ACTIVITY_NEW_DOCUMENT;
 import static android.view.Display.INVALID_DISPLAY;
 
 import static com.android.systemui.bubbles.BubbleDebugConfig.DEBUG_BUBBLE_EXPANDED_VIEW;
@@ -128,8 +130,12 @@
                             Log.d(TAG, "onActivityViewReady: calling startActivity, "
                                     + "bubble=" + getBubbleKey());
                         }
+                        Intent fillInIntent = new Intent();
+                        // Apply flags to make behaviour match documentLaunchMode=always.
+                        fillInIntent.addFlags(FLAG_ACTIVITY_NEW_DOCUMENT);
+                        fillInIntent.addFlags(FLAG_ACTIVITY_MULTIPLE_TASK);
                         try {
-                            mActivityView.startActivity(mBubbleIntent, options);
+                            mActivityView.startActivity(mBubbleIntent, fillInIntent, options);
                         } catch (RuntimeException e) {
                             // If there's a runtime exception here then there's something
                             // wrong with the intent, we can't really recover / try to populate
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 73093c6..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/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
index 514eb77..950fa8d 100644
--- a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
+++ b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
@@ -91,7 +91,7 @@
     protected final Context mContext;
     protected final SystemSupport mSystemSupport;
     protected final WindowManagerInternal mWindowManagerService;
-    private final GlobalActionPerformer mGlobalActionPerformer;
+    private final SystemActionPerformer mSystemActionPerformer;
     private final AccessibilityWindowManager mA11yWindowManager;
     private final DisplayManager mDisplayManager;
     private final PowerManager mPowerManager;
@@ -213,7 +213,7 @@
             AccessibilityServiceInfo accessibilityServiceInfo, int id, Handler mainHandler,
             Object lock, AccessibilitySecurityPolicy securityPolicy, SystemSupport systemSupport,
             WindowManagerInternal windowManagerInternal,
-            GlobalActionPerformer globalActionPerfomer,
+            SystemActionPerformer systemActionPerfomer,
             AccessibilityWindowManager a11yWindowManager) {
         mContext = context;
         mWindowManagerService = windowManagerInternal;
@@ -222,7 +222,7 @@
         mAccessibilityServiceInfo = accessibilityServiceInfo;
         mLock = lock;
         mSecurityPolicy = securityPolicy;
-        mGlobalActionPerformer = globalActionPerfomer;
+        mSystemActionPerformer = systemActionPerfomer;
         mSystemSupport = systemSupport;
         mInvocationHandler = new InvocationHandler(mainHandler.getLooper());
         mA11yWindowManager = a11yWindowManager;
@@ -760,7 +760,7 @@
                 return false;
             }
         }
-        return mGlobalActionPerformer.performGlobalAction(action);
+        return mSystemActionPerformer.performSystemAction(action);
     }
 
     @Override
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index 3b105ad..91269c7 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -198,7 +198,7 @@
 
     private final MainHandler mMainHandler;
 
-    private final GlobalActionPerformer mGlobalActionPerformer;
+    private final SystemActionPerformer mSystemActionPerformer;
 
     private MagnificationController mMagnificationController;
 
@@ -272,7 +272,7 @@
         mWindowManagerService = LocalServices.getService(WindowManagerInternal.class);
         mSecurityPolicy = new AccessibilitySecurityPolicy(mContext, this);
         mMainHandler = new MainHandler(mContext.getMainLooper());
-        mGlobalActionPerformer = new GlobalActionPerformer(mContext, mWindowManagerService);
+        mSystemActionPerformer = new SystemActionPerformer(mContext, mWindowManagerService);
         mA11yWindowManager = new AccessibilityWindowManager(mLock, mMainHandler,
                 mWindowManagerService, this, mSecurityPolicy, this);
         mA11yDisplayListener = new AccessibilityDisplayListener(mContext, mMainHandler);
@@ -730,7 +730,7 @@
         synchronized (mLock) {
             mUiAutomationManager.registerUiTestAutomationServiceLocked(owner, serviceClient,
                     mContext, accessibilityServiceInfo, sIdCounter++, mMainHandler,
-                    mSecurityPolicy, this, mWindowManagerService, mGlobalActionPerformer,
+                    mSecurityPolicy, this, mWindowManagerService, mSystemActionPerformer,
                     mA11yWindowManager, flags);
             onUserStateChangedLocked(getCurrentUserStateLocked());
         }
@@ -1450,7 +1450,7 @@
                 if (service == null) {
                     service = new AccessibilityServiceConnection(userState, mContext, componentName,
                             installedService, sIdCounter++, mMainHandler, mLock, mSecurityPolicy,
-                            this, mWindowManagerService, mGlobalActionPerformer,
+                            this, mWindowManagerService, mSystemActionPerformer,
                             mA11yWindowManager);
                 } else if (userState.mBoundServices.contains(service)) {
                     continue;
@@ -2482,7 +2482,7 @@
                     userState, mContext,
                     COMPONENT_NAME, info, sIdCounter++, mMainHandler, mLock, mSecurityPolicy,
                     AccessibilityManagerService.this, mWindowManagerService,
-                    mGlobalActionPerformer, mA11yWindowManager) {
+                    mSystemActionPerformer, mA11yWindowManager) {
                 @Override
                 public boolean supportsFlagForNotImportantViews(AccessibilityServiceInfo info) {
                     return true;
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java
index d7f61e5..e7f3ccc 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java
@@ -71,9 +71,9 @@
             AccessibilityServiceInfo accessibilityServiceInfo, int id, Handler mainHandler,
             Object lock, AccessibilitySecurityPolicy securityPolicy, SystemSupport systemSupport,
             WindowManagerInternal windowManagerInternal,
-            GlobalActionPerformer globalActionPerfomer, AccessibilityWindowManager awm) {
+            SystemActionPerformer systemActionPerfomer, AccessibilityWindowManager awm) {
         super(context, componentName, accessibilityServiceInfo, id, mainHandler, lock,
-                securityPolicy, systemSupport, windowManagerInternal, globalActionPerfomer, awm);
+                securityPolicy, systemSupport, windowManagerInternal, systemActionPerfomer, awm);
         mUserStateWeakReference = new WeakReference<UserState>(userState);
         mIntent = new Intent().setComponent(mComponentName);
         mMainHandler = mainHandler;
diff --git a/services/accessibility/java/com/android/server/accessibility/GlobalActionPerformer.java b/services/accessibility/java/com/android/server/accessibility/SystemActionPerformer.java
similarity index 87%
rename from services/accessibility/java/com/android/server/accessibility/GlobalActionPerformer.java
rename to services/accessibility/java/com/android/server/accessibility/SystemActionPerformer.java
index b9b2654..19ac0d3 100644
--- a/services/accessibility/java/com/android/server/accessibility/GlobalActionPerformer.java
+++ b/services/accessibility/java/com/android/server/accessibility/SystemActionPerformer.java
@@ -1,17 +1,17 @@
 /*
- ** Copyright 2017, The Android Open Source Project
- **
- ** Licensed under the Apache License, Version 2.0 (the "License");
- ** you may not use this file except in compliance with the License.
- ** You may obtain a copy of the License at
- **
- **     http://www.apache.org/licenses/LICENSE-2.0
- **
- ** Unless required by applicable law or agreed to in writing, software
- ** distributed under the License is distributed on an "AS IS" BASIS,
- ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- ** See the License for the specific language governing permissions and
- ** limitations under the License.
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
  */
 
 package com.android.server.accessibility;
@@ -40,12 +40,12 @@
 /**
  * Handle the back-end of AccessibilityService#performGlobalAction
  */
-public class GlobalActionPerformer {
+public class SystemActionPerformer {
     private final WindowManagerInternal mWindowManagerService;
     private final Context mContext;
     private Supplier<ScreenshotHelper> mScreenshotHelperSupplier;
 
-    public GlobalActionPerformer(Context context, WindowManagerInternal windowManagerInternal) {
+    public SystemActionPerformer(Context context, WindowManagerInternal windowManagerInternal) {
         mContext = context;
         mWindowManagerService = windowManagerInternal;
         mScreenshotHelperSupplier = null;
@@ -53,13 +53,16 @@
 
     // Used to mock ScreenshotHelper
     @VisibleForTesting
-    public GlobalActionPerformer(Context context, WindowManagerInternal windowManagerInternal,
+    public SystemActionPerformer(Context context, WindowManagerInternal windowManagerInternal,
             Supplier<ScreenshotHelper> screenshotHelperSupplier) {
         this(context, windowManagerInternal);
         mScreenshotHelperSupplier = screenshotHelperSupplier;
     }
 
-    public boolean performGlobalAction(int action) {
+    /**
+     * Performe the system action matching the given action id.
+     */
+    public boolean performSystemAction(int action) {
         final long identity = Binder.clearCallingIdentity();
         try {
             switch (action) {
diff --git a/services/accessibility/java/com/android/server/accessibility/UiAutomationManager.java b/services/accessibility/java/com/android/server/accessibility/UiAutomationManager.java
index 79d975d..7dd4a70 100644
--- a/services/accessibility/java/com/android/server/accessibility/UiAutomationManager.java
+++ b/services/accessibility/java/com/android/server/accessibility/UiAutomationManager.java
@@ -87,7 +87,7 @@
             AccessibilitySecurityPolicy securityPolicy,
             AbstractAccessibilityServiceConnection.SystemSupport systemSupport,
             WindowManagerInternal windowManagerInternal,
-            GlobalActionPerformer globalActionPerfomer,
+            SystemActionPerformer systemActionPerfomer,
             AccessibilityWindowManager awm, int flags) {
         synchronized (mLock) {
             accessibilityServiceInfo.setComponentName(COMPONENT_NAME);
@@ -108,7 +108,7 @@
             mSystemSupport = systemSupport;
             mUiAutomationService = new UiAutomationService(context, accessibilityServiceInfo, id,
                     mainHandler, mLock, securityPolicy, systemSupport, windowManagerInternal,
-                    globalActionPerfomer, awm);
+                    systemActionPerfomer, awm);
             mUiAutomationServiceOwner = owner;
             mUiAutomationFlags = flags;
             mUiAutomationServiceInfo = accessibilityServiceInfo;
@@ -226,9 +226,9 @@
                 int id, Handler mainHandler, Object lock,
                 AccessibilitySecurityPolicy securityPolicy,
                 SystemSupport systemSupport, WindowManagerInternal windowManagerInternal,
-                GlobalActionPerformer globalActionPerfomer, AccessibilityWindowManager awm) {
+                SystemActionPerformer systemActionPerfomer, AccessibilityWindowManager awm) {
             super(context, COMPONENT_NAME, accessibilityServiceInfo, id, mainHandler, lock,
-                    securityPolicy, systemSupport, windowManagerInternal, globalActionPerfomer,
+                    securityPolicy, systemSupport, windowManagerInternal, systemActionPerfomer,
                     awm);
             mMainHandler = mainHandler;
         }
diff --git a/services/core/Android.bp b/services/core/Android.bp
index 4e80977..4f4e47a 100644
--- a/services/core/Android.bp
+++ b/services/core/Android.bp
@@ -146,7 +146,8 @@
     name: "services.core.json.gz",
     srcs: [":checked-protolog.json"],
     out: ["services.core.protolog.json.gz"],
-    cmd: "gzip < $(in) > $(out)",
+    cmd: "$(location minigzip) -c < $(in) > $(out)",
+    tools: ["minigzip"],
 }
 
 prebuilt_etc {
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/ContextHubSystemService.java b/services/core/java/com/android/server/ContextHubSystemService.java
index 110847d..c6853a5 100644
--- a/services/core/java/com/android/server/ContextHubSystemService.java
+++ b/services/core/java/com/android/server/ContextHubSystemService.java
@@ -16,12 +16,12 @@
 
 package com.android.server;
 
-import com.android.internal.util.ConcurrentUtils;
-import com.android.server.location.ContextHubService;
-import com.android.server.SystemServerInitThreadPool;
 import android.content.Context;
 import android.util.Log;
 
+import com.android.internal.util.ConcurrentUtils;
+import com.android.server.location.ContextHubService;
+
 import java.util.concurrent.Future;
 
 class ContextHubSystemService extends SystemService {
@@ -32,7 +32,7 @@
 
     public ContextHubSystemService(Context context) {
         super(context);
-        mInit = SystemServerInitThreadPool.get().submit(() -> {
+        mInit = SystemServerInitThreadPool.submit(() -> {
             mContextHubService = new ContextHubService(context);
         }, "Init ContextHubSystemService");
     }
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/PersistentDataBlockService.java b/services/core/java/com/android/server/PersistentDataBlockService.java
index bd5ad96..73c8520 100644
--- a/services/core/java/com/android/server/PersistentDataBlockService.java
+++ b/services/core/java/com/android/server/PersistentDataBlockService.java
@@ -162,7 +162,7 @@
     @Override
     public void onStart() {
         // Do init on a separate thread, will join in PHASE_ACTIVITY_MANAGER_READY
-        SystemServerInitThreadPool.get().submit(() -> {
+        SystemServerInitThreadPool.submit(() -> {
             mAllowedUid = getAllowedUid(UserHandle.USER_SYSTEM);
             enforceChecksumValidity();
             formatIfOemUnlockEnabled();
diff --git a/services/core/java/com/android/server/SystemServerInitThreadPool.java b/services/core/java/com/android/server/SystemServerInitThreadPool.java
index ff6a537..5ed94e3 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,49 @@
     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) {
+    /**
+     * Submits a task for execution.
+     *
+     * @throws IllegalStateException if it hasn't been started or has been shut down already.
+     */
+    public static @NonNull Future<?> submit(@NonNull Runnable runnable,
+            @NonNull String description) {
+        Preconditions.checkNotNull(description, "description cannot be null");
+
+        SystemServerInitThreadPool instance;
+        synchronized (LOCK) {
+            Preconditions.checkState(sInstance != null, "Cannot get " + TAG
+                    + " - it has been shut down");
+            instance = sInstance;
+        }
+
+        return instance.submitTask(runnable, description);
+    }
+
+    private @NonNull Future<?> submitTask(@NonNull Runnable runnable,
+            @NonNull String description) {
         synchronized (mPendingTasks) {
+            Preconditions.checkState(!mShutDown, TAG + " already shut down");
             mPendingTasks.add(description);
         }
         return mService.submit(() -> {
@@ -83,10 +110,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 +153,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 +162,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/UiModeManagerService.java b/services/core/java/com/android/server/UiModeManagerService.java
index b9d7c68..a517467 100644
--- a/services/core/java/com/android/server/UiModeManagerService.java
+++ b/services/core/java/com/android/server/UiModeManagerService.java
@@ -288,7 +288,7 @@
         updateNightModeFromSettings(context, res, UserHandle.getCallingUserId());
 
         // Update the initial, static configurations.
-        SystemServerInitThreadPool.get().submit(() -> {
+        SystemServerInitThreadPool.submit(() -> {
             synchronized (mLock) {
                 updateConfigurationLocked();
                 sendConfigurationLocked();
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 7cbd1fc..8c672a1 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -350,6 +350,7 @@
 import com.android.server.am.ActivityManagerServiceDumpProcessesProto.UidObserverRegistrationProto;
 import com.android.server.appop.AppOpsService;
 import com.android.server.compat.CompatConfig;
+import com.android.server.compat.PlatformCompat;
 import com.android.server.contentcapture.ContentCaptureManagerInternal;
 import com.android.server.firewall.IntentFirewall;
 import com.android.server.job.JobSchedulerInternal;
@@ -1575,6 +1576,8 @@
     // Encapsulates the global setting "hidden_api_blacklist_exemptions"
     final HiddenApiSettings mHiddenApiBlacklist;
 
+    private final PlatformCompat mPlatformCompat;
+
     PackageManagerInternal mPackageManagerInt;
     PermissionManagerServiceInternal mPermissionManagerInt;
 
@@ -2427,6 +2430,7 @@
         mFactoryTest = FACTORY_TEST_OFF;
         mUgmInternal = LocalServices.getService(UriGrantsManagerInternal.class);
         mInternal = new LocalService();
+        mPlatformCompat = null;
     }
 
     // Note: This method is invoked on the main thread but may need to attach various
@@ -2563,6 +2567,9 @@
 
         mHiddenApiBlacklist = new HiddenApiSettings(mHandler, mContext);
 
+        mPlatformCompat = (PlatformCompat) ServiceManager.getService(
+                Context.PLATFORM_COMPAT_SERVICE);
+
         Watchdog.getInstance().addMonitor(this);
         Watchdog.getInstance().addThread(mHandler);
 
@@ -5015,7 +5022,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;
@@ -5042,6 +5051,9 @@
             mAtmInternal.preBindApplication(app.getWindowProcessController());
             final ActiveInstrumentation instr2 = app.getActiveInstrumentation();
             long[] disabledCompatChanges = CompatConfig.get().getDisabledChanges(app.info);
+            if (mPlatformCompat != null) {
+                mPlatformCompat.resetReporting(app.info);
+            }
             if (app.isolatedEntryPoint != null) {
                 // This is an isolated process which should just call an entry point instead of
                 // being bound to an application.
@@ -18353,7 +18365,7 @@
 
         @Override
         public int getCurrentUserId() {
-            return mUserController.getCurrentUserIdLU();
+            return mUserController.getCurrentUserId();
         }
 
         @Override
diff --git a/services/core/java/com/android/server/am/OomAdjuster.java b/services/core/java/com/android/server/am/OomAdjuster.java
index 3c2aee4..bb214bd 100644
--- a/services/core/java/com/android/server/am/OomAdjuster.java
+++ b/services/core/java/com/android/server/am/OomAdjuster.java
@@ -30,7 +30,7 @@
 import static android.app.ActivityManager.PROCESS_STATE_TOP;
 import static android.app.ActivityManager.PROCESS_STATE_TRANSIENT_BACKGROUND;
 import static android.os.Process.SCHED_OTHER;
-import static android.os.Process.THREAD_GROUP_BG_NONINTERACTIVE;
+import static android.os.Process.THREAD_GROUP_BACKGROUND;
 import static android.os.Process.THREAD_GROUP_DEFAULT;
 import static android.os.Process.THREAD_GROUP_RESTRICTED;
 import static android.os.Process.THREAD_GROUP_TOP_APP;
@@ -1759,7 +1759,7 @@
                 int processGroup;
                 switch (curSchedGroup) {
                     case ProcessList.SCHED_GROUP_BACKGROUND:
-                        processGroup = THREAD_GROUP_BG_NONINTERACTIVE;
+                        processGroup = THREAD_GROUP_BACKGROUND;
                         break;
                     case ProcessList.SCHED_GROUP_TOP_APP:
                     case ProcessList.SCHED_GROUP_TOP_APP_BOUND:
diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java
index c0af814..af126f2 100644
--- a/services/core/java/com/android/server/am/ProcessList.java
+++ b/services/core/java/com/android/server/am/ProcessList.java
@@ -215,7 +215,7 @@
     // Memory pages are 4K.
     static final int PAGE_SIZE = 4 * 1024;
 
-    // Activity manager's version of Process.THREAD_GROUP_BG_NONINTERACTIVE
+    // Activity manager's version of Process.THREAD_GROUP_BACKGROUND
     static final int SCHED_GROUP_BACKGROUND = 0;
       // Activity manager's version of Process.THREAD_GROUP_RESTRICTED
     static final int SCHED_GROUP_RESTRICTED = 1;
diff --git a/services/core/java/com/android/server/am/ProcessStatsService.java b/services/core/java/com/android/server/am/ProcessStatsService.java
index 6ffd8a9..5c840ad 100644
--- a/services/core/java/com/android/server/am/ProcessStatsService.java
+++ b/services/core/java/com/android/server/am/ProcessStatsService.java
@@ -1101,7 +1101,7 @@
         }
 
         boolean sepNeeded = false;
-        if (dumpAll || isCheckin) {
+        if ((dumpAll || isCheckin) && !currentOnly) {
             mWriteLock.lock();
             try {
                 ArrayList<String> files = getCommittedFiles(0, false, !isCheckin);
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/biometrics/face/FaceService.java b/services/core/java/com/android/server/biometrics/face/FaceService.java
index 9eb0d50..1b13212 100644
--- a/services/core/java/com/android/server/biometrics/face/FaceService.java
+++ b/services/core/java/com/android/server/biometrics/face/FaceService.java
@@ -1089,7 +1089,7 @@
         publishBinderService(Context.FACE_SERVICE, new FaceServiceWrapper());
         // Get the face daemon on FaceService's on thread so SystemServerInitThreadPool isn't
         // blocked
-        SystemServerInitThreadPool.get().submit(() -> mHandler.post(this::getFaceDaemon),
+        SystemServerInitThreadPool.submit(() -> mHandler.post(this::getFaceDaemon),
                 TAG + ".onStart");
     }
 
diff --git a/services/core/java/com/android/server/biometrics/fingerprint/FingerprintService.java b/services/core/java/com/android/server/biometrics/fingerprint/FingerprintService.java
index 320e102..d85af2e 100644
--- a/services/core/java/com/android/server/biometrics/fingerprint/FingerprintService.java
+++ b/services/core/java/com/android/server/biometrics/fingerprint/FingerprintService.java
@@ -725,7 +725,7 @@
     public void onStart() {
         super.onStart();
         publishBinderService(Context.FINGERPRINT_SERVICE, new FingerprintServiceWrapper());
-        SystemServerInitThreadPool.get().submit(this::getFingerprintDaemon, TAG + ".onStart");
+        SystemServerInitThreadPool.submit(this::getFingerprintDaemon, TAG + ".onStart");
     }
 
     @Override
diff --git a/services/core/java/com/android/server/compat/OWNERS b/services/core/java/com/android/server/compat/OWNERS
new file mode 100644
index 0000000..2b7cdb0
--- /dev/null
+++ b/services/core/java/com/android/server/compat/OWNERS
@@ -0,0 +1,7 @@
+# Use this reviewer by default.
+platform-compat-eng+reviews@google.com
+
+andreionea@google.com
+atrost@google.com
+mathewi@google.com
+satayev@google.com
diff --git a/services/core/java/com/android/server/compat/PlatformCompat.java b/services/core/java/com/android/server/compat/PlatformCompat.java
index 852b26d..a737888 100644
--- a/services/core/java/com/android/server/compat/PlatformCompat.java
+++ b/services/core/java/com/android/server/compat/PlatformCompat.java
@@ -105,6 +105,15 @@
         CompatConfig.get().dumpConfig(pw);
     }
 
+    /**
+     * Clears information stored about events reported on behalf of an app.
+     * To be called once upon app start or end. A second call would be a no-op.
+     * @param appInfo the app to reset
+     */
+    public void resetReporting(ApplicationInfo appInfo) {
+        mChangeReporter.resetReportedChanges(appInfo.uid);
+    }
+
     private ApplicationInfo getApplicationInfo(String packageName) {
         try {
             return mContext.getPackageManager().getApplicationInfo(packageName, 0);
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/engine/RuleEvaluationEngine.java b/services/core/java/com/android/server/integrity/engine/RuleEvaluationEngine.java
index fc44306..e90612e 100644
--- a/services/core/java/com/android/server/integrity/engine/RuleEvaluationEngine.java
+++ b/services/core/java/com/android/server/integrity/engine/RuleEvaluationEngine.java
@@ -16,6 +16,10 @@
 
 package com.android.server.integrity.engine;
 
+import android.util.Slog;
+
+import com.android.server.integrity.model.AppInstallMetadata;
+import com.android.server.integrity.model.IntegrityCheckResult;
 import com.android.server.integrity.model.Rule;
 
 import java.util.ArrayList;
@@ -24,8 +28,8 @@
 /**
  * The engine used to evaluate rules against app installs.
  *
- * <p>Every app install is evaluated against rules (pushed by the verifier) by the evaluation engine
- * to allow/block that install.
+ * <p>Every app install is evaluated against rules (pushed by the verifier) by the evaluation
+ * engine to allow/block that install.
  */
 public final class RuleEvaluationEngine {
     private static final String TAG = "RuleEvaluation";
@@ -34,15 +38,6 @@
     // installs against rules.
     private static RuleEvaluationEngine sRuleEvaluationEngine;
 
-    // The subset of rules loaded to be used to evaluate an app install request.
-    // TODO: Load rules relevant to app installs.
-    private List<Rule> mRules;
-
-    private RuleEvaluationEngine() {
-        // Initialize rules with the empty rule set.
-        mRules = new ArrayList<>();
-    }
-
     /**
      * Provide a singleton instance of the rule evaluation engine.
      */
@@ -52,4 +47,33 @@
         }
         return sRuleEvaluationEngine;
     }
+
+    /**
+     * Load, and match the list of rules against an app install metadata.
+     *
+     * @param appInstallMetadata Metadata of the app to be installed, and to evaluate the rules
+     *                           against.
+     * @return A rule matching the metadata. If there are multiple matching rules, returns any. If
+     * no rules are matching, returns {@link Rule#EMPTY}.
+     */
+    public IntegrityCheckResult evaluate(AppInstallMetadata appInstallMetadata) {
+        List<Rule> rules = loadRules(appInstallMetadata);
+        Rule matchedRule = RuleEvaluator.evaluateRules(rules, appInstallMetadata);
+        if (matchedRule == Rule.EMPTY) {
+            return IntegrityCheckResult.allow();
+        } else {
+            switch (matchedRule.getEffect()) {
+                case DENY:
+                    return IntegrityCheckResult.deny(matchedRule);
+                default:
+                    Slog.e(TAG, "Matched a non-DENY rule: " + matchedRule);
+                    return IntegrityCheckResult.allow();
+            }
+        }
+    }
+
+    private List<Rule> loadRules(AppInstallMetadata appInstallMetadata) {
+        // TODO: Load rules
+        return new ArrayList<>();
+    }
 }
diff --git a/services/core/java/com/android/server/integrity/engine/RuleEvaluator.java b/services/core/java/com/android/server/integrity/engine/RuleEvaluator.java
new file mode 100644
index 0000000..4ed581d
--- /dev/null
+++ b/services/core/java/com/android/server/integrity/engine/RuleEvaluator.java
@@ -0,0 +1,105 @@
+/*
+ * 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.engine;
+
+import android.util.Slog;
+
+import com.android.server.integrity.model.AppInstallMetadata;
+import com.android.server.integrity.model.AtomicFormula;
+import com.android.server.integrity.model.Formula;
+import com.android.server.integrity.model.OpenFormula;
+import com.android.server.integrity.model.Rule;
+
+import java.util.List;
+
+/**
+ * A helper class for evaluating rules against app install metadata to find if there are matching
+ * rules.
+ */
+final class RuleEvaluator {
+
+    private static final String TAG = "RuleEvaluator";
+
+    /**
+     * Match the list of rules against an app install metadata.
+     *
+     * @param rules              The list of rules to evaluate.
+     * @param appInstallMetadata Metadata of the app to be installed, and to evaluate the rules
+     *                           against.
+     * @return A rule matching the metadata. If there are multiple matching rules, returns any. If
+     * no rules are matching, returns {@link Rule#EMPTY}.
+     */
+    static Rule evaluateRules(List<Rule> rules, AppInstallMetadata appInstallMetadata) {
+        for (Rule rule : rules) {
+            if (isMatch(rule, appInstallMetadata)) {
+                return rule;
+            }
+        }
+        return Rule.EMPTY;
+    }
+
+    /**
+     * Match a rule against app install metadata.
+     */
+    private static boolean isMatch(Rule rule, AppInstallMetadata appInstallMetadata) {
+        return isMatch(rule.getFormula(), appInstallMetadata);
+    }
+
+    private static boolean isMatch(Formula formula, AppInstallMetadata appInstallMetadata) {
+        if (formula instanceof AtomicFormula) {
+            AtomicFormula atomicFormula = (AtomicFormula) formula;
+            switch (atomicFormula.getKey()) {
+                case PACKAGE_NAME:
+                    return atomicFormula.isMatch(appInstallMetadata.getPackageName());
+                case APP_CERTIFICATE:
+                    return atomicFormula.isMatch(appInstallMetadata.getAppCertificate());
+                case INSTALLER_NAME:
+                    return atomicFormula.isMatch(appInstallMetadata.getInstallerName());
+                case INSTALLER_CERTIFICATE:
+                    return atomicFormula.isMatch(appInstallMetadata.getInstallerCertificate());
+                case VERSION_CODE:
+                    return atomicFormula.isMatch(appInstallMetadata.getVersionCode());
+                case PRE_INSTALLED:
+                    return atomicFormula.isMatch(appInstallMetadata.isPreInstalled());
+                default:
+                    Slog.i(TAG, String.format("Returned no match for unknown key %s",
+                            atomicFormula.getKey()));
+                    return false;
+            }
+        } else if (formula instanceof OpenFormula) {
+            OpenFormula openFormula = (OpenFormula) formula;
+            // A rule is in disjunctive normal form, so there are no OR connectors.
+            switch (openFormula.getConnector()) {
+                case NOT:
+                    // NOT connector has only 1 formula attached.
+                    return !isMatch(openFormula.getFormulas().get(0), appInstallMetadata);
+                case AND:
+                    boolean result = true;
+                    for (Formula subFormula : openFormula.getFormulas()) {
+                        result &= isMatch(subFormula, appInstallMetadata);
+                    }
+                    return result;
+                default:
+                    Slog.i(TAG, String.format("Returned no match for unknown connector %s",
+                            openFormula.getConnector()));
+                    return false;
+            }
+        }
+
+        return false;
+    }
+}
diff --git a/services/core/java/com/android/server/integrity/model/AtomicFormula.java b/services/core/java/com/android/server/integrity/model/AtomicFormula.java
index a9cc62a4da..dde8c2a 100644
--- a/services/core/java/com/android/server/integrity/model/AtomicFormula.java
+++ b/services/core/java/com/android/server/integrity/model/AtomicFormula.java
@@ -20,6 +20,7 @@
 import static com.android.internal.util.Preconditions.checkNotNull;
 
 import android.annotation.Nullable;
+import android.util.Slog;
 
 /**
  * Represents a simple formula consisting of an app install metadata field and a value.
@@ -28,7 +29,9 @@
  */
 public final class AtomicFormula extends Formula {
 
-    enum Key {
+    private static final String TAG = "AtomicFormula";
+
+    public enum Key {
         PACKAGE_NAME,
         APP_CERTIFICATE,
         INSTALLER_NAME,
@@ -37,7 +40,7 @@
         PRE_INSTALLED
     }
 
-    enum Operator {
+    public enum Operator {
         EQ,
         LT,
         LE,
@@ -112,6 +115,79 @@
         return mBoolValue;
     }
 
+    /**
+     * Get string representation of the value of the key in the formula.
+     *
+     * @return string representation of the value of the key.
+     */
+    public String getValue() {
+        if (mStringValue != null) {
+            return mStringValue;
+        }
+        if (mIntValue != null) {
+            return mIntValue.toString();
+        }
+        return mBoolValue.toString();
+    }
+
+    @Override
+    public String toString() {
+        return String.format("%s %s %s", mKey, mOperator, getValue());
+    }
+
+    /**
+     * Check if the formula is true when substituting its {@link Key} with the string value.
+     *
+     * @param value String value to substitute the key with.
+     * @return {@code true} if the formula is true, and {@code false} otherwise.
+     */
+    public boolean isMatch(String value) {
+        switch (mOperator) {
+            case EQ:
+                return mStringValue.equals(value);
+        }
+        Slog.i(TAG, String.format("Found operator %s for value %s", mOperator, mStringValue));
+        return false;
+    }
+
+    /**
+     * Check if the formula is true when substituting its {@link Key} with the integer value.
+     *
+     * @param value Integer value to substitute the key with.
+     * @return {@code true} if the formula is true, and {@code false} otherwise.
+     */
+    public boolean isMatch(int value) {
+        switch (mOperator) {
+            case EQ:
+                return mIntValue == value;
+            case LE:
+                return mIntValue <= value;
+            case LT:
+                return mIntValue < value;
+            case GE:
+                return mIntValue >= value;
+            case GT:
+                return mIntValue > value;
+        }
+        Slog.i(TAG, String.format("Found operator %s for value %s", mOperator, mIntValue));
+        return false;
+    }
+
+    /**
+     * Check if the formula is true when substituting its {@link Key} with the boolean value.
+     *
+     * @param value Boolean value to substitute the key with.
+     * @return {@code true} if the formula is true, and {@code false} otherwise.
+     */
+    public boolean isMatch(boolean value) {
+        switch (mOperator) {
+            case EQ:
+                return mBoolValue == value;
+        }
+        Slog.i(TAG, String.format("Found operator %s for value %s", mOperator, mBoolValue));
+        return false;
+    }
+
     private void validateOperator(Key key, Operator operator) {
         boolean validOperator;
         switch (key) {
@@ -126,6 +202,7 @@
                 validOperator = true;
                 break;
             default:
+                Slog.i(TAG, String.format("Found operator %s for key %s", operator, key));
                 validOperator = false;
         }
         if (!validOperator) {
diff --git a/services/core/java/com/android/server/integrity/model/Formula.java b/services/core/java/com/android/server/integrity/model/Formula.java
index 4cfa2c7..9db4453 100644
--- a/services/core/java/com/android/server/integrity/model/Formula.java
+++ b/services/core/java/com/android/server/integrity/model/Formula.java
@@ -19,6 +19,6 @@
 /**
  * Represents a rule logic/content.
  */
-abstract class Formula {
+public abstract class Formula {
 
 }
diff --git a/services/core/java/com/android/server/integrity/model/IntegrityCheckResult.java b/services/core/java/com/android/server/integrity/model/IntegrityCheckResult.java
new file mode 100644
index 0000000..7aeb0c1
--- /dev/null
+++ b/services/core/java/com/android/server/integrity/model/IntegrityCheckResult.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 IntegrityCheckResult {
+
+    public enum Effect {
+        ALLOW,
+        DENY
+    }
+
+    private final Effect mEffect;
+    private final Rule mRule;
+
+    private IntegrityCheckResult(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 IntegrityCheckResult allow() {
+        return new IntegrityCheckResult(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 IntegrityCheckResult deny(Rule rule) {
+        return new IntegrityCheckResult(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..5ba9f46 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.
@@ -27,53 +29,52 @@
  */
 public final class OpenFormula extends Formula {
 
-    enum Connector {
+    public enum Connector {
         AND,
         OR,
         NOT
     }
 
     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;
+    @Override
+    public String toString() {
+        StringBuilder sb = new StringBuilder();
+        for (int i = 0; i < mFormulas.size(); i++) {
+            if (i > 0) {
+                sb.append(String.format(" %s ", mConnector));
+            }
+            sb.append(mFormulas.get(i).toString());
+        }
+        return sb.toString();
     }
 
-    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..41611d0 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
     }
 
@@ -61,4 +61,9 @@
     public Effect getEffect() {
         return mEffect;
     }
+
+    @Override
+    public String toString() {
+        return String.format("Rule: %s, %s", mFormula, mEffect);
+    }
 }
diff --git a/services/core/java/com/android/server/location/GnssLocationProvider.java b/services/core/java/com/android/server/location/GnssLocationProvider.java
index c6226fa..8bf01a3 100644
--- a/services/core/java/com/android/server/location/GnssLocationProvider.java
+++ b/services/core/java/com/android/server/location/GnssLocationProvider.java
@@ -74,6 +74,8 @@
 import com.android.internal.location.ProviderRequest;
 import com.android.internal.location.gnssmetrics.GnssMetrics;
 import com.android.internal.telephony.TelephonyIntents;
+import com.android.server.DeviceIdleInternal;
+import com.android.server.LocalServices;
 import com.android.server.location.GnssSatelliteBlacklistHelper.GnssSatelliteBlacklistCallback;
 import com.android.server.location.NtpTimeHelper.InjectNtpTimeCallback;
 
@@ -177,6 +179,7 @@
     private static final int AGPS_SUPL_MODE_MSA = 0x02;
     private static final int AGPS_SUPL_MODE_MSB = 0x01;
 
+    private static final int UPDATE_LOW_POWER_MODE = 1;
     private static final int SET_REQUEST = 3;
     private static final int INJECT_NTP_TIME = 5;
     // PSDS stands for Predicted Satellite Data Service
@@ -360,6 +363,12 @@
     private boolean mDisableGpsForPowerManager = false;
 
     /**
+     * True if the device idle controller has determined that the device is stationary. This is only
+     * updated when the device enters idle mode.
+     */
+    private volatile boolean mIsDeviceStationary = false;
+
+    /**
      * Properties loaded from PROPERTIES_FILE.
      * It must be accessed only inside {@link #mHandler}.
      */
@@ -451,6 +460,15 @@
     public GnssNavigationMessageProvider getGnssNavigationMessageProvider() {
         return mGnssNavigationMessageProvider;
     }
+
+    private final DeviceIdleInternal.StationaryListener mDeviceIdleStationaryListener =
+            isStationary -> {
+                mIsDeviceStationary = isStationary;
+                // Call updateLowPowerMode on handler thread so it's always called from the same
+                // thread.
+                mHandler.sendEmptyMessage(UPDATE_LOW_POWER_MODE);
+            };
+
     private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
         @Override
         public void onReceive(Context context, Intent intent) {
@@ -467,11 +485,22 @@
                 case ALARM_TIMEOUT:
                     hibernate();
                     break;
-                case PowerManager.ACTION_POWER_SAVE_MODE_CHANGED:
                 case PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED:
+                    DeviceIdleInternal deviceIdleService = LocalServices.getService(
+                            DeviceIdleInternal.class);
+                    if (mPowerManager.isDeviceIdleMode()) {
+                        deviceIdleService.registerStationaryListener(mDeviceIdleStationaryListener);
+                    } else {
+                        deviceIdleService.unregisterStationaryListener(
+                                mDeviceIdleStationaryListener);
+                    }
+                    // Intentional fall-through.
+                case PowerManager.ACTION_POWER_SAVE_MODE_CHANGED:
                 case Intent.ACTION_SCREEN_OFF:
                 case Intent.ACTION_SCREEN_ON:
-                    updateLowPowerMode();
+                    // Call updateLowPowerMode on handler thread so it's always called from the
+                    // same thread.
+                    mHandler.sendEmptyMessage(UPDATE_LOW_POWER_MODE);
                     break;
                 case CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED:
                 case TelephonyIntents.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED:
@@ -529,10 +558,9 @@
     }
 
     private void updateLowPowerMode() {
-        // Disable GPS if we are in device idle mode.
-        boolean disableGpsForPowerManager = mPowerManager.isDeviceIdleMode();
-        final PowerSaveState result =
-                mPowerManager.getPowerSaveState(ServiceType.LOCATION);
+        // Disable GPS if we are in device idle mode and the device is stationary.
+        boolean disableGpsForPowerManager = mPowerManager.isDeviceIdleMode() && mIsDeviceStationary;
+        final PowerSaveState result = mPowerManager.getPowerSaveState(ServiceType.LOCATION);
         switch (result.locationMode) {
             case PowerManager.LOCATION_MODE_GPS_DISABLED_WHEN_SCREEN_OFF:
             case PowerManager.LOCATION_MODE_ALL_DISABLED_WHEN_SCREEN_OFF:
@@ -2005,6 +2033,9 @@
                 case REPORT_SV_STATUS:
                     handleReportSvStatus((SvStatusInfo) msg.obj);
                     break;
+                case UPDATE_LOW_POWER_MODE:
+                    updateLowPowerMode();
+                    break;
             }
             if (msg.arg2 == 1) {
                 // wakelock was taken for this message, release it
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index d7efa1b..4457e9c 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -49,7 +49,6 @@
 import static android.content.Context.BIND_AUTO_CREATE;
 import static android.content.Context.BIND_FOREGROUND_SERVICE;
 import static android.content.Context.BIND_NOT_PERCEPTIBLE;
-import static android.content.pm.ActivityInfo.DOCUMENT_LAUNCH_ALWAYS;
 import static android.content.pm.PackageManager.FEATURE_LEANBACK;
 import static android.content.pm.PackageManager.FEATURE_TELEVISION;
 import static android.content.pm.PackageManager.MATCH_ALL;
@@ -88,7 +87,6 @@
 import static android.service.notification.NotificationListenerService.TRIM_LIGHT;
 import static android.util.StatsLogInternal.BUBBLE_DEVELOPER_ERROR_REPORTED__ERROR__ACTIVITY_INFO_MISSING;
 import static android.util.StatsLogInternal.BUBBLE_DEVELOPER_ERROR_REPORTED__ERROR__ACTIVITY_INFO_NOT_RESIZABLE;
-import static android.util.StatsLogInternal.BUBBLE_DEVELOPER_ERROR_REPORTED__ERROR__DOCUMENT_LAUNCH_NOT_ALWAYS;
 import static android.view.WindowManager.LayoutParams.TYPE_TOAST;
 
 import static com.android.server.am.PendingIntentRecord.FLAG_ACTIVITY_SENDER;
@@ -1985,6 +1983,7 @@
             public void updateAutogroupSummary(String key, boolean needsOngoingFlag) {
                 synchronized (mNotificationLock) {
                     NotificationRecord r = mNotificationsByKey.get(key);
+                    if (r == null) return;
                     updateAutobundledSummaryFlags(r.getUser().getIdentifier(),
                             r.sbn.getPackageName(), needsOngoingFlag);
                 }
@@ -5250,18 +5249,6 @@
                     + intent);
             return false;
         }
-        if (info.documentLaunchMode != DOCUMENT_LAUNCH_ALWAYS) {
-            StatsLog.write(StatsLog.BUBBLE_DEVELOPER_ERROR_REPORTED, packageName,
-                    BUBBLE_DEVELOPER_ERROR_REPORTED__ERROR__DOCUMENT_LAUNCH_NOT_ALWAYS);
-            Log.w(TAG, "Unable to send as bubble -- activity is not documentLaunchMode=always "
-                    + "for intent: " + intent);
-            return false;
-        }
-        if ((info.flags & ActivityInfo.FLAG_ALLOW_EMBEDDED) == 0) {
-            Log.w(TAG, "Unable to send as bubble -- activity is not embeddable for intent: "
-                    + intent);
-            return false;
-        }
         return true;
     }
 
diff --git a/services/core/java/com/android/server/os/SchedulingPolicyService.java b/services/core/java/com/android/server/os/SchedulingPolicyService.java
index 2371b04..c3ed7d7 100644
--- a/services/core/java/com/android/server/os/SchedulingPolicyService.java
+++ b/services/core/java/com/android/server/os/SchedulingPolicyService.java
@@ -21,7 +21,6 @@
 import android.os.IBinder;
 import android.os.ISchedulingPolicyService;
 import android.os.Process;
-import android.os.RemoteException;
 import android.util.Log;
 
 import com.android.server.SystemServerInitThreadPool;
@@ -64,7 +63,7 @@
         // (Note that if mediaserver thinks we're in boosted state before the crash,
         // the state could go out of sync temporarily until mediaserver enables/disable
         // boost next time, but this won't be a big issue.)
-        SystemServerInitThreadPool.get().submit(() -> {
+        SystemServerInitThreadPool.submit(() -> {
             synchronized (mDeathRecipient) {
                 // only do this if we haven't already got a request to boost.
                 if (mBoostedPid == -1) {
diff --git a/services/core/java/com/android/server/pm/AppsFilter.java b/services/core/java/com/android/server/pm/AppsFilter.java
index 61ea84f..05b6168 100644
--- a/services/core/java/com/android/server/pm/AppsFilter.java
+++ b/services/core/java/com/android/server/pm/AppsFilter.java
@@ -41,7 +41,7 @@
 import com.android.internal.R;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.server.FgThread;
-import com.android.server.compat.PlatformCompat;
+import com.android.server.compat.CompatConfig;
 
 import java.io.PrintWriter;
 import java.util.ArrayList;
@@ -131,24 +131,22 @@
 
     private static class FeatureConfigImpl implements FeatureConfig {
         private static final String FILTERING_ENABLED_NAME = "package_query_filtering_enabled";
-        private final PackageManagerService.Injector mInjector;
         private volatile boolean mFeatureEnabled = true;
+        private CompatConfig mCompatibility;
 
         private FeatureConfigImpl(PackageManagerService.Injector injector) {
-            mInjector = injector;
+            mCompatibility = injector.getCompatibility();
         }
 
         @Override
         public void onSystemReady() {
             mFeatureEnabled = DeviceConfig.getBoolean(
-                    NAMESPACE_PACKAGE_MANAGER_SERVICE, FILTERING_ENABLED_NAME,
-                    true);
+                    NAMESPACE_PACKAGE_MANAGER_SERVICE, FILTERING_ENABLED_NAME, true);
             DeviceConfig.addOnPropertiesChangedListener(
                     NAMESPACE_PACKAGE_MANAGER_SERVICE, FgThread.getExecutor(),
                     properties -> {
                         synchronized (FeatureConfigImpl.this) {
-                            mFeatureEnabled = properties.getBoolean(
-                                    FILTERING_ENABLED_NAME, true);
+                            mFeatureEnabled = properties.getBoolean(FILTERING_ENABLED_NAME, true);
                         }
                     });
         }
@@ -160,12 +158,7 @@
 
         @Override
         public boolean packageIsEnabled(PackageParser.Package pkg) {
-            final PlatformCompat compatibility = mInjector.getCompatibility();
-            if (compatibility == null) {
-                Slog.wtf(TAG, "PlatformCompat is null");
-                return mFeatureEnabled;
-            }
-            return compatibility.isChangeEnabled(
+            return mCompatibility.isChangeEnabled(
                     PackageManager.FILTER_APPLICATION_QUERY, pkg.applicationInfo);
         }
     }
@@ -202,19 +195,19 @@
             return false;
         }
         for (Intent intent : querying.mQueriesIntents) {
-            if (matches(intent, potentialTarget.providers, potentialTarget.activities,
-                    potentialTarget.services, potentialTarget.receivers)) {
+            if (matches(intent, potentialTarget)) {
                 return true;
             }
         }
         return false;
     }
 
-    private static boolean matches(Intent intent,
-            ArrayList<PackageParser.Provider> providerList,
-            ArrayList<? extends Component<? extends IntentInfo>>... componentLists) {
-        for (int p = providerList.size() - 1; p >= 0; p--) {
-            PackageParser.Provider provider = providerList.get(p);
+    private static boolean matches(Intent intent, PackageParser.Package potentialTarget) {
+        for (int p = potentialTarget.providers.size() - 1; p >= 0; p--) {
+            PackageParser.Provider provider = potentialTarget.providers.get(p);
+            if (!provider.info.exported) {
+                continue;
+            }
             final ProviderInfo providerInfo = provider.info;
             final Uri data = intent.getData();
             if ("content".equalsIgnoreCase(intent.getScheme())
@@ -223,19 +216,44 @@
                 return true;
             }
         }
+        for (int s = potentialTarget.services.size() - 1; s >= 0; s--) {
+            PackageParser.Service service = potentialTarget.services.get(s);
+            if (!service.info.exported) {
+                continue;
+            }
+            if (matchesAnyFilter(intent, service)) {
+                return true;
+            }
+        }
+        for (int a = potentialTarget.activities.size() - 1; a >= 0; a--) {
+            PackageParser.Activity activity = potentialTarget.activities.get(a);
+            if (!activity.info.exported) {
+                continue;
+            }
+            if (matchesAnyFilter(intent, activity)) {
+                return true;
+            }
+        }
+        for (int r = potentialTarget.receivers.size() - 1; r >= 0; r--) {
+            PackageParser.Activity receiver = potentialTarget.receivers.get(r);
+            if (!receiver.info.exported) {
+                continue;
+            }
+            if (matchesAnyFilter(intent, receiver)) {
+                return true;
+            }
+        }
+        return false;
+    }
 
-        for (int l = componentLists.length - 1; l >= 0; l--) {
-            ArrayList<? extends Component<? extends IntentInfo>> components = componentLists[l];
-            for (int c = components.size() - 1; c >= 0; c--) {
-                Component<? extends IntentInfo> component = components.get(c);
-                ArrayList<? extends IntentInfo> intents = component.intents;
-                for (int i = intents.size() - 1; i >= 0; i--) {
-                    IntentFilter intentFilter = intents.get(i);
-                    if (intentFilter.match(intent.getAction(), intent.getType(), intent.getScheme(),
-                            intent.getData(), intent.getCategories(), "AppsFilter") > 0) {
-                        return true;
-                    }
-                }
+    private static boolean matchesAnyFilter(
+            Intent intent, Component<? extends IntentInfo> component) {
+        ArrayList<? extends IntentInfo> intents = component.intents;
+        for (int i = intents.size() - 1; i >= 0; i--) {
+            IntentFilter intentFilter = intents.get(i);
+            if (intentFilter.match(intent.getAction(), intent.getType(), intent.getScheme(),
+                    intent.getData(), intent.getCategories(), "AppsFilter") > 0) {
+                return true;
             }
         }
         return false;
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..d675e36 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;
@@ -300,7 +299,7 @@
 import com.android.server.SystemConfig;
 import com.android.server.SystemServerInitThreadPool;
 import com.android.server.Watchdog;
-import com.android.server.compat.PlatformCompat;
+import com.android.server.compat.CompatConfig;
 import com.android.server.net.NetworkPolicyManagerInternal;
 import com.android.server.pm.Installer.InstallerException;
 import com.android.server.pm.Settings.DatabaseVersion;
@@ -835,7 +834,7 @@
         private final Singleton<StorageManager> mStorageManagerProducer;
         private final Singleton<AppOpsManager> mAppOpsManagerProducer;
         private final Singleton<AppsFilter> mAppsFilterProducer;
-        private final Singleton<PlatformCompat> mPlatformCompatProducer;
+        private final Singleton<CompatConfig> mPlatformCompatProducer;
 
         Injector(Context context, Object lock, Installer installer,
                 Object installLock, PackageAbiHelper abiHelper,
@@ -853,7 +852,7 @@
                 Producer<StorageManager> storageManagerProducer,
                 Producer<AppOpsManager> appOpsManagerProducer,
                 Producer<AppsFilter> appsFilterProducer,
-                Producer<PlatformCompat> platformCompatProducer) {
+                Producer<CompatConfig> platformCompatProducer) {
             mContext = context;
             mLock = lock;
             mInstaller = installer;
@@ -964,7 +963,7 @@
             return mAppsFilterProducer.get(this, mPackageManager);
         }
 
-        public PlatformCompat getCompatibility() {
+        public CompatConfig getCompatibility() {
             return mPlatformCompatProducer.get(this, mPackageManager);
         }
     }
@@ -2466,7 +2465,7 @@
                 new Injector.SystemServiceProducer<>(StorageManager.class),
                 new Injector.SystemServiceProducer<>(AppOpsManager.class),
                 (i, pm) -> AppsFilter.create(i),
-                (i, pm) -> (PlatformCompat) ServiceManager.getService("platform_compat"));
+                (i, pm) -> CompatConfig.get());
 
         PackageManagerService m = new PackageManagerService(injector, factoryTest, onlyCore);
         t.traceEnd(); // "create package manager"
@@ -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();
@@ -3135,7 +3134,7 @@
             List<String> deferPackages = reconcileAppsDataLI(StorageManager.UUID_PRIVATE_INTERNAL,
                     UserHandle.USER_SYSTEM, storageFlags, true /* migrateAppData */,
                     true /* onlyCoreApps */);
-            mPrepareAppDataFuture = SystemServerInitThreadPool.get().submit(() -> {
+            mPrepareAppDataFuture = SystemServerInitThreadPool.submit(() -> {
                 TimingsTraceLog traceLog = new TimingsTraceLog("SystemServerTimingAsync",
                         Trace.TRACE_TAG_PACKAGE_MANAGER);
                 traceLog.traceBegin("AppDataFixup");
@@ -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/PermissionPolicyService.java b/services/core/java/com/android/server/policy/PermissionPolicyService.java
index 77c16e3..df2b3ca 100644
--- a/services/core/java/com/android/server/policy/PermissionPolicyService.java
+++ b/services/core/java/com/android/server/policy/PermissionPolicyService.java
@@ -152,10 +152,10 @@
             for (int i = 0; i < numDangerousPerms; i++) {
                 PermissionInfo perm = dangerousPerms.get(i);
 
-                if (perm.isHardRestricted() || perm.backgroundPermission != null) {
+                if (perm.isRuntime()) {
                     appOpsService.startWatchingMode(getSwitchOp(perm.name), null, appOpsListener);
-                } else if (perm.isSoftRestricted()) {
-                    appOpsService.startWatchingMode(getSwitchOp(perm.name), null, appOpsListener);
+                }
+                if (perm.isSoftRestricted()) {
                     SoftRestrictedPermissionPolicy policy =
                             SoftRestrictedPermissionPolicy.forPermission(null, null, null,
                                     perm.name);
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..0b4ea99 100644
--- a/services/core/java/com/android/server/wm/WindowSurfaceController.java
+++ b/services/core/java/com/android/server/wm/WindowSurfaceController.java
@@ -37,8 +37,8 @@
 import android.util.Slog;
 import android.util.proto.ProtoOutputStream;
 import android.view.SurfaceControl;
-import android.view.SurfaceSession;
 import android.view.WindowContentFrameStats;
+import android.view.WindowManager;
 
 import com.android.server.protolog.common.ProtoLog;
 
@@ -85,7 +85,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;
 
@@ -109,6 +109,13 @@
                 .setFlags(flags)
                 .setMetadata(METADATA_WINDOW_TYPE, windowType)
                 .setMetadata(METADATA_OWNER_UID, ownerUid);
+
+        if ((win.getAttrs().privateFlags &
+                WindowManager.LayoutParams.PRIVATE_FLAG_USE_BLAST) != 0) {
+            b.setContainerLayer();
+        }
+
+
         mSurfaceControl = b.build();
         Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
     }
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index b118cdf..9569ac8 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -2186,7 +2186,7 @@
         }
 
         void postOnSystemServerInitThreadPool(Runnable runnable) {
-            SystemServerInitThreadPool.get().submit(runnable, LOG_TAG);
+            SystemServerInitThreadPool.submit(runnable, LOG_TAG);
         }
 
         public TransferOwnershipMetadataManager newTransferOwnershipMetadataManager() {
@@ -11320,6 +11320,28 @@
             }
         }
 
+        @Override
+        public boolean isActiveSupervisionApp(int uid) {
+            synchronized (getLockObject()) {
+                final ActiveAdmin admin = getActiveAdminWithPolicyForUidLocked(
+                        null, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER, uid);
+                if (admin == null) {
+                    return false;
+                }
+
+                final String supervisionString = mContext.getResources().getString(
+                        com.android.internal.R.string
+                                .config_defaultSupervisionProfileOwnerComponent);
+                if (supervisionString == null) {
+                    return false;
+                }
+
+                final ComponentName supervisorComponent = ComponentName.unflattenFromString(
+                        supervisionString);
+                return admin.info.getComponent().equals(supervisorComponent);
+            }
+        }
+
         private void notifyCrossProfileProvidersChanged(int userId, List<String> packages) {
             final List<OnCrossProfileWidgetProvidersChangeListener> listeners;
             synchronized (getLockObject()) {
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index de65002..4cf98d3 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
         }
@@ -635,7 +635,7 @@
         Slog.i(TAG, "Reading configuration...");
         final String TAG_SYSTEM_CONFIG = "ReadingSystemConfig";
         t.traceBegin(TAG_SYSTEM_CONFIG);
-        SystemServerInitThreadPool.get().submit(SystemConfig::getInstance, TAG_SYSTEM_CONFIG);
+        SystemServerInitThreadPool.submit(SystemConfig::getInstance, TAG_SYSTEM_CONFIG);
         t.traceEnd();
 
         // Platform compat service is used by ActivityManagerService, PackageManagerService, and
@@ -821,7 +821,7 @@
         // service, and permissions service, therefore we start it after them.
         // Start sensor service in a separate thread. Completion should be checked
         // before using it.
-        mSensorServiceStart = SystemServerInitThreadPool.get().submit(() -> {
+        mSensorServiceStart = SystemServerInitThreadPool.submit(() -> {
             TimingsTraceAndSlog traceLog = TimingsTraceAndSlog.newAsyncLog();
             traceLog.traceBegin(START_SENSOR_SERVICE);
             startSensorService();
@@ -946,7 +946,7 @@
             // ensure that it completes before the 32 bit relro process is forked
             // from the zygote. In the event that it takes too long, the webview
             // RELRO process will block, but it will do so without holding any locks.
-            mZygotePreload = SystemServerInitThreadPool.get().submit(() -> {
+            mZygotePreload = SystemServerInitThreadPool.submit(() -> {
                 try {
                     Slog.i(TAG, SECONDARY_ZYGOTE_PRELOAD);
                     TimingsTraceAndSlog traceLog = TimingsTraceAndSlog.newAsyncLog();
@@ -1058,7 +1058,7 @@
             // Start receiving calls from HIDL services. Start in in a separate thread
             // because it need to connect to SensorManager. This have to start
             // after START_SENSOR_SERVICE is done.
-            SystemServerInitThreadPool.get().submit(() -> {
+            SystemServerInitThreadPool.submit(() -> {
                 TimingsTraceAndSlog traceLog = TimingsTraceAndSlog.newAsyncLog();
                 traceLog.traceBegin(START_HIDL_SERVICES);
                 startHidlServices();
@@ -2050,7 +2050,7 @@
             final String WEBVIEW_PREPARATION = "WebViewFactoryPreparation";
             Future<?> webviewPrep = null;
             if (!mOnlyCore && mWebViewUpdateService != null) {
-                webviewPrep = SystemServerInitThreadPool.get().submit(() -> {
+                webviewPrep = SystemServerInitThreadPool.submit(() -> {
                     Slog.i(TAG, WEBVIEW_PREPARATION);
                     TimingsTraceAndSlog traceLog = TimingsTraceAndSlog.newAsyncLog();
                     traceLog.traceBegin(WEBVIEW_PREPARATION);
diff --git a/services/tests/mockingservicestests/src/com/android/server/DeviceIdleControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/DeviceIdleControllerTest.java
index 2aa625a..9c97305 100644
--- a/services/tests/mockingservicestests/src/com/android/server/DeviceIdleControllerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/DeviceIdleControllerTest.java
@@ -17,6 +17,7 @@
 
 import static androidx.test.InstrumentationRegistry.getContext;
 
+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.doReturn;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.inOrder;
@@ -31,6 +32,7 @@
 import static com.android.server.DeviceIdleController.LIGHT_STATE_OVERRIDE;
 import static com.android.server.DeviceIdleController.LIGHT_STATE_PRE_IDLE;
 import static com.android.server.DeviceIdleController.LIGHT_STATE_WAITING_FOR_NETWORK;
+import static com.android.server.DeviceIdleController.MSG_REPORT_STATIONARY_STATUS;
 import static com.android.server.DeviceIdleController.STATE_ACTIVE;
 import static com.android.server.DeviceIdleController.STATE_IDLE;
 import static com.android.server.DeviceIdleController.STATE_IDLE_MAINTENANCE;
@@ -51,6 +53,7 @@
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.anyLong;
 import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.argThat;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.ArgumentMatchers.longThat;
 import static org.mockito.Mockito.atLeastOnce;
@@ -72,6 +75,7 @@
 import android.net.NetworkInfo;
 import android.os.Handler;
 import android.os.Looper;
+import android.os.Message;
 import android.os.PowerManager;
 import android.os.PowerManagerInternal;
 import android.os.PowerSaveState;
@@ -87,11 +91,13 @@
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
-import org.mockito.Answers;
+import org.mockito.ArgumentCaptor;
 import org.mockito.InOrder;
 import org.mockito.Mock;
 import org.mockito.MockitoSession;
+import org.mockito.invocation.InvocationOnMock;
 import org.mockito.quality.Strictness;
+import org.mockito.stubbing.Answer;
 
 /**
  * Tests for {@link com.android.server.DeviceIdleController}.
@@ -99,6 +105,7 @@
 @RunWith(AndroidJUnit4.class)
 public class DeviceIdleControllerTest {
     private DeviceIdleController mDeviceIdleController;
+    private DeviceIdleController.MyHandler mHandler;
     private AnyMotionDetectorForTest mAnyMotionDetector;
     private AppStateTrackerForTest mAppStateTracker;
     private DeviceIdleController.Constants mConstants;
@@ -112,8 +119,6 @@
     @Mock
     private ContentResolver mContentResolver;
     @Mock
-    private DeviceIdleController.MyHandler mHandler;
-    @Mock
     private IActivityManager mIActivityManager;
     @Mock
     private LocationManager mLocationManager;
@@ -171,6 +176,23 @@
 
         @Override
         DeviceIdleController.MyHandler getHandler(DeviceIdleController controller) {
+            if (mHandler == null) {
+                mHandler = controller.new MyHandler(getContext().getMainLooper());
+                spyOn(mHandler);
+                doNothing().when(mHandler).handleMessage(argThat((message) ->
+                        message.what != MSG_REPORT_STATIONARY_STATUS));
+                doAnswer(new Answer<Boolean>() {
+                    @Override
+                    public Boolean answer(InvocationOnMock invocation) throws Throwable {
+                        Message msg = invocation.getArgument(0);
+                        mHandler.handleMessage(msg);
+                        return true;
+                    }
+                }).when(mHandler).sendMessageDelayed(
+                        argThat((message) -> message.what == MSG_REPORT_STATIONARY_STATUS),
+                        anyLong());
+            }
+
             return mHandler;
         }
 
@@ -236,6 +258,19 @@
         }
     }
 
+    private class StationaryListenerForTest implements DeviceIdleInternal.StationaryListener {
+        boolean motionExpected = false;
+        boolean isStationary = false;
+
+        @Override
+        public void onDeviceStationaryChanged(boolean isStationary) {
+            if (isStationary == motionExpected) {
+                fail("Unexpected device stationary status: " + isStationary);
+            }
+            this.isStationary = isStationary;
+        }
+    }
+
     @Before
     public void setUp() {
         mMockingSession = mockitoSession()
@@ -265,8 +300,6 @@
         doReturn(true).when(mSensorManager).registerListener(any(), any(), anyInt());
         mAppStateTracker = new AppStateTrackerForTest(getContext(), Looper.getMainLooper());
         mAnyMotionDetector = new AnyMotionDetectorForTest();
-        mHandler = mock(DeviceIdleController.MyHandler.class, Answers.RETURNS_DEEP_STUBS);
-        doNothing().when(mHandler).handleMessage(any());
         mInjector = new InjectorForTest(getContext());
         doNothing().when(mContentResolver).registerContentObserver(any(), anyBoolean(), any());
 
@@ -1724,6 +1757,86 @@
                 1.0f, curfactor, delta);
     }
 
+    @Test
+    public void testStationaryDetection_QuickDozeOff() {
+        setQuickDozeEnabled(false);
+        enterDeepState(STATE_IDLE);
+        // Regular progression through states, so time should have increased appropriately.
+        mInjector.nowElapsed += mConstants.IDLE_AFTER_INACTIVE_TIMEOUT + mConstants.SENSING_TIMEOUT
+                + mConstants.LOCATING_TIMEOUT;
+
+        StationaryListenerForTest stationaryListener = new StationaryListenerForTest();
+
+        mDeviceIdleController.registerStationaryListener(stationaryListener);
+
+        // Go to IDLE_MAINTENANCE
+        mDeviceIdleController.stepIdleStateLocked("testing");
+
+        // Back to IDLE
+        mDeviceIdleController.stepIdleStateLocked("testing");
+        assertTrue(stationaryListener.isStationary);
+
+        // Test motion
+        stationaryListener.motionExpected = true;
+        mDeviceIdleController.mMotionListener.onTrigger(null);
+        assertFalse(stationaryListener.isStationary);
+    }
+
+    @Test
+    public void testStationaryDetection_QuickDozeOn() {
+        setAlarmSoon(false);
+        enterDeepState(STATE_QUICK_DOZE_DELAY);
+        mDeviceIdleController.stepIdleStateLocked("testing");
+        verifyStateConditions(STATE_IDLE);
+        // Quick doze progression through states, so time should have increased appropriately.
+        mInjector.nowElapsed += mConstants.QUICK_DOZE_DELAY_TIMEOUT;
+        final ArgumentCaptor<AlarmManager.OnAlarmListener> alarmListener = ArgumentCaptor
+                .forClass(AlarmManager.OnAlarmListener.class);
+        doNothing().when(mAlarmManager).set(anyInt(), anyLong(), eq("DeviceIdleController.motion"),
+                alarmListener.capture(), any());
+
+        StationaryListenerForTest stationaryListener = new StationaryListenerForTest();
+
+        stationaryListener.motionExpected = true;
+        mDeviceIdleController.registerStationaryListener(stationaryListener);
+        assertFalse(stationaryListener.isStationary);
+
+        // Go to IDLE_MAINTENANCE
+        mDeviceIdleController.stepIdleStateLocked("testing");
+
+        mInjector.nowElapsed += mConstants.MOTION_INACTIVE_TIMEOUT / 2;
+
+        // Back to IDLE
+        mDeviceIdleController.stepIdleStateLocked("testing");
+
+        // Now enough time has passed.
+        mInjector.nowElapsed += mConstants.MOTION_INACTIVE_TIMEOUT / 2;
+        stationaryListener.motionExpected = false;
+        alarmListener.getValue().onAlarm();
+        assertTrue(stationaryListener.isStationary);
+
+        stationaryListener.motionExpected = true;
+        mDeviceIdleController.mMotionListener.onSensorChanged(null);
+        assertFalse(stationaryListener.isStationary);
+
+        // Since we're in quick doze, the device shouldn't stop idling.
+        verifyStateConditions(STATE_IDLE);
+
+        // Go to IDLE_MAINTENANCE
+        mDeviceIdleController.stepIdleStateLocked("testing");
+
+        mInjector.nowElapsed += mConstants.MOTION_INACTIVE_TIMEOUT / 2;
+
+        // Back to IDLE
+        mDeviceIdleController.stepIdleStateLocked("testing");
+
+        // Now enough time has passed.
+        mInjector.nowElapsed += mConstants.MOTION_INACTIVE_TIMEOUT / 2;
+        stationaryListener.motionExpected = false;
+        alarmListener.getValue().onAlarm();
+        assertTrue(stationaryListener.isStationary);
+    }
+
     private void enterDeepState(int state) {
         switch (state) {
             case STATE_ACTIVE:
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AbstractAccessibilityServiceConnectionTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AbstractAccessibilityServiceConnectionTest.java
index 43f251a..d11d987 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/AbstractAccessibilityServiceConnectionTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AbstractAccessibilityServiceConnectionTest.java
@@ -156,7 +156,7 @@
     @Mock private AccessibilityWindowManager mMockA11yWindowManager;
     @Mock private AbstractAccessibilityServiceConnection.SystemSupport mMockSystemSupport;
     @Mock private WindowManagerInternal mMockWindowManagerInternal;
-    @Mock private GlobalActionPerformer mMockGlobalActionPerformer;
+    @Mock private SystemActionPerformer mMockSystemActionPerformer;
     @Mock private IBinder mMockService;
     @Mock private IAccessibilityServiceClient mMockServiceInterface;
     @Mock private KeyEventDispatcher mMockKeyEventDispatcher;
@@ -221,7 +221,7 @@
 
         mServiceConnection = new TestAccessibilityServiceConnection(mMockContext, COMPONENT_NAME,
                 mSpyServiceInfo, SERVICE_ID, mHandler, new Object(), mMockSecurityPolicy,
-                mMockSystemSupport, mMockWindowManagerInternal, mMockGlobalActionPerformer,
+                mMockSystemSupport, mMockWindowManagerInternal, mMockSystemActionPerformer,
                 mMockA11yWindowManager);
         // Assume that the service is connected
         mServiceConnection.mService = mMockService;
@@ -489,7 +489,7 @@
     @Test
     public void performGlobalAction() {
         mServiceConnection.performGlobalAction(GLOBAL_ACTION_HOME);
-        verify(mMockGlobalActionPerformer).performGlobalAction(GLOBAL_ACTION_HOME);
+        verify(mMockSystemActionPerformer).performSystemAction(GLOBAL_ACTION_HOME);
     }
 
     @Test
@@ -776,10 +776,10 @@
                 AccessibilityServiceInfo accessibilityServiceInfo, int id, Handler mainHandler,
                 Object lock, AccessibilitySecurityPolicy securityPolicy,
                 SystemSupport systemSupport, WindowManagerInternal windowManagerInternal,
-                GlobalActionPerformer globalActionPerfomer,
+                SystemActionPerformer systemActionPerfomer,
                 AccessibilityWindowManager a11yWindowManager) {
             super(context, componentName, accessibilityServiceInfo, id, mainHandler, lock,
-                    securityPolicy, systemSupport, windowManagerInternal, globalActionPerfomer,
+                    securityPolicy, systemSupport, windowManagerInternal, systemActionPerfomer,
                     a11yWindowManager);
             mResolvedUserId = USER_ID;
         }
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityServiceConnectionTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityServiceConnectionTest.java
index 6be5a37..edf82ee 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityServiceConnectionTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityServiceConnectionTest.java
@@ -75,7 +75,7 @@
     @Mock AccessibilityWindowManager mMockA11yWindowManager;
     @Mock AbstractAccessibilityServiceConnection.SystemSupport mMockSystemSupport;
     @Mock WindowManagerInternal mMockWindowManagerInternal;
-    @Mock GlobalActionPerformer mMockGlobalActionPerformer;
+    @Mock SystemActionPerformer mMockSystemActionPerformer;
     @Mock KeyEventDispatcher mMockKeyEventDispatcher;
     @Mock MagnificationController mMockMagnificationController;
     @Mock IBinder mMockIBinder;
@@ -104,7 +104,7 @@
         mConnection = new AccessibilityServiceConnection(mMockUserState, mMockContext,
                 COMPONENT_NAME, mMockServiceInfo, SERVICE_ID, mHandler, new Object(),
                 mMockSecurityPolicy, mMockSystemSupport, mMockWindowManagerInternal,
-                mMockGlobalActionPerformer, mMockA11yWindowManager);
+                mMockSystemActionPerformer, mMockA11yWindowManager);
         when(mMockSecurityPolicy.canPerformGestures(mConnection)).thenReturn(true);
     }
 
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/GlobalActionPerformerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/SystemActionPerformerTest.java
similarity index 63%
rename from services/tests/servicestests/src/com/android/server/accessibility/GlobalActionPerformerTest.java
rename to services/tests/servicestests/src/com/android/server/accessibility/SystemActionPerformerTest.java
index c73be6f..37f5b87 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/GlobalActionPerformerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/SystemActionPerformerTest.java
@@ -1,17 +1,17 @@
 /*
- ** Copyright 2017, The Android Open Source Project
- **
- ** Licensed under the Apache License, Version 2.0 (the "License");
- ** you may not use this file except in compliance with the License.
- ** You may obtain a copy of the License at
- **
- **     http://www.apache.org/licenses/LICENSE-2.0
- **
- ** Unless required by applicable law or agreed to in writing, software
- ** distributed under the License is distributed on an "AS IS" BASIS,
- ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- ** See the License for the specific language governing permissions and
- ** limitations under the License.
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
  */
 
 package com.android.server.accessibility;
@@ -35,13 +35,11 @@
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
-import java.util.function.Consumer;
-
 /**
- * Tests for GlobalActionPerformer
+ * Tests for SystemActionPerformer
  */
-public class GlobalActionPerformerTest {
-    GlobalActionPerformer mGlobalActionPerformer;
+public class SystemActionPerformerTest {
+    SystemActionPerformer mSystemActionPerformer;
 
     @Mock Context mMockContext;
     @Mock WindowManagerInternal mMockWindowManagerInternal;
@@ -55,34 +53,34 @@
         when(mMockContext.getSystemService(android.app.Service.STATUS_BAR_SERVICE))
                 .thenReturn(mMockStatusBarManager);
 
-        mGlobalActionPerformer =
-                new GlobalActionPerformer(mMockContext, mMockWindowManagerInternal,
+        mSystemActionPerformer =
+                new SystemActionPerformer(mMockContext, mMockWindowManagerInternal,
                         () -> mMockScreenshotHelper);
     }
 
     @Test
     public void testNotifications_expandsNotificationPanel() {
-        mGlobalActionPerformer
-                .performGlobalAction(AccessibilityService.GLOBAL_ACTION_NOTIFICATIONS);
+        mSystemActionPerformer
+                .performSystemAction(AccessibilityService.GLOBAL_ACTION_NOTIFICATIONS);
         verify(mMockStatusBarManager).expandNotificationsPanel();
     }
 
     @Test
     public void testQuickSettings_requestsQuickSettingsPanel() {
-        mGlobalActionPerformer
-                .performGlobalAction(AccessibilityService.GLOBAL_ACTION_QUICK_SETTINGS);
+        mSystemActionPerformer
+                .performSystemAction(AccessibilityService.GLOBAL_ACTION_QUICK_SETTINGS);
         verify(mMockStatusBarManager).expandSettingsPanel();
     }
 
     @Test
     public void testPowerDialog_requestsFromWindowManager() {
-        mGlobalActionPerformer.performGlobalAction(AccessibilityService.GLOBAL_ACTION_POWER_DIALOG);
+        mSystemActionPerformer.performSystemAction(AccessibilityService.GLOBAL_ACTION_POWER_DIALOG);
         verify(mMockWindowManagerInternal).showGlobalActions();
     }
 
     @Test
     public void testScreenshot_requestsFromScreenshotHelper() {
-        mGlobalActionPerformer.performGlobalAction(
+        mSystemActionPerformer.performSystemAction(
                 AccessibilityService.GLOBAL_ACTION_TAKE_SCREENSHOT);
         verify(mMockScreenshotHelper).takeScreenshot(
                 eq(android.view.WindowManager.TAKE_SCREENSHOT_FULLSCREEN), anyBoolean(),
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/UiAutomationManagerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/UiAutomationManagerTest.java
index 210de53..deb6f71 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/UiAutomationManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/UiAutomationManagerTest.java
@@ -64,7 +64,7 @@
     @Mock AccessibilityWindowManager mMockA11yWindowManager;
     @Mock AbstractAccessibilityServiceConnection.SystemSupport mMockSystemSupport;
     @Mock WindowManagerInternal mMockWindowManagerInternal;
-    @Mock GlobalActionPerformer mMockGlobalActionPerformer;
+    @Mock SystemActionPerformer mMockSystemActionPerformer;
     @Mock IBinder mMockOwner;
     @Mock IAccessibilityServiceClient mMockAccessibilityServiceClient;
     @Mock IBinder mMockServiceAsBinder;
@@ -174,7 +174,7 @@
         mUiAutomationManager.registerUiTestAutomationServiceLocked(mMockOwner,
                 mMockAccessibilityServiceClient, mMockContext, mMockServiceInfo, SERVICE_ID,
                 mMessageCapturingHandler, mMockSecurityPolicy, mMockSystemSupport,
-                mMockWindowManagerInternal, mMockGlobalActionPerformer,
+                mMockWindowManagerInternal, mMockSystemActionPerformer,
                 mMockA11yWindowManager, flags);
     }
 
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
index a25e40f..9a1fd9c 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
@@ -2650,6 +2650,21 @@
         verifyStayOnWhilePluggedCleared(false);
     }
 
+    public void testIsActiveSupervisionApp() throws Exception {
+        when(mServiceContext.resources
+                .getString(R.string.config_defaultSupervisionProfileOwnerComponent))
+                .thenReturn(admin1.flattenToString());
+
+        final int PROFILE_USER = 15;
+        final int PROFILE_ADMIN = UserHandle.getUid(PROFILE_USER, 19436);
+        addManagedProfile(admin1, PROFILE_ADMIN, admin1);
+        mContext.binder.callingUid = PROFILE_ADMIN;
+
+        final DevicePolicyManagerInternal dpmi =
+                LocalServices.getService(DevicePolicyManagerInternal.class);
+        assertTrue(dpmi.isActiveSupervisionApp(PROFILE_ADMIN));
+    }
+
     // Test if lock timeout on managed profile is handled correctly depending on whether profile
     // uses separate challenge.
     public void testSetMaximumTimeToLockProfile() throws Exception {
diff --git a/services/tests/servicestests/src/com/android/server/integrity/engine/RuleEvaluatorTest.java b/services/tests/servicestests/src/com/android/server/integrity/engine/RuleEvaluatorTest.java
new file mode 100644
index 0000000..1fcd0ef
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/integrity/engine/RuleEvaluatorTest.java
@@ -0,0 +1,130 @@
+/*
+ * 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.engine;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+
+import com.android.server.integrity.model.AppInstallMetadata;
+import com.android.server.integrity.model.AtomicFormula;
+import com.android.server.integrity.model.OpenFormula;
+import com.android.server.integrity.model.Rule;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+@RunWith(JUnit4.class)
+public class RuleEvaluatorTest {
+
+    private static final String PACKAGE_NAME_1 = "com.test.app";
+    private static final String PACKAGE_NAME_2 = "com.test.app2";
+    private static final String APP_CERTIFICATE = "test_cert";
+    private static final AppInstallMetadata APP_INSTALL_METADATA =
+            new AppInstallMetadata.Builder()
+                    .setPackageName(PACKAGE_NAME_1)
+                    .setAppCertificate(APP_CERTIFICATE)
+                    .build();
+
+    @Test
+    public void testMatchRules_emptyRules() {
+        List<Rule> rules = new ArrayList<>();
+
+        Rule matchedRule = RuleEvaluator.evaluateRules(rules, APP_INSTALL_METADATA);
+
+        assertEquals(Rule.EMPTY, matchedRule);
+    }
+
+    @Test
+    public void testMatchRules_emptyMatch() {
+        Rule rule1 = new Rule(
+                new AtomicFormula(AtomicFormula.Key.PACKAGE_NAME, AtomicFormula.Operator.EQ,
+                        PACKAGE_NAME_2), Rule.Effect.DENY);
+
+        Rule matchedRule = RuleEvaluator.evaluateRules(Collections.singletonList(rule1),
+                APP_INSTALL_METADATA);
+
+        assertEquals(Rule.EMPTY, matchedRule);
+    }
+
+
+    @Test
+    public void testMatchRules_oneMatch() {
+        Rule rule1 = new Rule(
+                new AtomicFormula(AtomicFormula.Key.PACKAGE_NAME, AtomicFormula.Operator.EQ,
+                        PACKAGE_NAME_1), Rule.Effect.DENY);
+        Rule rule2 = new Rule(
+                new AtomicFormula(AtomicFormula.Key.PACKAGE_NAME, AtomicFormula.Operator.EQ,
+                        PACKAGE_NAME_2), Rule.Effect.DENY);
+
+        Rule matchedRule = RuleEvaluator.evaluateRules(Arrays.asList(rule1, rule2),
+                APP_INSTALL_METADATA);
+
+        assertEquals(rule1, matchedRule);
+    }
+
+    @Test
+    public void testMatchRules_multipleMatches() {
+        Rule rule1 = new Rule(
+                new AtomicFormula(AtomicFormula.Key.PACKAGE_NAME, AtomicFormula.Operator.EQ,
+                        PACKAGE_NAME_1), Rule.Effect.DENY);
+        OpenFormula openFormula2 = new OpenFormula(OpenFormula.Connector.AND, Arrays.asList(
+                new AtomicFormula(AtomicFormula.Key.PACKAGE_NAME, AtomicFormula.Operator.EQ,
+                        PACKAGE_NAME_1),
+                new AtomicFormula(AtomicFormula.Key.APP_CERTIFICATE,
+                        AtomicFormula.Operator.EQ,
+                        APP_CERTIFICATE)));
+        Rule rule2 = new Rule(
+                openFormula2, Rule.Effect.DENY);
+
+        Rule matchedRule = RuleEvaluator.evaluateRules(Arrays.asList(rule1, rule2),
+                APP_INSTALL_METADATA);
+
+        assertNotEquals(Rule.EMPTY, matchedRule);
+    }
+
+    @Test
+    public void testMatchRules_ruleWithNot() {
+        OpenFormula openFormula = new OpenFormula(OpenFormula.Connector.NOT,
+                Collections.singletonList(
+                        new AtomicFormula(AtomicFormula.Key.PACKAGE_NAME, AtomicFormula.Operator.EQ,
+                                PACKAGE_NAME_2)));
+        Rule rule = new Rule(openFormula, Rule.Effect.DENY);
+
+        Rule matchedRule = RuleEvaluator.evaluateRules(Collections.singletonList(rule),
+                APP_INSTALL_METADATA);
+
+        assertEquals(rule, matchedRule);
+    }
+
+    @Test
+    public void testMatchRules_ruleWithIntegerOperators() {
+        Rule rule1 = new Rule(
+                new AtomicFormula(AtomicFormula.Key.VERSION_CODE, AtomicFormula.Operator.GT,
+                        1), Rule.Effect.DENY);
+
+        Rule matchedRule = RuleEvaluator.evaluateRules(Collections.singletonList(rule1),
+                APP_INSTALL_METADATA);
+
+        assertEquals(rule1, matchedRule);
+    }
+}
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/servicestests/src/com/android/server/integrity/model/RuleTest.java b/services/tests/servicestests/src/com/android/server/integrity/model/RuleTest.java
index cf001be..d1fa0f9 100644
--- a/services/tests/servicestests/src/com/android/server/integrity/model/RuleTest.java
+++ b/services/tests/servicestests/src/com/android/server/integrity/model/RuleTest.java
@@ -25,13 +25,20 @@
 import org.junit.runner.RunWith;
 import org.junit.runners.JUnit4;
 
+import java.util.Arrays;
+
 @RunWith(JUnit4.class)
 public class RuleTest {
 
     private static final Rule.Effect DENY_EFFECT = Rule.Effect.DENY;
-    private static final Formula SIMPLE_FORMULA =
+    private static final String PACKAGE_NAME = "com.test.app";
+    private static final String APP_CERTIFICATE = "test_cert";
+    private static final Formula PACKAGE_NAME_ATOMIC_FORMULA =
             new AtomicFormula(AtomicFormula.Key.PACKAGE_NAME, AtomicFormula.Operator.EQ,
-                    "com.test.app");
+                    PACKAGE_NAME);
+    private static final Formula APP_CERTIFICATE_ATOMIC_FORMULA =
+            new AtomicFormula(AtomicFormula.Key.APP_CERTIFICATE, AtomicFormula.Operator.EQ,
+                    APP_CERTIFICATE);
 
     @Test
     public void testEmptyRule() {
@@ -43,9 +50,9 @@
 
     @Test
     public void testValidRule() {
-        Rule validRule = new Rule(SIMPLE_FORMULA, DENY_EFFECT);
+        Rule validRule = new Rule(PACKAGE_NAME_ATOMIC_FORMULA, DENY_EFFECT);
 
-        assertEquals(SIMPLE_FORMULA, validRule.getFormula());
+        assertEquals(PACKAGE_NAME_ATOMIC_FORMULA, validRule.getFormula());
         assertEquals(DENY_EFFECT, validRule.getEffect());
     }
 
@@ -54,7 +61,7 @@
         assertExpectException(
                 NullPointerException.class,
                 /* expectedExceptionMessageRegex */ null,
-                () -> new Rule(SIMPLE_FORMULA, null));
+                () -> new Rule(PACKAGE_NAME_ATOMIC_FORMULA, null));
     }
 
     @Test
@@ -64,4 +71,16 @@
                 /* expectedExceptionMessageRegex */ null,
                 () -> new Rule(null, DENY_EFFECT));
     }
+
+    @Test
+    public void testToString() {
+        OpenFormula openFormula = new OpenFormula(OpenFormula.Connector.AND,
+                Arrays.asList(PACKAGE_NAME_ATOMIC_FORMULA, APP_CERTIFICATE_ATOMIC_FORMULA));
+        Rule rule = new Rule(openFormula, Rule.Effect.DENY);
+
+        String toString = rule.toString();
+
+        assertEquals(String.format("Rule: PACKAGE_NAME EQ %s AND APP_CERTIFICATE EQ %s, DENY",
+                PACKAGE_NAME, APP_CERTIFICATE), toString);
+    }
 }
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 7115af9..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/services/usage/java/com/android/server/usage/UsageStatsService.java b/services/usage/java/com/android/server/usage/UsageStatsService.java
index 34eb3f1..ecee709 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsService.java
@@ -1733,10 +1733,13 @@
         public void registerAppUsageLimitObserver(int observerId, String[] packages,
                 long timeLimitMs, long timeUsedMs, PendingIntent callbackIntent,
                 String callingPackage) {
+            final int callingUid = Binder.getCallingUid();
+            final DevicePolicyManagerInternal dpmInternal = getDpmInternal();
             if (!hasPermissions(callingPackage,
-                    Manifest.permission.SUSPEND_APPS, Manifest.permission.OBSERVE_APP_USAGE)) {
-                throw new SecurityException("Caller doesn't have both SUSPEND_APPS and "
-                        + "OBSERVE_APP_USAGE permissions");
+                    Manifest.permission.SUSPEND_APPS, Manifest.permission.OBSERVE_APP_USAGE)
+                    && (dpmInternal != null && !dpmInternal.isActiveSupervisionApp(callingUid))) {
+                throw new SecurityException("Caller must be the active supervision app or "
+                        + "it must have both SUSPEND_APPS and OBSERVE_APP_USAGE permissions");
             }
 
             if (packages == null || packages.length == 0) {
@@ -1745,7 +1748,6 @@
             if (callbackIntent == null && timeUsedMs < timeLimitMs) {
                 throw new NullPointerException("callbackIntent can't be null");
             }
-            final int callingUid = Binder.getCallingUid();
             final int userId = UserHandle.getUserId(callingUid);
             final long token = Binder.clearCallingIdentity();
             try {
@@ -1758,13 +1760,15 @@
 
         @Override
         public void unregisterAppUsageLimitObserver(int observerId, String callingPackage) {
+            final int callingUid = Binder.getCallingUid();
+            final DevicePolicyManagerInternal dpmInternal = getDpmInternal();
             if (!hasPermissions(callingPackage,
-                    Manifest.permission.SUSPEND_APPS, Manifest.permission.OBSERVE_APP_USAGE)) {
-                throw new SecurityException("Caller doesn't have both SUSPEND_APPS and "
-                        + "OBSERVE_APP_USAGE permissions");
+                    Manifest.permission.SUSPEND_APPS, Manifest.permission.OBSERVE_APP_USAGE)
+                    && (dpmInternal != null && !dpmInternal.isActiveSupervisionApp(callingUid))) {
+                throw new SecurityException("Caller must be the active supervision app or "
+                        + "it must have both SUSPEND_APPS and OBSERVE_APP_USAGE permissions");
             }
 
-            final int callingUid = Binder.getCallingUid();
             final int userId = UserHandle.getUserId(callingUid);
             final long token = Binder.clearCallingIdentity();
             try {
diff --git a/startop/scripts/app_startup/app_startup_runner.py b/startop/scripts/app_startup/app_startup_runner.py
index fa1c4e6..25ee6f7 100755
--- a/startop/scripts/app_startup/app_startup_runner.py
+++ b/startop/scripts/app_startup/app_startup_runner.py
@@ -233,13 +233,17 @@
                                      simulate: bool,
                                      inodes_path: str,
                                      timeout: int,
-                                     compiler_type: CompilerType) -> DataFrame:
+                                     compiler_type: CompilerType,
+                                     requires_trace_collection: bool) -> DataFrame:
   """ Executes run based on perfetto trace. """
-  passed, perfetto_trace_file = run_perfetto_collector(collector_info,
-                                                       timeout,
-                                                       simulate)
-  if not passed:
-    raise RuntimeError('Cannot run perfetto collector!')
+  if requires_trace_collection:
+    passed, perfetto_trace_file = run_perfetto_collector(collector_info,
+                                                         timeout,
+                                                         simulate)
+    if not passed:
+      raise RuntimeError('Cannot run perfetto collector!')
+  else:
+    perfetto_trace_file = tempfile.NamedTemporaryFile()
 
   with perfetto_trace_file:
     for combos in run_combos:
@@ -271,7 +275,8 @@
     simulate: bool,
     inodes_path: str,
     timeout: int,
-    compiler_type: CompilerType):
+    compiler_type: CompilerType,
+    requires_trace_collection: bool):
   # nothing will work if the screen isn't unlocked first.
   cmd_utils.execute_arbitrary_command([_UNLOCK_SCREEN_SCRIPT],
                                       timeout,
@@ -284,7 +289,8 @@
                                                 simulate,
                                                 inodes_path,
                                                 timeout,
-                                                compiler_type)
+                                                compiler_type,
+                                                requires_trace_collection)
 
 def gather_results(commands: Iterable[Tuple[DataFrame]],
                    key_list: List[str], value_list: List[Tuple[str, ...]]):
@@ -369,11 +375,13 @@
                                                                       CollectorPackageInfo)
 
   print_utils.debug_print_gen("grouped run combinations: ", grouped_combos())
+  requires_trace_collection = any(i in _TRACING_READAHEADS for i in opts.readaheads)
   exec = execute_run_combos(grouped_combos(),
                             opts.simulate,
                             opts.inodes,
                             opts.timeout,
-                            opts.compiler_type)
+                            opts.compiler_type,
+                            requires_trace_collection)
 
   results = gather_results(exec, _COMBINATORIAL_OPTIONS, combos())
 
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/CellInfoNr.java b/telephony/java/android/telephony/CellInfoNr.java
index 9775abd..cea8323 100644
--- a/telephony/java/android/telephony/CellInfoNr.java
+++ b/telephony/java/android/telephony/CellInfoNr.java
@@ -19,6 +19,8 @@
 import android.annotation.NonNull;
 import android.os.Parcel;
 
+import dalvik.annotation.codegen.CovariantReturnType;
+
 import java.util.Objects;
 
 /**
@@ -46,6 +48,7 @@
     /**
      * @return a {@link CellIdentityNr} instance.
      */
+    @CovariantReturnType(returnType = CellIdentityNr.class, presentAfter = 29)
     @Override
     @NonNull
     public CellIdentity getCellIdentity() {
@@ -55,6 +58,7 @@
     /**
      * @return a {@link CellSignalStrengthNr} instance.
      */
+    @CovariantReturnType(returnType = CellSignalStrengthNr.class, presentAfter = 29)
     @Override
     @NonNull
     public CellSignalStrength getCellSignalStrength() {
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 667a9ce..ee76a6f 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -6877,6 +6877,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/android/telephony/ims/ImsMmTelManager.java b/telephony/java/android/telephony/ims/ImsMmTelManager.java
index a1a7fcc..2fad847 100644
--- a/telephony/java/android/telephony/ims/ImsMmTelManager.java
+++ b/telephony/java/android/telephony/ims/ImsMmTelManager.java
@@ -183,19 +183,17 @@
         /**
          * Notifies the framework when the IMS Provider is registered to the IMS network.
          *
-         * @param imsTransportType the radio access technology. Valid values are defined in
-         * {@link android.telephony.AccessNetworkConstants.TransportType}.
+         * @param imsTransportType the radio access technology.
          */
-        public void onRegistered(int imsTransportType) {
+        public void onRegistered(@AccessNetworkConstants.TransportType int imsTransportType) {
         }
 
         /**
          * Notifies the framework when the IMS Provider is trying to register the IMS network.
          *
-         * @param imsTransportType the radio access technology. Valid values are defined in
-         * {@link android.telephony.AccessNetworkConstants.TransportType}.
+         * @param imsTransportType the radio access technology.
          */
-        public void onRegistering(int imsTransportType) {
+        public void onRegistering(@AccessNetworkConstants.TransportType int imsTransportType) {
         }
 
         /**
@@ -207,15 +205,14 @@
         }
 
         /**
-         * A failure has occurred when trying to handover registration to another technology type,
-         * defined in {@link android.telephony.AccessNetworkConstants.TransportType}
+         * A failure has occurred when trying to handover registration to another technology type.
          *
-         * @param imsTransportType The
-         *         {@link android.telephony.AccessNetworkConstants.TransportType}
-         *         transport type that has failed to handover registration to.
+         * @param imsTransportType The transport type that has failed to handover registration to.
          * @param info A {@link ImsReasonInfo} that identifies the reason for failure.
          */
-        public void onTechnologyChangeFailed(int imsTransportType, @Nullable ImsReasonInfo info) {
+        public void onTechnologyChangeFailed(
+                @AccessNetworkConstants.TransportType int imsTransportType,
+                @Nullable ImsReasonInfo info) {
         }
 
         /**
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/test-mock/src/android/test/mock/MockContext.java b/test-mock/src/android/test/mock/MockContext.java
index a95b6f1..fcd4701 100644
--- a/test-mock/src/android/test/mock/MockContext.java
+++ b/test-mock/src/android/test/mock/MockContext.java
@@ -758,6 +758,12 @@
 
     /** {@hide} */
     @Override
+    public Context createContextAsUser(UserHandle user) {
+        throw new UnsupportedOperationException();
+    }
+
+    /** {@hide} */
+    @Override
     public int getUserId() {
         throw new UnsupportedOperationException();
     }
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;
diff --git a/tools/apilint/apilint.py b/tools/apilint/apilint.py
index 9e42c04..912c1ad 100644
--- a/tools/apilint/apilint.py
+++ b/tools/apilint/apilint.py
@@ -1976,7 +1976,9 @@
     """Catches missing nullability annotations"""
 
     for f in clazz.fields:
-        if f.value is not None and 'static' in f.split and 'final' in f.split:
+        if "enum_constant" in f.split:
+            continue  # Enum constants are never null
+        if f.value is not None and 'final' in f.split:
             continue  # Nullability of constants can be inferred.
         if f.typ not in PRIMITIVES and not has_nullability(f.annotations):
             error(clazz, f, "M12", "Field must be marked either @NonNull or @Nullable")
@@ -1985,8 +1987,12 @@
         verify_nullability_args(clazz, c)
 
     for m in clazz.methods:
-        if m.name == "writeToParcel" or m.name == "onReceive":
-            continue  # Parcelable.writeToParcel() and BroadcastReceiver.onReceive() are not yet annotated
+        if m.name == "writeToParcel" or m.name == "onReceive" or m.name == "onBind":
+            continue  # Parcelable.writeToParcel(), BroadcastReceiver.onReceive(), and Service.onBind() are not yet annotated
+
+        if (m.name == "equals" and m.args == ["java.lang.Object"] or
+                m.name == "toString" and m.args == []):
+            continue  # Nullability of equals and toString is implicit.
 
         if m.typ not in PRIMITIVES and not has_nullability(m.annotations):
             error(clazz, m, "M12", "Return value must be marked either @NonNull or @Nullable")
diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java
index 5782f5b..5496e83 100644
--- a/wifi/java/android/net/wifi/WifiManager.java
+++ b/wifi/java/android/net/wifi/WifiManager.java
@@ -1813,12 +1813,13 @@
     }
 
     /**
-     * Remove the Passpoint configuration identified by its FQDN (Fully Qualified Domain Name).
+     * Remove the Passpoint configuration identified by its FQDN (Fully Qualified Domain Name) added
+     * by the caller.
      *
-     * @param fqdn The FQDN of the Passpoint configuration to be removed
+     * @param fqdn The FQDN of the Passpoint configuration added by the caller to be removed
      * @throws IllegalArgumentException if no configuration is associated with the given FQDN or
      *                                  Passpoint is not enabled on the device.
-     * @deprecated This is no longer supported.
+     * @deprecated This will be non-functional in a future release.
      */
     @Deprecated
     @RequiresPermission(anyOf = {
@@ -1842,12 +1843,12 @@
     }
 
     /**
-     * Return the list of installed Passpoint configurations.
+     * Return the list of installed Passpoint configurations added by the caller.
      *
      * An empty list will be returned when no configurations are installed.
      *
-     * @return A list of {@link PasspointConfiguration}
-     * @deprecated This is no longer supported.
+     * @return A list of {@link PasspointConfiguration} added by the caller
+     * @deprecated This will be non-functional in a future release.
      */
     @Deprecated
     @RequiresPermission(anyOf = {