Merge "Switch from android.text.format.Time"
diff --git a/Android.bp b/Android.bp
index b20b085..b2ed0de 100644
--- a/Android.bp
+++ b/Android.bp
@@ -248,10 +248,6 @@
     name: "framework-minus-apex",
     defaults: ["framework-defaults"],
     javac_shard_size: 150,
-    required: [
-        "framework-platform-compat-config",
-        "libcore-platform-compat-config",
-    ],
 }
 
 java_library {
@@ -262,6 +258,11 @@
         "framework-minus-apex",
         "jobscheduler-framework",
     ],
+    required: [
+        "framework-platform-compat-config",
+        "libcore-platform-compat-config",
+        "services-platform-compat-config",
+    ],
     sdk_version: "core_platform",
 }
 
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/TimeController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/TimeController.java
index 4c11947..1bb9e96 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/TimeController.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/TimeController.java
@@ -18,13 +18,20 @@
 
 import static com.android.server.job.JobSchedulerService.sElapsedRealtimeClock;
 
+import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.AlarmManager;
 import android.app.AlarmManager.OnAlarmListener;
+import android.content.ContentResolver;
 import android.content.Context;
+import android.database.ContentObserver;
+import android.net.Uri;
+import android.os.Handler;
 import android.os.Process;
 import android.os.UserHandle;
 import android.os.WorkSource;
+import android.provider.Settings;
+import android.util.KeyValueListParser;
 import android.util.Log;
 import android.util.Slog;
 import android.util.TimeUtils;
@@ -32,6 +39,7 @@
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.IndentingPrintWriter;
+import com.android.server.job.ConstantsProto;
 import com.android.server.job.JobSchedulerService;
 import com.android.server.job.StateControllerProto;
 
@@ -55,6 +63,9 @@
     /** Delay alarm tag for logging purposes */
     private final String DELAY_TAG = "*job.delay*";
 
+    private final Handler mHandler;
+    private final TcConstants mTcConstants;
+
     private long mNextJobExpiredElapsedMillis;
     private long mNextDelayExpiredElapsedMillis;
 
@@ -70,6 +81,14 @@
         mNextJobExpiredElapsedMillis = Long.MAX_VALUE;
         mNextDelayExpiredElapsedMillis = Long.MAX_VALUE;
         mChainedAttributionEnabled = mService.isChainedAttributionEnabled();
+
+        mHandler = new Handler(mContext.getMainLooper());
+        mTcConstants = new TcConstants(mHandler);
+    }
+
+    @Override
+    public void onSystemServicesReady() {
+        mTcConstants.start(mContext.getContentResolver());
     }
 
     /**
@@ -294,8 +313,7 @@
                 } else {
                     if (!wouldBeReadyWithConstraintLocked(job, JobStatus.CONSTRAINT_TIMING_DELAY)) {
                         if (DEBUG) {
-                            Slog.i(TAG,
-                                    "Skipping " + job + " because delay won't make it ready.");
+                            Slog.i(TAG, "Skipping " + job + " because delay won't make it ready.");
                         }
                         continue;
                     }
@@ -354,7 +372,8 @@
     /**
      * Set an alarm with the {@link android.app.AlarmManager} for the next time at which a job's
      * delay will expire.
-     * This alarm <b>will</b> wake up the phone.
+     * This alarm <b>will not</b> wake up the phone if
+     * {@link TcConstants#USE_NON_WAKEUP_ALARM_FOR_DELAY} is true.
      */
     private void setDelayExpiredAlarmLocked(long alarmTimeElapsedMillis, WorkSource ws) {
         alarmTimeElapsedMillis = maybeAdjustAlarmTime(alarmTimeElapsedMillis);
@@ -362,8 +381,11 @@
             return;
         }
         mNextDelayExpiredElapsedMillis = alarmTimeElapsedMillis;
-        updateAlarmWithListenerLocked(DELAY_TAG, mNextDelayExpiredListener,
-                mNextDelayExpiredElapsedMillis, ws);
+        final int alarmType =
+                mTcConstants.USE_NON_WAKEUP_ALARM_FOR_DELAY
+                        ? AlarmManager.ELAPSED_REALTIME : AlarmManager.ELAPSED_REALTIME_WAKEUP;
+        updateAlarmWithListenerLocked(DELAY_TAG, alarmType,
+                mNextDelayExpiredListener, mNextDelayExpiredElapsedMillis, ws);
     }
 
     /**
@@ -377,16 +399,16 @@
             return;
         }
         mNextJobExpiredElapsedMillis = alarmTimeElapsedMillis;
-        updateAlarmWithListenerLocked(DEADLINE_TAG, mDeadlineExpiredListener,
-                mNextJobExpiredElapsedMillis, ws);
+        updateAlarmWithListenerLocked(DEADLINE_TAG, AlarmManager.ELAPSED_REALTIME_WAKEUP,
+                mDeadlineExpiredListener, mNextJobExpiredElapsedMillis, ws);
     }
 
     private long maybeAdjustAlarmTime(long proposedAlarmTimeElapsedMillis) {
         return Math.max(proposedAlarmTimeElapsedMillis, sElapsedRealtimeClock.millis());
     }
 
-    private void updateAlarmWithListenerLocked(String tag, OnAlarmListener listener,
-            long alarmTimeElapsed, WorkSource ws) {
+    private void updateAlarmWithListenerLocked(String tag, @AlarmManager.AlarmType int alarmType,
+            OnAlarmListener listener, long alarmTimeElapsed, WorkSource ws) {
         ensureAlarmServiceLocked();
         if (alarmTimeElapsed == Long.MAX_VALUE) {
             mAlarmService.cancel(listener);
@@ -394,7 +416,7 @@
             if (DEBUG) {
                 Slog.d(TAG, "Setting " + tag + " for: " + alarmTimeElapsed);
             }
-            mAlarmService.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, alarmTimeElapsed,
+            mAlarmService.set(alarmType, alarmTimeElapsed,
                     AlarmManager.WINDOW_HEURISTIC, 0, tag, listener, null, ws);
         }
     }
@@ -422,9 +444,77 @@
     };
 
     @VisibleForTesting
-    void recheckAlarmsLocked() {
-        checkExpiredDeadlinesAndResetAlarm();
-        checkExpiredDelaysAndResetAlarm();
+    class TcConstants extends ContentObserver {
+        private ContentResolver mResolver;
+        private final KeyValueListParser mParser = new KeyValueListParser(',');
+
+        private static final String KEY_USE_NON_WAKEUP_ALARM_FOR_DELAY =
+                "use_non_wakeup_delay_alarm";
+
+        private static final boolean DEFAULT_USE_NON_WAKEUP_ALARM_FOR_DELAY = true;
+
+        /**
+         * Whether or not TimeController should skip setting wakeup alarms for jobs that aren't
+         * ready now.
+         */
+        public boolean USE_NON_WAKEUP_ALARM_FOR_DELAY = DEFAULT_USE_NON_WAKEUP_ALARM_FOR_DELAY;
+
+        /**
+         * Creates a content observer.
+         *
+         * @param handler The handler to run {@link #onChange} on, or null if none.
+         */
+        TcConstants(Handler handler) {
+            super(handler);
+        }
+
+        private void start(ContentResolver resolver) {
+            mResolver = resolver;
+            mResolver.registerContentObserver(Settings.Global.getUriFor(
+                    Settings.Global.JOB_SCHEDULER_TIME_CONTROLLER_CONSTANTS), false, this);
+            onChange(true, null);
+        }
+
+        @Override
+        public void onChange(boolean selfChange, Uri uri) {
+            final String constants = Settings.Global.getString(
+                    mResolver, Settings.Global.JOB_SCHEDULER_TIME_CONTROLLER_CONSTANTS);
+
+            try {
+                mParser.setString(constants);
+            } catch (Exception e) {
+                // Failed to parse the settings string, log this and move on with defaults.
+                Slog.e(TAG, "Bad jobscheduler time controller settings", e);
+            }
+
+            USE_NON_WAKEUP_ALARM_FOR_DELAY = mParser.getBoolean(
+                    KEY_USE_NON_WAKEUP_ALARM_FOR_DELAY, DEFAULT_USE_NON_WAKEUP_ALARM_FOR_DELAY);
+            // Intentionally not calling checkExpiredDelaysAndResetAlarm() here. There's no need to
+            // iterate through the entire list again for this constant change. The next delay alarm
+            // that is set will make use of the new constant value.
+        }
+
+        private void dump(IndentingPrintWriter pw) {
+            pw.println();
+            pw.println("TimeController:");
+            pw.increaseIndent();
+            pw.printPair(KEY_USE_NON_WAKEUP_ALARM_FOR_DELAY,
+                    USE_NON_WAKEUP_ALARM_FOR_DELAY).println();
+            pw.decreaseIndent();
+        }
+
+        private void dump(ProtoOutputStream proto) {
+            final long tcToken = proto.start(ConstantsProto.TIME_CONTROLLER);
+            proto.write(ConstantsProto.TimeController.USE_NON_WAKEUP_ALARM_FOR_DELAY,
+                    USE_NON_WAKEUP_ALARM_FOR_DELAY);
+            proto.end(tcToken);
+        }
+    }
+
+    @VisibleForTesting
+    @NonNull
+    TcConstants getTcConstants() {
+        return mTcConstants;
     }
 
     @Override
@@ -501,4 +591,14 @@
         proto.end(mToken);
         proto.end(token);
     }
+
+    @Override
+    public void dumpConstants(IndentingPrintWriter pw) {
+        mTcConstants.dump(pw);
+    }
+
+    @Override
+    public void dumpConstants(ProtoOutputStream proto) {
+        mTcConstants.dump(proto);
+    }
 }
diff --git a/api/current.txt b/api/current.txt
index 593710314..652f23f 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -13460,6 +13460,7 @@
   public final class GestureLibraries {
     method public static android.gesture.GestureLibrary fromFile(String);
     method public static android.gesture.GestureLibrary fromFile(java.io.File);
+    method @NonNull public static android.gesture.GestureLibrary fromFileDescriptor(@NonNull android.os.ParcelFileDescriptor);
     method public static android.gesture.GestureLibrary fromPrivateFile(android.content.Context, String);
     method public static android.gesture.GestureLibrary fromRawResource(android.content.Context, @RawRes int);
   }
@@ -17073,6 +17074,7 @@
     field public static final int REQUEST_AVAILABLE_CAPABILITIES_RAW = 3; // 0x3
     field public static final int REQUEST_AVAILABLE_CAPABILITIES_READ_SENSOR_SETTINGS = 5; // 0x5
     field public static final int REQUEST_AVAILABLE_CAPABILITIES_SECURE_IMAGE_DATA = 13; // 0xd
+    field public static final int REQUEST_AVAILABLE_CAPABILITIES_SYSTEM_CAMERA = 14; // 0xe
     field public static final int REQUEST_AVAILABLE_CAPABILITIES_YUV_REPROCESSING = 7; // 0x7
     field public static final int SCALER_CROPPING_TYPE_CENTER_ONLY = 0; // 0x0
     field public static final int SCALER_CROPPING_TYPE_FREEFORM = 1; // 0x1
@@ -42240,6 +42242,7 @@
     method public int speak(CharSequence, int, android.os.Bundle, String);
     method @Deprecated public int speak(String, int, java.util.HashMap<java.lang.String,java.lang.String>);
     method public int stop();
+    method public int synthesizeToFile(@NonNull CharSequence, @NonNull android.os.Bundle, @NonNull android.os.ParcelFileDescriptor, @NonNull String);
     method public int synthesizeToFile(CharSequence, android.os.Bundle, java.io.File, String);
     method @Deprecated public int synthesizeToFile(String, java.util.HashMap<java.lang.String,java.lang.String>, String);
     field public static final String ACTION_TTS_QUEUE_PROCESSING_COMPLETED = "android.speech.tts.TTS_QUEUE_PROCESSING_COMPLETED";
diff --git a/api/system-current.txt b/api/system-current.txt
index 76d017c..ed2616a 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -29,6 +29,7 @@
     field public static final String BIND_CONTENT_SUGGESTIONS_SERVICE = "android.permission.BIND_CONTENT_SUGGESTIONS_SERVICE";
     field public static final String BIND_DIRECTORY_SEARCH = "android.permission.BIND_DIRECTORY_SEARCH";
     field public static final String BIND_EUICC_SERVICE = "android.permission.BIND_EUICC_SERVICE";
+    field public static final String BIND_EXTERNAL_STORAGE_SERVICE = "android.permission.BIND_EXTERNAL_STORAGE_SERVICE";
     field public static final String BIND_IMS_SERVICE = "android.permission.BIND_IMS_SERVICE";
     field public static final String BIND_KEYGUARD_APPWIDGET = "android.permission.BIND_KEYGUARD_APPWIDGET";
     field public static final String BIND_NETWORK_RECOMMENDATION_SERVICE = "android.permission.BIND_NETWORK_RECOMMENDATION_SERVICE";
@@ -192,6 +193,7 @@
     field public static final String SUBSTITUTE_NOTIFICATION_APP_NAME = "android.permission.SUBSTITUTE_NOTIFICATION_APP_NAME";
     field public static final String SUBSTITUTE_SHARE_TARGET_APP_NAME_AND_ICON = "android.permission.SUBSTITUTE_SHARE_TARGET_APP_NAME_AND_ICON";
     field public static final String SUSPEND_APPS = "android.permission.SUSPEND_APPS";
+    field public static final String SYSTEM_CAMERA = "android.permission.SYSTEM_CAMERA";
     field public static final String TETHER_PRIVILEGED = "android.permission.TETHER_PRIVILEGED";
     field public static final String TV_INPUT_HARDWARE = "android.permission.TV_INPUT_HARDWARE";
     field public static final String TV_VIRTUAL_REMOTE_CONTROLLER = "android.permission.TV_VIRTUAL_REMOTE_CONTROLLER";
@@ -513,6 +515,8 @@
   }
 
   public class Notification implements android.os.Parcelable {
+    method @Nullable public android.util.Pair<android.app.RemoteInput,android.app.Notification.Action> findRemoteInputActionPair(boolean);
+    method @NonNull public java.util.List<android.app.Notification.Action> getContextualActions();
     field public static final String CATEGORY_CAR_EMERGENCY = "car_emergency";
     field public static final String CATEGORY_CAR_INFORMATION = "car_information";
     field public static final String CATEGORY_CAR_WARNING = "car_warning";
@@ -521,6 +525,10 @@
     field public static final int FLAG_AUTOGROUP_SUMMARY = 1024; // 0x400
   }
 
+  public static final class Notification.MessagingStyle.Message {
+    method @Nullable public static android.app.Notification.MessagingStyle.Message getMessageFromBundle(@NonNull android.os.Bundle);
+  }
+
   public static final class Notification.TvExtender implements android.app.Notification.Extender {
     ctor public Notification.TvExtender();
     ctor public Notification.TvExtender(android.app.Notification);
@@ -543,6 +551,7 @@
     method public void populateFromXml(org.xmlpull.v1.XmlPullParser);
     method public org.json.JSONObject toJson() throws org.json.JSONException;
     method public void writeXml(org.xmlpull.v1.XmlSerializer) throws java.io.IOException;
+    field public static final int USER_LOCKED_SOUND = 32; // 0x20
   }
 
   public final class NotificationChannelGroup implements android.os.Parcelable {
@@ -6648,6 +6657,10 @@
     field @NonNull public static final android.os.Parcelable.Creator<android.service.notification.SnoozeCriterion> CREATOR;
   }
 
+  public class StatusBarNotification implements android.os.Parcelable {
+    method public boolean isAppGroup();
+  }
+
 }
 
 package android.service.oemlock {
@@ -6767,6 +6780,20 @@
 
 }
 
+package android.service.storage {
+
+  public abstract class ExternalStorageService extends android.app.Service {
+    ctor public ExternalStorageService();
+    method @NonNull public final android.os.IBinder onBind(@NonNull android.content.Intent);
+    method public abstract void onEndSession(@NonNull String) throws java.io.IOException;
+    method public abstract void onStartSession(@NonNull String, int, @NonNull android.os.ParcelFileDescriptor, @NonNull String, @NonNull String) throws java.io.IOException;
+    field public static final int FLAG_SESSION_ATTRIBUTE_INDEXABLE = 2; // 0x2
+    field public static final int FLAG_SESSION_TYPE_FUSE = 1; // 0x1
+    field public static final String SERVICE_INTERFACE = "android.service.storage.ExternalStorageService";
+  }
+
+}
+
 package android.service.textclassifier {
 
   public abstract class TextClassifierService extends android.app.Service {
diff --git a/api/test-current.txt b/api/test-current.txt
index e9615111..1b2c59d 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -619,10 +619,13 @@
     method public int describeContents();
     method public static android.content.AutofillOptions forWhitelistingItself();
     method public boolean isAugmentedAutofillEnabled(@NonNull android.content.Context);
+    method public boolean isAutofillDisabledLocked(@NonNull android.content.ComponentName);
     method public void writeToParcel(android.os.Parcel, int);
     field @NonNull public static final android.os.Parcelable.Creator<android.content.AutofillOptions> CREATOR;
+    field public long appDisabledExpiration;
     field public boolean augmentedAutofillEnabled;
     field public final boolean compatModeEnabled;
+    field @Nullable public android.util.ArrayMap<java.lang.String,java.lang.Long> disabledActivities;
     field public final int loggingLevel;
     field @Nullable public android.util.ArraySet<android.content.ComponentName> whitelistedActivitiesForAugmentedAutofill;
   }
diff --git a/cmds/incidentd/src/WorkDirectory.cpp b/cmds/incidentd/src/WorkDirectory.cpp
index 0570c3a..8dcb865 100644
--- a/cmds/incidentd/src/WorkDirectory.cpp
+++ b/cmds/incidentd/src/WorkDirectory.cpp
@@ -664,7 +664,7 @@
             nanosleep(&spec, nullptr);
         }
         clock_gettime(CLOCK_REALTIME, &spec);
-        timestampNs = (spec.tv_sec) * 1000 + spec.tv_nsec;
+        timestampNs = int64_t(spec.tv_sec) * 1000 + spec.tv_nsec;
     } while (file_exists_locked(timestampNs));
     return timestampNs;
 }
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 372eab2..ac53118 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -3278,6 +3278,7 @@
      * @hide
      */
     @Nullable
+    @SystemApi
     public Pair<RemoteInput, Action> findRemoteInputActionPair(boolean requiresFreeform) {
         if (actions == null) {
             return null;
@@ -3304,7 +3305,8 @@
      *
      * @hide
      */
-    public List<Notification.Action> getContextualActions() {
+    @SystemApi
+    public @NonNull List<Notification.Action> getContextualActions() {
         if (actions == null) return Collections.emptyList();
 
         List<Notification.Action> contextualActions = new ArrayList<>();
@@ -7730,7 +7732,8 @@
              * @hide
              */
             @Nullable
-            public static Message getMessageFromBundle(Bundle bundle) {
+            @SystemApi
+            public static Message getMessageFromBundle(@NonNull Bundle bundle) {
                 try {
                     if (!bundle.containsKey(KEY_TEXT) || !bundle.containsKey(KEY_TIMESTAMP)) {
                         return null;
diff --git a/core/java/android/app/NotificationChannel.java b/core/java/android/app/NotificationChannel.java
index 3effd11..93e4ddc 100644
--- a/core/java/android/app/NotificationChannel.java
+++ b/core/java/android/app/NotificationChannel.java
@@ -110,6 +110,7 @@
     /**
      * @hide
      */
+    @SystemApi
     public static final int USER_LOCKED_SOUND = 0x00000020;
 
     /**
diff --git a/core/java/android/app/UiAutomation.java b/core/java/android/app/UiAutomation.java
index ed2b991..f9b96c5 100644
--- a/core/java/android/app/UiAutomation.java
+++ b/core/java/android/app/UiAutomation.java
@@ -242,7 +242,7 @@
             mUiAutomationConnection.connect(mClient, flags);
             mFlags = flags;
         } catch (RemoteException re) {
-            throw new RuntimeException("Error while connecting UiAutomation", re);
+            throw new RuntimeException("Error while connecting " + this, re);
         }
 
         synchronized (mLock) {
@@ -255,7 +255,7 @@
                     final long elapsedTimeMillis = SystemClock.uptimeMillis() - startTimeMillis;
                     final long remainingTimeMillis = CONNECT_TIMEOUT_MILLIS - elapsedTimeMillis;
                     if (remainingTimeMillis <= 0) {
-                        throw new RuntimeException("Error while connecting UiAutomation");
+                        throw new RuntimeException("Error while connecting " + this);
                     }
                     try {
                         mLock.wait(remainingTimeMillis);
@@ -290,7 +290,7 @@
         synchronized (mLock) {
             if (mIsConnecting) {
                 throw new IllegalStateException(
-                        "Cannot call disconnect() while connecting!");
+                        "Cannot call disconnect() while connecting " + this);
             }
             throwIfNotConnectedLocked();
             mConnectionId = CONNECTION_ID_UNDEFINED;
@@ -299,7 +299,7 @@
             // Calling out without a lock held.
             mUiAutomationConnection.disconnect();
         } catch (RemoteException re) {
-            throw new RuntimeException("Error while disconnecting UiAutomation", re);
+            throw new RuntimeException("Error while disconnecting " + this, re);
         } finally {
             mRemoteCallbackThread.quit();
             mRemoteCallbackThread = null;
@@ -1184,19 +1184,29 @@
         return result;
     }
 
+    @Override
+    public String toString() {
+        final StringBuilder stringBuilder = new StringBuilder();
+        stringBuilder.append("UiAutomation@").append(Integer.toHexString(hashCode()));
+        stringBuilder.append("[id=").append(mConnectionId);
+        stringBuilder.append(", flags=").append(mFlags);
+        stringBuilder.append("]");
+        return stringBuilder.toString();
+    }
+
     private boolean isConnectedLocked() {
         return mConnectionId != CONNECTION_ID_UNDEFINED;
     }
 
     private void throwIfConnectedLocked() {
         if (mConnectionId != CONNECTION_ID_UNDEFINED) {
-            throw new IllegalStateException("UiAutomation not connected!");
+            throw new IllegalStateException("UiAutomation not connected, " + this);
         }
     }
 
     private void throwIfNotConnectedLocked() {
         if (!isConnectedLocked()) {
-            throw new IllegalStateException("UiAutomation not connected!");
+            throw new IllegalStateException("UiAutomation not connected, " + this);
         }
     }
 
@@ -1220,6 +1230,9 @@
                         mConnectionId = connectionId;
                         mLock.notifyAll();
                     }
+                    if (Build.IS_DEBUGGABLE) {
+                        Log.v(LOG_TAG, "Init " + UiAutomation.this);
+                    }
                 }
 
                 @Override
diff --git a/core/java/android/content/AutofillOptions.java b/core/java/android/content/AutofillOptions.java
index 8fb9501..082663e 100644
--- a/core/java/android/content/AutofillOptions.java
+++ b/core/java/android/content/AutofillOptions.java
@@ -21,6 +21,8 @@
 import android.app.ActivityThread;
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.os.SystemClock;
+import android.util.ArrayMap;
 import android.util.ArraySet;
 import android.util.Log;
 import android.view.autofill.AutofillManager;
@@ -62,6 +64,18 @@
     @Nullable
     public ArraySet<ComponentName> whitelistedActivitiesForAugmentedAutofill;
 
+    /**
+     * The package disable expiration by autofill service.
+     */
+    public long appDisabledExpiration;
+
+    /**
+     * The disabled Activities of the package. key is component name string, value is when they
+     * will be enabled.
+     */
+    @Nullable
+    public ArrayMap<String, Long> disabledActivities;
+
     public AutofillOptions(int loggingLevel, boolean compatModeEnabled) {
         this.loggingLevel = loggingLevel;
         this.compatModeEnabled = compatModeEnabled;
@@ -82,6 +96,27 @@
     }
 
     /**
+     * Returns if autofill is disabled by service to the given activity.
+     */
+    public boolean isAutofillDisabledLocked(@NonNull ComponentName componentName) {
+        final long elapsedTime = SystemClock.elapsedRealtime();
+        final String component = componentName.flattenToString();
+        // Check app first.
+        if (appDisabledExpiration >= elapsedTime) return true;
+
+        // Then check activities.
+        if (disabledActivities != null) {
+            final Long expiration = disabledActivities.get(component);
+            if (expiration != null) {
+                if (expiration >= elapsedTime) return true;
+                disabledActivities.remove(component);
+            }
+        }
+        appDisabledExpiration = 0;
+        return false;
+    }
+
+    /**
      * @hide
      */
     @TestApi
@@ -110,7 +145,8 @@
     @Override
     public String toString() {
         return "AutofillOptions [loggingLevel=" + loggingLevel + ", compatMode=" + compatModeEnabled
-                + ", augmentedAutofillEnabled=" + augmentedAutofillEnabled + "]";
+                + ", augmentedAutofillEnabled=" + augmentedAutofillEnabled
+                + ", appDisabledExpiration=" + appDisabledExpiration + "]";
     }
 
     /** @hide */
@@ -122,6 +158,11 @@
             pw.print(", whitelistedActivitiesForAugmentedAutofill=");
             pw.print(whitelistedActivitiesForAugmentedAutofill);
         }
+        pw.print(", appDisabledExpiration="); pw.print(appDisabledExpiration);
+        if (disabledActivities != null) {
+            pw.print(", disabledActivities=");
+            pw.print(disabledActivities);
+        }
     }
 
     @Override
@@ -135,6 +176,16 @@
         parcel.writeBoolean(compatModeEnabled);
         parcel.writeBoolean(augmentedAutofillEnabled);
         parcel.writeArraySet(whitelistedActivitiesForAugmentedAutofill);
+        parcel.writeLong(appDisabledExpiration);
+        final int size = disabledActivities != null ? disabledActivities.size() : 0;
+        parcel.writeInt(size);
+        if (size > 0) {
+            for (int i = 0; i < size; i++) {
+                final String key = disabledActivities.keyAt(i);
+                parcel.writeString(key);
+                parcel.writeLong(disabledActivities.get(key));
+            }
+        }
     }
 
     public static final @android.annotation.NonNull Parcelable.Creator<AutofillOptions> CREATOR =
@@ -148,6 +199,14 @@
                     options.augmentedAutofillEnabled = parcel.readBoolean();
                     options.whitelistedActivitiesForAugmentedAutofill =
                             (ArraySet<ComponentName>) parcel.readArraySet(null);
+                    options.appDisabledExpiration = parcel.readLong();
+                    final int size = parcel.readInt();
+                    if (size > 0) {
+                        options.disabledActivities = new ArrayMap<>();
+                        for (int i = 0; i < size; i++) {
+                            options.disabledActivities.put(parcel.readString(), parcel.readLong());
+                        }
+                    }
                     return options;
                 }
 
diff --git a/core/java/android/content/ContentProvider.java b/core/java/android/content/ContentProvider.java
index 4ea3726..f297c06 100644
--- a/core/java/android/content/ContentProvider.java
+++ b/core/java/android/content/ContentProvider.java
@@ -17,6 +17,7 @@
 package android.content;
 
 import static android.Manifest.permission.INTERACT_ACROSS_USERS;
+import static android.Manifest.permission.INTERACT_ACROSS_USERS_FULL;
 import static android.app.AppOpsManager.MODE_ALLOWED;
 import static android.app.AppOpsManager.MODE_DEFAULT;
 import static android.app.AppOpsManager.MODE_ERRORED;
@@ -645,9 +646,11 @@
     }
 
     boolean checkUser(int pid, int uid, Context context) {
-        return UserHandle.getUserId(uid) == context.getUserId()
-                || mSingleUser
-                || context.checkPermission(INTERACT_ACROSS_USERS, pid, uid)
+        if (UserHandle.getUserId(uid) == context.getUserId() || mSingleUser) {
+            return true;
+        }
+        return context.checkPermission(INTERACT_ACROSS_USERS, pid, uid) == PERMISSION_GRANTED
+                || context.checkPermission(INTERACT_ACROSS_USERS_FULL, pid, uid)
                 == PERMISSION_GRANTED;
     }
 
@@ -1030,10 +1033,12 @@
 
     /** @hide */
     public final void setTransportLoggingEnabled(boolean enabled) {
-        if (enabled) {
-            mTransport.mInterface = new LoggingContentInterface(getClass().getSimpleName(), this);
-        } else {
-            mTransport.mInterface = this;
+        if (mTransport != null) {
+            if (enabled) {
+                mTransport.mInterface = new LoggingContentInterface(getClass().getSimpleName(), this);
+            } else {
+                mTransport.mInterface = this;
+            }
         }
     }
 
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index bb5ced5..2c53faa 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -38,6 +38,7 @@
 import android.app.IApplicationThread;
 import android.app.IServiceConnection;
 import android.app.VrManager;
+import android.compat.IPlatformCompat;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
 import android.content.res.AssetManager;
@@ -3228,6 +3229,7 @@
             ROLE_SERVICE,
             //@hide ROLE_CONTROLLER_SERVICE,
             CAMERA_SERVICE,
+            //@hide: PLATFORM_COMPAT_SERVICE,
             PRINT_SERVICE,
             CONSUMER_IR_SERVICE,
             //@hide: TRUST_SERVICE,
@@ -4597,6 +4599,13 @@
     public static final String STATS_MANAGER = "stats";
 
     /**
+     * Use with {@link android.os.ServiceManager.getService()} to retrieve a
+     * {@link IPlatformCompat} IBinder for communicating with the platform compat service.
+     * @hide
+     */
+    public static final String PLATFORM_COMPAT_SERVICE = "platform_compat";
+
+    /**
      * Service to capture a bugreport.
      * @see #getSystemService(String)
      * @see android.os.BugreportManager
diff --git a/core/java/android/content/rollback/RollbackManager.java b/core/java/android/content/rollback/RollbackManager.java
index 73b8a48..1609f53 100644
--- a/core/java/android/content/rollback/RollbackManager.java
+++ b/core/java/android/content/rollback/RollbackManager.java
@@ -74,7 +74,10 @@
     }
 
     /**
-     * Returns a list of all currently available rollbacks.
+     * Returns a list of all currently available rollbacks. This includes ones for very recently
+     * installed packages (even if onFinished has not yet been called). As a result, packages that
+     * very recently failed to install may also be included, but those rollbacks will fail with
+     * 'rollback not available'.
      *
      * @throws SecurityException if the caller does not have appropriate permissions.
      */
diff --git a/core/java/android/gesture/GestureLibraries.java b/core/java/android/gesture/GestureLibraries.java
index 611d9ab..5e31ce6 100644
--- a/core/java/android/gesture/GestureLibraries.java
+++ b/core/java/android/gesture/GestureLibraries.java
@@ -16,14 +16,16 @@
 
 package android.gesture;
 
+import android.annotation.NonNull;
 import android.annotation.RawRes;
+import android.os.ParcelFileDescriptor;
 import android.util.Log;
 import static android.gesture.GestureConstants.*;
 import android.content.Context;
 
 import java.io.File;
+import java.io.FileDescriptor;
 import java.io.FileOutputStream;
-import java.io.FileNotFoundException;
 import java.io.IOException;
 import java.io.FileInputStream;
 import java.io.InputStream;
@@ -41,6 +43,11 @@
         return new FileGestureLibrary(path);
     }
 
+    @NonNull
+    public static GestureLibrary fromFileDescriptor(@NonNull ParcelFileDescriptor pfd) {
+        return new FileGestureLibrary(pfd.getFileDescriptor());
+    }
+
     public static GestureLibrary fromPrivateFile(Context context, String name) {
         return fromFile(context.getFileStreamPath(name));
     }
@@ -50,55 +57,83 @@
     }
 
     private static class FileGestureLibrary extends GestureLibrary {
+        // Either a file or an fd is used
         private final File mPath;
+        private final FileDescriptor mFd;
 
         public FileGestureLibrary(File path) {
             mPath = path;
+            mFd = null;
         }
 
+        public FileGestureLibrary(FileDescriptor fd) {
+            mPath = null;
+            mFd = fd;
+        }
+
+        /**
+         * <p>If this GestureLibrary was created using a FileDescriptor,
+         * this method will always return false.
+         */
         @Override
         public boolean isReadOnly() {
-            return !mPath.canWrite();
+            if (mPath != null) {
+                return !mPath.canWrite();
+            }
+            return false;
         }
 
         public boolean save() {
             if (!mStore.hasChanged()) return true;
+            boolean result = false;
 
-            final File file = mPath;
+            if (mPath != null) {
+                final File file = mPath;
 
-            final File parentFile = file.getParentFile();
-            if (!parentFile.exists()) {
-                if (!parentFile.mkdirs()) {
-                    return false;
+                final File parentFile = file.getParentFile();
+                if (!parentFile.exists()) {
+                    if (!parentFile.mkdirs()) {
+                        return false;
+                    }
+                }
+
+                try {
+                    //noinspection ResultOfMethodCallIgnored
+                    file.createNewFile();
+                    mStore.save(new FileOutputStream(file), true);
+                    result = true;
+                } catch (IOException e) {
+                    Log.d(LOG_TAG, "Could not save the gesture library in " + mPath, e);
+                }
+            } else {
+                try {
+                    mStore.save(new FileOutputStream(mFd), true);
+                    result = true;
+                } catch (IOException e) {
+                    Log.d(LOG_TAG, "Could not save the gesture library", e);
                 }
             }
-
-            boolean result = false;
-            try {
-                //noinspection ResultOfMethodCallIgnored
-                file.createNewFile();
-                mStore.save(new FileOutputStream(file), true);
-                result = true;
-            } catch (FileNotFoundException e) {
-                Log.d(LOG_TAG, "Could not save the gesture library in " + mPath, e);
-            } catch (IOException e) {
-                Log.d(LOG_TAG, "Could not save the gesture library in " + mPath, e);
-            }
-
             return result;
         }
 
         public boolean load() {
             boolean result = false;
-            final File file = mPath;
-            if (file.exists() && file.canRead()) {
+            if (mPath != null) {
+                final File file = mPath;
+                if (file.exists() && file.canRead()) {
+                    try {
+                        mStore.load(new FileInputStream(file), true);
+                        result = true;
+                    } catch (IOException e) {
+                        Log.d(LOG_TAG, "Could not load the gesture library from " + mPath, e);
+                    }
+                }
+            } else {
                 try {
-                    mStore.load(new FileInputStream(file), true);
+                    mStore.load(new FileInputStream(mFd), true);
                     result = true;
-                } catch (FileNotFoundException e) {
-                    Log.d(LOG_TAG, "Could not load the gesture library from " + mPath, e);
                 } catch (IOException e) {
-                    Log.d(LOG_TAG, "Could not load the gesture library from " + mPath, e);
+                    Log.d(LOG_TAG, "Could not load the gesture library", e);
                 }
             }
 
diff --git a/core/java/android/hardware/biometrics/BiometricPrompt.java b/core/java/android/hardware/biometrics/BiometricPrompt.java
index 1142a07..fb6b231 100644
--- a/core/java/android/hardware/biometrics/BiometricPrompt.java
+++ b/core/java/android/hardware/biometrics/BiometricPrompt.java
@@ -100,9 +100,12 @@
     /**
      * @hide
      */
-    public static final int DISMISSED_REASON_POSITIVE = 1;
+    public static final int DISMISSED_REASON_CONFIRMED = 1;
 
     /**
+     * Dialog is done animating away after user clicked on the button set via
+     * {@link BiometricPrompt.Builder#setNegativeButton(CharSequence, Executor,
+     * DialogInterface.OnClickListener)}.
      * @hide
      */
     public static final int DISMISSED_REASON_NEGATIVE = 2;
@@ -112,6 +115,25 @@
      */
     public static final int DISMISSED_REASON_USER_CANCEL = 3;
 
+    /**
+     * Authenticated, confirmation not required. Dialog animated away.
+     * @hide
+     */
+    public static final int DISMISSED_REASON_CONFIRM_NOT_REQUIRED = 4;
+
+    /**
+     * Error message shown on SystemUI. When BiometricService receives this, the UI is already
+     * gone.
+     * @hide
+     */
+    public static final int DISMISSED_REASON_ERROR = 5;
+
+    /**
+     * Dialog dismissal requested by BiometricService.
+     * @hide
+     */
+    public static final int DISMISSED_REASON_SERVER_REQUESTED = 6;
+
     private static class ButtonInfo {
         Executor executor;
         DialogInterface.OnClickListener listener;
@@ -362,7 +384,7 @@
         @Override
         public void onDialogDismissed(int reason) throws RemoteException {
             // Check the reason and invoke OnClickListener(s) if necessary
-            if (reason == DISMISSED_REASON_POSITIVE) {
+            if (reason == DISMISSED_REASON_CONFIRMED) {
                 mPositiveButtonInfo.executor.execute(() -> {
                     mPositiveButtonInfo.listener.onClick(null, DialogInterface.BUTTON_POSITIVE);
                 });
diff --git a/core/java/android/hardware/biometrics/IBiometricServiceReceiverInternal.aidl b/core/java/android/hardware/biometrics/IBiometricServiceReceiverInternal.aidl
index 180daaf..ca6114e 100644
--- a/core/java/android/hardware/biometrics/IBiometricServiceReceiverInternal.aidl
+++ b/core/java/android/hardware/biometrics/IBiometricServiceReceiverInternal.aidl
@@ -27,8 +27,8 @@
     // Notify BiometricService that authentication was successful. If user confirmation is required,
     // the auth token must be submitted into KeyStore.
     void onAuthenticationSucceeded(boolean requireConfirmation, in byte[] token);
-    // Notify BiometricService that an error has occurred.
-    void onAuthenticationFailed(int cookie, boolean requireConfirmation);
+    // Notify BiometricService authentication was rejected.
+    void onAuthenticationFailed();
     // Notify BiometricService than an error has occured. Forward to the correct receiver depending
     // on the cookie.
     void onError(int cookie, int error, String message);
diff --git a/core/java/android/hardware/camera2/CameraCharacteristics.java b/core/java/android/hardware/camera2/CameraCharacteristics.java
index 1b31792..fbcc785 100644
--- a/core/java/android/hardware/camera2/CameraCharacteristics.java
+++ b/core/java/android/hardware/camera2/CameraCharacteristics.java
@@ -1955,6 +1955,7 @@
      *   <li>{@link #REQUEST_AVAILABLE_CAPABILITIES_LOGICAL_MULTI_CAMERA LOGICAL_MULTI_CAMERA}</li>
      *   <li>{@link #REQUEST_AVAILABLE_CAPABILITIES_MONOCHROME MONOCHROME}</li>
      *   <li>{@link #REQUEST_AVAILABLE_CAPABILITIES_SECURE_IMAGE_DATA SECURE_IMAGE_DATA}</li>
+     *   <li>{@link #REQUEST_AVAILABLE_CAPABILITIES_SYSTEM_CAMERA SYSTEM_CAMERA}</li>
      * </ul></p>
      * <p>This key is available on all devices.</p>
      *
@@ -1973,6 +1974,7 @@
      * @see #REQUEST_AVAILABLE_CAPABILITIES_LOGICAL_MULTI_CAMERA
      * @see #REQUEST_AVAILABLE_CAPABILITIES_MONOCHROME
      * @see #REQUEST_AVAILABLE_CAPABILITIES_SECURE_IMAGE_DATA
+     * @see #REQUEST_AVAILABLE_CAPABILITIES_SYSTEM_CAMERA
      */
     @PublicKey
     @NonNull
diff --git a/core/java/android/hardware/camera2/CameraMetadata.java b/core/java/android/hardware/camera2/CameraMetadata.java
index a0170da..2fa6125 100644
--- a/core/java/android/hardware/camera2/CameraMetadata.java
+++ b/core/java/android/hardware/camera2/CameraMetadata.java
@@ -996,6 +996,14 @@
      */
     public static final int REQUEST_AVAILABLE_CAPABILITIES_SECURE_IMAGE_DATA = 13;
 
+    /**
+     * <p>The camera device is only accessible by Android's system components and privileged
+     * applications. Processes need to have the android.permission.SYSTEM_CAMERA in
+     * addition to android.permission.CAMERA in order to connect to this camera device.</p>
+     * @see CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES
+     */
+    public static final int REQUEST_AVAILABLE_CAPABILITIES_SYSTEM_CAMERA = 14;
+
     //
     // Enumeration values for CameraCharacteristics#SCALER_CROPPING_TYPE
     //
diff --git a/core/java/android/hardware/radio/TunerCallbackAdapter.java b/core/java/android/hardware/radio/TunerCallbackAdapter.java
index 0fb93e5..beff0f7 100644
--- a/core/java/android/hardware/radio/TunerCallbackAdapter.java
+++ b/core/java/android/hardware/radio/TunerCallbackAdapter.java
@@ -211,10 +211,12 @@
 
     @Override
     public void onProgramListUpdated(ProgramList.Chunk chunk) {
-        synchronized (mLock) {
-            if (mProgramList == null) return;
-            mProgramList.apply(Objects.requireNonNull(chunk));
-        }
+        mHandler.post(() -> {
+            synchronized (mLock) {
+                if (mProgramList == null) return;
+                mProgramList.apply(Objects.requireNonNull(chunk));
+            }
+        });
     }
 
     @Override
diff --git a/core/java/android/inputmethodservice/SoftInputWindow.java b/core/java/android/inputmethodservice/SoftInputWindow.java
index 0513fee..356b344 100644
--- a/core/java/android/inputmethodservice/SoftInputWindow.java
+++ b/core/java/android/inputmethodservice/SoftInputWindow.java
@@ -21,6 +21,7 @@
 import android.annotation.IntDef;
 import android.app.Dialog;
 import android.content.Context;
+import android.content.pm.PackageManager;
 import android.graphics.Rect;
 import android.os.Debug;
 import android.os.IBinder;
@@ -50,6 +51,7 @@
     final int mWindowType;
     final int mGravity;
     final boolean mTakesFocus;
+    final boolean mAutomotiveHideNavBarForKeyboard;
     private final Rect mBounds = new Rect();
 
     @Retention(SOURCE)
@@ -134,6 +136,8 @@
         mWindowType = windowType;
         mGravity = gravity;
         mTakesFocus = takesFocus;
+        mAutomotiveHideNavBarForKeyboard = context.getResources().getBoolean(
+                com.android.internal.R.bool.config_automotiveHideNavBarForKeyboard);
         initDockWindow();
     }
 
@@ -247,6 +251,11 @@
             windowModFlags |= WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
         }
 
+        if (isAutomotive() && mAutomotiveHideNavBarForKeyboard) {
+            windowSetFlags |= WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS;
+            windowModFlags |= WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS;
+        }
+
         getWindow().setFlags(windowSetFlags, windowModFlags);
     }
 
@@ -338,6 +347,10 @@
         mWindowState = newState;
     }
 
+    private boolean isAutomotive() {
+        return getContext().getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE);
+    }
+
     private static String stateToString(@SoftInputWindowState int state) {
         switch (state) {
             case SoftInputWindowState.TOKEN_PENDING:
diff --git a/core/java/android/os/image/DynamicSystemManager.java b/core/java/android/os/image/DynamicSystemManager.java
index e4f88c5..77fd946 100644
--- a/core/java/android/os/image/DynamicSystemManager.java
+++ b/core/java/android/os/image/DynamicSystemManager.java
@@ -20,6 +20,7 @@
 import android.annotation.SystemService;
 import android.content.Context;
 import android.gsi.GsiProgress;
+import android.os.ParcelFileDescriptor;
 import android.os.RemoteException;
 
 /**
@@ -52,22 +53,39 @@
     /** The DynamicSystemManager.Session represents a started session for the installation. */
     public class Session {
         private Session() {}
+
         /**
-         * Write a chunk of the DynamicSystem system image
+         * Set the file descriptor that points to a ashmem which will be used
+         * to fetch data during the submitFromAshmem.
          *
-         * @return {@code true} if the call succeeds. {@code false} if there is any native runtime
-         *     error.
+         * @param ashmem fd that points to a ashmem
+         * @param size size of the ashmem file
          */
         @RequiresPermission(android.Manifest.permission.MANAGE_DYNAMIC_SYSTEM)
-        public boolean write(byte[] buf) {
+        public boolean setAshmem(ParcelFileDescriptor ashmem, long size) {
             try {
-                return mService.write(buf);
+                return mService.setAshmem(ashmem, size);
             } catch (RemoteException e) {
                 throw new RuntimeException(e.toString());
             }
         }
 
         /**
+         * Submit bytes to the DSU partition from the ashmem previously set with
+         * setAshmem.
+         *
+         * @param size Number of bytes
+         * @return true on success, false otherwise.
+         */
+        @RequiresPermission(android.Manifest.permission.MANAGE_DYNAMIC_SYSTEM)
+        public boolean submitFromAshmem(int size) {
+            try {
+                return mService.submitFromAshmem(size);
+            } catch (RemoteException e) {
+                throw new RuntimeException(e.toString());
+            }
+        }
+        /**
          * Finish write and make device to boot into the it after reboot.
          *
          * @return {@code true} if the call succeeds. {@code false} if there is any native runtime
diff --git a/core/java/android/os/image/IDynamicSystemService.aidl b/core/java/android/os/image/IDynamicSystemService.aidl
index 2f4ab2d..a6de170 100644
--- a/core/java/android/os/image/IDynamicSystemService.aidl
+++ b/core/java/android/os/image/IDynamicSystemService.aidl
@@ -79,10 +79,20 @@
     boolean setEnable(boolean enable, boolean oneShot);
 
     /**
-     * Write a chunk of the DynamicSystem system image
+     * Set the file descriptor that points to a ashmem which will be used
+     * to fetch data during the submitFromAshmem.
      *
-     * @return true if the call succeeds
+     * @param fd            fd that points to a ashmem
+     * @param size          size of the ashmem file
      */
-    boolean write(in byte[] buf);
+    boolean setAshmem(in ParcelFileDescriptor fd, long size);
 
+    /**
+     * Submit bytes to the DSU partition from the ashmem previously set with
+     * setAshmem.
+     *
+     * @param bytes         number of bytes that can be read from stream.
+     * @return              true on success, false otherwise.
+     */
+    boolean submitFromAshmem(long bytes);
 }
diff --git a/core/java/android/provider/DeviceConfig.java b/core/java/android/provider/DeviceConfig.java
index 4f7c8c5..bc03e51 100644
--- a/core/java/android/provider/DeviceConfig.java
+++ b/core/java/android/provider/DeviceConfig.java
@@ -706,16 +706,11 @@
         synchronized (sLock) {
             for (int i = 0; i < sListeners.size(); i++) {
                 if (namespace.equals(sListeners.valueAt(i).first)) {
-                    final int j = i;
-                    sListeners.valueAt(i).second.execute(new Runnable() {
-                        @Override
-                        public void run() {
-                            Map<String, String> propertyMap = new HashMap(1);
-                            propertyMap.put(name, value);
-                            sListeners.keyAt(j)
-                                    .onPropertiesChanged(new Properties(namespace, propertyMap));
-                        }
-
+                    final OnPropertiesChangedListener listener = sListeners.keyAt(i);
+                    sListeners.valueAt(i).second.execute(() -> {
+                        Map<String, String> propertyMap = new ArrayMap<>(1);
+                        propertyMap.put(name, value);
+                        listener.onPropertiesChanged(new Properties(namespace, propertyMap));
                     });
                 }
             }
diff --git a/core/java/android/provider/MediaStore.java b/core/java/android/provider/MediaStore.java
index 2299aad..a959913f 100644
--- a/core/java/android/provider/MediaStore.java
+++ b/core/java/android/provider/MediaStore.java
@@ -3592,10 +3592,23 @@
     }
 
     /** @hide */
+    public static Uri scanFile(ContentProviderClient client, File file) {
+        return scan(client, SCAN_FILE_CALL, file, false);
+    }
+
+    /** @hide */
     private static Uri scan(Context context, String method, File file,
             boolean originatedFromShell) {
         final ContentResolver resolver = context.getContentResolver();
         try (ContentProviderClient client = resolver.acquireContentProviderClient(AUTHORITY)) {
+            return scan(client, method, file, originatedFromShell);
+        }
+    }
+
+    /** @hide */
+    private static Uri scan(ContentProviderClient client, String method, File file,
+            boolean originatedFromShell) {
+        try {
             final Bundle in = new Bundle();
             in.putParcelable(Intent.EXTRA_STREAM, Uri.fromFile(file));
             in.putBoolean(EXTRA_ORIGINATED_FROM_SHELL, originatedFromShell);
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 9abb64f..e486235 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -16,16 +16,19 @@
 
 package android.provider;
 
-import static android.provider.SettingsValidators.ANY_INTEGER_VALIDATOR;
-import static android.provider.SettingsValidators.ANY_STRING_VALIDATOR;
-import static android.provider.SettingsValidators.BOOLEAN_VALIDATOR;
-import static android.provider.SettingsValidators.COMPONENT_NAME_VALIDATOR;
-import static android.provider.SettingsValidators.LENIENT_IP_ADDRESS_VALIDATOR;
-import static android.provider.SettingsValidators.LOCALE_VALIDATOR;
-import static android.provider.SettingsValidators.NON_NEGATIVE_INTEGER_VALIDATOR;
-import static android.provider.SettingsValidators.NULLABLE_COMPONENT_NAME_VALIDATOR;
-import static android.provider.SettingsValidators.PACKAGE_NAME_VALIDATOR;
-import static android.provider.SettingsValidators.URI_VALIDATOR;
+import static android.provider.settings.validators.SettingsValidators.ANY_INTEGER_VALIDATOR;
+import static android.provider.settings.validators.SettingsValidators.ANY_STRING_VALIDATOR;
+import static android.provider.settings.validators.SettingsValidators.BOOLEAN_VALIDATOR;
+import static android.provider.settings.validators.SettingsValidators.COMPONENT_NAME_VALIDATOR;
+import static android.provider.settings.validators.SettingsValidators.JSON_OBJECT_VALIDATOR;
+import static android.provider.settings.validators.SettingsValidators.LENIENT_IP_ADDRESS_VALIDATOR;
+import static android.provider.settings.validators.SettingsValidators.LOCALE_VALIDATOR;
+import static android.provider.settings.validators.SettingsValidators.NON_NEGATIVE_INTEGER_VALIDATOR;
+import static android.provider.settings.validators.SettingsValidators.NULLABLE_COMPONENT_NAME_VALIDATOR;
+import static android.provider.settings.validators.SettingsValidators.PACKAGE_NAME_VALIDATOR;
+import static android.provider.settings.validators.SettingsValidators.TILE_LIST_VALIDATOR;
+import static android.provider.settings.validators.SettingsValidators.TTS_LIST_VALIDATOR;
+import static android.provider.settings.validators.SettingsValidators.URI_VALIDATOR;
 
 import android.Manifest;
 import android.annotation.IntDef;
@@ -80,7 +83,12 @@
 import android.os.ResultReceiver;
 import android.os.ServiceManager;
 import android.os.UserHandle;
-import android.provider.SettingsValidators.Validator;
+import android.provider.settings.validators.ComponentNameListValidator;
+import android.provider.settings.validators.DiscreteValueValidator;
+import android.provider.settings.validators.InclusiveFloatRangeValidator;
+import android.provider.settings.validators.InclusiveIntegerRangeValidator;
+import android.provider.settings.validators.PackageNameListValidator;
+import android.provider.settings.validators.Validator;
 import android.speech.tts.TextToSpeech;
 import android.telephony.SubscriptionManager;
 import android.text.TextUtils;
@@ -3149,7 +3157,7 @@
         public static final String END_BUTTON_BEHAVIOR = "end_button_behavior";
 
         private static final Validator END_BUTTON_BEHAVIOR_VALIDATOR =
-                new SettingsValidators.InclusiveIntegerRangeValidator(0, 3);
+                new InclusiveIntegerRangeValidator(0, 3);
 
         /**
          * END_BUTTON_BEHAVIOR value for "go home".
@@ -3351,7 +3359,7 @@
             "bluetooth_discoverability";
 
         private static final Validator BLUETOOTH_DISCOVERABILITY_VALIDATOR =
-                new SettingsValidators.InclusiveIntegerRangeValidator(0, 2);
+                new InclusiveIntegerRangeValidator(0, 2);
 
         /**
          * Bluetooth discoverability timeout.  If this value is nonzero, then
@@ -3495,7 +3503,7 @@
         public static final String PEAK_REFRESH_RATE = "peak_refresh_rate";
 
         private static final Validator PEAK_REFRESH_RATE_VALIDATOR =
-                new SettingsValidators.InclusiveFloatRangeValidator(24f, Float.MAX_VALUE);
+                new InclusiveFloatRangeValidator(24f, Float.MAX_VALUE);
 
         /**
          * The amount of time in milliseconds before the device goes to sleep or begins
@@ -3524,7 +3532,7 @@
         public static final String SCREEN_BRIGHTNESS_FOR_VR = "screen_brightness_for_vr";
 
         private static final Validator SCREEN_BRIGHTNESS_FOR_VR_VALIDATOR =
-                new SettingsValidators.InclusiveIntegerRangeValidator(0, 255);
+                new InclusiveIntegerRangeValidator(0, 255);
 
         /**
          * Control whether to enable automatic brightness mode.
@@ -3542,7 +3550,7 @@
         public static final String SCREEN_AUTO_BRIGHTNESS_ADJ = "screen_auto_brightness_adj";
 
         private static final Validator SCREEN_AUTO_BRIGHTNESS_ADJ_VALIDATOR =
-                new SettingsValidators.InclusiveFloatRangeValidator(-1, 1);
+                new InclusiveFloatRangeValidator(-1, 1);
 
         /**
          * SCREEN_BRIGHTNESS_MODE value for manual mode.
@@ -3676,7 +3684,7 @@
                 "haptic_feedback_intensity";
 
         private static final Validator VIBRATION_INTENSITY_VALIDATOR =
-                new SettingsValidators.InclusiveIntegerRangeValidator(0, 3);
+                new InclusiveIntegerRangeValidator(0, 3);
 
         /**
          * Ringer volume. This is used internally, changing this value will not
@@ -3766,7 +3774,7 @@
         public static final String MASTER_BALANCE = "master_balance";
 
         private static final Validator MASTER_BALANCE_VALIDATOR =
-                new SettingsValidators.InclusiveFloatRangeValidator(-1.f, 1.f);
+                new InclusiveFloatRangeValidator(-1.f, 1.f);
 
         /**
          * Whether the notifications should use the ring volume (value of 1) or
@@ -4004,7 +4012,7 @@
 
         /** @hide */
         public static final Validator TIME_12_24_VALIDATOR =
-                new SettingsValidators.DiscreteValueValidator(new String[] {"12", "24", null});
+                new DiscreteValueValidator(new String[] {"12", "24", null});
 
         /**
          * Date format string
@@ -4090,7 +4098,7 @@
 
         /** @hide */
         public static final Validator USER_ROTATION_VALIDATOR =
-                new SettingsValidators.InclusiveIntegerRangeValidator(0, 3);
+                new InclusiveIntegerRangeValidator(0, 3);
 
         /**
          * Control whether the rotation lock toggle in the System UI should be hidden.
@@ -4179,7 +4187,7 @@
 
         /** @hide */
         public static final Validator TTY_MODE_VALIDATOR =
-                new SettingsValidators.InclusiveIntegerRangeValidator(0, 3);
+                new InclusiveIntegerRangeValidator(0, 3);
 
         /**
          * Whether the sounds effects (key clicks, lid open ...) are enabled. The value is
@@ -4381,7 +4389,7 @@
 
         /** @hide */
         public static final Validator SIP_CALL_OPTIONS_VALIDATOR =
-                new SettingsValidators.DiscreteValueValidator(
+                new DiscreteValueValidator(
                         new String[] {"SIP_ALWAYS", "SIP_ADDRESS_ONLY"});
 
         /**
@@ -4428,7 +4436,7 @@
 
         /** @hide */
         public static final Validator POINTER_SPEED_VALIDATOR =
-                new SettingsValidators.InclusiveFloatRangeValidator(-7, 7);
+                new InclusiveFloatRangeValidator(-7, 7);
 
         /**
          * Whether lock-to-app will be triggered by long-press on recents.
@@ -6352,7 +6360,7 @@
         public static final String LOCK_SCREEN_CUSTOM_CLOCK_FACE = "lock_screen_custom_clock_face";
 
         private static final Validator LOCK_SCREEN_CUSTOM_CLOCK_FACE_VALIDATOR =
-                SettingsValidators.JSON_OBJECT_VALIDATOR;
+                JSON_OBJECT_VALIDATOR;
 
         /**
          * Indicates which clock face to show on lock screen and AOD while docked.
@@ -6509,7 +6517,7 @@
             "enabled_accessibility_services";
 
         private static final Validator ENABLED_ACCESSIBILITY_SERVICES_VALIDATOR =
-                new SettingsValidators.ComponentNameListValidator(":");
+                new ComponentNameListValidator(":");
 
         /**
          * List of the accessibility services to which the user has granted
@@ -6521,7 +6529,7 @@
             "touch_exploration_granted_accessibility_services";
 
         private static final Validator TOUCH_EXPLORATION_GRANTED_ACCESSIBILITY_SERVICES_VALIDATOR =
-                new SettingsValidators.ComponentNameListValidator(":");
+                new ComponentNameListValidator(":");
 
         /**
          * Whether the Global Actions Panel is enabled.
@@ -6696,7 +6704,7 @@
                 "accessibility_display_magnification_scale";
 
         private static final Validator ACCESSIBILITY_DISPLAY_MAGNIFICATION_SCALE_VALIDATOR =
-                new SettingsValidators.InclusiveFloatRangeValidator(1.0f, Float.MAX_VALUE);
+                new InclusiveFloatRangeValidator(1.0f, Float.MAX_VALUE);
 
         /**
          * Unused mangnification setting
@@ -6780,7 +6788,7 @@
                 "accessibility_captioning_preset";
 
         private static final Validator ACCESSIBILITY_CAPTIONING_PRESET_VALIDATOR =
-                new SettingsValidators.DiscreteValueValidator(new String[]{"-1", "0", "1", "2",
+                new DiscreteValueValidator(new String[]{"-1", "0", "1", "2",
                         "3", "4"});
 
         /**
@@ -6824,7 +6832,7 @@
                 "accessibility_captioning_edge_type";
 
         private static final Validator ACCESSIBILITY_CAPTIONING_EDGE_TYPE_VALIDATOR =
-                new SettingsValidators.DiscreteValueValidator(new String[]{"0", "1", "2"});
+                new DiscreteValueValidator(new String[]{"0", "1", "2"});
 
         /**
          * Integer property that specifes the edge color for captions as a
@@ -6870,7 +6878,7 @@
                 "accessibility_captioning_typeface";
 
         private static final Validator ACCESSIBILITY_CAPTIONING_TYPEFACE_VALIDATOR =
-                new SettingsValidators.DiscreteValueValidator(new String[]{"DEFAULT",
+                new DiscreteValueValidator(new String[]{"DEFAULT",
                         "MONOSPACE", "SANS_SERIF", "SERIF"});
 
         /**
@@ -6882,7 +6890,7 @@
                 "accessibility_captioning_font_scale";
 
         private static final Validator ACCESSIBILITY_CAPTIONING_FONT_SCALE_VALIDATOR =
-                new SettingsValidators.InclusiveFloatRangeValidator(0.5f, 2.0f);
+                new InclusiveFloatRangeValidator(0.5f, 2.0f);
 
         /**
          * Setting that specifies whether display color inversion is enabled.
@@ -6923,7 +6931,7 @@
                 "accessibility_display_daltonizer";
 
         private static final Validator ACCESSIBILITY_DISPLAY_DALTONIZER_VALIDATOR =
-                new SettingsValidators.DiscreteValueValidator(
+                new DiscreteValueValidator(
                         new String[] {"-1", "0", "11", "12", "13"});
 
         /**
@@ -7112,8 +7120,7 @@
          */
         public static final String TTS_DEFAULT_LOCALE = "tts_default_locale";
 
-        private static final Validator TTS_DEFAULT_LOCALE_VALIDATOR =
-                new SettingsValidators.TTSListValidator();
+        private static final Validator TTS_DEFAULT_LOCALE_VALIDATOR = TTS_LIST_VALIDATOR;
 
         /**
          * Space delimited list of plugin packages that are enabled.
@@ -7121,7 +7128,7 @@
         public static final String TTS_ENABLED_PLUGINS = "tts_enabled_plugins";
 
         private static final Validator TTS_ENABLED_PLUGINS_VALIDATOR =
-                new SettingsValidators.PackageNameListValidator(" ");
+                new PackageNameListValidator(" ");
 
         /**
          * @deprecated Use {@link android.provider.Settings.Global#WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON}
@@ -7315,7 +7322,7 @@
                 "preferred_tty_mode";
 
         private static final Validator PREFERRED_TTY_MODE_VALIDATOR =
-                new SettingsValidators.DiscreteValueValidator(new String[]{"0", "1", "2", "3"});
+                new DiscreteValueValidator(new String[]{"0", "1", "2", "3"});
 
         /**
          * Whether the enhanced voice privacy mode is enabled.
@@ -7630,7 +7637,7 @@
         public static final String INCALL_POWER_BUTTON_BEHAVIOR = "incall_power_button_behavior";
 
         private static final Validator INCALL_POWER_BUTTON_BEHAVIOR_VALIDATOR =
-                new SettingsValidators.DiscreteValueValidator(new String[]{"1", "2"});
+                new DiscreteValueValidator(new String[]{"1", "2"});
 
         /**
          * INCALL_POWER_BUTTON_BEHAVIOR value for "turn off screen".
@@ -7851,7 +7858,7 @@
         public static final String UI_NIGHT_MODE = "ui_night_mode";
 
         private static final Validator UI_NIGHT_MODE_VALIDATOR =
-                new SettingsValidators.InclusiveIntegerRangeValidator(0, 2);
+                new InclusiveIntegerRangeValidator(0, 2);
 
         /**
          * Whether screensavers are enabled.
@@ -7871,7 +7878,7 @@
         public static final String SCREENSAVER_COMPONENTS = "screensaver_components";
 
         private static final Validator SCREENSAVER_COMPONENTS_VALIDATOR =
-                new SettingsValidators.ComponentNameListValidator(",");
+                new ComponentNameListValidator(",");
 
         /**
          * If screensavers are enabled, whether the screensaver should be automatically launched
@@ -8023,7 +8030,7 @@
                 "enabled_notification_assistant";
 
         private static final Validator ENABLED_NOTIFICATION_ASSISTANT_VALIDATOR =
-                new SettingsValidators.ComponentNameListValidator(":");
+                new ComponentNameListValidator(":");
 
         /**
          * Read only list of the service components that the current user has explicitly allowed to
@@ -8038,7 +8045,7 @@
         public static final String ENABLED_NOTIFICATION_LISTENERS = "enabled_notification_listeners";
 
         private static final Validator ENABLED_NOTIFICATION_LISTENERS_VALIDATOR =
-                new SettingsValidators.ComponentNameListValidator(":");
+                new ComponentNameListValidator(":");
 
         /**
          * Read only list of the packages that the current user has explicitly allowed to
@@ -8053,7 +8060,7 @@
                 "enabled_notification_policy_access_packages";
 
         private static final Validator ENABLED_NOTIFICATION_POLICY_ACCESS_PACKAGES_VALIDATOR =
-                new SettingsValidators.PackageNameListValidator(":");
+                new PackageNameListValidator(":");
 
         /**
          * Defines whether managed profile ringtones should be synced from it's parent profile
@@ -8350,16 +8357,6 @@
                 BOOLEAN_VALIDATOR;
 
         /**
-         * Whether or not the face unlock education screen has been shown to the user.
-         * @hide
-         */
-        public static final String FACE_UNLOCK_EDUCATION_INFO_DISPLAYED =
-                "face_unlock_education_info_displayed";
-
-        private static final Validator FACE_UNLOCK_EDUCATION_INFO_DISPLAYED_VALIDATOR =
-                BOOLEAN_VALIDATOR;
-
-        /**
          * Whether or not debugging is enabled.
          * @hide
          */
@@ -8446,7 +8443,7 @@
         public static final String NIGHT_DISPLAY_AUTO_MODE = "night_display_auto_mode";
 
         private static final Validator NIGHT_DISPLAY_AUTO_MODE_VALIDATOR =
-                new SettingsValidators.InclusiveIntegerRangeValidator(0, 2);
+                new InclusiveIntegerRangeValidator(0, 2);
 
         /**
          * Control the color temperature of Night Display, represented in Kelvin.
@@ -8507,7 +8504,7 @@
         public static final String ENABLED_VR_LISTENERS = "enabled_vr_listeners";
 
         private static final Validator ENABLED_VR_LISTENERS_VALIDATOR =
-                new SettingsValidators.ComponentNameListValidator(":");
+                new ComponentNameListValidator(":");
 
         /**
          * Behavior of the display while in VR mode.
@@ -8519,7 +8516,7 @@
         public static final String VR_DISPLAY_MODE = "vr_display_mode";
 
         private static final Validator VR_DISPLAY_MODE_VALIDATOR =
-                new SettingsValidators.DiscreteValueValidator(new String[]{"0", "1"});
+                new DiscreteValueValidator(new String[]{"0", "1"});
 
         /**
          * Lower the display persistence while the system is in VR mode.
@@ -8638,8 +8635,7 @@
          */
         public static final String QS_TILES = "sysui_qs_tiles";
 
-        private static final Validator QS_TILES_VALIDATOR =
-                new SettingsValidators.TileListValidator();
+        private static final Validator QS_TILES_VALIDATOR = TILE_LIST_VALIDATOR;
 
         /**
          * Specifies whether the web action API is enabled.
@@ -8705,8 +8701,7 @@
          */
         public static final String QS_AUTO_ADDED_TILES = "qs_auto_tiles";
 
-        private static final Validator QS_AUTO_ADDED_TILES_VALIDATOR =
-                new SettingsValidators.TileListValidator();
+        private static final Validator QS_AUTO_ADDED_TILES_VALIDATOR = TILE_LIST_VALIDATOR;
 
         /**
          * Whether the Lockdown button should be shown in the power menu.
@@ -8867,7 +8862,7 @@
                 "theme_customization_overlay_packages";
 
         private static final Validator THEME_CUSTOMIZATION_OVERLAY_PACKAGES_VALIDATOR =
-                SettingsValidators.JSON_OBJECT_VALIDATOR;
+                JSON_OBJECT_VALIDATOR;
 
         /**
          * Navigation bar mode.
@@ -8879,7 +8874,7 @@
         public static final String NAVIGATION_MODE =
                 "navigation_mode";
         private static final Validator NAVIGATION_MODE_VALIDATOR =
-                new SettingsValidators.DiscreteValueValidator(new String[] {"0", "1", "2"});
+                new DiscreteValueValidator(new String[] {"0", "1", "2"});
 
         /**
          * Controls whether aware is enabled.
@@ -9182,8 +9177,6 @@
             VALIDATORS.put(FACE_UNLOCK_APP_ENABLED, FACE_UNLOCK_APP_ENABLED_VALIDATOR);
             VALIDATORS.put(FACE_UNLOCK_ALWAYS_REQUIRE_CONFIRMATION,
                     FACE_UNLOCK_ALWAYS_REQUIRE_CONFIRMATION_VALIDATOR);
-            VALIDATORS.put(FACE_UNLOCK_EDUCATION_INFO_DISPLAYED,
-                    FACE_UNLOCK_EDUCATION_INFO_DISPLAYED_VALIDATOR);
             VALIDATORS.put(ASSIST_GESTURE_ENABLED, ASSIST_GESTURE_ENABLED_VALIDATOR);
             VALIDATORS.put(ASSIST_GESTURE_SILENCE_ALERTS_ENABLED,
                     ASSIST_GESTURE_SILENCE_ALERTS_ENABLED_VALIDATOR);
@@ -10583,22 +10576,6 @@
                BOOLEAN_VALIDATOR;
 
        /**
-        * Whether to notify the user of carrier networks.
-        * <p>
-        * If not connected and the scan results have a carrier network, we will
-        * put this notification up. If we attempt to connect to a network or
-        * the carrier network(s) disappear, we remove the notification. When we
-        * show the notification, we will not show it again for
-        * {@link android.provider.Settings.Global#WIFI_NETWORKS_AVAILABLE_REPEAT_DELAY} time.
-        * @hide
-        */
-       public static final String WIFI_CARRIER_NETWORKS_AVAILABLE_NOTIFICATION_ON =
-               "wifi_carrier_networks_available_notification_on";
-
-       private static final Validator WIFI_CARRIER_NETWORKS_AVAILABLE_NOTIFICATION_ON_VALIDATOR =
-               BOOLEAN_VALIDATOR;
-
-       /**
         * {@hide}
         */
        public static final String WIMAX_NETWORKS_AVAILABLE_NOTIFICATION_ON =
@@ -10733,7 +10710,7 @@
                 "network_recommendations_enabled";
 
         private static final Validator NETWORK_RECOMMENDATIONS_ENABLED_VALIDATOR =
-                new SettingsValidators.DiscreteValueValidator(new String[] {"-1", "0", "1"});
+                new DiscreteValueValidator(new String[] {"-1", "0", "1"});
 
         /**
          * Which package name to use for network recommendations. If null, network recommendations
@@ -11058,12 +11035,6 @@
        public static final String WIFI_P2P_DEVICE_NAME = "wifi_p2p_device_name";
 
        /**
-        * The min time between wifi disable and wifi enable
-        * @hide
-        */
-       public static final String WIFI_REENABLE_DELAY_MS = "wifi_reenable_delay";
-
-       /**
         * Timeout for ephemeral networks when all known BSSIDs go out of range. We will disconnect
         * from an ephemeral network if there is no BSSID for that network with a non-null score that
         * has been seen in this time period.
@@ -12590,7 +12561,7 @@
         public static final String EMERGENCY_TONE = "emergency_tone";
 
         private static final Validator EMERGENCY_TONE_VALIDATOR =
-                new SettingsValidators.DiscreteValueValidator(new String[] {"0", "1", "2"});
+                new DiscreteValueValidator(new String[] {"0", "1", "2"});
 
         /**
          * CDMA only settings
@@ -12620,7 +12591,7 @@
                 "enable_automatic_system_server_heap_dumps";
 
         private static final Validator ENABLE_AUTOMATIC_SYSTEM_SERVER_HEAP_DUMPS_VALIDATOR =
-                new SettingsValidators.DiscreteValueValidator(new String[] {"0", "1"});
+                new DiscreteValueValidator(new String[] {"0", "1"});
 
         /**
          * See RIL_PreferredNetworkType in ril.h
@@ -12814,7 +12785,7 @@
                 "low_power_sticky_auto_disable_level";
 
         private static final Validator LOW_POWER_MODE_STICKY_AUTO_DISABLE_LEVEL_VALIDATOR =
-                new SettingsValidators.InclusiveIntegerRangeValidator(0, 100);
+                new InclusiveIntegerRangeValidator(0, 100);
 
         /**
          * Whether sticky battery saver should be deactivated once the battery level has reached the
@@ -12826,7 +12797,7 @@
                 "low_power_sticky_auto_disable_enabled";
 
         private static final Validator LOW_POWER_MODE_STICKY_AUTO_DISABLE_ENABLED_VALIDATOR =
-                new SettingsValidators.DiscreteValueValidator(new String[] {"0", "1"});
+                new DiscreteValueValidator(new String[] {"0", "1"});
 
         /**
          * Battery level [1-100] at which low power mode automatically turns on.
@@ -12841,7 +12812,7 @@
         public static final String LOW_POWER_MODE_TRIGGER_LEVEL = "low_power_trigger_level";
 
         private static final Validator LOW_POWER_MODE_TRIGGER_LEVEL_VALIDATOR =
-                new SettingsValidators.InclusiveIntegerRangeValidator(0, 100);
+                new InclusiveIntegerRangeValidator(0, 100);
 
         /**
          * Whether battery saver is currently set to trigger based on percentage, dynamic power
@@ -12854,7 +12825,7 @@
         public static final String AUTOMATIC_POWER_SAVE_MODE = "automatic_power_save_mode";
 
         private static final Validator AUTOMATIC_POWER_SAVE_MODE_VALIDATOR =
-                new SettingsValidators.DiscreteValueValidator(new String[] {"0", "1"});
+                new DiscreteValueValidator(new String[] {"0", "1"});
 
         /**
          * The setting that backs the disable threshold for the setPowerSavingsWarning api in
@@ -12867,7 +12838,7 @@
         public static final String DYNAMIC_POWER_SAVINGS_DISABLE_THRESHOLD =
                 "dynamic_power_savings_disable_threshold";
         private static final Validator DYNAMIC_POWER_SAVINGS_VALIDATOR =
-                new SettingsValidators.InclusiveIntegerRangeValidator(0, 100);
+                new InclusiveIntegerRangeValidator(0, 100);
 
         /**
          * The setting which backs the setDynamicPowerSaveHint api in PowerManager.
@@ -13017,7 +12988,7 @@
         public static final String ENCODED_SURROUND_OUTPUT = "encoded_surround_output";
 
         private static final Validator ENCODED_SURROUND_OUTPUT_VALIDATOR =
-                new SettingsValidators.DiscreteValueValidator(new String[] {"0", "1", "2", "3"});
+                new DiscreteValueValidator(new String[] {"0", "1", "2", "3"});
 
         /**
          * Surround sounds formats that are enabled when ENCODED_SURROUND_OUTPUT is set to
@@ -13768,7 +13739,7 @@
         public static final String POWER_BUTTON_LONG_PRESS =
                 "power_button_long_press";
         private static final Validator POWER_BUTTON_LONG_PRESS_VALIDATOR =
-                new SettingsValidators.InclusiveIntegerRangeValidator(0, 5);
+                new InclusiveIntegerRangeValidator(0, 5);
 
         /**
          * Overrides internal R.integer.config_veryLongPressOnPowerBehavior.
@@ -13779,7 +13750,7 @@
         public static final String POWER_BUTTON_VERY_LONG_PRESS =
                 "power_button_very_long_press";
         private static final Validator POWER_BUTTON_VERY_LONG_PRESS_VALIDATOR =
-                new SettingsValidators.InclusiveIntegerRangeValidator(0, 1);
+                new InclusiveIntegerRangeValidator(0, 1);
 
         /**
          * Settings to backup. This is here so that it's in the same place as the settings
@@ -13814,7 +13785,6 @@
             NETWORK_RECOMMENDATIONS_ENABLED,
             WIFI_WAKEUP_ENABLED,
             WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON,
-            WIFI_CARRIER_NETWORKS_AVAILABLE_NOTIFICATION_ON,
             USE_OPEN_WIFI_PACKAGE,
             WIFI_WATCHDOG_POOR_NETWORK_TEST_ENABLED,
             EMERGENCY_TONE,
@@ -13883,8 +13853,6 @@
             VALIDATORS.put(PRIVATE_DNS_MODE, PRIVATE_DNS_MODE_VALIDATOR);
             VALIDATORS.put(PRIVATE_DNS_SPECIFIER, PRIVATE_DNS_SPECIFIER_VALIDATOR);
             VALIDATORS.put(SOFT_AP_TIMEOUT_ENABLED, SOFT_AP_TIMEOUT_ENABLED_VALIDATOR);
-            VALIDATORS.put(WIFI_CARRIER_NETWORKS_AVAILABLE_NOTIFICATION_ON,
-                    WIFI_CARRIER_NETWORKS_AVAILABLE_NOTIFICATION_ON_VALIDATOR);
             VALIDATORS.put(WIFI_SCAN_THROTTLE_ENABLED, WIFI_SCAN_THROTTLE_ENABLED_VALIDATOR);
             VALIDATORS.put(APP_AUTO_RESTRICTION_ENABLED, APP_AUTO_RESTRICTION_ENABLED_VALIDATOR);
             VALIDATORS.put(ZEN_DURATION, ZEN_DURATION_VALIDATOR);
diff --git a/core/java/android/provider/SettingsValidators.java b/core/java/android/provider/SettingsValidators.java
deleted file mode 100644
index e4cf9fd..0000000
--- a/core/java/android/provider/SettingsValidators.java
+++ /dev/null
@@ -1,332 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.provider;
-
-import android.annotation.Nullable;
-import android.content.ComponentName;
-import android.net.Uri;
-import android.text.TextUtils;
-
-import com.android.internal.util.ArrayUtils;
-
-import org.json.JSONException;
-import org.json.JSONObject;
-
-import java.util.Locale;
-
-/**
- * This class provides both interface for validation and common validators
- * used to ensure Settings have meaningful values.
- *
- * @hide
- */
-public class SettingsValidators {
-
-    public static final Validator BOOLEAN_VALIDATOR =
-            new DiscreteValueValidator(new String[] {"0", "1"});
-
-    public static final Validator ANY_STRING_VALIDATOR = new Validator() {
-        @Override
-        public boolean validate(@Nullable String value) {
-            return true;
-        }
-    };
-
-    public static final Validator NON_NEGATIVE_INTEGER_VALIDATOR = new Validator() {
-        @Override
-        public boolean validate(@Nullable String value) {
-            try {
-                return Integer.parseInt(value) >= 0;
-            } catch (NumberFormatException e) {
-                return false;
-            }
-        }
-    };
-
-    public static final Validator ANY_INTEGER_VALIDATOR = new Validator() {
-        @Override
-        public boolean validate(@Nullable String value) {
-            try {
-                Integer.parseInt(value);
-                return true;
-            } catch (NumberFormatException e) {
-                return false;
-            }
-        }
-    };
-
-    public static final Validator URI_VALIDATOR = new Validator() {
-        @Override
-        public boolean validate(@Nullable String value) {
-            try {
-                Uri.decode(value);
-                return true;
-            } catch (IllegalArgumentException e) {
-                return false;
-            }
-        }
-    };
-
-    /**
-     * Does not allow a setting to have a null {@link ComponentName}. Use {@link
-     * SettingsValidators#NULLABLE_COMPONENT_NAME_VALIDATOR} instead if a setting can have a
-     * nullable {@link ComponentName}.
-     */
-    public static final Validator COMPONENT_NAME_VALIDATOR = new Validator() {
-        @Override
-        public boolean validate(@Nullable String value) {
-            return value != null && ComponentName.unflattenFromString(value) != null;
-        }
-    };
-
-    /**
-     * Allows a setting to have a null {@link ComponentName}.
-     */
-    public static final Validator NULLABLE_COMPONENT_NAME_VALIDATOR = new Validator() {
-        @Override
-        public boolean validate(@Nullable String value) {
-            return value == null || COMPONENT_NAME_VALIDATOR.validate(value);
-        }
-    };
-
-    public static final Validator PACKAGE_NAME_VALIDATOR = new Validator() {
-        @Override
-        public boolean validate(@Nullable String value) {
-            return value != null && isStringPackageName(value);
-        }
-
-        private boolean isStringPackageName(String value) {
-            // The name may contain uppercase or lowercase letters ('A' through 'Z'), numbers,
-            // and underscores ('_'). However, individual package name parts may only
-            // start with letters.
-            // (https://developer.android.com/guide/topics/manifest/manifest-element.html#package)
-            if (value == null) {
-                return false;
-            }
-            String[] subparts = value.split("\\.");
-            boolean isValidPackageName = true;
-            for (String subpart : subparts) {
-                isValidPackageName &= isSubpartValidForPackageName(subpart);
-                if (!isValidPackageName) break;
-            }
-            return isValidPackageName;
-        }
-
-        private boolean isSubpartValidForPackageName(String subpart) {
-            if (subpart.length() == 0) return false;
-            boolean isValidSubpart = Character.isLetter(subpart.charAt(0));
-            for (int i = 1; i < subpart.length(); i++) {
-                isValidSubpart &= (Character.isLetterOrDigit(subpart.charAt(i))
-                                || (subpart.charAt(i) == '_'));
-                if (!isValidSubpart) break;
-            }
-            return isValidSubpart;
-        }
-    };
-
-    public static final Validator LENIENT_IP_ADDRESS_VALIDATOR = new Validator() {
-        private static final int MAX_IPV6_LENGTH = 45;
-
-        @Override
-        public boolean validate(@Nullable String value) {
-            if (value == null) {
-                return false;
-            }
-            return value.length() <= MAX_IPV6_LENGTH;
-        }
-    };
-
-    public static final Validator LOCALE_VALIDATOR = new Validator() {
-        @Override
-        public boolean validate(@Nullable String value) {
-            if (value == null) {
-                return false;
-            }
-            Locale[] validLocales = Locale.getAvailableLocales();
-            for (Locale locale : validLocales) {
-                if (value.equals(locale.toString())) {
-                    return true;
-                }
-            }
-            return false;
-        }
-    };
-
-    /** {@link Validator} that checks whether a value is a valid {@link JSONObject}. */
-    public static final Validator JSON_OBJECT_VALIDATOR = (value) -> {
-        if (TextUtils.isEmpty(value)) {
-            return false;
-        }
-        try {
-            new JSONObject(value);
-            return true;
-        } catch (JSONException e) {
-            return false;
-        }
-    };
-
-    public interface Validator {
-        /**
-         * Returns whether the input value is valid. Subclasses should handle the case where the
-         * input value is {@code null}.
-         */
-        boolean validate(@Nullable String value);
-    }
-
-    public static final class DiscreteValueValidator implements Validator {
-        private final String[] mValues;
-
-        public DiscreteValueValidator(String[] values) {
-            mValues = values;
-        }
-
-        @Override
-        public boolean validate(@Nullable String value) {
-            return ArrayUtils.contains(mValues, value);
-        }
-    }
-
-    public static final class InclusiveIntegerRangeValidator implements Validator {
-        private final int mMin;
-        private final int mMax;
-
-        public InclusiveIntegerRangeValidator(int min, int max) {
-            mMin = min;
-            mMax = max;
-        }
-
-        @Override
-        public boolean validate(@Nullable String value) {
-            try {
-                final int intValue = Integer.parseInt(value);
-                return intValue >= mMin && intValue <= mMax;
-            } catch (NumberFormatException e) {
-                return false;
-            }
-        }
-    }
-
-    public static final class InclusiveFloatRangeValidator implements Validator {
-        private final float mMin;
-        private final float mMax;
-
-        public InclusiveFloatRangeValidator(float min, float max) {
-            mMin = min;
-            mMax = max;
-        }
-
-        @Override
-        public boolean validate(@Nullable String value) {
-            try {
-                final float floatValue = Float.parseFloat(value);
-                return floatValue >= mMin && floatValue <= mMax;
-            } catch (NumberFormatException | NullPointerException e) {
-                return false;
-            }
-        }
-    }
-
-    public static final class ComponentNameListValidator implements Validator {
-        private final String mSeparator;
-
-        public ComponentNameListValidator(String separator) {
-            mSeparator = separator;
-        }
-
-        @Override
-        public boolean validate(@Nullable String value) {
-            if (value == null) {
-                return false;
-            }
-            String[] elements = value.split(mSeparator);
-            for (String element : elements) {
-                if (!COMPONENT_NAME_VALIDATOR.validate(element)) {
-                    return false;
-                }
-            }
-            return true;
-        }
-    }
-
-    public static final class PackageNameListValidator implements Validator {
-        private final String mSeparator;
-
-        public PackageNameListValidator(String separator) {
-            mSeparator = separator;
-        }
-
-        @Override
-        public boolean validate(@Nullable String value) {
-            if (value == null) {
-                return false;
-            }
-            String[] elements = value.split(mSeparator);
-            for (String element : elements) {
-                if (!PACKAGE_NAME_VALIDATOR.validate(element)) {
-                    return false;
-                }
-            }
-            return true;
-        }
-    }
-
-    private abstract static class ListValidator implements Validator {
-        public boolean validate(@Nullable String value) {
-            if (!isEntryValid(value)) {
-                return false;
-            }
-            String[] items = value.split(",");
-            for (String item : items) {
-                if (!isItemValid(item)) {
-                    return false;
-                }
-            }
-            return true;
-        }
-
-        protected abstract boolean isEntryValid(String entry);
-
-        protected abstract boolean isItemValid(String item);
-    }
-
-    /** Ensure a restored value is a string in the format the text-to-speech system handles */
-    public static final class TTSListValidator extends ListValidator {
-        protected boolean isEntryValid(String entry) {
-            return entry != null && entry.length() > 0;
-        }
-
-        protected boolean isItemValid(String item) {
-            String[] parts = item.split(":");
-            // Replaces any old language separator (-) with the new one (_)
-            return ((parts.length == 2)
-                    && (parts[0].length() > 0)
-                    && ANY_STRING_VALIDATOR.validate(parts[0])
-                    && LOCALE_VALIDATOR.validate(parts[1].replace('-', '_')));
-        }
-    }
-
-    /** Ensure a restored value is suitable to be used as a tile name */
-    public static final class TileListValidator extends ListValidator {
-        protected boolean isEntryValid(String entry) {
-            return entry != null;
-        }
-
-        protected boolean isItemValid(String item) {
-            return item.length() > 0 && ANY_STRING_VALIDATOR.validate(item);
-        }
-    }
-}
diff --git a/core/java/android/provider/settings/validators/ComponentNameListValidator.java b/core/java/android/provider/settings/validators/ComponentNameListValidator.java
new file mode 100644
index 0000000..b6b867a
--- /dev/null
+++ b/core/java/android/provider/settings/validators/ComponentNameListValidator.java
@@ -0,0 +1,40 @@
+/*
+ * 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.provider.settings.validators;
+
+import static android.provider.settings.validators.SettingsValidators.COMPONENT_NAME_VALIDATOR;
+
+/**
+ * Validate a list of compoments.
+ *
+ * @hide
+ */
+public final class ComponentNameListValidator extends ListValidator {
+    public ComponentNameListValidator(String separator) {
+        super(separator);
+    }
+
+    @Override
+    protected boolean isEntryValid(String entry) {
+        return entry != null;
+    }
+
+    @Override
+    protected boolean isItemValid(String item) {
+        return COMPONENT_NAME_VALIDATOR.validate(item);
+    }
+}
diff --git a/core/java/android/provider/settings/validators/DiscreteValueValidator.java b/core/java/android/provider/settings/validators/DiscreteValueValidator.java
new file mode 100644
index 0000000..183651f
--- /dev/null
+++ b/core/java/android/provider/settings/validators/DiscreteValueValidator.java
@@ -0,0 +1,39 @@
+/*
+ * 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.provider.settings.validators;
+
+import android.annotation.Nullable;
+
+import com.android.internal.util.ArrayUtils;
+
+/**
+ * Validate a value exists in an array of known good values
+ *
+ * @hide
+ */
+public final class DiscreteValueValidator implements Validator {
+    private final String[] mValues;
+
+    public DiscreteValueValidator(String[] values) {
+        mValues = values;
+    }
+
+    @Override
+    public boolean validate(@Nullable String value) {
+        return ArrayUtils.contains(mValues, value);
+    }
+}
diff --git a/core/java/android/provider/settings/validators/InclusiveFloatRangeValidator.java b/core/java/android/provider/settings/validators/InclusiveFloatRangeValidator.java
new file mode 100644
index 0000000..38400ac
--- /dev/null
+++ b/core/java/android/provider/settings/validators/InclusiveFloatRangeValidator.java
@@ -0,0 +1,44 @@
+/*
+ * 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.provider.settings.validators;
+
+import android.annotation.Nullable;
+
+/**
+ * Validate a float value lies within a given (boundary inclusive) range.
+ *
+ * @hide
+ */
+public final class InclusiveFloatRangeValidator implements Validator {
+    private final float mMin;
+    private final float mMax;
+
+    public InclusiveFloatRangeValidator(float min, float max) {
+        mMin = min;
+        mMax = max;
+    }
+
+    @Override
+    public boolean validate(@Nullable String value) {
+        try {
+            final float floatValue = Float.parseFloat(value);
+            return floatValue >= mMin && floatValue <= mMax;
+        } catch (NumberFormatException | NullPointerException e) {
+            return false;
+        }
+    }
+}
diff --git a/core/java/android/provider/settings/validators/InclusiveIntegerRangeValidator.java b/core/java/android/provider/settings/validators/InclusiveIntegerRangeValidator.java
new file mode 100644
index 0000000..e53c252
--- /dev/null
+++ b/core/java/android/provider/settings/validators/InclusiveIntegerRangeValidator.java
@@ -0,0 +1,44 @@
+/*
+ * 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.provider.settings.validators;
+
+import android.annotation.Nullable;
+
+/**
+ * Validate an integer value lies within a given (boundary inclusive) range.
+ *
+ * @hide
+ */
+public final class InclusiveIntegerRangeValidator implements Validator {
+    private final int mMin;
+    private final int mMax;
+
+    public InclusiveIntegerRangeValidator(int min, int max) {
+        mMin = min;
+        mMax = max;
+    }
+
+    @Override
+    public boolean validate(@Nullable String value) {
+        try {
+            final int intValue = Integer.parseInt(value);
+            return intValue >= mMin && intValue <= mMax;
+        } catch (NumberFormatException e) {
+            return false;
+        }
+    }
+}
diff --git a/core/java/android/provider/settings/validators/ListValidator.java b/core/java/android/provider/settings/validators/ListValidator.java
new file mode 100644
index 0000000..a6001d2
--- /dev/null
+++ b/core/java/android/provider/settings/validators/ListValidator.java
@@ -0,0 +1,51 @@
+/*
+ * 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.provider.settings.validators;
+
+import android.annotation.Nullable;
+
+/**
+ * Validate the elements in a list.
+ *
+ * @hide
+ */
+abstract class ListValidator implements Validator {
+
+    private String mListSplitRegex;
+
+    ListValidator(String listSplitRegex) {
+        mListSplitRegex = listSplitRegex;
+    }
+
+    public boolean validate(@Nullable String value) {
+        if (!isEntryValid(value)) {
+            return false;
+        }
+        String[] items = value.split(",");
+        for (String item : items) {
+            if (!isItemValid(item)) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    protected abstract boolean isEntryValid(String entry);
+
+    protected abstract boolean isItemValid(String item);
+}
+
diff --git a/core/java/android/provider/settings/validators/PackageNameListValidator.java b/core/java/android/provider/settings/validators/PackageNameListValidator.java
new file mode 100644
index 0000000..bc7fc13
--- /dev/null
+++ b/core/java/android/provider/settings/validators/PackageNameListValidator.java
@@ -0,0 +1,40 @@
+/*
+ * 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.provider.settings.validators;
+
+import static android.provider.settings.validators.SettingsValidators.PACKAGE_NAME_VALIDATOR;
+
+/**
+ * Validate a list of package names.
+ *
+ * @hide
+ */
+public final class PackageNameListValidator extends ListValidator {
+    public PackageNameListValidator(String separator) {
+        super(separator);
+    }
+
+    @Override
+    protected boolean isEntryValid(String entry) {
+        return entry != null;
+    }
+
+    @Override
+    protected boolean isItemValid(String item) {
+        return PACKAGE_NAME_VALIDATOR.validate(item);
+    }
+}
diff --git a/core/java/android/provider/settings/validators/SettingsValidators.java b/core/java/android/provider/settings/validators/SettingsValidators.java
new file mode 100644
index 0000000..562c638
--- /dev/null
+++ b/core/java/android/provider/settings/validators/SettingsValidators.java
@@ -0,0 +1,183 @@
+/*
+ * 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.provider.settings.validators;
+
+import android.annotation.Nullable;
+import android.content.ComponentName;
+import android.net.Uri;
+import android.text.TextUtils;
+
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.util.Locale;
+
+/**
+ * This class provides both interface for validation and common validators
+ * used to ensure Settings have meaningful values.
+ *
+ * @hide
+ */
+public class SettingsValidators {
+
+    public static final Validator BOOLEAN_VALIDATOR =
+            new DiscreteValueValidator(new String[] {"0", "1"});
+
+    public static final Validator ANY_STRING_VALIDATOR = new Validator() {
+        @Override
+        public boolean validate(@Nullable String value) {
+            return true;
+        }
+    };
+
+    public static final Validator NON_NEGATIVE_INTEGER_VALIDATOR = new Validator() {
+        @Override
+        public boolean validate(@Nullable String value) {
+            try {
+                return Integer.parseInt(value) >= 0;
+            } catch (NumberFormatException e) {
+                return false;
+            }
+        }
+    };
+
+    public static final Validator ANY_INTEGER_VALIDATOR = new Validator() {
+        @Override
+        public boolean validate(@Nullable String value) {
+            try {
+                Integer.parseInt(value);
+                return true;
+            } catch (NumberFormatException e) {
+                return false;
+            }
+        }
+    };
+
+    public static final Validator URI_VALIDATOR = new Validator() {
+        @Override
+        public boolean validate(@Nullable String value) {
+            try {
+                Uri.decode(value);
+                return true;
+            } catch (IllegalArgumentException e) {
+                return false;
+            }
+        }
+    };
+
+    /**
+     * Does not allow a setting to have a null {@link ComponentName}. Use {@link
+     * SettingsValidators#NULLABLE_COMPONENT_NAME_VALIDATOR} instead if a setting can have a
+     * nullable {@link ComponentName}.
+     */
+    public static final Validator COMPONENT_NAME_VALIDATOR = new Validator() {
+        @Override
+        public boolean validate(@Nullable String value) {
+            return value != null && ComponentName.unflattenFromString(value) != null;
+        }
+    };
+
+    /**
+     * Allows a setting to have a null {@link ComponentName}.
+     */
+    public static final Validator NULLABLE_COMPONENT_NAME_VALIDATOR = new Validator() {
+        @Override
+        public boolean validate(@Nullable String value) {
+            return value == null || COMPONENT_NAME_VALIDATOR.validate(value);
+        }
+    };
+
+    public static final Validator PACKAGE_NAME_VALIDATOR = new Validator() {
+        @Override
+        public boolean validate(@Nullable String value) {
+            return value != null && isStringPackageName(value);
+        }
+
+        private boolean isStringPackageName(String value) {
+            // The name may contain uppercase or lowercase letters ('A' through 'Z'), numbers,
+            // and underscores ('_'). However, individual package name parts may only
+            // start with letters.
+            // (https://developer.android.com/guide/topics/manifest/manifest-element.html#package)
+            if (value == null) {
+                return false;
+            }
+            String[] subparts = value.split("\\.");
+            boolean isValidPackageName = true;
+            for (String subpart : subparts) {
+                isValidPackageName &= isSubpartValidForPackageName(subpart);
+                if (!isValidPackageName) break;
+            }
+            return isValidPackageName;
+        }
+
+        private boolean isSubpartValidForPackageName(String subpart) {
+            if (subpart.length() == 0) return false;
+            boolean isValidSubpart = Character.isLetter(subpart.charAt(0));
+            for (int i = 1; i < subpart.length(); i++) {
+                isValidSubpart &= (Character.isLetterOrDigit(subpart.charAt(i))
+                                || (subpart.charAt(i) == '_'));
+                if (!isValidSubpart) break;
+            }
+            return isValidSubpart;
+        }
+    };
+
+    public static final Validator LENIENT_IP_ADDRESS_VALIDATOR = new Validator() {
+        private static final int MAX_IPV6_LENGTH = 45;
+
+        @Override
+        public boolean validate(@Nullable String value) {
+            if (value == null) {
+                return false;
+            }
+            return value.length() <= MAX_IPV6_LENGTH;
+        }
+    };
+
+    public static final Validator LOCALE_VALIDATOR = new Validator() {
+        @Override
+        public boolean validate(@Nullable String value) {
+            if (value == null) {
+                return false;
+            }
+            Locale[] validLocales = Locale.getAvailableLocales();
+            for (Locale locale : validLocales) {
+                if (value.equals(locale.toString())) {
+                    return true;
+                }
+            }
+            return false;
+        }
+    };
+
+    /** {@link Validator} that checks whether a value is a valid {@link JSONObject}. */
+    public static final Validator JSON_OBJECT_VALIDATOR = (value) -> {
+        if (TextUtils.isEmpty(value)) {
+            return false;
+        }
+        try {
+            new JSONObject(value);
+            return true;
+        } catch (JSONException e) {
+            return false;
+        }
+    };
+
+    public static final Validator TTS_LIST_VALIDATOR = new TTSListValidator();
+
+    public static final Validator TILE_LIST_VALIDATOR = new TileListValidator();
+}
diff --git a/core/java/android/provider/settings/validators/TTSListValidator.java b/core/java/android/provider/settings/validators/TTSListValidator.java
new file mode 100644
index 0000000..6c73471
--- /dev/null
+++ b/core/java/android/provider/settings/validators/TTSListValidator.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.provider.settings.validators;
+
+import static android.provider.settings.validators.SettingsValidators.ANY_STRING_VALIDATOR;
+import static android.provider.settings.validators.SettingsValidators.LOCALE_VALIDATOR;
+
+/**
+ * Ensure a restored value is a string in the format the text-to-speech system handles
+ *
+ * @hide
+ */
+final class TTSListValidator extends ListValidator {
+
+    TTSListValidator() {
+        super(",");
+    }
+
+    protected boolean isEntryValid(String entry) {
+        return entry != null && entry.length() > 0;
+    }
+
+    protected boolean isItemValid(String item) {
+        String[] parts = item.split(":");
+        // Replaces any old language separator (-) with the new one (_)
+        return ((parts.length == 2)
+                && (parts[0].length() > 0)
+                && ANY_STRING_VALIDATOR.validate(parts[0])
+                && LOCALE_VALIDATOR.validate(parts[1].replace('-', '_')));
+    }
+}
diff --git a/core/java/android/provider/settings/validators/TileListValidator.java b/core/java/android/provider/settings/validators/TileListValidator.java
new file mode 100644
index 0000000..c696442
--- /dev/null
+++ b/core/java/android/provider/settings/validators/TileListValidator.java
@@ -0,0 +1,38 @@
+/*
+ * 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.provider.settings.validators;
+
+import static android.provider.settings.validators.SettingsValidators.ANY_STRING_VALIDATOR;
+
+/**
+ * Ensure a restored value is suitable to be used as a tile name
+ *
+ * @hide
+ */
+final class TileListValidator extends ListValidator {
+    TileListValidator() {
+        super(",");
+    }
+
+    protected boolean isEntryValid(String entry) {
+        return entry != null;
+    }
+
+    protected boolean isItemValid(String item) {
+        return item.length() > 0 && ANY_STRING_VALIDATOR.validate(item);
+    }
+}
diff --git a/core/java/android/provider/settings/validators/Validator.java b/core/java/android/provider/settings/validators/Validator.java
new file mode 100644
index 0000000..393a03d
--- /dev/null
+++ b/core/java/android/provider/settings/validators/Validator.java
@@ -0,0 +1,32 @@
+/*
+ * 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.provider.settings.validators;
+
+import android.annotation.Nullable;
+
+/**
+ * Interface for a settings value validator.
+ *
+ * @hide
+ */
+public interface Validator {
+    /**
+     * Returns whether the input value is valid. Subclasses should handle the case where the
+     * input value is {@code null}.
+     */
+    boolean validate(@Nullable String value);
+}
diff --git a/core/java/android/service/notification/StatusBarNotification.java b/core/java/android/service/notification/StatusBarNotification.java
index 905c781..205df7e 100644
--- a/core/java/android/service/notification/StatusBarNotification.java
+++ b/core/java/android/service/notification/StatusBarNotification.java
@@ -17,6 +17,7 @@
 package android.service.notification;
 
 import android.annotation.NonNull;
+import android.annotation.SystemApi;
 import android.annotation.UnsupportedAppUsage;
 import android.app.Notification;
 import android.app.NotificationManager;
@@ -172,6 +173,7 @@
      *
      * @hide
      */
+    @SystemApi
     public boolean isAppGroup() {
         if (getNotification().getGroup() != null || getNotification().getSortKey() != null) {
             return true;
diff --git a/core/java/android/service/storage/ExternalStorageService.java b/core/java/android/service/storage/ExternalStorageService.java
new file mode 100644
index 0000000..cc8116d0
--- /dev/null
+++ b/core/java/android/service/storage/ExternalStorageService.java
@@ -0,0 +1,168 @@
+/*
+ * 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.service.storage;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.SdkConstant;
+import android.annotation.SystemApi;
+import android.app.Service;
+import android.content.Intent;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.ParcelFileDescriptor;
+import android.os.ParcelableException;
+import android.os.RemoteCallback;
+import android.os.RemoteException;
+
+import java.io.IOException;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * A service to handle filesystem I/O from other apps.
+ *
+ * <p>To extend this class, you must declare the service in your manifest file with the
+ * {@link android.Manifest.permission#BIND_EXTERNAL_STORAGE_SERVICE} permission,
+ * and include an intent filter with the {@link #SERVICE_INTERFACE} action.
+ * For example:</p>
+ * <pre>
+ *     &lt;service android:name=".ExternalStorageServiceImpl"
+ *             android:exported="true"
+ *             android:priority="100"
+ *             android:permission="android.permission.BIND_EXTERNAL_STORAGE_SERVICE"&gt;
+ *         &lt;intent-filter&gt;
+ *             &lt;action android:name="android.service.storage.ExternalStorageService" /&gt;
+ *         &lt;/intent-filter&gt;
+ *     &lt;/service&gt;
+ * </pre>
+ * @hide
+ */
+@SystemApi
+public abstract class ExternalStorageService extends Service {
+    /**
+     * The Intent action that a service must respond to. Add it as an intent filter in the
+     * manifest declaration of the implementing service.
+     */
+    @SdkConstant(SdkConstant.SdkConstantType.SERVICE_ACTION)
+    public static final String SERVICE_INTERFACE = "android.service.storage.ExternalStorageService";
+    /**
+     * Whether the session associated with the device file descriptor when calling
+     * {@link #onStartSession} is a FUSE session.
+     */
+    public static final int FLAG_SESSION_TYPE_FUSE = 1 << 0;
+
+    /**
+     * Whether the upper file system path specified when calling {@link #onStartSession}
+     * should be indexed.
+     */
+    public static final int FLAG_SESSION_ATTRIBUTE_INDEXABLE = 1 << 1;
+
+    /**
+     * {@link Bundle} key for a {@link String} value.
+     *
+     * {@hide}
+     */
+    public static final String EXTRA_SESSION_ID =
+            "android.service.storage.extra.session_id";
+    /**
+     * {@link Bundle} key for a {@link ParcelableException} value.
+     *
+     * {@hide}
+     */
+    public static final String EXTRA_ERROR =
+            "android.service.storage.extra.error";
+
+    /** @hide */
+    @IntDef(flag = true, prefix = {"FLAG_SESSION_"},
+        value = {FLAG_SESSION_TYPE_FUSE, FLAG_SESSION_ATTRIBUTE_INDEXABLE})
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface SessionFlag {}
+
+    private final ExternalStorageServiceWrapper mWrapper = new ExternalStorageServiceWrapper();
+    private final Handler mHandler = new Handler(Looper.getMainLooper(), null, true);
+
+    /**
+     * Called when the system starts a session associated with {@code deviceFd}
+     * identified by {@code sessionId} to handle filesystem I/O for other apps. The type of
+     * session and other attributes are passed in {@code flag}.
+     *
+     * <p> I/O is received as requests originating from {@code upperFileSystemPath} on
+     * {@code deviceFd}. Implementors should handle the I/O by responding to these requests
+     * using the data on the {@code lowerFileSystemPath}.
+     *
+     * <p> Additional calls to start a session for the same {@code sessionId} while the session
+     * is still starting or already started should have no effect.
+     */
+    public abstract void onStartSession(@NonNull String sessionId, @SessionFlag int flag,
+            @NonNull ParcelFileDescriptor deviceFd, @NonNull String upperFileSystemPath,
+            @NonNull String lowerFileSystemPath) throws IOException;
+
+    /**
+     * Called when the system ends the session identified by {@code sessionId}. Implementors should
+     * stop handling filesystem I/O and clean up resources from the ended session.
+     *
+     * <p> Additional calls to end a session for the same {@code sessionId} while the session
+     * is still ending or has not started should have no effect.
+     */
+    public abstract void onEndSession(@NonNull String sessionId) throws IOException;
+
+    @Override
+    @NonNull
+    public final IBinder onBind(@NonNull Intent intent) {
+        return mWrapper;
+    }
+
+    private class ExternalStorageServiceWrapper extends IExternalStorageService.Stub {
+        @Override
+        public void startSession(String sessionId, @SessionFlag int flag,
+                ParcelFileDescriptor deviceFd, String upperPath, String lowerPath,
+                RemoteCallback callback) throws RemoteException {
+            mHandler.post(() -> {
+                try {
+                    onStartSession(sessionId, flag, deviceFd, upperPath, lowerPath);
+                    sendResult(sessionId, null /* throwable */, callback);
+                } catch (Throwable t) {
+                    sendResult(sessionId, t, callback);
+                }
+            });
+        }
+
+        @Override
+        public void endSession(String sessionId, RemoteCallback callback) throws RemoteException {
+            mHandler.post(() -> {
+                try {
+                    onEndSession(sessionId);
+                    sendResult(sessionId, null /* throwable */, callback);
+                } catch (Throwable t) {
+                    sendResult(sessionId, t, callback);
+                }
+            });
+        }
+
+        private void sendResult(String sessionId, Throwable throwable, RemoteCallback callback) {
+            Bundle bundle = new Bundle();
+            bundle.putString(EXTRA_SESSION_ID, sessionId);
+            if (throwable != null) {
+                bundle.putParcelable(EXTRA_ERROR, new ParcelableException(throwable));
+            }
+            callback.sendResult(bundle);
+        }
+    }
+}
diff --git a/core/java/android/service/storage/IExternalStorageService.aidl b/core/java/android/service/storage/IExternalStorageService.aidl
new file mode 100644
index 0000000..ae46f1f
--- /dev/null
+++ b/core/java/android/service/storage/IExternalStorageService.aidl
@@ -0,0 +1,30 @@
+/*
+ * 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.service.storage;
+
+import android.os.ParcelFileDescriptor;
+import android.os.RemoteCallback;
+
+/**
+ * @hide
+ */
+oneway interface IExternalStorageService
+{
+    void startSession(@utf8InCpp String sessionId, int type, in ParcelFileDescriptor deviceFd,
+         @utf8InCpp String upperPath, @utf8InCpp String lowerPath, in RemoteCallback callback);
+    void endSession(@utf8InCpp String sessionId, in RemoteCallback callback);
+}
\ No newline at end of file
diff --git a/core/java/android/service/textclassifier/TextClassifierService.java b/core/java/android/service/textclassifier/TextClassifierService.java
index 30c4e90..9108c33 100644
--- a/core/java/android/service/textclassifier/TextClassifierService.java
+++ b/core/java/android/service/textclassifier/TextClassifierService.java
@@ -460,6 +460,7 @@
                 return;
             }
             try {
+                Slog.w(LOG_TAG, "Request fail: " + error);
                 callback.onFailure();
             } catch (RemoteException e) {
                 Slog.d(LOG_TAG, "Error calling callback");
diff --git a/core/java/android/speech/tts/TextToSpeech.java b/core/java/android/speech/tts/TextToSpeech.java
index 100774c..44446ad0 100644
--- a/core/java/android/speech/tts/TextToSpeech.java
+++ b/core/java/android/speech/tts/TextToSpeech.java
@@ -16,6 +16,7 @@
 package android.speech.tts;
 
 import android.annotation.IntDef;
+import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.RawRes;
 import android.annotation.SdkConstant;
@@ -858,23 +859,20 @@
         }
 
         // Post connection case
-        runActionNoReconnect(new Action<Void>() {
-            @Override
-            public Void run(ITextToSpeechService service) throws RemoteException {
-                service.setCallback(getCallerIdentity(), null);
-                service.stop(getCallerIdentity());
-                mServiceConnection.disconnect();
-                // Context#unbindService does not result in a call to
-                // ServiceConnection#onServiceDisconnected. As a result, the
-                // service ends up being destroyed (if there are no other open
-                // connections to it) but the process lives on and the
-                // ServiceConnection continues to refer to the destroyed service.
-                //
-                // This leads to tons of log spam about SynthThread being dead.
-                mServiceConnection = null;
-                mCurrentEngine = null;
-                return null;
-            }
+        runActionNoReconnect((ITextToSpeechService service) -> {
+            service.setCallback(getCallerIdentity(), null);
+            service.stop(getCallerIdentity());
+            mServiceConnection.disconnect();
+            // Context#unbindService does not result in a call to
+            // ServiceConnection#onServiceDisconnected. As a result, the
+            // service ends up being destroyed (if there are no other open
+            // connections to it) but the process lives on and the
+            // ServiceConnection continues to refer to the destroyed service.
+            //
+            // This leads to tons of log spam about SynthThread being dead.
+            mServiceConnection = null;
+            mCurrentEngine = null;
+            return null;
         }, null, "shutdown", false);
     }
 
@@ -1105,17 +1103,14 @@
                      final int queueMode,
                      final Bundle params,
                      final String utteranceId) {
-        return runAction(new Action<Integer>() {
-            @Override
-            public Integer run(ITextToSpeechService service) throws RemoteException {
-                Uri utteranceUri = mUtterances.get(text);
-                if (utteranceUri != null) {
-                    return service.playAudio(getCallerIdentity(), utteranceUri, queueMode,
-                            getParams(params), utteranceId);
-                } else {
-                    return service.speak(getCallerIdentity(), text, queueMode, getParams(params),
-                            utteranceId);
-                }
+        return runAction((ITextToSpeechService service) -> {
+            Uri utteranceUri = mUtterances.get(text);
+            if (utteranceUri != null) {
+                return service.playAudio(getCallerIdentity(), utteranceUri, queueMode,
+                        getParams(params), utteranceId);
+            } else {
+                return service.speak(getCallerIdentity(), text, queueMode, getParams(params),
+                        utteranceId);
             }
         }, ERROR, "speak");
     }
@@ -1178,16 +1173,13 @@
      */
     public int playEarcon(final String earcon, final int queueMode,
             final Bundle params, final String utteranceId) {
-        return runAction(new Action<Integer>() {
-            @Override
-            public Integer run(ITextToSpeechService service) throws RemoteException {
-                Uri earconUri = mEarcons.get(earcon);
-                if (earconUri == null) {
-                    return ERROR;
-                }
-                return service.playAudio(getCallerIdentity(), earconUri, queueMode,
-                        getParams(params), utteranceId);
+        return runAction((ITextToSpeechService service) -> {
+            Uri earconUri = mEarcons.get(earcon);
+            if (earconUri == null) {
+                return ERROR;
             }
+            return service.playAudio(getCallerIdentity(), earconUri, queueMode,
+                    getParams(params), utteranceId);
         }, ERROR, "playEarcon");
     }
 
@@ -1242,12 +1234,9 @@
      */
     public int playSilentUtterance(final long durationInMs, final int queueMode,
             final String utteranceId) {
-        return runAction(new Action<Integer>() {
-            @Override
-            public Integer run(ITextToSpeechService service) throws RemoteException {
-                return service.playSilence(getCallerIdentity(), durationInMs,
-                                           queueMode, utteranceId);
-            }
+        return runAction((ITextToSpeechService service) -> {
+            return service.playSilence(getCallerIdentity(), durationInMs,
+                                        queueMode, utteranceId);
         }, ERROR, "playSilentUtterance");
     }
 
@@ -1302,26 +1291,23 @@
      */
     @Deprecated
     public Set<String> getFeatures(final Locale locale) {
-        return runAction(new Action<Set<String>>() {
-            @Override
-            public Set<String> run(ITextToSpeechService service) throws RemoteException {
-                String[] features = null;
-                try {
-                    features = service.getFeaturesForLanguage(
-                        locale.getISO3Language(), locale.getISO3Country(), locale.getVariant());
-                } catch(MissingResourceException e) {
-                    Log.w(TAG, "Couldn't retrieve 3 letter ISO 639-2/T language and/or ISO 3166 " +
-                            "country code for locale: " + locale, e);
-                    return null;
-                }
-
-                if (features != null) {
-                    final Set<String> featureSet = new HashSet<String>();
-                    Collections.addAll(featureSet, features);
-                    return featureSet;
-                }
+        return runAction((ITextToSpeechService service) -> {
+            String[] features = null;
+            try {
+                features = service.getFeaturesForLanguage(
+                    locale.getISO3Language(), locale.getISO3Country(), locale.getVariant());
+            } catch (MissingResourceException e) {
+                Log.w(TAG, "Couldn't retrieve 3 letter ISO 639-2/T language and/or ISO 3166 "
+                        + "country code for locale: " + locale, e);
                 return null;
             }
+
+            if (features != null) {
+                final Set<String> featureSet = new HashSet<String>();
+                Collections.addAll(featureSet, features);
+                return featureSet;
+            }
+            return null;
         }, null, "getFeatures");
     }
 
@@ -1334,11 +1320,8 @@
      * @return {@code true} if the TTS engine is speaking.
      */
     public boolean isSpeaking() {
-        return runAction(new Action<Boolean>() {
-            @Override
-            public Boolean run(ITextToSpeechService service) throws RemoteException {
-                return service.isSpeaking();
-            }
+        return runAction((ITextToSpeechService service) -> {
+            return service.isSpeaking();
         }, false, "isSpeaking");
     }
 
@@ -1349,11 +1332,8 @@
      * @return {@link #ERROR} or {@link #SUCCESS}.
      */
     public int stop() {
-        return runAction(new Action<Integer>() {
-            @Override
-            public Integer run(ITextToSpeechService service) throws RemoteException {
-                return service.stop(getCallerIdentity());
-            }
+        return runAction((ITextToSpeechService service) -> {
+            return service.stop(getCallerIdentity());
         }, ERROR, "stop");
     }
 
@@ -1447,13 +1427,10 @@
      */
     @Deprecated
     public Locale getDefaultLanguage() {
-        return runAction(new Action<Locale>() {
-            @Override
-            public Locale run(ITextToSpeechService service) throws RemoteException {
-                String[] defaultLanguage = service.getClientDefaultLanguage();
+        return runAction((ITextToSpeechService service) -> {
+            String[] defaultLanguage = service.getClientDefaultLanguage();
 
-                return new Locale(defaultLanguage[0], defaultLanguage[1], defaultLanguage[2]);
-            }
+            return new Locale(defaultLanguage[0], defaultLanguage[1], defaultLanguage[2]);
         }, null, "getDefaultLanguage");
     }
 
@@ -1474,83 +1451,80 @@
      *         {@link #LANG_MISSING_DATA} and {@link #LANG_NOT_SUPPORTED}.
      */
     public int setLanguage(final Locale loc) {
-        return runAction(new Action<Integer>() {
-            @Override
-            public Integer run(ITextToSpeechService service) throws RemoteException {
-                if (loc == null) {
-                    return LANG_NOT_SUPPORTED;
-                }
-                String language = null, country = null;
-                try {
-                    language = loc.getISO3Language();
-                } catch (MissingResourceException e) {
-                    Log.w(TAG, "Couldn't retrieve ISO 639-2/T language code for locale: " + loc, e);
-                    return LANG_NOT_SUPPORTED;
-                }
-
-                try {
-                    country = loc.getISO3Country();
-                } catch (MissingResourceException e) {
-                    Log.w(TAG, "Couldn't retrieve ISO 3166 country code for locale: " + loc, e);
-                    return LANG_NOT_SUPPORTED;
-                }
-
-                String variant = loc.getVariant();
-
-                // As of API level 21, setLanguage is implemented using setVoice.
-                // (which, in the default implementation, will call loadLanguage on the service
-                // interface).
-
-                // Sanitize locale using isLanguageAvailable.
-                int result = service.isLanguageAvailable(language, country, variant);
-                if (result >= LANG_AVAILABLE) {
-                    // Get the default voice for the locale.
-                    String voiceName = service.getDefaultVoiceNameFor(language, country, variant);
-                    if (TextUtils.isEmpty(voiceName)) {
-                        Log.w(TAG, "Couldn't find the default voice for " + language + "-" +
-                                country + "-" + variant);
-                        return LANG_NOT_SUPPORTED;
-                    }
-
-                    // Load it.
-                    if (service.loadVoice(getCallerIdentity(), voiceName) == TextToSpeech.ERROR) {
-                        Log.w(TAG, "The service claimed " + language + "-" + country + "-"
-                                + variant + " was available with voice name " + voiceName
-                                + " but loadVoice returned ERROR");
-                        return LANG_NOT_SUPPORTED;
-                    }
-
-                    // Set the language/country/variant of the voice, so #getLanguage will return
-                    // the currently set voice locale when called.
-                    Voice voice = getVoice(service, voiceName);
-                    if (voice == null) {
-                        Log.w(TAG, "getDefaultVoiceNameFor returned " + voiceName + " for locale "
-                                + language + "-" + country + "-" + variant
-                                + " but getVoice returns null");
-                        return LANG_NOT_SUPPORTED;
-                    }
-                    String voiceLanguage = "";
-                    try {
-                        voiceLanguage = voice.getLocale().getISO3Language();
-                    } catch (MissingResourceException e) {
-                        Log.w(TAG, "Couldn't retrieve ISO 639-2/T language code for locale: " +
-                                voice.getLocale(), e);
-                    }
-
-                    String voiceCountry = "";
-                    try {
-                        voiceCountry = voice.getLocale().getISO3Country();
-                    } catch (MissingResourceException e) {
-                        Log.w(TAG, "Couldn't retrieve ISO 3166 country code for locale: " +
-                                voice.getLocale(), e);
-                    }
-                    mParams.putString(Engine.KEY_PARAM_VOICE_NAME, voiceName);
-                    mParams.putString(Engine.KEY_PARAM_LANGUAGE, voiceLanguage);
-                    mParams.putString(Engine.KEY_PARAM_COUNTRY, voiceCountry);
-                    mParams.putString(Engine.KEY_PARAM_VARIANT, voice.getLocale().getVariant());
-                }
-                return result;
+        return runAction((ITextToSpeechService service) -> {
+            if (loc == null) {
+                return LANG_NOT_SUPPORTED;
             }
+            String language = null, country = null;
+            try {
+                language = loc.getISO3Language();
+            } catch (MissingResourceException e) {
+                Log.w(TAG, "Couldn't retrieve ISO 639-2/T language code for locale: " + loc, e);
+                return LANG_NOT_SUPPORTED;
+            }
+
+            try {
+                country = loc.getISO3Country();
+            } catch (MissingResourceException e) {
+                Log.w(TAG, "Couldn't retrieve ISO 3166 country code for locale: " + loc, e);
+                return LANG_NOT_SUPPORTED;
+            }
+
+            String variant = loc.getVariant();
+
+            // As of API level 21, setLanguage is implemented using setVoice.
+            // (which, in the default implementation, will call loadLanguage on the service
+            // interface).
+
+            // Sanitize locale using isLanguageAvailable.
+            int result = service.isLanguageAvailable(language, country, variant);
+            if (result >= LANG_AVAILABLE) {
+                // Get the default voice for the locale.
+                String voiceName = service.getDefaultVoiceNameFor(language, country, variant);
+                if (TextUtils.isEmpty(voiceName)) {
+                    Log.w(TAG, "Couldn't find the default voice for " + language + "-"
+                            + country + "-" + variant);
+                    return LANG_NOT_SUPPORTED;
+                }
+
+                // Load it.
+                if (service.loadVoice(getCallerIdentity(), voiceName) == TextToSpeech.ERROR) {
+                    Log.w(TAG, "The service claimed " + language + "-" + country + "-"
+                            + variant + " was available with voice name " + voiceName
+                            + " but loadVoice returned ERROR");
+                    return LANG_NOT_SUPPORTED;
+                }
+
+                // Set the language/country/variant of the voice, so #getLanguage will return
+                // the currently set voice locale when called.
+                Voice voice = getVoice(service, voiceName);
+                if (voice == null) {
+                    Log.w(TAG, "getDefaultVoiceNameFor returned " + voiceName + " for locale "
+                            + language + "-" + country + "-" + variant
+                            + " but getVoice returns null");
+                    return LANG_NOT_SUPPORTED;
+                }
+                String voiceLanguage = "";
+                try {
+                    voiceLanguage = voice.getLocale().getISO3Language();
+                } catch (MissingResourceException e) {
+                    Log.w(TAG, "Couldn't retrieve ISO 639-2/T language code for locale: "
+                            + voice.getLocale(), e);
+                }
+
+                String voiceCountry = "";
+                try {
+                    voiceCountry = voice.getLocale().getISO3Country();
+                } catch (MissingResourceException e) {
+                    Log.w(TAG, "Couldn't retrieve ISO 3166 country code for locale: "
+                            + voice.getLocale(), e);
+                }
+                mParams.putString(Engine.KEY_PARAM_VOICE_NAME, voiceName);
+                mParams.putString(Engine.KEY_PARAM_LANGUAGE, voiceLanguage);
+                mParams.putString(Engine.KEY_PARAM_COUNTRY, voiceCountry);
+                mParams.putString(Engine.KEY_PARAM_VARIANT, voice.getLocale().getVariant());
+            }
+            return result;
         }, LANG_NOT_SUPPORTED, "setLanguage");
     }
 
@@ -1582,16 +1556,13 @@
      */
     @Deprecated
     public Locale getLanguage() {
-        return runAction(new Action<Locale>() {
-            @Override
-            public Locale run(ITextToSpeechService service) {
-                /* No service call, but we're accessing mParams, hence need for
-                   wrapping it as an Action instance */
-                String lang = mParams.getString(Engine.KEY_PARAM_LANGUAGE, "");
-                String country = mParams.getString(Engine.KEY_PARAM_COUNTRY, "");
-                String variant = mParams.getString(Engine.KEY_PARAM_VARIANT, "");
-                return new Locale(lang, country, variant);
-            }
+        return runAction((ITextToSpeechService service) -> {
+            /* No service call, but we're accessing mParams, hence need for
+               wrapping it as an Action instance */
+            String lang = mParams.getString(Engine.KEY_PARAM_LANGUAGE, "");
+            String country = mParams.getString(Engine.KEY_PARAM_COUNTRY, "");
+            String variant = mParams.getString(Engine.KEY_PARAM_VARIANT, "");
+            return new Locale(lang, country, variant);
         }, null, "getLanguage");
     }
 
@@ -1599,19 +1570,16 @@
      * Query the engine about the set of available languages.
      */
     public Set<Locale> getAvailableLanguages() {
-        return runAction(new Action<Set<Locale>>() {
-            @Override
-            public Set<Locale> run(ITextToSpeechService service) throws RemoteException {
-                List<Voice> voices = service.getVoices();
-                if (voices == null) {
-                    return new HashSet<Locale>();
-                }
-                HashSet<Locale> locales = new HashSet<Locale>();
-                for (Voice voice : voices) {
-                    locales.add(voice.getLocale());
-                }
-                return locales;
+        return runAction((ITextToSpeechService service) -> {
+            List<Voice> voices = service.getVoices();
+            if (voices == null) {
+                return new HashSet<Locale>();
             }
+            HashSet<Locale> locales = new HashSet<Locale>();
+            for (Voice voice : voices) {
+                locales.add(voice.getLocale());
+            }
+            return locales;
         }, null, "getAvailableLanguages");
     }
 
@@ -1625,12 +1593,9 @@
      * @see Voice
      */
     public Set<Voice> getVoices() {
-        return runAction(new Action<Set<Voice>>() {
-            @Override
-            public Set<Voice> run(ITextToSpeechService service) throws RemoteException {
-                List<Voice> voices = service.getVoices();
-                return (voices != null)  ? new HashSet<Voice>(voices) : new HashSet<Voice>();
-            }
+        return runAction((ITextToSpeechService service) -> {
+            List<Voice> voices = service.getVoices();
+            return (voices != null)  ? new HashSet<Voice>(voices) : new HashSet<Voice>();
         }, null, "getVoices");
     }
 
@@ -1645,36 +1610,33 @@
      * @see Voice
      */
     public int setVoice(final Voice voice) {
-        return runAction(new Action<Integer>() {
-            @Override
-            public Integer run(ITextToSpeechService service) throws RemoteException {
-                int result = service.loadVoice(getCallerIdentity(), voice.getName());
-                if (result == SUCCESS) {
-                    mParams.putString(Engine.KEY_PARAM_VOICE_NAME, voice.getName());
+        return runAction((ITextToSpeechService service) -> {
+            int result = service.loadVoice(getCallerIdentity(), voice.getName());
+            if (result == SUCCESS) {
+                mParams.putString(Engine.KEY_PARAM_VOICE_NAME, voice.getName());
 
-                    // Set the language/country/variant, so #getLanguage will return the voice
-                    // locale when called.
-                    String language = "";
-                    try {
-                        language = voice.getLocale().getISO3Language();
-                    } catch (MissingResourceException e) {
-                        Log.w(TAG, "Couldn't retrieve ISO 639-2/T language code for locale: " +
-                                voice.getLocale(), e);
-                    }
-
-                    String country = "";
-                    try {
-                        country = voice.getLocale().getISO3Country();
-                    } catch (MissingResourceException e) {
-                        Log.w(TAG, "Couldn't retrieve ISO 3166 country code for locale: " +
-                                voice.getLocale(), e);
-                    }
-                    mParams.putString(Engine.KEY_PARAM_LANGUAGE, language);
-                    mParams.putString(Engine.KEY_PARAM_COUNTRY, country);
-                    mParams.putString(Engine.KEY_PARAM_VARIANT, voice.getLocale().getVariant());
+                // Set the language/country/variant, so #getLanguage will return the voice
+                // locale when called.
+                String language = "";
+                try {
+                    language = voice.getLocale().getISO3Language();
+                } catch (MissingResourceException e) {
+                    Log.w(TAG, "Couldn't retrieve ISO 639-2/T language code for locale: "
+                            + voice.getLocale(), e);
                 }
-                return result;
+
+                String country = "";
+                try {
+                    country = voice.getLocale().getISO3Country();
+                } catch (MissingResourceException e) {
+                    Log.w(TAG, "Couldn't retrieve ISO 3166 country code for locale: "
+                            + voice.getLocale(), e);
+                }
+                mParams.putString(Engine.KEY_PARAM_LANGUAGE, language);
+                mParams.putString(Engine.KEY_PARAM_COUNTRY, country);
+                mParams.putString(Engine.KEY_PARAM_VARIANT, voice.getLocale().getVariant());
             }
+            return result;
         }, LANG_NOT_SUPPORTED, "setVoice");
     }
 
@@ -1689,15 +1651,12 @@
      * @see Voice
      */
     public Voice getVoice() {
-        return runAction(new Action<Voice>() {
-            @Override
-            public Voice run(ITextToSpeechService service) throws RemoteException {
-                String voiceName = mParams.getString(Engine.KEY_PARAM_VOICE_NAME, "");
-                if (TextUtils.isEmpty(voiceName)) {
-                    return null;
-                }
-                return getVoice(service, voiceName);
+        return runAction((ITextToSpeechService service) -> {
+            String voiceName = mParams.getString(Engine.KEY_PARAM_VOICE_NAME, "");
+            if (TextUtils.isEmpty(voiceName)) {
+                return null;
             }
+            return getVoice(service, voiceName);
         }, null, "getVoice");
     }
 
@@ -1730,45 +1689,42 @@
      *     on error.
      */
     public Voice getDefaultVoice() {
-        return runAction(new Action<Voice>() {
-            @Override
-            public Voice run(ITextToSpeechService service) throws RemoteException {
+        return runAction((ITextToSpeechService service) -> {
 
-                String[] defaultLanguage = service.getClientDefaultLanguage();
+            String[] defaultLanguage = service.getClientDefaultLanguage();
 
-                if (defaultLanguage == null || defaultLanguage.length == 0) {
-                    Log.e(TAG, "service.getClientDefaultLanguage() returned empty array");
-                    return null;
-                }
-                String language = defaultLanguage[0];
-                String country = (defaultLanguage.length > 1) ? defaultLanguage[1] : "";
-                String variant = (defaultLanguage.length > 2) ? defaultLanguage[2] : "";
-
-                // Sanitize the locale using isLanguageAvailable.
-                int result = service.isLanguageAvailable(language, country, variant);
-                if (result < LANG_AVAILABLE) {
-                    // The default language is not supported.
-                    return null;
-                }
-
-                // Get the default voice name
-                String voiceName = service.getDefaultVoiceNameFor(language, country, variant);
-                if (TextUtils.isEmpty(voiceName)) {
-                    return null;
-                }
-
-                // Find it
-                List<Voice> voices = service.getVoices();
-                if (voices == null) {
-                    return null;
-                }
-                for (Voice voice : voices) {
-                    if (voice.getName().equals(voiceName)) {
-                        return voice;
-                    }
-                }
+            if (defaultLanguage == null || defaultLanguage.length == 0) {
+                Log.e(TAG, "service.getClientDefaultLanguage() returned empty array");
                 return null;
             }
+            String language = defaultLanguage[0];
+            String country = (defaultLanguage.length > 1) ? defaultLanguage[1] : "";
+            String variant = (defaultLanguage.length > 2) ? defaultLanguage[2] : "";
+
+            // Sanitize the locale using isLanguageAvailable.
+            int result = service.isLanguageAvailable(language, country, variant);
+            if (result < LANG_AVAILABLE) {
+                // The default language is not supported.
+                return null;
+            }
+
+            // Get the default voice name
+            String voiceName = service.getDefaultVoiceNameFor(language, country, variant);
+            if (TextUtils.isEmpty(voiceName)) {
+                return null;
+            }
+
+            // Find it
+            List<Voice> voices = service.getVoices();
+            if (voices == null) {
+                return null;
+            }
+            for (Voice voice : voices) {
+                if (voice.getName().equals(voiceName)) {
+                    return voice;
+                }
+            }
+            return null;
         }, null, "getDefaultVoice");
     }
 
@@ -1784,31 +1740,55 @@
      *         {@link #LANG_MISSING_DATA} and {@link #LANG_NOT_SUPPORTED}.
      */
     public int isLanguageAvailable(final Locale loc) {
-        return runAction(new Action<Integer>() {
-            @Override
-            public Integer run(ITextToSpeechService service) throws RemoteException {
-                String language = null, country = null;
+        return runAction((ITextToSpeechService service) -> {
+            String language = null, country = null;
 
-                try {
-                    language = loc.getISO3Language();
-                } catch (MissingResourceException e) {
-                    Log.w(TAG, "Couldn't retrieve ISO 639-2/T language code for locale: " + loc, e);
-                    return LANG_NOT_SUPPORTED;
-                }
-
-                try {
-                    country = loc.getISO3Country();
-                } catch (MissingResourceException e) {
-                    Log.w(TAG, "Couldn't retrieve ISO 3166 country code for locale: " + loc, e);
-                    return LANG_NOT_SUPPORTED;
-                }
-
-                return service.isLanguageAvailable(language, country, loc.getVariant());
+            try {
+                language = loc.getISO3Language();
+            } catch (MissingResourceException e) {
+                Log.w(TAG, "Couldn't retrieve ISO 639-2/T language code for locale: " + loc, e);
+                return LANG_NOT_SUPPORTED;
             }
+
+            try {
+                country = loc.getISO3Country();
+            } catch (MissingResourceException e) {
+                Log.w(TAG, "Couldn't retrieve ISO 3166 country code for locale: " + loc, e);
+                return LANG_NOT_SUPPORTED;
+            }
+
+            return service.isLanguageAvailable(language, country, loc.getVariant());
         }, LANG_NOT_SUPPORTED, "isLanguageAvailable");
     }
 
     /**
+     * Synthesizes the given text to a ParcelFileDescriptor using the specified parameters.
+     * This method is asynchronous, i.e. the method just adds the request to the queue of TTS
+     * requests and then returns. The synthesis might not have finished (or even started!) at the
+     * time when this method returns. In order to reliably detect errors during synthesis,
+     * we recommend setting an utterance progress listener (see
+     * {@link #setOnUtteranceProgressListener}).
+     *
+     * @param text The text that should be synthesized. No longer than
+     *            {@link #getMaxSpeechInputLength()} characters.
+     * @param params Parameters for the request. Can be null.
+     *            Engine specific parameters may be passed in but the parameter keys
+     *            must be prefixed by the name of the engine they are intended for. For example
+     *            the keys "com.svox.pico_foo" and "com.svox.pico:bar" will be passed to the engine
+     *            named "com.svox.pico" if it is being used.
+     * @param fileDescriptor ParcelFileDescriptor to write the generated audio data to.
+     * @param utteranceId An unique identifier for this request.
+     * @return {@link #ERROR} or {@link #SUCCESS} of <b>queuing</b> the synthesizeToFile operation.
+     */
+    public int synthesizeToFile(@NonNull final CharSequence text, @NonNull final Bundle params,
+            @NonNull final ParcelFileDescriptor fileDescriptor, @NonNull final String utteranceId) {
+        return runAction((ITextToSpeechService service) -> {
+            return service.synthesizeToFileDescriptor(getCallerIdentity(), text,
+                    fileDescriptor, getParams(params), utteranceId);
+        }, ERROR, "synthesizeToFile");
+    }
+
+    /**
      * Synthesizes the given text to a file using the specified parameters.
      * This method is asynchronous, i.e. the method just adds the request to the queue of TTS
      * requests and then returns. The synthesis might not have finished (or even started!) at the
@@ -1829,33 +1809,26 @@
      */
     public int synthesizeToFile(final CharSequence text, final Bundle params,
             final File file, final String utteranceId) {
-        return runAction(new Action<Integer>() {
-            @Override
-            public Integer run(ITextToSpeechService service) throws RemoteException {
-                ParcelFileDescriptor fileDescriptor;
-                int returnValue;
-                try {
-                    if(file.exists() && !file.canWrite()) {
-                        Log.e(TAG, "Can't write to " + file);
-                        return ERROR;
-                    }
-                    fileDescriptor = ParcelFileDescriptor.open(file,
-                            ParcelFileDescriptor.MODE_WRITE_ONLY |
-                            ParcelFileDescriptor.MODE_CREATE |
-                            ParcelFileDescriptor.MODE_TRUNCATE);
-                    returnValue = service.synthesizeToFileDescriptor(getCallerIdentity(), text,
-                            fileDescriptor, getParams(params), utteranceId);
-                    fileDescriptor.close();
-                    return returnValue;
-                } catch (FileNotFoundException e) {
-                    Log.e(TAG, "Opening file " + file + " failed", e);
-                    return ERROR;
-                } catch (IOException e) {
-                    Log.e(TAG, "Closing file " + file + " failed", e);
-                    return ERROR;
-                }
-            }
-        }, ERROR, "synthesizeToFile");
+        if (file.exists() && !file.canWrite()) {
+            Log.e(TAG, "Can't write to " + file);
+            return ERROR;
+        }
+        try (
+            ParcelFileDescriptor fileDescriptor = ParcelFileDescriptor.open(file,
+                ParcelFileDescriptor.MODE_WRITE_ONLY
+                | ParcelFileDescriptor.MODE_CREATE
+                | ParcelFileDescriptor.MODE_TRUNCATE);
+        ) {
+            int returnValue = synthesizeToFile(text, params, fileDescriptor, utteranceId);
+            fileDescriptor.close();
+            return returnValue;
+        } catch (FileNotFoundException e) {
+            Log.e(TAG, "Opening file " + file + " failed", e);
+            return ERROR;
+        } catch (IOException e) {
+            Log.e(TAG, "Closing file " + file + " failed", e);
+            return ERROR;
+        }
     }
 
     /**
diff --git a/core/java/android/text/format/DateUtils.java b/core/java/android/text/format/DateUtils.java
index e94b800..b00a938 100644
--- a/core/java/android/text/format/DateUtils.java
+++ b/core/java/android/text/format/DateUtils.java
@@ -32,6 +32,9 @@
 import libcore.icu.RelativeDateTimeFormatter;
 
 import java.io.IOException;
+import java.time.Instant;
+import java.time.LocalDateTime;
+import java.time.ZoneId;
 import java.util.Calendar;
 import java.util.Date;
 import java.util.Formatter;
@@ -502,17 +505,21 @@
      * @return true if the supplied when is today else false
      */
     public static boolean isToday(long when) {
-        Time time = new Time();
-        time.set(when);
+        return isSameDate(when, System.currentTimeMillis());
+    }
 
-        int thenYear = time.year;
-        int thenMonth = time.month;
-        int thenMonthDay = time.monthDay;
+    private static boolean isSameDate(long oneMillis, long twoMillis) {
+        ZoneId zoneId = ZoneId.systemDefault();
 
-        time.set(System.currentTimeMillis());
-        return (thenYear == time.year)
-                && (thenMonth == time.month)
-                && (thenMonthDay == time.monthDay);
+        Instant oneInstant = Instant.ofEpochMilli(oneMillis);
+        LocalDateTime oneLocalDateTime = LocalDateTime.ofInstant(oneInstant, zoneId);
+
+        Instant twoInstant = Instant.ofEpochMilli(twoMillis);
+        LocalDateTime twoLocalDateTime = LocalDateTime.ofInstant(twoInstant, zoneId);
+
+        return (oneLocalDateTime.getYear() == twoLocalDateTime.getYear())
+                && (oneLocalDateTime.getMonthValue() == twoLocalDateTime.getMonthValue())
+                && (oneLocalDateTime.getDayOfMonth() == twoLocalDateTime.getDayOfMonth());
     }
 
     /**
diff --git a/core/java/android/util/ArrayMap.java b/core/java/android/util/ArrayMap.java
index d899dde..3f44e48 100644
--- a/core/java/android/util/ArrayMap.java
+++ b/core/java/android/util/ArrayMap.java
@@ -48,6 +48,8 @@
  * you have no control over this shrinking -- if you set a capacity and then remove an
  * item, it may reduce the capacity to better match the current size.  In the future an
  * explicit call to set the capacity should turn off this aggressive shrinking behavior.</p>
+ *
+ * <p>This structure is <b>NOT</b> thread-safe.</p>
  */
 public final class ArrayMap<K, V> implements Map<K, V> {
     private static final boolean DEBUG = false;
@@ -103,15 +105,21 @@
     static Object[] mTwiceBaseCache;
     @UnsupportedAppUsage(maxTargetSdk = 28) // Allocations are an implementation detail.
     static int mTwiceBaseCacheSize;
+    /**
+     * Separate locks for each cache since each can be accessed independently of the other without
+     * risk of a deadlock.
+     */
+    private static final Object sBaseCacheLock = new Object();
+    private static final Object sTwiceBaseCacheLock = new Object();
 
-    final boolean mIdentityHashCode;
+    private final boolean mIdentityHashCode;
     @UnsupportedAppUsage(maxTargetSdk = 28) // Hashes are an implementation detail. Use public key/value API.
     int[] mHashes;
     @UnsupportedAppUsage(maxTargetSdk = 28) // Storage is an implementation detail. Use public key/value API.
     Object[] mArray;
     @UnsupportedAppUsage(maxTargetSdk = 28) // Use size()
     int mSize;
-    MapCollections<K, V> mCollections;
+    private MapCollections<K, V> mCollections;
 
     private static int binarySearchHashes(int[] hashes, int N, int hash) {
         try {
@@ -209,31 +217,57 @@
             throw new UnsupportedOperationException("ArrayMap is immutable");
         }
         if (size == (BASE_SIZE*2)) {
-            synchronized (ArrayMap.class) {
+            synchronized (sTwiceBaseCacheLock) {
                 if (mTwiceBaseCache != null) {
                     final Object[] array = mTwiceBaseCache;
                     mArray = array;
-                    mTwiceBaseCache = (Object[])array[0];
-                    mHashes = (int[])array[1];
-                    array[0] = array[1] = null;
-                    mTwiceBaseCacheSize--;
-                    if (DEBUG) Log.d(TAG, "Retrieving 2x cache " + mHashes
-                            + " now have " + mTwiceBaseCacheSize + " entries");
-                    return;
+                    try {
+                        mTwiceBaseCache = (Object[]) array[0];
+                        mHashes = (int[]) array[1];
+                        if (mHashes != null) {
+                            array[0] = array[1] = null;
+                            mTwiceBaseCacheSize--;
+                            if (DEBUG) {
+                                Log.d(TAG, "Retrieving 2x cache " + mHashes
+                                        + " now have " + mTwiceBaseCacheSize + " entries");
+                            }
+                            return;
+                        }
+                    } catch (ClassCastException e) {
+                    }
+                    // Whoops!  Someone trampled the array (probably due to not protecting
+                    // their access with a lock).  Our cache is corrupt; report and give up.
+                    Slog.wtf(TAG, "Found corrupt ArrayMap cache: [0]=" + array[0]
+                            + " [1]=" + array[1]);
+                    mTwiceBaseCache = null;
+                    mTwiceBaseCacheSize = 0;
                 }
             }
         } else if (size == BASE_SIZE) {
-            synchronized (ArrayMap.class) {
+            synchronized (sBaseCacheLock) {
                 if (mBaseCache != null) {
                     final Object[] array = mBaseCache;
                     mArray = array;
-                    mBaseCache = (Object[])array[0];
-                    mHashes = (int[])array[1];
-                    array[0] = array[1] = null;
-                    mBaseCacheSize--;
-                    if (DEBUG) Log.d(TAG, "Retrieving 1x cache " + mHashes
-                            + " now have " + mBaseCacheSize + " entries");
-                    return;
+                    try {
+                        mBaseCache = (Object[]) array[0];
+                        mHashes = (int[]) array[1];
+                        if (mHashes != null) {
+                            array[0] = array[1] = null;
+                            mBaseCacheSize--;
+                            if (DEBUG) {
+                                Log.d(TAG, "Retrieving 1x cache " + mHashes
+                                        + " now have " + mBaseCacheSize + " entries");
+                            }
+                            return;
+                        }
+                    } catch (ClassCastException e) {
+                    }
+                    // Whoops!  Someone trampled the array (probably due to not protecting
+                    // their access with a lock).  Our cache is corrupt; report and give up.
+                    Slog.wtf(TAG, "Found corrupt ArrayMap cache: [0]=" + array[0]
+                            + " [1]=" + array[1]);
+                    mBaseCache = null;
+                    mBaseCacheSize = 0;
                 }
             }
         }
@@ -242,10 +276,14 @@
         mArray = new Object[size<<1];
     }
 
+    /**
+     * Make sure <b>NOT</b> to call this method with arrays that can still be modified. In other
+     * words, don't pass mHashes or mArray in directly.
+     */
     @UnsupportedAppUsage(maxTargetSdk = 28) // Allocations are an implementation detail.
     private static void freeArrays(final int[] hashes, final Object[] array, final int size) {
         if (hashes.length == (BASE_SIZE*2)) {
-            synchronized (ArrayMap.class) {
+            synchronized (sTwiceBaseCacheLock) {
                 if (mTwiceBaseCacheSize < CACHE_SIZE) {
                     array[0] = mTwiceBaseCache;
                     array[1] = hashes;
@@ -259,7 +297,7 @@
                 }
             }
         } else if (hashes.length == BASE_SIZE) {
-            synchronized (ArrayMap.class) {
+            synchronized (sBaseCacheLock) {
                 if (mBaseCacheSize < CACHE_SIZE) {
                     array[0] = mBaseCache;
                     array[1] = hashes;
diff --git a/core/java/android/util/ArraySet.java b/core/java/android/util/ArraySet.java
index 3fa914f..44c5af2 100644
--- a/core/java/android/util/ArraySet.java
+++ b/core/java/android/util/ArraySet.java
@@ -23,6 +23,7 @@
 
 import java.lang.reflect.Array;
 import java.util.Collection;
+import java.util.ConcurrentModificationException;
 import java.util.Iterator;
 import java.util.Map;
 import java.util.Set;
@@ -46,6 +47,8 @@
  * you have no control over this shrinking -- if you set a capacity and then remove an
  * item, it may reduce the capacity to better match the current size.  In the future an
  * explicit call to set the capacity should turn off this aggressive shrinking behavior.</p>
+ *
+ * <p>This structure is <b>NOT</b> thread-safe.</p>
  */
 public final class ArraySet<E> implements Collection<E>, Set<E> {
     private static final boolean DEBUG = false;
@@ -72,15 +75,30 @@
     static int sBaseCacheSize;
     static Object[] sTwiceBaseCache;
     static int sTwiceBaseCacheSize;
+    /**
+     * Separate locks for each cache since each can be accessed independently of the other without
+     * risk of a deadlock.
+     */
+    private static final Object sBaseCacheLock = new Object();
+    private static final Object sTwiceBaseCacheLock = new Object();
 
-    final boolean mIdentityHashCode;
+    private final boolean mIdentityHashCode;
     @UnsupportedAppUsage(maxTargetSdk = 28) // Hashes are an implementation detail. Use public API.
     int[] mHashes;
     @UnsupportedAppUsage(maxTargetSdk = 28) // Storage is an implementation detail. Use public API.
     Object[] mArray;
     @UnsupportedAppUsage(maxTargetSdk = 28) // Use size()
     int mSize;
-    MapCollections<E, E> mCollections;
+    private MapCollections<E, E> mCollections;
+
+    private int binarySearch(int[] hashes, int hash) {
+        try {
+            return ContainerHelpers.binarySearch(hashes, mSize, hash);
+        } catch (ArrayIndexOutOfBoundsException e) {
+            throw new ConcurrentModificationException();
+        }
+    }
+
 
     @UnsupportedAppUsage(maxTargetSdk = 28) // Hashes are an implementation detail. Use indexOfKey(Object).
     private int indexOf(Object key, int hash) {
@@ -91,7 +109,7 @@
             return ~0;
         }
 
-        int index = ContainerHelpers.binarySearch(mHashes, N, hash);
+        int index = binarySearch(mHashes, hash);
 
         // If the hash code wasn't found, then we have no entry for this key.
         if (index < 0) {
@@ -130,7 +148,7 @@
             return ~0;
         }
 
-        int index = ContainerHelpers.binarySearch(mHashes, N, 0);
+        int index = binarySearch(mHashes, 0);
 
         // If the hash code wasn't found, then we have no entry for this key.
         if (index < 0) {
@@ -163,20 +181,22 @@
     @UnsupportedAppUsage(maxTargetSdk = 28) // Allocations are an implementation detail.
     private void allocArrays(final int size) {
         if (size == (BASE_SIZE * 2)) {
-            synchronized (ArraySet.class) {
+            synchronized (sTwiceBaseCacheLock) {
                 if (sTwiceBaseCache != null) {
                     final Object[] array = sTwiceBaseCache;
                     try {
                         mArray = array;
                         sTwiceBaseCache = (Object[]) array[0];
                         mHashes = (int[]) array[1];
-                        array[0] = array[1] = null;
-                        sTwiceBaseCacheSize--;
-                        if (DEBUG) {
-                            Log.d(TAG, "Retrieving 2x cache " + mHashes + " now have "
-                                    + sTwiceBaseCacheSize + " entries");
-                    }
-                    return;
+                        if (mHashes != null) {
+                            array[0] = array[1] = null;
+                            sTwiceBaseCacheSize--;
+                            if (DEBUG) {
+                                Log.d(TAG, "Retrieving 2x cache " + mHashes + " now have "
+                                        + sTwiceBaseCacheSize + " entries");
+                            }
+                            return;
+                        }
                     } catch (ClassCastException e) {
                     }
                     // Whoops!  Someone trampled the array (probably due to not protecting
@@ -188,20 +208,22 @@
                 }
             }
         } else if (size == BASE_SIZE) {
-            synchronized (ArraySet.class) {
+            synchronized (sBaseCacheLock) {
                 if (sBaseCache != null) {
                     final Object[] array = sBaseCache;
                     try {
                         mArray = array;
                         sBaseCache = (Object[]) array[0];
                         mHashes = (int[]) array[1];
-                        array[0] = array[1] = null;
-                        sBaseCacheSize--;
-                        if (DEBUG) {
-                            Log.d(TAG, "Retrieving 1x cache " + mHashes + " now have " + sBaseCacheSize
-                                    + " entries");
+                        if (mHashes != null) {
+                            array[0] = array[1] = null;
+                            sBaseCacheSize--;
+                            if (DEBUG) {
+                                Log.d(TAG, "Retrieving 1x cache " + mHashes + " now have "
+                                        + sBaseCacheSize + " entries");
+                            }
+                            return;
                         }
-                        return;
                     } catch (ClassCastException e) {
                     }
                     // Whoops!  Someone trampled the array (probably due to not protecting
@@ -218,10 +240,14 @@
         mArray = new Object[size];
     }
 
+    /**
+     * Make sure <b>NOT</b> to call this method with arrays that can still be modified. In other
+     * words, don't pass mHashes or mArray in directly.
+     */
     @UnsupportedAppUsage(maxTargetSdk = 28) // Allocations are an implementation detail.
     private static void freeArrays(final int[] hashes, final Object[] array, final int size) {
         if (hashes.length == (BASE_SIZE * 2)) {
-            synchronized (ArraySet.class) {
+            synchronized (sTwiceBaseCacheLock) {
                 if (sTwiceBaseCacheSize < CACHE_SIZE) {
                     array[0] = sTwiceBaseCache;
                     array[1] = hashes;
@@ -237,7 +263,7 @@
                 }
             }
         } else if (hashes.length == BASE_SIZE) {
-            synchronized (ArraySet.class) {
+            synchronized (sBaseCacheLock) {
                 if (sBaseCacheSize < CACHE_SIZE) {
                     array[0] = sBaseCache;
                     array[1] = hashes;
@@ -308,10 +334,16 @@
     @Override
     public void clear() {
         if (mSize != 0) {
-            freeArrays(mHashes, mArray, mSize);
+            final int[] ohashes = mHashes;
+            final Object[] oarray = mArray;
+            final int osize = mSize;
             mHashes = EmptyArray.INT;
             mArray = EmptyArray.OBJECT;
             mSize = 0;
+            freeArrays(ohashes, oarray, osize);
+        }
+        if (mSize != 0) {
+            throw new ConcurrentModificationException();
         }
     }
 
@@ -320,6 +352,7 @@
      * items.
      */
     public void ensureCapacity(int minimumCapacity) {
+        final int oSize = mSize;
         if (mHashes.length < minimumCapacity) {
             final int[] ohashes = mHashes;
             final Object[] oarray = mArray;
@@ -330,6 +363,9 @@
             }
             freeArrays(ohashes, oarray, mSize);
         }
+        if (mSize != oSize) {
+            throw new ConcurrentModificationException();
+        }
     }
 
     /**
@@ -400,11 +436,10 @@
      *
      * @param value the object to add.
      * @return {@code true} if this set is modified, {@code false} otherwise.
-     * @throws ClassCastException
-     *             when the class of the object is inappropriate for this set.
      */
     @Override
     public boolean add(E value) {
+        final int oSize = mSize;
         final int hash;
         int index;
         if (value == null) {
@@ -419,9 +454,9 @@
         }
 
         index = ~index;
-        if (mSize >= mHashes.length) {
-            final int n = mSize >= (BASE_SIZE * 2) ? (mSize + (mSize >> 1))
-                    : (mSize >= BASE_SIZE ? (BASE_SIZE * 2) : BASE_SIZE);
+        if (oSize >= mHashes.length) {
+            final int n = oSize >= (BASE_SIZE * 2) ? (oSize + (oSize >> 1))
+                    : (oSize >= BASE_SIZE ? (BASE_SIZE * 2) : BASE_SIZE);
 
             if (DEBUG) Log.d(TAG, "add: grow from " + mHashes.length + " to " + n);
 
@@ -429,21 +464,29 @@
             final Object[] oarray = mArray;
             allocArrays(n);
 
+            if (oSize != mSize) {
+                throw new ConcurrentModificationException();
+            }
+
             if (mHashes.length > 0) {
-                if (DEBUG) Log.d(TAG, "add: copy 0-" + mSize + " to 0");
+                if (DEBUG) Log.d(TAG, "add: copy 0-" + oSize + " to 0");
                 System.arraycopy(ohashes, 0, mHashes, 0, ohashes.length);
                 System.arraycopy(oarray, 0, mArray, 0, oarray.length);
             }
 
-            freeArrays(ohashes, oarray, mSize);
+            freeArrays(ohashes, oarray, oSize);
         }
 
-        if (index < mSize) {
+        if (index < oSize) {
             if (DEBUG) {
-                Log.d(TAG, "add: move " + index + "-" + (mSize - index) + " to " + (index + 1));
+                Log.d(TAG, "add: move " + index + "-" + (oSize - index) + " to " + (index + 1));
             }
-            System.arraycopy(mHashes, index, mHashes, index + 1, mSize - index);
-            System.arraycopy(mArray, index, mArray, index + 1, mSize - index);
+            System.arraycopy(mHashes, index, mHashes, index + 1, oSize - index);
+            System.arraycopy(mArray, index, mArray, index + 1, oSize - index);
+        }
+
+        if (oSize != mSize || index >= mHashes.length) {
+            throw new ConcurrentModificationException();
         }
 
         mHashes[index] = hash;
@@ -458,6 +501,7 @@
      * @hide
      */
     public void append(E value) {
+        final int oSize = mSize;
         final int index = mSize;
         final int hash = value == null ? 0
                 : (mIdentityHashCode ? System.identityHashCode(value) : value.hashCode());
@@ -476,6 +520,11 @@
             add(value);
             return;
         }
+
+        if (oSize != mSize) {
+            throw new ConcurrentModificationException();
+        }
+
         mSize = index + 1;
         mHashes[index] = hash;
         mArray[index] = value;
@@ -492,6 +541,9 @@
             if (N > 0) {
                 System.arraycopy(array.mHashes, 0, mHashes, 0, N);
                 System.arraycopy(array.mArray, 0, mArray, 0, N);
+                if (0 != mSize) {
+                    throw new ConcurrentModificationException();
+                }
                 mSize = N;
             }
         } else {
@@ -549,12 +601,14 @@
             // Check if exception should be thrown outside of the critical path.
             throw new ArrayIndexOutOfBoundsException(index);
         }
+        final int oSize = mSize;
         final Object old = mArray[index];
-        if (mSize <= 1) {
+        if (oSize <= 1) {
             // Now empty.
             if (DEBUG) Log.d(TAG, "remove: shrink from " + mHashes.length + " to 0");
             clear();
         } else {
+            final int nSize = oSize - 1;
             if (shouldShrink()) {
                 // Shrunk enough to reduce size of arrays.
                 final int n = getNewShrunkenSize();
@@ -565,31 +619,33 @@
                 final Object[] oarray = mArray;
                 allocArrays(n);
 
-                mSize--;
                 if (index > 0) {
                     if (DEBUG) Log.d(TAG, "remove: copy from 0-" + index + " to 0");
                     System.arraycopy(ohashes, 0, mHashes, 0, index);
                     System.arraycopy(oarray, 0, mArray, 0, index);
                 }
-                if (index < mSize) {
+                if (index < nSize) {
                     if (DEBUG) {
-                        Log.d(TAG, "remove: copy from " + (index + 1) + "-" + mSize
+                        Log.d(TAG, "remove: copy from " + (index + 1) + "-" + nSize
                                 + " to " + index);
                     }
-                    System.arraycopy(ohashes, index + 1, mHashes, index, mSize - index);
-                    System.arraycopy(oarray, index + 1, mArray, index, mSize - index);
+                    System.arraycopy(ohashes, index + 1, mHashes, index, nSize - index);
+                    System.arraycopy(oarray, index + 1, mArray, index, nSize - index);
                 }
             } else {
-                mSize--;
-                if (index < mSize) {
+                if (index < nSize) {
                     if (DEBUG) {
-                        Log.d(TAG, "remove: move " + (index + 1) + "-" + mSize + " to " + index);
+                        Log.d(TAG, "remove: move " + (index + 1) + "-" + nSize + " to " + index);
                     }
-                    System.arraycopy(mHashes, index + 1, mHashes, index, mSize - index);
-                    System.arraycopy(mArray, index + 1, mArray, index, mSize - index);
+                    System.arraycopy(mHashes, index + 1, mHashes, index, nSize - index);
+                    System.arraycopy(mArray, index + 1, mArray, index, nSize - index);
                 }
-                mArray[mSize] = null;
+                mArray[nSize] = null;
             }
+            if (oSize != mSize) {
+                throw new ConcurrentModificationException();
+            }
+            mSize = nSize;
         }
         return (E) old;
     }
diff --git a/core/java/android/util/LocalLog.java b/core/java/android/util/LocalLog.java
index 8b5659b..3a1df3e 100644
--- a/core/java/android/util/LocalLog.java
+++ b/core/java/android/util/LocalLog.java
@@ -60,9 +60,19 @@
     }
 
     public synchronized void dump(PrintWriter pw) {
+        dump("", pw);
+    }
+
+    /**
+     * Dumps the content of local log to print writer with each log entry predeced with indent
+     *
+     * @param indent indent that precedes each log entry
+     * @param pw printer writer to write into
+     */
+    public synchronized void dump(String indent, PrintWriter pw) {
         Iterator<String> itr = mLog.iterator();
         while (itr.hasNext()) {
-            pw.println(itr.next());
+            pw.printf("%s%s\n", indent, itr.next());
         }
     }
 
diff --git a/core/java/android/util/TimeUtils.java b/core/java/android/util/TimeUtils.java
index f8b38e9..07cecd3 100644
--- a/core/java/android/util/TimeUtils.java
+++ b/core/java/android/util/TimeUtils.java
@@ -62,9 +62,9 @@
     }
 
     /**
-     * Tries to return a frozen ICU time zone that would have had the specified offset
-     * and DST value at the specified moment in the specified country.
-     * Returns null if no suitable zone could be found.
+     * Returns a frozen ICU time zone that has / would have had the specified offset and DST value
+     * at the specified moment in the specified country. Returns null if no suitable zone could be
+     * found.
      */
     private static android.icu.util.TimeZone getIcuTimeZone(
             int offset, boolean dst, long when, String country) {
@@ -73,8 +73,15 @@
         }
 
         android.icu.util.TimeZone bias = android.icu.util.TimeZone.getDefault();
-        return TimeZoneFinder.getInstance()
-                .lookupTimeZoneByCountryAndOffset(country, offset, dst, when, bias);
+        CountryTimeZones countryTimeZones =
+                TimeZoneFinder.getInstance().lookupCountryTimeZones(country);
+        if (countryTimeZones == null) {
+            return null;
+        }
+
+        CountryTimeZones.OffsetResult offsetResult =
+                countryTimeZones.lookupByOffsetWithBias(offset, dst, when, bias);
+        return offsetResult != null ? offsetResult.mTimeZone : null;
     }
 
     /**
diff --git a/core/java/android/view/accessibility/AccessibilityRecord.java b/core/java/android/view/accessibility/AccessibilityRecord.java
index 42275a3..d7d7e21 100644
--- a/core/java/android/view/accessibility/AccessibilityRecord.java
+++ b/core/java/android/view/accessibility/AccessibilityRecord.java
@@ -856,6 +856,8 @@
         mScrollY = record.mScrollY;
         mMaxScrollX = record.mMaxScrollX;
         mMaxScrollY = record.mMaxScrollY;
+        mScrollDeltaX = record.mScrollDeltaX;
+        mScrollDeltaY = record.mScrollDeltaY;
         mAddedCount = record.mAddedCount;
         mRemovedCount = record.mRemovedCount;
         mClassName = record.mClassName;
@@ -882,6 +884,8 @@
         mScrollY = 0;
         mMaxScrollX = 0;
         mMaxScrollY = 0;
+        mScrollDeltaX = UNDEFINED;
+        mScrollDeltaY = UNDEFINED;
         mAddedCount = UNDEFINED;
         mRemovedCount = UNDEFINED;
         mClassName = null;
@@ -921,6 +925,8 @@
         append(builder, "ScrollY", mScrollY);
         append(builder, "MaxScrollX", mMaxScrollX);
         append(builder, "MaxScrollY", mMaxScrollY);
+        append(builder, "ScrollDeltaX", mScrollDeltaX);
+        append(builder, "ScrollDeltaY", mScrollDeltaY);
         append(builder, "AddedCount", mAddedCount);
         append(builder, "RemovedCount", mRemovedCount);
         append(builder, "ParcelableData", mParcelableData);
diff --git a/core/java/android/view/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java
index 1f7ae0e..3bbd321 100644
--- a/core/java/android/view/autofill/AutofillManager.java
+++ b/core/java/android/view/autofill/AutofillManager.java
@@ -43,6 +43,7 @@
 import android.os.IBinder;
 import android.os.Parcelable;
 import android.os.RemoteException;
+import android.os.SystemClock;
 import android.service.autofill.AutofillService;
 import android.service.autofill.FillEventHistory;
 import android.service.autofill.UserData;
@@ -1737,6 +1738,26 @@
 
             final SyncResultReceiver receiver = new SyncResultReceiver(SYNC_CALLS_TIMEOUT_MS);
             final ComponentName componentName = client.autofillClientGetComponentName();
+
+            if (!mEnabledForAugmentedAutofillOnly && mOptions != null
+                    && mOptions.isAutofillDisabledLocked(componentName)) {
+                if (mOptions.isAugmentedAutofillEnabled(mContext)) {
+                    if (sDebug) {
+                        Log.d(TAG, "startSession(" + componentName + "): disabled by service but "
+                                + "whitelisted for augmented autofill");
+                        flags |= FLAG_ADD_CLIENT_ENABLED_FOR_AUGMENTED_AUTOFILL_ONLY;
+                    }
+                } else {
+                    if (sDebug) {
+                        Log.d(TAG, "startSession(" + componentName + "): ignored because "
+                                + "disabled by service and not whitelisted for augmented autofill");
+                    }
+                    setSessionFinished(AutofillManager.STATE_DISABLED_BY_SERVICE, null);
+                    client.autofillClientResetableStateAvailable();
+                    return;
+                }
+            }
+
             mService.startSession(client.autofillClientGetActivityToken(),
                     mServiceClient.asBinder(), id, bounds, value, mContext.getUserId(),
                     mCallback != null, flags, componentName,
@@ -2067,6 +2088,7 @@
                     mServiceClientCleaner.clean();
                     mServiceClientCleaner = null;
                 }
+                notifyReenableAutofill();
             }
         }
         sDebug = (flags & SET_STATE_FLAG_DEBUG) != 0;
@@ -2403,6 +2425,37 @@
         }
     }
 
+    private void notifyDisableAutofill(long disableDuration, ComponentName componentName) {
+        synchronized (mLock) {
+            if (mOptions == null) {
+                return;
+            }
+            long expiration = SystemClock.elapsedRealtime() + disableDuration;
+            // Protect it against overflow
+            if (expiration < 0) {
+                expiration = Long.MAX_VALUE;
+            }
+            if (componentName != null) {
+                if (mOptions.disabledActivities == null) {
+                    mOptions.disabledActivities = new ArrayMap<>();
+                }
+                mOptions.disabledActivities.put(componentName.flattenToString(), expiration);
+            } else {
+                mOptions.appDisabledExpiration = expiration;
+            }
+        }
+    }
+
+    void notifyReenableAutofill() {
+        synchronized (mLock) {
+            if (mOptions == null) {
+                return;
+            }
+            mOptions.appDisabledExpiration = 0;
+            mOptions.disabledActivities = null;
+        }
+    }
+
     private void notifyNoFillUi(int sessionId, AutofillId id, int sessionFinishedState) {
         if (sVerbose) {
             Log.v(TAG, "notifyNoFillUi(): sessionId=" + sessionId + ", autofillId=" + id
@@ -3181,6 +3234,15 @@
         }
 
         @Override
+        public void notifyDisableAutofill(long disableDuration, ComponentName componentName)
+                throws RemoteException {
+            final AutofillManager afm = mAfm.get();
+            if (afm != null) {
+                afm.post(() -> afm.notifyDisableAutofill(disableDuration, componentName));
+            }
+        }
+
+        @Override
         public void dispatchUnhandledKey(int sessionId, AutofillId id, KeyEvent fullScreen) {
             final AutofillManager afm = mAfm.get();
             if (afm != null) {
diff --git a/core/java/android/view/autofill/IAutoFillManagerClient.aidl b/core/java/android/view/autofill/IAutoFillManagerClient.aidl
index 85def7f..84949c8 100644
--- a/core/java/android/view/autofill/IAutoFillManagerClient.aidl
+++ b/core/java/android/view/autofill/IAutoFillManagerClient.aidl
@@ -18,6 +18,7 @@
 
 import java.util.List;
 
+import android.content.ComponentName;
 import android.content.Intent;
 import android.content.IntentSender;
 import android.graphics.Rect;
@@ -111,4 +112,8 @@
     */
    void getAugmentedAutofillClient(in IResultReceiver result);
 
+   /**
+    * Notifies disables autofill for the app or activity.
+    */
+   void notifyDisableAutofill(long disableDuration, in ComponentName componentName);
 }
diff --git a/core/java/android/view/contentcapture/MainContentCaptureSession.java b/core/java/android/view/contentcapture/MainContentCaptureSession.java
index cee7943..1e7440b 100644
--- a/core/java/android/view/contentcapture/MainContentCaptureSession.java
+++ b/core/java/android/view/contentcapture/MainContentCaptureSession.java
@@ -480,6 +480,8 @@
             return;
         }
 
+        mNextFlushForTextChanged = false;
+
         final int numberEvents = mEvents.size();
         final String reasonString = getFlushReasonAsString(reason);
         if (sDebug) {
@@ -495,10 +497,6 @@
         try {
             mHandler.removeMessages(MSG_FLUSH);
 
-            if (reason == FLUSH_REASON_TEXT_CHANGE_TIMEOUT) {
-                mNextFlushForTextChanged = false;
-            }
-
             final ParceledListSlice<ContentCaptureEvent> events = clearEvents();
             mDirectServiceInterface.sendEvents(events, reason, mManager.mOptions);
         } catch (RemoteException e) {
diff --git a/core/java/android/view/textclassifier/SystemTextClassifier.java b/core/java/android/view/textclassifier/SystemTextClassifier.java
index a97c330..66f7504 100644
--- a/core/java/android/view/textclassifier/SystemTextClassifier.java
+++ b/core/java/android/view/textclassifier/SystemTextClassifier.java
@@ -309,7 +309,7 @@
         }
 
         public void onFailure() {
-            Log.e(LOG_TAG, "Request failed.", null);
+            Log.e(LOG_TAG, "Request failed at " + mName, null);
             mLatch.countDown();
         }
 
diff --git a/core/java/android/webkit/PermissionRequest.java b/core/java/android/webkit/PermissionRequest.java
index 18ec334..ac145b1 100644
--- a/core/java/android/webkit/PermissionRequest.java
+++ b/core/java/android/webkit/PermissionRequest.java
@@ -32,7 +32,7 @@
  * avoid unintentionally granting requests for new permissions, you should pass the
  * specific permissions you intend to grant to {@link #grant(String[]) grant()},
  * and avoid writing code like this example:
- * <pre>
+ * <pre class="prettyprint">
  * permissionRequest.grant(permissionRequest.getResources())  // This is wrong!!!
  * </pre>
  * See the WebView's release notes for information about new protected resources.
diff --git a/core/java/android/webkit/WebChromeClient.java b/core/java/android/webkit/WebChromeClient.java
index 95fe963..4db6308 100644
--- a/core/java/android/webkit/WebChromeClient.java
+++ b/core/java/android/webkit/WebChromeClient.java
@@ -519,15 +519,17 @@
          * may not be supported and applications wishing to support these sources
          * or more advanced file operations should build their own Intent.
          *
-         * <pre>
-         * How to use:
-         * 1. Build an intent using {@link #createIntent}
-         * 2. Fire the intent using {@link android.app.Activity#startActivityForResult}.
-         * 3. Check for ActivityNotFoundException and take a user friendly action if thrown.
-         * 4. Listen the result using {@link android.app.Activity#onActivityResult}
-         * 5. Parse the result using {@link #parseResult} only if media capture was not requested.
-         * 6. Send the result using filePathCallback of {@link WebChromeClient#onShowFileChooser}
-         * </pre>
+         * <p>How to use:
+         * <ol>
+         *   <li>Build an intent using {@link #createIntent}</li>
+         *   <li>Fire the intent using {@link android.app.Activity#startActivityForResult}.</li>
+         *   <li>Check for ActivityNotFoundException and take a user friendly action if thrown.</li>
+         *   <li>Listen the result using {@link android.app.Activity#onActivityResult}</li>
+         *   <li>Parse the result using {@link #parseResult} only if media capture was not
+         *   requested.</li>
+         *   <li>Send the result using filePathCallback of {@link
+         *   WebChromeClient#onShowFileChooser}</li>
+         * </ol>
          *
          * @return an Intent that supports basic file chooser sources.
          */
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index a35659e..e4b5eaa 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -762,7 +762,7 @@
      * encoded. If the data is base64 encoded, the value of the encoding
      * parameter must be {@code "base64"}. HTML can be encoded with {@link
      * android.util.Base64#encodeToString(byte[],int)} like so:
-     * <pre>
+     * <pre class="prettyprint">
      * String unencodedHtml =
      *     "&lt;html&gt;&lt;body&gt;'%28' is the code for '('&lt;/body&gt;&lt;/html&gt;";
      * String encodedHtml = Base64.encodeToString(unencodedHtml.getBytes(), Base64.NO_PADDING);
@@ -1854,7 +1854,7 @@
      * important security note below for implications.
      * <p> Note that injected objects will not appear in JavaScript until the page is next
      * (re)loaded. JavaScript should be enabled before injecting the object. For example:
-     * <pre>
+     * <pre class="prettyprint">
      * class JsObject {
      *    {@literal @}JavascriptInterface
      *    public String toString() { return "injectedObject"; }
diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java
index 00206fc..faeecda 100644
--- a/core/java/com/android/internal/app/ChooserActivity.java
+++ b/core/java/com/android/internal/app/ChooserActivity.java
@@ -164,6 +164,8 @@
     public static final String LAUNCH_LOCATON_DIRECT_SHARE = "direct_share";
     private static final int APP_PREDICTION_SHARE_TARGET_QUERY_PACKAGE_LIMIT = 20;
     public static final String APP_PREDICTION_INTENT_FILTER_KEY = "intent_filter";
+
+    private boolean mIsAppPredictorComponentAvailable;
     private AppPredictor mAppPredictor;
     private AppPredictor.Callback mAppPredictorCallback;
     private Map<ChooserTarget, AppTarget> mDirectShareAppTargetCache;
@@ -617,6 +619,9 @@
                 .addTaggedData(MetricsEvent.FIELD_SHARESHEET_MIMETYPE, target.getType())
                 .addTaggedData(MetricsEvent.FIELD_TIME_TO_APP_TARGETS, systemCost));
 
+        // This is the only place this value is being set. Effectively final.
+        mIsAppPredictorComponentAvailable = isAppPredictionServiceAvailable();
+
         AppPredictor appPredictor = getAppPredictorForDirectShareIfEnabled();
         if (appPredictor != null) {
             mDirectShareAppTargetCache = new HashMap<>();
@@ -707,6 +712,32 @@
     }
 
     /**
+     * Returns true if app prediction service is defined and the component exists on device.
+     */
+    private boolean isAppPredictionServiceAvailable() {
+        final String appPredictionServiceName =
+                getString(R.string.config_defaultAppPredictionService);
+        if (appPredictionServiceName == null) {
+            return false;
+        }
+        final ComponentName appPredictionComponentName =
+                ComponentName.unflattenFromString(appPredictionServiceName);
+        if (appPredictionComponentName == null) {
+            return false;
+        }
+
+        // Check if the app prediction component actually exists on the device.
+        Intent intent = new Intent();
+        intent.setComponent(appPredictionComponentName);
+        if (getPackageManager().resolveService(intent, PackageManager.MATCH_ALL) == null) {
+            Log.e(TAG, "App prediction service is defined, but does not exist: "
+                    + appPredictionServiceName);
+            return false;
+        }
+        return true;
+    }
+
+    /**
      * Check if the profile currently used is a work profile.
      * @return true if it is work profile, false if it is parent profile (or no work profile is
      * set up)
@@ -1693,6 +1724,9 @@
 
     @Nullable
     private AppPredictor getAppPredictor() {
+        if (!mIsAppPredictorComponentAvailable) {
+            return null;
+        }
         if (mAppPredictor == null
                     && getPackageManager().getAppPredictionServicePackageName() != null) {
             final IntentFilter filter = getTargetIntentFilter();
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index 88d0e85..8503ab8 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -4750,11 +4750,11 @@
             final long uptime = mClocks.uptimeMillis();
 
             boolean updateHistory = false;
-            if (isScreenDoze(state)) {
+            if (isScreenDoze(state) && !isScreenDoze(oldState)) {
                 mHistoryCur.states |= HistoryItem.STATE_SCREEN_DOZE_FLAG;
                 mScreenDozeTimer.startRunningLocked(elapsedRealtime);
                 updateHistory = true;
-            } else if (isScreenDoze(oldState)) {
+            } else if (isScreenDoze(oldState) && !isScreenDoze(state)) {
                 mHistoryCur.states &= ~HistoryItem.STATE_SCREEN_DOZE_FLAG;
                 mScreenDozeTimer.stopRunningLocked(elapsedRealtime);
                 updateHistory = true;
diff --git a/core/java/com/android/internal/statusbar/IStatusBar.aidl b/core/java/com/android/internal/statusbar/IStatusBar.aidl
index cf0394d..9441825 100644
--- a/core/java/com/android/internal/statusbar/IStatusBar.aidl
+++ b/core/java/com/android/internal/statusbar/IStatusBar.aidl
@@ -153,7 +153,7 @@
 
     // Used to show the dialog when BiometricService starts authentication
     void showBiometricDialog(in Bundle bundle, IBiometricServiceReceiverInternal receiver, int type,
-            boolean requireConfirmation, int userId);
+            boolean requireConfirmation, int userId, String opPackageName);
     // Used to hide the dialog when a biometric is authenticated
     void onBiometricAuthenticated(boolean authenticated, String failureReason);
     // Used to set a temporary message, e.g. fingerprint not recognized, finger moved too fast, etc
diff --git a/core/java/com/android/internal/statusbar/IStatusBarService.aidl b/core/java/com/android/internal/statusbar/IStatusBarService.aidl
index 85ae18e..4c3a177 100644
--- a/core/java/com/android/internal/statusbar/IStatusBarService.aidl
+++ b/core/java/com/android/internal/statusbar/IStatusBarService.aidl
@@ -101,7 +101,7 @@
 
     // Used to show the dialog when BiometricService starts authentication
     void showBiometricDialog(in Bundle bundle, IBiometricServiceReceiverInternal receiver, int type,
-            boolean requireConfirmation, int userId);
+            boolean requireConfirmation, int userId, String opPackageName);
     // Used to hide the dialog when a biometric is authenticated
     void onBiometricAuthenticated(boolean authenticated, String failureReason);
     // Used to set a temporary message, e.g. fingerprint not recognized, finger moved too fast, etc
diff --git a/core/jni/Android.bp b/core/jni/Android.bp
index fb68bc7..4c54539 100644
--- a/core/jni/Android.bp
+++ b/core/jni/Android.bp
@@ -339,6 +339,8 @@
     cppflags: ["-Wno-conversion-null"],
 
     srcs: [
+        "android/graphics/apex/android_region.cpp",
+
         "android_graphics_Canvas.cpp",
         "android_graphics_ColorSpace.cpp",
         "android_graphics_drawable_AnimatedVectorDrawable.cpp",
@@ -386,10 +388,12 @@
     local_include_dirs: [
         "include",  // NEEDED FOR ANDROID RUNTIME
         "android/graphics",
+        "android/graphics/apex/include",
     ],
 
     export_include_dirs: [
         ".",
+        "android/graphics/apex/include",
     ],
 
     include_dirs: [
@@ -417,6 +421,8 @@
     target: {
         android: {
             srcs: [ // sources that depend on android only libraries
+                "android/graphics/apex/android_canvas.cpp",
+
                 "android_view_TextureLayer.cpp",
                 "android_view_ThreadedRenderer.cpp",
                 "android/graphics/BitmapRegionDecoder.cpp",
diff --git a/core/jni/android/graphics/Region.cpp b/core/jni/android/graphics/Region.cpp
index 90ca3bb..1fb5fe3 100644
--- a/core/jni/android/graphics/Region.cpp
+++ b/core/jni/android/graphics/Region.cpp
@@ -360,8 +360,4 @@
                                 NELEM(gRegionIterMethods));
 }
 
-SkRegion* android_graphics_Region_getSkRegion(JNIEnv* env, jobject regionObj) {
-    return GetSkRegion(env, regionObj);
-}
-
 } // namespace android
diff --git a/core/jni/android/graphics/Region.h b/core/jni/android/graphics/Region.h
deleted file mode 100644
index 2e8e109..0000000
--- a/core/jni/android/graphics/Region.h
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * Copyright (C) 2011 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 _ANDROID_GRAPHICS_REGION_H_
-#define _ANDROID_GRAPHICS_REGION_H_
-
-#include "jni.h"
-#include "SkRegion.h"
-
-namespace android {
-
-/* Gets the underlying SkRegion from a Region object. */
-extern SkRegion* android_graphics_Region_getSkRegion(JNIEnv* env, jobject regionObj);
-
-} // namespace android
-
-#endif // _ANDROID_GRAPHICS_REGION_H_
diff --git a/core/jni/android/graphics/apex/android_canvas.cpp b/core/jni/android/graphics/apex/android_canvas.cpp
new file mode 100644
index 0000000..7a4495f
--- /dev/null
+++ b/core/jni/android/graphics/apex/android_canvas.cpp
@@ -0,0 +1,70 @@
+/*
+ * 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 "android/graphics/canvas.h"
+
+#include "GraphicsJNI.h"
+
+#include <hwui/Canvas.h>
+#include <utils/Color.h>
+
+#include <SkBitmap.h>
+
+using namespace android;
+
+static inline Canvas* toCanvas(ACanvas* aCanvas) {
+    return reinterpret_cast<Canvas*>(aCanvas);
+}
+
+static inline ACanvas* toACanvas(Canvas* canvas) {
+    return reinterpret_cast<ACanvas*>(canvas);
+}
+
+bool ACanvas_isSupportedPixelFormat(int32_t bufferFormat) {
+    ANativeWindow_Buffer buffer { 0, 0, 0, bufferFormat, nullptr, {0} };
+    const SkColorType colorType = uirenderer::ANativeWindowToImageInfo(buffer, nullptr).colorType();
+    return kUnknown_SkColorType != colorType;
+}
+
+ACanvas* ACanvas_getNativeHandleFromJava(JNIEnv* env, jobject canvasObj) {
+    return toACanvas(GraphicsJNI::getNativeCanvas(env, canvasObj));
+}
+
+void ACanvas_setBuffer(ACanvas* canvas, const ANativeWindow_Buffer* buffer,
+                       int32_t /*android_dataspace_t*/ dataspace) {
+    SkBitmap bitmap;
+    if (buffer != nullptr && buffer->width > 0 && buffer->height > 0) {
+        sk_sp<SkColorSpace> cs(uirenderer::DataSpaceToColorSpace((android_dataspace)dataspace));
+        SkImageInfo imageInfo = uirenderer::ANativeWindowToImageInfo(*buffer, cs);
+        ssize_t rowBytes = buffer->stride * imageInfo.bytesPerPixel();
+        bitmap.setInfo(imageInfo, rowBytes);
+        bitmap.setPixels(buffer->bits);
+    }
+
+    toCanvas(canvas)->setBitmap(bitmap);
+}
+
+void ACanvas_clipRect(ACanvas* canvas, const ARect& clipRect, bool /*doAA*/) {
+    //TODO update Canvas to take antialias param
+    toCanvas(canvas)->clipRect(clipRect.left, clipRect.top, clipRect.right, clipRect.bottom,
+                               SkClipOp::kIntersect);
+}
+
+void ACanvas_clipOutRect(ACanvas* canvas, const ARect& clipRect, bool /*doAA*/) {
+    //TODO update Canvas to take antialias param
+    toCanvas(canvas)->clipRect(clipRect.left, clipRect.top, clipRect.right, clipRect.bottom,
+                               SkClipOp::kDifference);
+}
diff --git a/core/jni/android/graphics/apex/android_region.cpp b/core/jni/android/graphics/apex/android_region.cpp
new file mode 100644
index 0000000..2030e7e
--- /dev/null
+++ b/core/jni/android/graphics/apex/android_region.cpp
@@ -0,0 +1,60 @@
+/*
+ * 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 "android/graphics/region.h"
+
+#include "GraphicsJNI.h"
+
+#include <SkRegion.h>
+
+static inline SkRegion::Iterator* ARegionIter_to_SkRegionIter(ARegionIterator* iterator) {
+    return reinterpret_cast<SkRegion::Iterator*>(iterator);
+}
+
+static inline ARegionIterator* SkRegionIter_to_ARegionIter(SkRegion::Iterator* iterator) {
+    return reinterpret_cast<ARegionIterator*>(iterator);
+}
+
+ARegionIterator* ARegionIterator_acquireIterator(JNIEnv* env, jobject regionObj) {
+    SkRegion* region = GraphicsJNI::getNativeRegion(env, regionObj);
+    return (!region) ? nullptr : SkRegionIter_to_ARegionIter(new SkRegion::Iterator(*region));
+}
+
+void ARegionIterator_releaseIterator(ARegionIterator* iterator) {
+    delete ARegionIter_to_SkRegionIter(iterator);
+}
+
+bool ARegionIterator_isComplex(ARegionIterator* iterator) {
+    return ARegionIter_to_SkRegionIter(iterator)->rgn()->isComplex();
+}
+
+bool ARegionIterator_isDone(ARegionIterator* iterator) {
+    return ARegionIter_to_SkRegionIter(iterator)->done();
+}
+
+void ARegionIterator_next(ARegionIterator* iterator) {
+    ARegionIter_to_SkRegionIter(iterator)->next();
+}
+
+ARect ARegionIterator_getRect(ARegionIterator* iterator) {
+    const SkIRect& rect = ARegionIter_to_SkRegionIter(iterator)->rect();
+    return { rect.fLeft, rect.fTop, rect.fRight, rect.fBottom };
+}
+
+ARect ARegionIterator_getTotalBounds(ARegionIterator* iterator) {
+    const SkIRect& bounds = ARegionIter_to_SkRegionIter(iterator)->rgn()->getBounds();
+    return { bounds.fLeft, bounds.fTop, bounds.fRight, bounds.fBottom };
+}
diff --git a/core/jni/android/graphics/apex/include/android/graphics/canvas.h b/core/jni/android/graphics/apex/include/android/graphics/canvas.h
new file mode 100644
index 0000000..c35a7d6
--- /dev/null
+++ b/core/jni/android/graphics/apex/include/android/graphics/canvas.h
@@ -0,0 +1,66 @@
+/*
+ * 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 ANDROID_GRAPHICS_CANVAS_H
+#define ANDROID_GRAPHICS_CANVAS_H
+
+#include <android/native_window.h>
+#include <android/rect.h>
+#include <jni.h>
+
+__BEGIN_DECLS
+
+/**
+* Opaque handle for a native graphics canvas.
+*/
+typedef struct ACanvas ACanvas;
+
+//  One of AHardwareBuffer_Format.
+bool ACanvas_isSupportedPixelFormat(int32_t bufferFormat);
+
+/**
+ * Returns a native handle to a Java android.graphics.Canvas
+ *
+ * @param env
+ * @param canvas
+ * @return ACanvas* that is only valid for the life of the jobject.
+ */
+ACanvas* ACanvas_getNativeHandleFromJava(JNIEnv* env, jobject canvas);
+
+/**
+ * Updates the canvas to render into the pixels in the provided buffer
+ *
+ * @param canvas
+ * @param buffer The buffer that will provide the backing store for this canvas.  The buffer must
+ *               remain valid until the this method is called again with either another active
+ *               buffer or nullptr.  If nullptr is given the canvas will release the previous buffer
+ *               and set an empty backing store.
+ * @param dataspace
+ */
+void ACanvas_setBuffer(ACanvas* canvas, const ANativeWindow_Buffer* buffer,
+                       int32_t /*android_dataspace_t*/ dataspace);
+
+/**
+ * Clips operations on the canvas to the intersection of the current clip and the provided clipRect.
+ */
+void ACanvas_clipRect(ACanvas* canvas, const ARect& clipRect, bool doAntiAlias = false);
+
+/**
+ * Clips operations on the canvas to the difference of the current clip and the provided clipRect.
+ */
+void ACanvas_clipOutRect(ACanvas* canvas, const ARect& clipRect, bool doAntiAlias = false);
+
+__END_DECLS
+#endif // ANDROID_GRAPHICS_CANVAS_H
\ No newline at end of file
diff --git a/core/jni/android/graphics/apex/include/android/graphics/region.h b/core/jni/android/graphics/apex/include/android/graphics/region.h
new file mode 100644
index 0000000..961067a
--- /dev/null
+++ b/core/jni/android/graphics/apex/include/android/graphics/region.h
@@ -0,0 +1,76 @@
+/*
+ * 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 ANDROID_GRAPHICS_REGION_H
+#define ANDROID_GRAPHICS_REGION_H
+
+#include <android/rect.h>
+#include <sys/cdefs.h>
+#include <jni.h>
+
+__BEGIN_DECLS
+
+/**
+* Opaque handle for a native graphics region iterator.
+*/
+typedef struct ARegionIterator ARegionIterator;
+
+/**
+ * Returns a iterator for a Java android.graphics.Region
+ *
+ * @param env
+ * @param region
+ * @return ARegionIterator that must be closed and must not live longer than the life
+ *         of the jobject.  It returns nullptr if the region is not a valid object.
+ */
+ARegionIterator* ARegionIterator_acquireIterator(JNIEnv* env, jobject region);
+
+void ARegionIterator_releaseIterator(ARegionIterator* iterator);
+
+bool ARegionIterator_isComplex(ARegionIterator* iterator);
+
+bool ARegionIterator_isDone(ARegionIterator* iterator);
+
+void ARegionIterator_next(ARegionIterator* iterator);
+
+ARect ARegionIterator_getRect(ARegionIterator* iterator);
+
+ARect ARegionIterator_getTotalBounds(ARegionIterator* iterator);
+
+__END_DECLS
+
+#ifdef	__cplusplus
+namespace android {
+namespace graphics {
+    class RegionIterator {
+    public:
+        RegionIterator(JNIEnv* env, jobject region)
+                : mIterator(ARegionIterator_acquireIterator(env, region)) {}
+        ~RegionIterator() { ARegionIterator_releaseIterator(mIterator); }
+
+        bool isValid() const { return mIterator != nullptr; }
+        bool isComplex() { return ARegionIterator_isComplex(mIterator); }
+        bool isDone() { return ARegionIterator_isDone(mIterator); }
+        void next() { ARegionIterator_next(mIterator); }
+        ARect getRect() { return ARegionIterator_getRect(mIterator); }
+        ARect getTotalBounds() const { return ARegionIterator_getTotalBounds(mIterator); }
+    private:
+        ARegionIterator* mIterator;
+    };
+}; // namespace graphics
+}; // namespace android
+
+#endif // __cplusplus
+#endif // ANDROID_GRAPHICS_REGION_H
\ No newline at end of file
diff --git a/core/jni/android_graphics_GraphicBuffer.cpp b/core/jni/android_graphics_GraphicBuffer.cpp
index bb9254c..43d22eb 100644
--- a/core/jni/android_graphics_GraphicBuffer.cpp
+++ b/core/jni/android_graphics_GraphicBuffer.cpp
@@ -21,11 +21,6 @@
 #include <inttypes.h>
 
 #include "android_os_Parcel.h"
-#include "android/graphics/GraphicsJNI.h"
-
-#include <android_runtime/AndroidRuntime.h>
-#include <android_runtime/android_graphics_GraphicBuffer.h>
-
 #include <binder/Parcel.h>
 
 #include <log/log.h>
@@ -33,10 +28,10 @@
 #include <ui/GraphicBuffer.h>
 #include <ui/PixelFormat.h>
 
-#include <hwui/Bitmap.h>
-
-#include <SkCanvas.h>
-#include <SkBitmap.h>
+#include <android/native_window.h>
+#include <android/graphics/canvas.h>
+#include <android_runtime/android_graphics_GraphicBuffer.h>
+#include <private/android/AHardwareBufferHelpers.h>
 
 #include <private/gui/ComposerService.h>
 
@@ -146,23 +141,8 @@
 // Canvas management
 // ----------------------------------------------------------------------------
 
-static inline SkColorType convertPixelFormat(int32_t format) {
-    switch (format) {
-        case PIXEL_FORMAT_RGBA_8888:
-            return kN32_SkColorType;
-        case PIXEL_FORMAT_RGBX_8888:
-            return kN32_SkColorType;
-        case PIXEL_FORMAT_RGBA_FP16:
-            return kRGBA_F16_SkColorType;
-        case PIXEL_FORMAT_RGB_565:
-            return kRGB_565_SkColorType;
-        default:
-            return kUnknown_SkColorType;
-    }
-}
-
 static jboolean android_graphics_GraphicBuffer_lockCanvas(JNIEnv* env, jobject,
-        jlong wrapperHandle, jobject canvas, jobject dirtyRect) {
+        jlong wrapperHandle, jobject canvasObj, jobject dirtyRect) {
 
     GraphicBufferWrapper* wrapper =
                 reinterpret_cast<GraphicBufferWrapper*>(wrapperHandle);
@@ -191,24 +171,16 @@
         return JNI_FALSE;
     }
 
-    ssize_t bytesCount = buffer->getStride() * bytesPerPixel(buffer->getPixelFormat());
+    ANativeWindow_Buffer nativeBuffer;
+    nativeBuffer.width = buffer->getWidth();
+    nativeBuffer.height = buffer->getHeight();
+    nativeBuffer.stride = buffer->getStride();
+    nativeBuffer.format = AHardwareBuffer_convertFromPixelFormat(buffer->getPixelFormat());
+    nativeBuffer.bits = bits;
 
-    SkBitmap bitmap;
-    bitmap.setInfo(SkImageInfo::Make(buffer->getWidth(), buffer->getHeight(),
-                                     convertPixelFormat(buffer->getPixelFormat()),
-                                     kPremul_SkAlphaType),
-                   bytesCount);
-
-    if (buffer->getWidth() > 0 && buffer->getHeight() > 0) {
-        bitmap.setPixels(bits);
-    } else {
-        bitmap.setPixels(NULL);
-    }
-
-    Canvas* nativeCanvas = GraphicsJNI::getNativeCanvas(env, canvas);
-    nativeCanvas->setBitmap(bitmap);
-    nativeCanvas->clipRect(rect.left, rect.top, rect.right, rect.bottom,
-            SkClipOp::kIntersect);
+    ACanvas* canvas = ACanvas_getNativeHandleFromJava(env, canvasObj);
+    ACanvas_setBuffer(canvas, &nativeBuffer, ADATASPACE_UNKNOWN);
+    ACanvas_clipRect(canvas, {rect.left, rect.top, rect.right, rect.bottom});
 
     if (dirtyRect) {
         INVOKEV(dirtyRect, gRectClassInfo.set,
@@ -219,13 +191,13 @@
 }
 
 static jboolean android_graphics_GraphicBuffer_unlockCanvasAndPost(JNIEnv* env, jobject,
-        jlong wrapperHandle, jobject canvas) {
+        jlong wrapperHandle, jobject canvasObj) {
+    // release the buffer from the canvas
+    ACanvas* canvas = ACanvas_getNativeHandleFromJava(env, canvasObj);
+    ACanvas_setBuffer(canvas, nullptr, ADATASPACE_UNKNOWN);
 
     GraphicBufferWrapper* wrapper =
                 reinterpret_cast<GraphicBufferWrapper*>(wrapperHandle);
-    Canvas* nativeCanvas = GraphicsJNI::getNativeCanvas(env, canvas);
-    nativeCanvas->setBitmap(SkBitmap());
-
     if (wrapper) {
         status_t status = wrapper->get()->unlock();
         return status == 0 ? JNI_TRUE : JNI_FALSE;
diff --git a/core/jni/android_hardware_input_InputWindowHandle.cpp b/core/jni/android_hardware_input_InputWindowHandle.cpp
index 98680fa1e..64caf33 100644
--- a/core/jni/android_hardware_input_InputWindowHandle.cpp
+++ b/core/jni/android_hardware_input_InputWindowHandle.cpp
@@ -22,7 +22,7 @@
 #include <android_runtime/AndroidRuntime.h>
 #include <utils/threads.h>
 
-#include <android/graphics/Region.h>
+#include <android/graphics/region.h>
 #include <gui/SurfaceControl.h>
 #include <ui/Region.h>
 
@@ -128,10 +128,9 @@
     jobject regionObj = env->GetObjectField(obj,
             gInputWindowHandleClassInfo.touchableRegion);
     if (regionObj) {
-        SkRegion* region = android_graphics_Region_getSkRegion(env, regionObj);
-        for (SkRegion::Iterator it(*region); !it.done(); it.next()) {
-            const SkIRect& rect = it.rect();
-            mInfo.addTouchableRegion(Rect(rect.fLeft, rect.fTop, rect.fRight, rect.fBottom));
+        for (graphics::RegionIterator it(env, regionObj); !it.isDone(); it.next()) {
+            ARect rect = it.getRect();
+            mInfo.addTouchableRegion(Rect(rect.left, rect.top, rect.right, rect.bottom));
         }
         env->DeleteLocalRef(regionObj);
     }
diff --git a/core/jni/android_view_InputChannel.cpp b/core/jni/android_view_InputChannel.cpp
index 7e3b083..8553a2c 100644
--- a/core/jni/android_view_InputChannel.cpp
+++ b/core/jni/android_view_InputChannel.cpp
@@ -210,8 +210,7 @@
     if (parcel) {
         bool isInitialized = parcel->readInt32();
         if (isInitialized) {
-            InputChannel* inputChannel = new InputChannel();
-            inputChannel->read(*parcel);
+            sp<InputChannel> inputChannel = InputChannel::read(*parcel);
 
             NativeInputChannel* nativeInputChannel = new NativeInputChannel(inputChannel);
 
diff --git a/core/jni/android_view_Surface.cpp b/core/jni/android_view_Surface.cpp
index d65e252..4c2e91f 100644
--- a/core/jni/android_view_Surface.cpp
+++ b/core/jni/android_view_Surface.cpp
@@ -20,15 +20,16 @@
 
 #include "jni.h"
 #include <nativehelper/JNIHelp.h>
-#include "android_os_Parcel.h"
-#include "android/graphics/GraphicsJNI.h"
-
 #include "core_jni_helpers.h"
+
+#include <android/graphics/canvas.h>
 #include <android_runtime/android_graphics_GraphicBuffer.h>
 #include <android_runtime/android_graphics_SurfaceTexture.h>
 #include <android_runtime/android_view_Surface.h>
 #include <android_runtime/Log.h>
+#include <private/android/AHardwareBufferHelpers.h>
 
+#include "android_os_Parcel.h"
 #include <binder/Parcel.h>
 
 #include <gui/Surface.h>
@@ -39,11 +40,6 @@
 #include <ui/Rect.h>
 #include <ui/Region.h>
 
-#include <SkCanvas.h>
-#include <SkBitmap.h>
-#include <SkImage.h>
-#include <SkRegion.h>
-
 #include <utils/misc.h>
 #include <utils/Log.h>
 
@@ -232,37 +228,21 @@
         dirtyRectPtr = &dirtyRect;
     }
 
-    ANativeWindow_Buffer outBuffer;
-    status_t err = surface->lock(&outBuffer, dirtyRectPtr);
+    ANativeWindow_Buffer buffer;
+    status_t err = surface->lock(&buffer, dirtyRectPtr);
     if (err < 0) {
         const char* const exception = (err == NO_MEMORY) ?
-                OutOfResourcesException :
-                "java/lang/IllegalArgumentException";
+                OutOfResourcesException : IllegalArgumentException;
         jniThrowException(env, exception, NULL);
         return 0;
     }
 
-    SkImageInfo info = SkImageInfo::Make(outBuffer.width, outBuffer.height,
-                                         convertPixelFormat(outBuffer.format),
-                                         outBuffer.format == PIXEL_FORMAT_RGBX_8888
-                                                 ? kOpaque_SkAlphaType : kPremul_SkAlphaType);
-
-    SkBitmap bitmap;
-    ssize_t bpr = outBuffer.stride * bytesPerPixel(outBuffer.format);
-    bitmap.setInfo(info, bpr);
-    if (outBuffer.width > 0 && outBuffer.height > 0) {
-        bitmap.setPixels(outBuffer.bits);
-    } else {
-        // be safe with an empty bitmap.
-        bitmap.setPixels(NULL);
-    }
-
-    Canvas* nativeCanvas = GraphicsJNI::getNativeCanvas(env, canvasObj);
-    nativeCanvas->setBitmap(bitmap);
+    ACanvas* canvas = ACanvas_getNativeHandleFromJava(env, canvasObj);
+    ACanvas_setBuffer(canvas, &buffer, static_cast<int32_t>(surface->getBuffersDataSpace()));
 
     if (dirtyRectPtr) {
-        nativeCanvas->clipRect(dirtyRect.left, dirtyRect.top,
-                dirtyRect.right, dirtyRect.bottom, SkClipOp::kIntersect);
+        ACanvas_clipRect(canvas, {dirtyRect.left, dirtyRect.top,
+                                  dirtyRect.right, dirtyRect.bottom});
     }
 
     if (dirtyRectObj) {
@@ -288,8 +268,8 @@
     }
 
     // detach the canvas from the surface
-    Canvas* nativeCanvas = GraphicsJNI::getNativeCanvas(env, canvasObj);
-    nativeCanvas->setBitmap(SkBitmap());
+    ACanvas* canvas = ACanvas_getNativeHandleFromJava(env, canvasObj);
+    ACanvas_setBuffer(canvas, nullptr, ADATASPACE_UNKNOWN);
 
     // unlock surface
     status_t err = surface->unlockAndPost();
diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp
index 5cbf81c..67f52f4 100644
--- a/core/jni/android_view_SurfaceControl.cpp
+++ b/core/jni/android_view_SurfaceControl.cpp
@@ -20,9 +20,9 @@
 #include "android_os_Parcel.h"
 #include "android_util_Binder.h"
 #include "android_hardware_input_InputWindowHandle.h"
-#include "android/graphics/Region.h"
 #include "core_jni_helpers.h"
 
+#include <android/graphics/region.h>
 #include <android_runtime/AndroidRuntime.h>
 #include <android-base/chrono_utils.h>
 #include <nativehelper/JNIHelp.h>
@@ -425,20 +425,19 @@
 static void nativeSetTransparentRegionHint(JNIEnv* env, jclass clazz, jlong transactionObj,
         jlong nativeObject, jobject regionObj) {
     SurfaceControl* const ctrl = reinterpret_cast<SurfaceControl *>(nativeObject);
-    SkRegion* region = android_graphics_Region_getSkRegion(env, regionObj);
-    if (!region) {
+    graphics::RegionIterator iterator(env, regionObj);
+    if (!iterator.isValid()) {
         doThrowIAE(env);
         return;
     }
 
-    const SkIRect& b(region->getBounds());
-    Region reg(Rect(b.fLeft, b.fTop, b.fRight, b.fBottom));
-    if (region->isComplex()) {
-        SkRegion::Iterator it(*region);
-        while (!it.done()) {
-            const SkIRect& r(it.rect());
-            reg.addRectUnchecked(r.fLeft, r.fTop, r.fRight, r.fBottom);
-            it.next();
+    ARect bounds = iterator.getTotalBounds();
+    Region reg({bounds.left, bounds.top, bounds.right, bounds.bottom});
+    if (iterator.isComplex()) {
+        while (!iterator.isDone()) {
+            ARect rect = iterator.getRect();
+            reg.addRectUnchecked(rect.left, rect.top, rect.right, rect.bottom);
+            iterator.next();
         }
     }
 
@@ -497,11 +496,8 @@
 
     auto transaction = reinterpret_cast<SurfaceComposerClient::Transaction*>(transactionObj);
 
-    std::vector<uint8_t> byteData(parcel->dataSize());
-    memcpy(byteData.data(), parcel->data(), parcel->dataSize());
-
     SurfaceControl* const ctrl = reinterpret_cast<SurfaceControl*>(nativeObject);
-    transaction->setMetadata(ctrl, id, std::move(byteData));
+    transaction->setMetadata(ctrl, id, *parcel);
 }
 
 static void nativeSetColor(JNIEnv* env, jclass clazz, jlong transactionObj,
diff --git a/core/jni/android_view_TextureView.cpp b/core/jni/android_view_TextureView.cpp
index 15319ad..1f69c8b 100644
--- a/core/jni/android_view_TextureView.cpp
+++ b/core/jni/android_view_TextureView.cpp
@@ -25,11 +25,7 @@
 #include <gui/GLConsumer.h>
 #include <gui/Surface.h>
 
-#include <SkBitmap.h>
-#include <SkCanvas.h>
-#include <SkImage.h>
-
-#include "android/graphics/GraphicsJNI.h"
+#include <android/graphics/canvas.h>
 
 #include "core_jni_helpers.h"
 
@@ -70,33 +66,6 @@
 // Native layer
 // ----------------------------------------------------------------------------
 
-// FIXME: consider exporting this to share (e.g. android_view_Surface.cpp)
-static inline SkImageInfo convertPixelFormat(const ANativeWindow_Buffer& buffer) {
-    SkColorType colorType = kUnknown_SkColorType;
-    SkAlphaType alphaType = kOpaque_SkAlphaType;
-    switch (buffer.format) {
-        case WINDOW_FORMAT_RGBA_8888:
-            colorType = kN32_SkColorType;
-            alphaType = kPremul_SkAlphaType;
-            break;
-        case WINDOW_FORMAT_RGBX_8888:
-            colorType = kN32_SkColorType;
-            alphaType = kOpaque_SkAlphaType;
-            break;
-        case AHARDWAREBUFFER_FORMAT_R16G16B16A16_FLOAT:
-            colorType = kRGBA_F16_SkColorType;
-            alphaType = kPremul_SkAlphaType;
-            break;
-        case WINDOW_FORMAT_RGB_565:
-            colorType = kRGB_565_SkColorType;
-            alphaType = kOpaque_SkAlphaType;
-            break;
-        default:
-            break;
-    }
-    return SkImageInfo::Make(buffer.width, buffer.height, colorType, alphaType);
-}
-
 /**
  * This is a private API, and this implementation is also provided in the NDK.
  * However, the NDK links against android_runtime, which means that using the
@@ -134,14 +103,12 @@
 }
 
 static jboolean android_view_TextureView_lockCanvas(JNIEnv* env, jobject,
-        jlong nativeWindow, jobject canvas, jobject dirtyRect) {
+        jlong nativeWindow, jobject canvasObj, jobject dirtyRect) {
 
     if (!nativeWindow) {
         return JNI_FALSE;
     }
 
-    ANativeWindow_Buffer buffer;
-
     Rect rect(Rect::EMPTY_RECT);
     if (dirtyRect) {
         rect.left = GET_INT(dirtyRect, gRectClassInfo.left);
@@ -152,25 +119,14 @@
         rect.set(Rect(0x3FFF, 0x3FFF));
     }
 
+    ANativeWindow_Buffer outBuffer;
     sp<ANativeWindow> window((ANativeWindow*) nativeWindow);
-    int32_t status = native_window_lock(window.get(), &buffer, &rect);
+    int32_t status = native_window_lock(window.get(), &outBuffer, &rect);
     if (status) return JNI_FALSE;
 
-    ssize_t bytesCount = buffer.stride * bytesPerPixel(buffer.format);
-
-    SkBitmap bitmap;
-    bitmap.setInfo(convertPixelFormat(buffer), bytesCount);
-
-    if (buffer.width > 0 && buffer.height > 0) {
-        bitmap.setPixels(buffer.bits);
-    } else {
-        bitmap.setPixels(NULL);
-    }
-
-    Canvas* nativeCanvas = GraphicsJNI::getNativeCanvas(env, canvas);
-    nativeCanvas->setBitmap(bitmap);
-    nativeCanvas->clipRect(rect.left, rect.top, rect.right, rect.bottom,
-            SkClipOp::kIntersect);
+    ACanvas* canvas = ACanvas_getNativeHandleFromJava(env, canvasObj);
+    ACanvas_setBuffer(canvas, &outBuffer, ANativeWindow_getBuffersDataSpace(window.get()));
+    ACanvas_clipRect(canvas, {rect.left, rect.top, rect.right, rect.bottom});
 
     if (dirtyRect) {
         INVOKEV(dirtyRect, gRectClassInfo.set,
@@ -181,10 +137,11 @@
 }
 
 static void android_view_TextureView_unlockCanvasAndPost(JNIEnv* env, jobject,
-        jlong nativeWindow, jobject canvas) {
+        jlong nativeWindow, jobject canvasObj) {
 
-    Canvas* nativeCanvas = GraphicsJNI::getNativeCanvas(env, canvas);
-    nativeCanvas->setBitmap(SkBitmap());
+    // release the buffer from the canvas
+    ACanvas* canvas = ACanvas_getNativeHandleFromJava(env, canvasObj);
+    ACanvas_setBuffer(canvas, nullptr, ADATASPACE_UNKNOWN);
 
     if (nativeWindow) {
         sp<ANativeWindow> window((ANativeWindow*) nativeWindow);
diff --git a/core/proto/android/providers/settings/global.proto b/core/proto/android/providers/settings/global.proto
index 0070694..f2ca0a4 100644
--- a/core/proto/android/providers/settings/global.proto
+++ b/core/proto/android/providers/settings/global.proto
@@ -995,7 +995,7 @@
         optional SettingProto display_certification_on = 4 [ (android.privacy).dest = DEST_AUTOMATIC ];
         optional SettingProto display_wps_config = 5 [ (android.privacy).dest = DEST_AUTOMATIC ];
         optional SettingProto networks_available_notification_on = 6 [ (android.privacy).dest = DEST_AUTOMATIC ];
-        optional SettingProto carrier_networks_available_notification_on = 7 [ (android.privacy).dest = DEST_AUTOMATIC ];
+        reserved 7; reserved "carrier_networks_available_notification_on";
         optional SettingProto networks_available_repeat_delay = 8 [ (android.privacy).dest = DEST_AUTOMATIC ];
         optional SettingProto country_code = 9;
         optional SettingProto framework_scan_interval_ms = 10 [ (android.privacy).dest = DEST_AUTOMATIC ];
@@ -1013,7 +1013,7 @@
         optional SettingProto watchdog_poor_network_test_enabled = 22 [ (android.privacy).dest = DEST_AUTOMATIC ];
         optional SettingProto suspend_optimizations_enabled = 23 [ (android.privacy).dest = DEST_AUTOMATIC ];
         optional SettingProto verbose_logging_enabled = 24 [ (android.privacy).dest = DEST_AUTOMATIC ];
-        reserved 25; // connected_mac_randomization_enabled
+        reserved 25; reserved "connected_mac_randomization_enabled";
         optional SettingProto max_dhcp_retry_count = 26 [ (android.privacy).dest = DEST_AUTOMATIC ];
         optional SettingProto mobile_data_transition_wakelock_timeout_ms = 27 [ (android.privacy).dest = DEST_AUTOMATIC ];
         // Controls whether WiFi configurations created by a Device Owner app should
@@ -1025,7 +1025,7 @@
         optional SettingProto device_owner_configs_lockdown = 28 [ (android.privacy).dest = DEST_AUTOMATIC ];
         optional SettingProto frequency_band = 29 [ (android.privacy).dest = DEST_AUTOMATIC ];
         optional SettingProto p2p_device_name = 30;
-        optional SettingProto reenable_delay_ms = 31 [ (android.privacy).dest = DEST_AUTOMATIC ];
+        reserved 31; reserved "reenable_delay_ms";
         optional SettingProto ephemeral_out_of_range_timeout_ms = 32 [ (android.privacy).dest = DEST_AUTOMATIC ];
         optional SettingProto on_when_proxy_disconnected = 33 [ (android.privacy).dest = DEST_AUTOMATIC ];
         optional SettingProto bounce_delay_override_ms = 34 [ (android.privacy).dest = DEST_AUTOMATIC ];
diff --git a/core/proto/android/server/activitymanagerservice.proto b/core/proto/android/server/activitymanagerservice.proto
index a1ff9b9..d54b6b0 100644
--- a/core/proto/android/server/activitymanagerservice.proto
+++ b/core/proto/android/server/activitymanagerservice.proto
@@ -367,10 +367,8 @@
         optional int32 id = 3;
         optional bool is_proc = 4;
         optional bool has_activities = 5;
-        oneof ss_kb {
-            int64 pss_kb = 6;
-            int64 rss_kb = 9;
-        }
+        optional int64 pss_kb = 6;
+        optional int64 rss_kb = 9;
         optional int64 swap_pss_kb = 7;
         repeated MemItem sub_items = 8;
     }
diff --git a/core/proto/android/server/jobscheduler.proto b/core/proto/android/server/jobscheduler.proto
index aac144c..79167ab 100644
--- a/core/proto/android/server/jobscheduler.proto
+++ b/core/proto/android/server/jobscheduler.proto
@@ -308,6 +308,8 @@
         // Whether or not TimeController should skip setting wakeup alarms for jobs that aren't
         // ready now.
         reserved 1; // skip_not_ready_jobs
+        // Whether or not TimeController will use a non-wakeup alarm for delay constraints.
+        optional bool use_non_wakeup_alarm_for_delay = 2;
     }
     optional TimeController time_controller = 25;
 
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 6a20484..9738759 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -1264,6 +1264,15 @@
         android:description="@string/permdesc_camera"
         android:protectionLevel="dangerous|instant" />
 
+      <!-- @SystemApi Required in addition to android.permission.CAMERA to be able to access
+           system only camera devices.
+           <p>Protection level: system|signature
+           @hide -->
+    <permission android:name="android.permission.SYSTEM_CAMERA"
+        android:permissionGroup="android.permission-group.UNDEFINED"
+        android:label="@string/permlab_systemCamera"
+        android:description="@string/permdesc_systemCamera"
+        android:protectionLevel="system|signature" />
 
     <!-- ====================================================================== -->
     <!-- Permissions for accessing the device sensors                           -->
@@ -4458,12 +4467,12 @@
     <!-- @SystemApi Allows to access all app shortcuts.
          @hide -->
     <permission android:name="android.permission.ACCESS_SHORTCUTS"
-        android:protectionLevel="signature|textClassifier" />
+        android:protectionLevel="signature|appPredictor" />
 
     <!-- @SystemApi Allows unlimited calls to shortcut mutation APIs.
          @hide -->
     <permission android:name="android.permission.UNLIMITED_SHORTCUTS_API_CALLS"
-        android:protectionLevel="signature|textClassifier" />
+        android:protectionLevel="signature|appPredictor" />
 
     <!-- @SystemApi Allows an application to read the runtime profiles of other apps.
          @hide <p>Not for use by third-party applications. -->
@@ -4524,6 +4533,13 @@
     <permission android:name="android.permission.BIND_EXPLICIT_HEALTH_CHECK_SERVICE"
         android:protectionLevel="signature|privileged" />
 
+    <!-- @SystemApi Must be required by an {@link android.service.storage.ExternalStorageService} to
+         ensure that only the system can bind to it.
+         @hide This is not a third-party API (intended for OEMs and system apps).
+    -->
+    <permission android:name="android.permission.BIND_EXTERNAL_STORAGE_SERVICE"
+        android:protectionLevel="signature" />
+
     <!-- @hide Permission that allows configuring appops.
      <p>Not for use by third-party applications. -->
     <permission android:name="android.permission.MANAGE_APPOPS"
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 40c2cbe..ab8a2b0 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -1703,16 +1703,16 @@
         <!-- Add packages here -->
     </string-array>
 
-    <!-- The set of system packages on device that are queryable regardless of the contents of their
-         manifest. -->
+    <!-- The set of system packages on device that are queryable by any app regardless of the
+         contents of its manifest. -->
     <string-array name="config_forceQueryablePackages" translatable="false">
         <item>com.android.settings</item>
         <item>com.android.providers.settings</item>
         <!-- Add packages here -->
     </string-array>
 
-    <!-- If true, will force all packages on any system partition as queryable regardless of the
-         contents of their manifest. -->
+    <!-- If true, will force all packages on any system partition as queryable by any app regardless
+         of the contents of its manifest. -->
     <bool name="config_forceSystemPackagesQueryable">false</bool>
 
     <!-- Component name of the default wallpaper. This will be ImageWallpaper if not
@@ -4286,4 +4286,9 @@
     <!-- The list of packages to automatically opt out of refresh rates higher than 60hz because
          of known compatibility issues. -->
     <string-array name="config_highRefreshRateBlacklist"></string-array>
+
+    <!-- Whether or not to hide the navigation bar when the soft keyboard is visible in order to
+         create additional screen real estate outside beyond the keyboard. Note that the user needs
+         to have a confirmed way to dismiss the keyboard when desired. -->
+    <bool name="config_automotiveHideNavBarForKeyboard">false</bool>
 </resources>
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index d1d7bf5..2b97bf8 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -1173,6 +1173,11 @@
     <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
     <string name="permdesc_camera">This app can take pictures and record videos using the camera at any time.</string>
 
+    <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. [CHAR_LIMIT=NONE] -->
+    <string name="permlab_systemCamera">Allow an application or service access to system cameras to take pictures and videos</string>
+    <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. [CHAR_LIMIT=NONE] -->
+    <string name="permdesc_systemCamera">This privileged | system app can take pictures and record videos using a system camera at any time. Requires the android.permission.CAMERA permission to be held by the app as well</string>
+
     <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
     <string name="permlab_vibrate">control vibration</string>
     <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
@@ -3377,8 +3382,6 @@
 
     <!-- Notification title for a nearby open wireless network.-->
     <string name="wifi_available_title">Connect to open Wi\u2011Fi network</string>
-    <!-- Notification title for a nearby carrier wireless network.-->
-    <string name="wifi_available_carrier_network_title">Connect to carrier Wi\u2011Fi network</string>
     <!-- Notification title when the system is connecting to the specified network. The network name is specified in the notification content. -->
     <string name="wifi_available_title_connecting">Connecting to Wi\u2011Fi network</string>
     <!-- Notification title when the system has connected to the network. The network name is specified in the notification content. -->
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index f7ae453..c37a457 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -2070,7 +2070,6 @@
   <java-symbol type="plurals" name="wifi_available" />
   <java-symbol type="plurals" name="wifi_available_detailed" />
   <java-symbol type="string" name="wifi_available_title" />
-  <java-symbol type="string" name="wifi_available_carrier_network_title" />
   <java-symbol type="string" name="wifi_available_title_connecting" />
   <java-symbol type="string" name="wifi_available_title_connected" />
   <java-symbol type="string" name="wifi_available_title_failed_to_connect" />
@@ -3847,4 +3846,6 @@
   <java-symbol type="string" name="config_factoryResetPackage" />
   <java-symbol type="array" name="config_highRefreshRateBlacklist" />
 
+  <java-symbol type="bool" name="config_automotiveHideNavBarForKeyboard" />
+
 </resources>
diff --git a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/StartProgramListUpdatesFanoutTest.java b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/StartProgramListUpdatesFanoutTest.java
index 3d9a1d9..7c6271c 100644
--- a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/StartProgramListUpdatesFanoutTest.java
+++ b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/StartProgramListUpdatesFanoutTest.java
@@ -19,6 +19,7 @@
 import static org.mockito.Matchers.any;
 import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.timeout;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
@@ -43,6 +44,7 @@
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 import org.mockito.stubbing.Answer;
+import org.mockito.verification.VerificationWithTimeout;
 
 import java.util.Arrays;
 import java.util.HashSet;
@@ -56,6 +58,8 @@
 public class StartProgramListUpdatesFanoutTest {
     private static final String TAG = "BroadcastRadioTests.hal2.StartProgramListUpdatesFanout";
 
+    private static final VerificationWithTimeout CB_TIMEOUT = timeout(100);
+
     // Mocks
     @Mock IBroadcastRadio mBroadcastRadioMock;
     @Mock ITunerSession mHalTunerSessionMock;
@@ -200,10 +204,10 @@
 
         // Adding mDabEnsembleInfo should not update any client.
         updateHalProgramInfo(false, Arrays.asList(mDabEnsembleInfo), null);
-        verify(mAidlTunerCallbackMocks[0], times(1)).onProgramListUpdated(any());
-        verify(mAidlTunerCallbackMocks[1], times(2)).onProgramListUpdated(any());
-        verify(mAidlTunerCallbackMocks[2], times(1)).onProgramListUpdated(any());
-        verify(mAidlTunerCallbackMocks[3], times(2)).onProgramListUpdated(any());
+        verify(mAidlTunerCallbackMocks[0], CB_TIMEOUT.times(1)).onProgramListUpdated(any());
+        verify(mAidlTunerCallbackMocks[1], CB_TIMEOUT.times(2)).onProgramListUpdated(any());
+        verify(mAidlTunerCallbackMocks[2], CB_TIMEOUT.times(1)).onProgramListUpdated(any());
+        verify(mAidlTunerCallbackMocks[3], CB_TIMEOUT.times(2)).onProgramListUpdated(any());
     }
 
     @Test
@@ -240,7 +244,7 @@
 
         // Update the HAL with mModifiedAmFmInfo, and verify only the remaining client is updated.
         updateHalProgramInfo(true, Arrays.asList(mModifiedAmFmInfo), null);
-        verify(mAidlTunerCallbackMocks[0], times(1)).onProgramListUpdated(any());
+        verify(mAidlTunerCallbackMocks[0], CB_TIMEOUT.times(1)).onProgramListUpdated(any());
         verifyAidlClientReceivedChunk(mAidlTunerCallbackMocks[1], true,
                 Arrays.asList(mModifiedAmFmInfo), null);
 
@@ -313,6 +317,6 @@
         }
         ProgramList.Chunk expectedChunk = new ProgramList.Chunk(purge, true, modifiedSet,
                 removedSet);
-        verify(clientMock).onProgramListUpdated(expectedChunk);
+        verify(clientMock, CB_TIMEOUT).onProgramListUpdated(expectedChunk);
     }
 }
diff --git a/core/tests/coretests/src/android/provider/SettingsBackupTest.java b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
index bdbf368..97b7ae9 100644
--- a/core/tests/coretests/src/android/provider/SettingsBackupTest.java
+++ b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
@@ -544,7 +544,6 @@
                     Settings.Global.WIFI_ON,
                     Settings.Global.WIFI_P2P_DEVICE_NAME,
                     Settings.Global.WIFI_P2P_PENDING_FACTORY_RESET,
-                    Settings.Global.WIFI_REENABLE_DELAY_MS,
                     Settings.Global.WIFI_RTT_BACKGROUND_EXEC_GAP_MS,
                     Settings.Global.WIFI_SAVED_STATE,
                     Settings.Global.WIFI_SCAN_ALWAYS_AVAILABLE,
@@ -721,7 +720,6 @@
                  Settings.Secure.BIOMETRIC_DEBUG_ENABLED,
                  Settings.Secure.FACE_UNLOCK_ATTENTION_REQUIRED,
                  Settings.Secure.FACE_UNLOCK_DIVERSITY_REQUIRED,
-                 Settings.Secure.FACE_UNLOCK_EDUCATION_INFO_DISPLAYED,
                  Settings.Secure.MANAGED_PROVISIONING_DPC_DOWNLOADED);
 
     @Test
diff --git a/core/tests/coretests/src/android/provider/SettingsValidatorsTest.java b/core/tests/coretests/src/android/provider/settings/validators/SettingsValidatorsTest.java
similarity index 85%
rename from core/tests/coretests/src/android/provider/SettingsValidatorsTest.java
rename to core/tests/coretests/src/android/provider/settings/validators/SettingsValidatorsTest.java
index eea8c83..5f042d3 100644
--- a/core/tests/coretests/src/android/provider/SettingsValidatorsTest.java
+++ b/core/tests/coretests/src/android/provider/settings/validators/SettingsValidatorsTest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2018 The Android Open Source Project
+ * 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.
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package android.provider;
+package android.provider.settings.validators;
 
 import static com.google.common.truth.Truth.assertThat;
 
@@ -23,7 +23,7 @@
 import static org.junit.Assert.fail;
 
 import android.platform.test.annotations.Presubmit;
-import android.provider.SettingsValidators.Validator;
+import android.provider.Settings;
 
 import androidx.test.filters.SmallTest;
 import androidx.test.runner.AndroidJUnit4;
@@ -138,7 +138,7 @@
     @Test
     public void testDiscreteValueValidator() {
         String[] beerTypes = new String[]{"Ale", "American IPA", "Stout"};
-        Validator v = new SettingsValidators.DiscreteValueValidator(beerTypes);
+        Validator v = new DiscreteValueValidator(beerTypes);
         assertTrue(v.validate("Ale"));
         assertTrue(v.validate("American IPA"));
         assertTrue(v.validate("Stout"));
@@ -148,14 +148,14 @@
     @Test
     public void testDiscreteValueValidator_onNullValue_returnsFalse() {
         String[] discreteTypes = new String[]{"Type1", "Type2"};
-        Validator v = new SettingsValidators.DiscreteValueValidator(discreteTypes);
+        Validator v = new DiscreteValueValidator(discreteTypes);
 
         assertFalse(v.validate(null));
     }
 
     @Test
     public void testInclusiveIntegerRangeValidator() {
-        Validator v = new SettingsValidators.InclusiveIntegerRangeValidator(0, 5);
+        Validator v = new InclusiveIntegerRangeValidator(0, 5);
         assertTrue(v.validate("0"));
         assertTrue(v.validate("2"));
         assertTrue(v.validate("5"));
@@ -165,14 +165,14 @@
 
     @Test
     public void testInclusiveIntegerRangeValidator_onNullValue_returnsFalse() {
-        Validator v = new SettingsValidators.InclusiveIntegerRangeValidator(0, 5);
+        Validator v = new InclusiveIntegerRangeValidator(0, 5);
 
         assertFalse(v.validate(null));
     }
 
     @Test
     public void testInclusiveFloatRangeValidator() {
-        Validator v = new SettingsValidators.InclusiveFloatRangeValidator(0.0f, 5.0f);
+        Validator v = new InclusiveFloatRangeValidator(0.0f, 5.0f);
         assertTrue(v.validate("0.0"));
         assertTrue(v.validate("2.0"));
         assertTrue(v.validate("5.0"));
@@ -182,14 +182,14 @@
 
     @Test
     public void testInclusiveFloatRangeValidator_onNullValue_returnsFalse() {
-        Validator v = new SettingsValidators.InclusiveFloatRangeValidator(0.0f, 5.0f);
+        Validator v = new InclusiveFloatRangeValidator(0.0f, 5.0f);
 
         assertFalse(v.validate(null));
     }
 
     @Test
     public void testComponentNameListValidator() {
-        Validator v = new SettingsValidators.ComponentNameListValidator(",");
+        Validator v = new ComponentNameListValidator(",");
         assertTrue(v.validate("com.android.localtransport/.LocalTransport,"
                 + "com.google.android.gms/.backup.migrate.service.D2dTransport"));
         assertFalse(v.validate("com.google.5android,android"));
@@ -197,21 +197,21 @@
 
     @Test
     public void testComponentNameListValidator_onNullValue_returnsFalse() {
-        Validator v = new SettingsValidators.ComponentNameListValidator(",");
+        Validator v = new ComponentNameListValidator(",");
 
         assertFalse(v.validate(null));
     }
 
     @Test
     public void testPackageNameListValidator() {
-        Validator v = new SettingsValidators.PackageNameListValidator(",");
+        Validator v = new PackageNameListValidator(",");
         assertTrue(v.validate("com.android.localtransport.LocalTransport,com.google.android.gms"));
         assertFalse(v.validate("5com.android.internal.backup.LocalTransport,android"));
     }
 
     @Test
     public void testPackageNameListValidator_onNullValue_returnsFalse() {
-        Validator v = new SettingsValidators.PackageNameListValidator(",");
+        Validator v = new PackageNameListValidator(",");
 
         assertFalse(v.validate(null));
     }
@@ -256,51 +256,41 @@
 
     @Test
     public void testTTSListValidator_withValidInput_returnsTrue() {
-        Validator v = new SettingsValidators.TTSListValidator();
-
-        assertTrue(v.validate("com.foo.ttsengine:en-US,com.bar.ttsengine:es_ES"));
+        assertTrue(
+                SettingsValidators.TTS_LIST_VALIDATOR.validate(
+                        "com.foo.ttsengine:en-US,com.bar.ttsengine:es_ES"));
     }
 
     @Test
     public void testTTSListValidator_withInvalidInput_returnsFalse() {
-        Validator v = new SettingsValidators.TTSListValidator();
-
-        assertFalse(v.validate("com.foo.ttsengine:eng-USA,INVALID"));
+        assertFalse(
+                SettingsValidators.TTS_LIST_VALIDATOR.validate(
+                        "com.foo.ttsengine:eng-USA,INVALID"));
     }
 
     @Test
     public void testTTSListValidator_withEmptyInput_returnsFalse() {
-        Validator v = new SettingsValidators.TTSListValidator();
-
-        assertFalse(v.validate(""));
+        assertFalse(SettingsValidators.TTS_LIST_VALIDATOR.validate(""));
     }
 
     @Test
     public void testTTSListValidator_withNullInput_returnsFalse() {
-        Validator v = new SettingsValidators.TTSListValidator();
-
-        assertFalse(v.validate(null));
+        assertFalse(SettingsValidators.TTS_LIST_VALIDATOR.validate(null));
     }
 
     @Test
     public void testTileListValidator_withValidInput_returnsTrue() {
-        Validator v = new SettingsValidators.TileListValidator();
-
-        assertTrue(v.validate("1,2,3,4"));
+        assertTrue(SettingsValidators.TILE_LIST_VALIDATOR.validate("1,2,3,4"));
     }
 
     @Test
     public void testTileListValidator_withMissingValue_returnsFalse() {
-        Validator v = new SettingsValidators.TileListValidator();
-
-        assertFalse(v.validate("1,,3"));
+        assertFalse(SettingsValidators.TILE_LIST_VALIDATOR.validate("1,,3"));
     }
 
     @Test
     public void testTileListValidator_withNullInput_returnsFalse() {
-        Validator v = new SettingsValidators.TileListValidator();
-
-        assertFalse(v.validate(null));
+        assertFalse(SettingsValidators.TILE_LIST_VALIDATOR.validate(null));
     }
 
     @Test
diff --git a/core/tests/coretests/src/android/util/ArraySetTest.java b/core/tests/coretests/src/android/util/ArraySetTest.java
new file mode 100644
index 0000000..f1bebfb
--- /dev/null
+++ b/core/tests/coretests/src/android/util/ArraySetTest.java
@@ -0,0 +1,113 @@
+/*
+ * 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.util;
+
+import androidx.test.filters.LargeTest;
+
+import junit.framework.TestCase;
+
+import org.junit.After;
+import org.junit.Test;
+
+import java.util.ConcurrentModificationException;
+
+/**
+ * Unit tests for ArraySet that don't belong in CTS.
+ */
+@LargeTest
+public class ArraySetTest extends TestCase {
+    private static final String TAG = "ArraySetTest";
+    ArraySet<String> mSet = new ArraySet<>();
+
+    @After
+    public void tearDown() {
+        mSet = null;
+    }
+
+    /**
+     * Attempt to generate a ConcurrentModificationException in ArraySet.
+     * <p>
+     * ArraySet is explicitly documented to be non-thread-safe, yet it's easy to accidentally screw
+     * this up; ArraySet should (in the spirit of the core Java collection types) make an effort to
+     * catch this and throw ConcurrentModificationException instead of crashing somewhere in its
+     * internals.
+     */
+    @Test
+    public void testConcurrentModificationException() throws Exception {
+        final int testDurMs = 10_000;
+        System.out.println("Starting ArraySet concurrency test");
+        new Thread(() -> {
+            int i = 0;
+            while (mSet != null) {
+                try {
+                    mSet.add(String.format("key %d", i++));
+                } catch (ArrayIndexOutOfBoundsException e) {
+                    Log.e(TAG, "concurrent modification uncaught, causing indexing failure", e);
+                    fail("Concurrent modification uncaught, causing indexing failure: " + e);
+                } catch (ClassCastException e) {
+                    Log.e(TAG, "concurrent modification uncaught, causing cache corruption", e);
+                    fail("Concurrent modification uncaught, causing cache corruption: " + e);
+                } catch (ConcurrentModificationException e) {
+                    System.out.println("[successfully caught CME at put #" + i
+                            + " size=" + (mSet == null ? "??" : String.valueOf(mSet.size())) + "]");
+                    if (i % 200 == 0) {
+                        System.out.print(".");
+                    }
+                }
+            }
+        }).start();
+        for (int i = 0; i < (testDurMs / 100); i++) {
+            try {
+                if (mSet.size() % 4 == 0) {
+                    mSet.clear();
+                }
+                System.out.print("X");
+            } catch (ArrayIndexOutOfBoundsException e) {
+                Log.e(TAG, "concurrent modification uncaught, causing indexing failure", e);
+                fail("Concurrent modification uncaught, causing indexing failure: " + e);
+            } catch (ClassCastException e) {
+                Log.e(TAG, "concurrent modification uncaught, causing cache corruption", e);
+                fail("Concurrent modification uncaught, causing cache corruption: " + e);
+            } catch (ConcurrentModificationException e) {
+                System.out.println(
+                        "[successfully caught CME at clear #" + i + " size=" + mSet.size() + "]");
+            }
+        }
+    }
+
+    /**
+     * Check to make sure the same operations behave as expected in a single thread.
+     */
+    @Test
+    public void testNonConcurrentAccesses() throws Exception {
+        for (int i = 0; i < 100000; i++) {
+            try {
+                mSet.add(String.format("key %d", i++));
+                if (i % 200 == 0) {
+                    System.out.print(".");
+                }
+                if (i % 500 == 0) {
+                    mSet.clear();
+                    System.out.print("X");
+                }
+            } catch (ConcurrentModificationException e) {
+                Log.e(TAG, "concurrent modification caught on single thread", e);
+                fail();
+            }
+        }
+    }
+}
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index 4187c80..89523d6 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -322,6 +322,8 @@
         <permission name="android.permission.SET_WALLPAPER" />
         <permission name="android.permission.SET_WALLPAPER_COMPONENT" />
         <permission name="android.permission.REQUEST_NOTIFICATION_ASSISTANT_SERVICE" />
+        <!-- Permission required to test system only camera devices. -->
+        <permission name="android.permission.SYSTEM_CAMERA" />
         <!-- Permission required to test ExplicitHealthCheckServiceImpl. -->
         <permission name="android.permission.BIND_EXPLICIT_HEALTH_CHECK_SERVICE"/>
     </privapp-permissions>
diff --git a/libs/hwui/utils/Color.cpp b/libs/hwui/utils/Color.cpp
index 9a27f28..b93759f 100644
--- a/libs/hwui/utils/Color.cpp
+++ b/libs/hwui/utils/Color.cpp
@@ -21,6 +21,7 @@
 
 #ifdef __ANDROID__ // Layoutlib does not support hardware buffers or native windows
 #include <android/hardware_buffer.h>
+#include <android/native_window.h>
 #endif
 
 #include <algorithm>
@@ -30,11 +31,11 @@
 namespace uirenderer {
 
 #ifdef __ANDROID__ // Layoutlib does not support hardware buffers or native windows
-SkImageInfo BufferDescriptionToImageInfo(const AHardwareBuffer_Desc& bufferDesc,
-                                         sk_sp<SkColorSpace> colorSpace) {
+static inline SkImageInfo createImageInfo(int32_t width, int32_t height, int32_t format,
+                                          sk_sp<SkColorSpace> colorSpace) {
     SkColorType colorType = kUnknown_SkColorType;
     SkAlphaType alphaType = kOpaque_SkAlphaType;
-    switch (bufferDesc.format) {
+    switch (format) {
         case AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM:
             colorType = kN32_SkColorType;
             alphaType = kPremul_SkAlphaType;
@@ -56,10 +57,20 @@
             alphaType = kPremul_SkAlphaType;
             break;
         default:
-            ALOGV("Unsupported format: %d, return unknown by default", bufferDesc.format);
+            ALOGV("Unsupported format: %d, return unknown by default", format);
             break;
     }
-    return SkImageInfo::Make(bufferDesc.width, bufferDesc.height, colorType, alphaType, colorSpace);
+    return SkImageInfo::Make(width, height, colorType, alphaType, colorSpace);
+}
+
+SkImageInfo ANativeWindowToImageInfo(const ANativeWindow_Buffer& buffer,
+                                     sk_sp<SkColorSpace> colorSpace) {
+    return createImageInfo(buffer.width, buffer.height, buffer.format, colorSpace);
+}
+
+SkImageInfo BufferDescriptionToImageInfo(const AHardwareBuffer_Desc& bufferDesc,
+                                         sk_sp<SkColorSpace> colorSpace) {
+    return createImageInfo(bufferDesc.width, bufferDesc.height, bufferDesc.format, colorSpace);
 }
 #endif
 
diff --git a/libs/hwui/utils/Color.h b/libs/hwui/utils/Color.h
index 7c2378a..07b5ec8 100644
--- a/libs/hwui/utils/Color.h
+++ b/libs/hwui/utils/Color.h
@@ -25,6 +25,7 @@
 #include <SkColorSpace.h>
 #include <SkImageInfo.h>
 
+struct ANativeWindow_Buffer;
 struct AHardwareBuffer_Desc;
 
 namespace android {
@@ -91,8 +92,13 @@
     return srgb <= 0.04045f ? srgb / 12.92f : powf((srgb + 0.055f) / 1.055f, 2.4f);
 }
 
+#ifdef __ANDROID__ // Layoutlib does not support hardware buffers or native windows
+ANDROID_API SkImageInfo ANativeWindowToImageInfo(const ANativeWindow_Buffer& buffer,
+                                                 sk_sp<SkColorSpace> colorSpace);
+
 SkImageInfo BufferDescriptionToImageInfo(const AHardwareBuffer_Desc& bufferDesc,
                                          sk_sp<SkColorSpace> colorSpace);
+#endif
 
 android::PixelFormat ColorTypeToPixelFormat(SkColorType colorType);
 ANDROID_API SkColorType PixelFormatToColorType(android::PixelFormat format);
diff --git a/libs/input/Android.bp b/libs/input/Android.bp
index 89d3cc4..16f2917 100644
--- a/libs/input/Android.bp
+++ b/libs/input/Android.bp
@@ -20,6 +20,7 @@
     ],
 
     shared_libs: [
+        "libbinder",
         "libcutils",
         "liblog",
         "libutils",
diff --git a/libs/input/SpriteController.cpp b/libs/input/SpriteController.cpp
index c1868d3..fd386e9 100644
--- a/libs/input/SpriteController.cpp
+++ b/libs/input/SpriteController.cpp
@@ -245,7 +245,8 @@
         if (update.state.surfaceControl != NULL && (becomingVisible || becomingHidden
                 || (wantSurfaceVisibleAndDrawn && (update.state.dirty & (DIRTY_ALPHA
                         | DIRTY_POSITION | DIRTY_TRANSFORMATION_MATRIX | DIRTY_LAYER
-                        | DIRTY_VISIBILITY | DIRTY_HOTSPOT | DIRTY_DISPLAY_ID))))) {
+                        | DIRTY_VISIBILITY | DIRTY_HOTSPOT | DIRTY_DISPLAY_ID
+                        | DIRTY_ICON_STYLE))))) {
             needApplyTransaction = true;
 
             if (wantSurfaceVisibleAndDrawn
@@ -274,6 +275,21 @@
                         update.state.transformationMatrix.dtdy);
             }
 
+            if (wantSurfaceVisibleAndDrawn
+                    && (becomingVisible
+                            || (update.state.dirty & (DIRTY_HOTSPOT | DIRTY_ICON_STYLE)))) {
+                Parcel p;
+                p.writeInt32(update.state.icon.style);
+                p.writeFloat(update.state.icon.hotSpotX);
+                p.writeFloat(update.state.icon.hotSpotY);
+
+                // Pass cursor metadata in the sprite surface so that when Android is running as a
+                // client OS (e.g. ARC++) the host OS can get the requested cursor metadata and
+                // update mouse cursor in the host OS.
+                t.setMetadata(
+                        update.state.surfaceControl, METADATA_MOUSE_CURSOR, p);
+            }
+
             int32_t surfaceLayer = mOverlayLayer + update.state.layer;
             if (wantSurfaceVisibleAndDrawn
                     && (becomingVisible || (update.state.dirty & DIRTY_LAYER))) {
@@ -397,9 +413,14 @@
         } else {
             dirty = DIRTY_BITMAP;
         }
+
+        if (mLocked.state.icon.style != icon.style) {
+            mLocked.state.icon.style = icon.style;
+            dirty |= DIRTY_ICON_STYLE;
+        }
     } else if (mLocked.state.icon.isValid()) {
         mLocked.state.icon.bitmap.reset();
-        dirty = DIRTY_BITMAP | DIRTY_HOTSPOT;
+        dirty = DIRTY_BITMAP | DIRTY_HOTSPOT | DIRTY_ICON_STYLE;
     } else {
         return; // setting to invalid icon and already invalid so nothing to do
     }
diff --git a/libs/input/SpriteController.h b/libs/input/SpriteController.h
index 5b216f5..79a904f 100644
--- a/libs/input/SpriteController.h
+++ b/libs/input/SpriteController.h
@@ -55,11 +55,12 @@
  * Icon that a sprite displays, including its hotspot.
  */
 struct SpriteIcon {
-    inline SpriteIcon() : hotSpotX(0), hotSpotY(0) { }
-    inline SpriteIcon(const SkBitmap& bitmap, float hotSpotX, float hotSpotY) :
-            bitmap(bitmap), hotSpotX(hotSpotX), hotSpotY(hotSpotY) { }
+    inline SpriteIcon() : style(0), hotSpotX(0), hotSpotY(0) { }
+    inline SpriteIcon(const SkBitmap& bitmap, int32_t style, float hotSpotX, float hotSpotY) :
+            bitmap(bitmap), style(style), hotSpotX(hotSpotX), hotSpotY(hotSpotY) { }
 
     SkBitmap bitmap;
+    int32_t style;
     float hotSpotX;
     float hotSpotY;
 
@@ -69,11 +70,12 @@
             bitmap.readPixels(bitmapCopy.info(), bitmapCopy.getPixels(), bitmapCopy.rowBytes(),
                     0, 0);
         }
-        return SpriteIcon(bitmapCopy, hotSpotX, hotSpotY);
+        return SpriteIcon(bitmapCopy, style, hotSpotX, hotSpotY);
     }
 
     inline void reset() {
         bitmap.reset();
+        style = 0;
         hotSpotX = 0;
         hotSpotY = 0;
     }
@@ -149,15 +151,15 @@
     SpriteController(const sp<Looper>& looper, int32_t overlayLayer);
 
     /* Creates a new sprite, initially invisible. */
-    sp<Sprite> createSprite();
+    virtual sp<Sprite> createSprite();
 
     /* Opens or closes a transaction to perform a batch of sprite updates as part of
      * a single operation such as setPosition and setAlpha.  It is not necessary to
      * open a transaction when updating a single property.
      * Calls to openTransaction() nest and must be matched by an equal number
      * of calls to closeTransaction(). */
-    void openTransaction();
-    void closeTransaction();
+    virtual void openTransaction();
+    virtual void closeTransaction();
 
 private:
     enum {
@@ -174,6 +176,7 @@
         DIRTY_VISIBILITY = 1 << 5,
         DIRTY_HOTSPOT = 1 << 6,
         DIRTY_DISPLAY_ID = 1 << 7,
+        DIRTY_ICON_STYLE = 1 << 8,
     };
 
     /* Describes the state of a sprite.
diff --git a/libs/input/TEST_MAPPING b/libs/input/TEST_MAPPING
new file mode 100644
index 0000000..fe74c62
--- /dev/null
+++ b/libs/input/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+    "presubmit": [
+        {
+            "name": "libinputservice_test"
+        }
+    ]
+}
diff --git a/libs/input/tests/Android.bp b/libs/input/tests/Android.bp
new file mode 100644
index 0000000..e83b2a7
--- /dev/null
+++ b/libs/input/tests/Android.bp
@@ -0,0 +1,45 @@
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+cc_test {
+    name: "libinputservice_test",
+    srcs: [
+        "PointerController_test.cpp",
+    ],
+    shared_libs: [
+        "libinputservice",
+        "libgui",
+        "libhwui",
+        "libutils",
+    ],
+    static_libs: [
+        "libgmock",
+        "libgtest",
+    ],
+    header_libs: [
+        "libbase_headers",
+        "libinputflinger_headers",
+    ],
+    include_dirs: [
+        "frameworks/base/libs",
+    ],
+    cflags: [
+        "-Wall",
+        "-Werror",
+        "-Wextra",
+    ],
+    test_suites: [
+        "general-tests",
+    ],
+}
diff --git a/libs/input/tests/PointerController_test.cpp b/libs/input/tests/PointerController_test.cpp
new file mode 100644
index 0000000..92efb4e
--- /dev/null
+++ b/libs/input/tests/PointerController_test.cpp
@@ -0,0 +1,215 @@
+/*
+ * 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 "mocks/MockSprite.h"
+#include "mocks/MockSpriteController.h"
+
+#include <input/PointerController.h>
+#include <input/SpriteController.h>
+
+#include <atomic>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <thread>
+
+namespace android {
+
+enum TestCursorType {
+    CURSOR_TYPE_DEFAULT = 0,
+    CURSOR_TYPE_HOVER,
+    CURSOR_TYPE_TOUCH,
+    CURSOR_TYPE_ANCHOR,
+    CURSOR_TYPE_ADDITIONAL_1,
+    CURSOR_TYPE_ADDITIONAL_2,
+    CURSOR_TYPE_CUSTOM = -1,
+};
+
+using ::testing::AllOf;
+using ::testing::Field;
+using ::testing::NiceMock;
+using ::testing::Mock;
+using ::testing::Return;
+using ::testing::Test;
+
+std::pair<float, float> getHotSpotCoordinatesForType(int32_t type) {
+    return std::make_pair(type * 10, type * 10 + 5);
+}
+
+class MockPointerControllerPolicyInterface : public PointerControllerPolicyInterface {
+public:
+    virtual void loadPointerIcon(SpriteIcon* icon, int32_t displayId) override;
+    virtual void loadPointerResources(PointerResources* outResources, int32_t displayId) override;
+    virtual void loadAdditionalMouseResources(std::map<int32_t, SpriteIcon>* outResources,
+            std::map<int32_t, PointerAnimation>* outAnimationResources, int32_t displayId) override;
+    virtual int32_t getDefaultPointerIconId() override;
+    virtual int32_t getCustomPointerIconId() override;
+
+private:
+    void loadPointerIconForType(SpriteIcon* icon, int32_t cursorType);
+};
+
+void MockPointerControllerPolicyInterface::loadPointerIcon(SpriteIcon* icon, int32_t) {
+    loadPointerIconForType(icon, CURSOR_TYPE_DEFAULT);
+}
+
+void MockPointerControllerPolicyInterface::loadPointerResources(PointerResources* outResources,
+        int32_t) {
+    loadPointerIconForType(&outResources->spotHover, CURSOR_TYPE_HOVER);
+    loadPointerIconForType(&outResources->spotTouch, CURSOR_TYPE_TOUCH);
+    loadPointerIconForType(&outResources->spotAnchor, CURSOR_TYPE_ANCHOR);
+}
+
+void MockPointerControllerPolicyInterface::loadAdditionalMouseResources(
+        std::map<int32_t, SpriteIcon>* outResources,
+        std::map<int32_t, PointerAnimation>* outAnimationResources,
+        int32_t) {
+    SpriteIcon icon;
+    PointerAnimation anim;
+
+    for (int32_t cursorType : {CURSOR_TYPE_ADDITIONAL_1, CURSOR_TYPE_ADDITIONAL_2}) {
+        loadPointerIconForType(&icon, cursorType);
+        anim.animationFrames.push_back(icon);
+        anim.durationPerFrame = 10;
+        (*outResources)[cursorType] = icon;
+        (*outAnimationResources)[cursorType] = anim;
+    }
+}
+
+int32_t MockPointerControllerPolicyInterface::getDefaultPointerIconId() {
+    return CURSOR_TYPE_DEFAULT;
+}
+
+int32_t MockPointerControllerPolicyInterface::getCustomPointerIconId() {
+    return CURSOR_TYPE_CUSTOM;
+}
+
+void MockPointerControllerPolicyInterface::loadPointerIconForType(SpriteIcon* icon, int32_t type) {
+    icon->style = type;
+    std::pair<float, float> hotSpot = getHotSpotCoordinatesForType(type);
+    icon->hotSpotX = hotSpot.first;
+    icon->hotSpotY = hotSpot.second;
+}
+
+class PointerControllerTest : public Test {
+protected:
+    PointerControllerTest();
+    ~PointerControllerTest();
+
+    sp<MockSprite> mPointerSprite;
+    sp<MockPointerControllerPolicyInterface> mPolicy;
+    sp<MockSpriteController> mSpriteController;
+    sp<PointerController> mPointerController;
+
+private:
+    void loopThread();
+
+    std::atomic<bool> mRunning = true;
+    class MyLooper : public Looper {
+    public:
+        MyLooper() : Looper(false) {}
+        ~MyLooper() = default;
+    };
+    sp<MyLooper> mLooper;
+    std::thread mThread;
+};
+
+PointerControllerTest::PointerControllerTest() : mPointerSprite(new NiceMock<MockSprite>),
+        mLooper(new MyLooper), mThread(&PointerControllerTest::loopThread, this) {
+
+    mSpriteController = new NiceMock<MockSpriteController>(mLooper);
+    mPolicy = new MockPointerControllerPolicyInterface();
+
+    EXPECT_CALL(*mSpriteController, createSprite())
+            .WillOnce(Return(mPointerSprite));
+
+    mPointerController = new PointerController(mPolicy, mLooper, mSpriteController);
+
+    DisplayViewport viewport;
+    viewport.displayId = ADISPLAY_ID_DEFAULT;
+    viewport.logicalRight = 1600;
+    viewport.logicalBottom = 1200;
+    viewport.physicalRight = 800;
+    viewport.physicalBottom = 600;
+    viewport.deviceWidth = 400;
+    viewport.deviceHeight = 300;
+    mPointerController->setDisplayViewport(viewport);
+}
+
+PointerControllerTest::~PointerControllerTest() {
+    mRunning.store(false, std::memory_order_relaxed);
+    mThread.join();
+}
+
+void PointerControllerTest::loopThread() {
+    Looper::setForThread(mLooper);
+
+    while (mRunning.load(std::memory_order_relaxed)) {
+        mLooper->pollOnce(100);
+    }
+}
+
+TEST_F(PointerControllerTest, useDefaultCursorTypeByDefault) {
+    mPointerController->unfade(PointerController::TRANSITION_IMMEDIATE);
+
+    std::pair<float, float> hotspot = getHotSpotCoordinatesForType(CURSOR_TYPE_DEFAULT);
+    EXPECT_CALL(*mPointerSprite, setVisible(true));
+    EXPECT_CALL(*mPointerSprite, setAlpha(1.0f));
+    EXPECT_CALL(*mPointerSprite, setIcon(
+            AllOf(
+                    Field(&SpriteIcon::style, CURSOR_TYPE_DEFAULT),
+                    Field(&SpriteIcon::hotSpotX, hotspot.first),
+                    Field(&SpriteIcon::hotSpotY, hotspot.second))));
+    mPointerController->reloadPointerResources();
+}
+
+TEST_F(PointerControllerTest, updatePointerIcon) {
+    mPointerController->unfade(PointerController::TRANSITION_IMMEDIATE);
+
+    int32_t type = CURSOR_TYPE_ADDITIONAL_1;
+    std::pair<float, float> hotspot = getHotSpotCoordinatesForType(type);
+    EXPECT_CALL(*mPointerSprite, setVisible(true));
+    EXPECT_CALL(*mPointerSprite, setAlpha(1.0f));
+    EXPECT_CALL(*mPointerSprite, setIcon(
+            AllOf(
+                    Field(&SpriteIcon::style, type),
+                    Field(&SpriteIcon::hotSpotX, hotspot.first),
+                    Field(&SpriteIcon::hotSpotY, hotspot.second))));
+    mPointerController->updatePointerIcon(type);
+}
+
+TEST_F(PointerControllerTest, setCustomPointerIcon) {
+    mPointerController->unfade(PointerController::TRANSITION_IMMEDIATE);
+
+    int32_t style = CURSOR_TYPE_CUSTOM;
+    float hotSpotX = 15;
+    float hotSpotY = 20;
+
+    SpriteIcon icon;
+    icon.style = style;
+    icon.hotSpotX = hotSpotX;
+    icon.hotSpotY = hotSpotY;
+
+    EXPECT_CALL(*mPointerSprite, setVisible(true));
+    EXPECT_CALL(*mPointerSprite, setAlpha(1.0f));
+    EXPECT_CALL(*mPointerSprite, setIcon(
+            AllOf(
+                    Field(&SpriteIcon::style, style),
+                    Field(&SpriteIcon::hotSpotX, hotSpotX),
+                    Field(&SpriteIcon::hotSpotY, hotSpotY))));
+    mPointerController->setCustomPointerIcon(icon);
+}
+
+}  // namespace android
diff --git a/libs/input/tests/mocks/MockSprite.h b/libs/input/tests/mocks/MockSprite.h
new file mode 100644
index 0000000..013b79c
--- /dev/null
+++ b/libs/input/tests/mocks/MockSprite.h
@@ -0,0 +1,41 @@
+/*
+ * 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 _MOCK_SPRITE_H
+#define _MOCK_SPRITE_H
+
+#include <input/SpriteController.h>
+
+#include <gmock/gmock.h>
+
+namespace android {
+
+class MockSprite : public Sprite {
+public:
+    virtual ~MockSprite() = default;
+
+    MOCK_METHOD(void, setIcon, (const SpriteIcon& icon), (override));
+    MOCK_METHOD(void, setVisible, (bool), (override));
+    MOCK_METHOD(void, setPosition, (float, float), (override));
+    MOCK_METHOD(void, setLayer, (int32_t), (override));
+    MOCK_METHOD(void, setAlpha, (float), (override));
+    MOCK_METHOD(void, setTransformationMatrix, (const SpriteTransformationMatrix&), (override));
+    MOCK_METHOD(void, setDisplayId, (int32_t), (override));
+};
+
+}  // namespace android
+
+#endif  // _MOCK_SPRITE_H
diff --git a/libs/input/tests/mocks/MockSpriteController.h b/libs/input/tests/mocks/MockSpriteController.h
new file mode 100644
index 0000000..a034f66
--- /dev/null
+++ b/libs/input/tests/mocks/MockSpriteController.h
@@ -0,0 +1,39 @@
+/*
+ * 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 _MOCK_SPRITE_CONTROLLER_H
+#define _MOCK_SPRITE_CONTROLLER_H
+
+#include "MockSprite.h"
+
+#include <input/SpriteController.h>
+
+namespace android {
+
+class MockSpriteController : public SpriteController {
+
+public:
+    MockSpriteController(sp<Looper> looper) : SpriteController(looper, 0) {}
+    ~MockSpriteController() {}
+
+    MOCK_METHOD(sp<Sprite>, createSprite, (), (override));
+    MOCK_METHOD(void, openTransaction, (), (override));
+    MOCK_METHOD(void, closeTransaction, (), (override));
+};
+
+}  // namespace android
+
+#endif  // _MOCK_SPRITE_CONTROLLER_H
diff --git a/media/Android.bp b/media/Android.bp
index 29064ad..ef32239 100644
--- a/media/Android.bp
+++ b/media/Android.bp
@@ -67,6 +67,7 @@
         "apex/java/android/media/Session2Link.java",
         "apex/java/android/media/Session2Token.java",
     ],
+    path: "apex/java",
 }
 
 filegroup {
@@ -79,6 +80,7 @@
         "apex/java/android/media/IMediaSession2.aidl",
         "apex/java/android/media/IMediaSession2Service.aidl",
     ],
+    path: "apex/java",
 }
 
 filegroup {
@@ -98,6 +100,7 @@
         "apex/java/android/media/BufferingParams.java",
         "apex/java/android/media/ProxyDataSourceCallback.java",
     ],
+    path: "apex/java",
 }
 
 metalava_updatable_media_args = " --error UnhiddenSystemApi " +
diff --git a/media/java/android/media/ExifInterface.java b/media/java/android/media/ExifInterface.java
index 148ffaf..55583d5 100644
--- a/media/java/android/media/ExifInterface.java
+++ b/media/java/android/media/ExifInterface.java
@@ -37,6 +37,7 @@
 import libcore.io.Streams;
 
 import java.io.BufferedInputStream;
+import java.io.BufferedOutputStream;
 import java.io.ByteArrayInputStream;
 import java.io.DataInput;
 import java.io.DataInputStream;
@@ -1866,14 +1867,17 @@
 
         FileInputStream in = null;
         FileOutputStream out = null;
+        File originalFile = null;
+        if (mFilename != null) {
+            originalFile = new File(mFilename);
+        }
         File tempFile = null;
         try {
             // Move the original file to temporary file.
             if (mFilename != null) {
                 tempFile = new File(mFilename + ".tmp");
-                File originalFile = new File(mFilename);
                 if (!originalFile.renameTo(tempFile)) {
-                    throw new IOException("Could'nt rename to " + tempFile.getAbsolutePath());
+                    throw new IOException("Couldn't rename to " + tempFile.getAbsolutePath());
                 }
             } else if (mSeekableFileDescriptor != null) {
                 tempFile = File.createTempFile("temp", "jpg");
@@ -1882,8 +1886,8 @@
                 out = new FileOutputStream(tempFile);
                 Streams.copy(in, out);
             }
-        } catch (ErrnoException e) {
-            throw e.rethrowAsIOException();
+        } catch (Exception e) {
+            throw new IOException("Failed to copy original file to temp file", e);
         } finally {
             IoUtils.closeQuietly(in);
             IoUtils.closeQuietly(out);
@@ -1900,9 +1904,18 @@
                 Os.lseek(mSeekableFileDescriptor, 0, OsConstants.SEEK_SET);
                 out = new FileOutputStream(mSeekableFileDescriptor);
             }
-            saveJpegAttributes(in, out);
-        } catch (ErrnoException e) {
-            throw e.rethrowAsIOException();
+            try (BufferedInputStream bufferedIn = new BufferedInputStream(in);
+                 BufferedOutputStream bufferedOut = new BufferedOutputStream(out)) {
+                saveJpegAttributes(bufferedIn, bufferedOut);
+            }
+        } catch (Exception e) {
+            if (mFilename != null) {
+                if (!tempFile.renameTo(originalFile)) {
+                    throw new IOException("Couldn't restore original file: "
+                            + originalFile.getAbsolutePath());
+                }
+            }
+            throw new IOException("Failed to save new file", e);
         } finally {
             IoUtils.closeQuietly(in);
             IoUtils.closeQuietly(out);
diff --git a/media/java/android/media/MediaScannerConnection.java b/media/java/android/media/MediaScannerConnection.java
index 471fa2c..7eec8d9 100644
--- a/media/java/android/media/MediaScannerConnection.java
+++ b/media/java/android/media/MediaScannerConnection.java
@@ -16,17 +16,20 @@
 
 package android.media;
 
+import android.annotation.UnsupportedAppUsage;
 import android.content.ComponentName;
+import android.content.ContentProviderClient;
 import android.content.Context;
-import android.content.Intent;
 import android.content.ServiceConnection;
-import android.media.IMediaScannerListener;
-import android.media.IMediaScannerService;
 import android.net.Uri;
+import android.os.Build;
 import android.os.IBinder;
-import android.os.RemoteException;
+import android.provider.MediaStore;
 import android.util.Log;
 
+import com.android.internal.os.BackgroundThread;
+
+import java.io.File;
 
 /**
  * MediaScannerConnection provides a way for applications to pass a
@@ -38,20 +41,24 @@
  * to the client of the MediaScannerConnection class.
  */
 public class MediaScannerConnection implements ServiceConnection {
-
     private static final String TAG = "MediaScannerConnection";
 
-    private Context mContext;
-    private MediaScannerConnectionClient mClient;
-    private IMediaScannerService mService;
-    private boolean mConnected; // true if connect() has been called since last disconnect()
+    private final Context mContext;
+    private final MediaScannerConnectionClient mClient;
 
+    private ContentProviderClient mProvider;
+
+    @Deprecated
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.O)
+    private IMediaScannerService mService;
+    @Deprecated
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.O)
+    private boolean mConnected;
+    @Deprecated
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.O)
     private final IMediaScannerListener.Stub mListener = new IMediaScannerListener.Stub() {
+        @Override
         public void scanCompleted(String path, Uri uri) {
-            MediaScannerConnectionClient client = mClient;
-            if (client != null) {
-                client.onScanCompleted(path, uri);
-            }
         }
     };
 
@@ -81,15 +88,6 @@
          * MediaScanner service has been established.
          */
         public void onMediaScannerConnected();
-
-        /**
-         * Called to notify the client when the media scanner has finished
-         * scanning a file.
-         * @param path the path to the file that has been scanned.
-         * @param uri the Uri for the file if the scanning operation succeeded
-         * and the file was added to the media database, or null if scanning failed.
-         */
-        public void onScanCompleted(String path, Uri uri);
     }
 
     /**
@@ -111,13 +109,12 @@
      */
     public void connect() {
         synchronized (this) {
-            if (!mConnected) {
-                Intent intent = new Intent(IMediaScannerService.class.getName());
-                intent.setComponent(
-                        new ComponentName("com.android.providers.media",
-                                "com.android.providers.media.MediaScannerService"));
-                mContext.bindService(intent, this, Context.BIND_AUTO_CREATE);
-                mConnected = true;
+            if (mProvider == null) {
+                mProvider = mContext.getContentResolver()
+                        .acquireContentProviderClient(MediaStore.AUTHORITY);
+                if (mClient != null) {
+                    mClient.onMediaScannerConnected();
+                }
             }
         }
     }
@@ -127,22 +124,9 @@
      */
     public void disconnect() {
         synchronized (this) {
-            if (mConnected) {
-                if (false) {
-                    Log.v(TAG, "Disconnecting from Media Scanner");
-                }
-                try {
-                    mContext.unbindService(this);
-                    if (mClient instanceof ClientProxy) {
-                        mClient = null;
-                    }
-                    mService = null;
-                } catch (IllegalArgumentException ex) {
-                    if (false) {
-                        Log.v(TAG, "disconnect failed: " + ex);
-                    }
-                }
-                mConnected = false;
+            if (mProvider != null) {
+                mProvider.close();
+                mProvider = null;
             }
         }
     }
@@ -152,7 +136,7 @@
      * @return true if we are connected, false otherwise
      */
     public synchronized boolean isConnected() {
-        return (mService != null && mConnected);
+        return (mProvider != null);
     }
 
     /**
@@ -166,55 +150,15 @@
      */
      public void scanFile(String path, String mimeType) {
         synchronized (this) {
-            if (mService == null || !mConnected) {
+            if (mProvider == null) {
                 throw new IllegalStateException("not connected to MediaScannerService");
             }
-            try {
-                if (false) {
-                    Log.v(TAG, "Scanning file " + path);
+            BackgroundThread.getExecutor().execute(() -> {
+                final Uri uri = scanFileQuietly(mProvider, new File(path));
+                if (mClient != null) {
+                    mClient.onScanCompleted(path, uri);
                 }
-                mService.requestScanFile(path, mimeType, mListener);
-            } catch (RemoteException e) {
-                if (false) {
-                    Log.d(TAG, "Failed to scan file " + path);
-                }
-            }
-        }
-    }
-
-    static class ClientProxy implements MediaScannerConnectionClient {
-        final String[] mPaths;
-        final String[] mMimeTypes;
-        final OnScanCompletedListener mClient;
-        MediaScannerConnection mConnection;
-        int mNextPath;
-
-        ClientProxy(String[] paths, String[] mimeTypes, OnScanCompletedListener client) {
-            mPaths = paths;
-            mMimeTypes = mimeTypes;
-            mClient = client;
-        }
-
-        public void onMediaScannerConnected() {
-            scanNextPath();
-        }
-
-        public void onScanCompleted(String path, Uri uri) {
-            if (mClient != null) {
-                mClient.onScanCompleted(path, uri);
-            }
-            scanNextPath();
-        }
-
-        void scanNextPath() {
-            if (mNextPath >= mPaths.length) {
-                mConnection.disconnect();
-                mConnection = null;
-                return;
-            }
-            String mimeType = mMimeTypes != null ? mMimeTypes[mNextPath] : null;
-            mConnection.scanFile(mPaths[mNextPath], mimeType);
-            mNextPath++;
+            });
         }
     }
 
@@ -237,36 +181,76 @@
      */
     public static void scanFile(Context context, String[] paths, String[] mimeTypes,
             OnScanCompletedListener callback) {
-        ClientProxy client = new ClientProxy(paths, mimeTypes, callback);
-        MediaScannerConnection connection = new MediaScannerConnection(context, client);
-        client.mConnection = connection;
-        connection.connect();
-    }
-
-    /**
-     * Part of the ServiceConnection interface.  Do not call.
-     */
-    public void onServiceConnected(ComponentName className, IBinder service) {
-        if (false) {
-            Log.v(TAG, "Connected to Media Scanner");
-        }
-        synchronized (this) {
-            mService = IMediaScannerService.Stub.asInterface(service);
-            if (mService != null && mClient != null) {
-                mClient.onMediaScannerConnected();
+        BackgroundThread.getExecutor().execute(() -> {
+            try (ContentProviderClient client = context.getContentResolver()
+                    .acquireContentProviderClient(MediaStore.AUTHORITY)) {
+                for (String path : paths) {
+                    final Uri uri = scanFileQuietly(client, new File(path));
+                    if (callback != null) {
+                        callback.onScanCompleted(path, uri);
+                    }
+                }
             }
+        });
+    }
+
+    private static Uri scanFileQuietly(ContentProviderClient client, File file) {
+        Uri uri = null;
+        try {
+            uri = MediaStore.scanFile(client, file);
+            Log.d(TAG, "Scanned " + file + " to " + uri);
+        } catch (Exception e) {
+            Log.w(TAG, "Failed to scan " + file + ": " + e);
+        }
+        return uri;
+    }
+
+    @Deprecated
+    static class ClientProxy implements MediaScannerConnectionClient {
+        @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.O)
+        final String[] mPaths;
+        @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.O)
+        final String[] mMimeTypes;
+        @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.O)
+        final OnScanCompletedListener mClient;
+        @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.O)
+        MediaScannerConnection mConnection;
+        @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.O)
+        int mNextPath;
+
+        @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.O)
+        ClientProxy(String[] paths, String[] mimeTypes, OnScanCompletedListener client) {
+            mPaths = paths;
+            mMimeTypes = mimeTypes;
+            mClient = client;
+        }
+
+        @Override
+        public void onMediaScannerConnected() {
+        }
+
+        @Override
+        public void onScanCompleted(String path, Uri uri) {
+        }
+
+        @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.O)
+        void scanNextPath() {
         }
     }
 
     /**
      * Part of the ServiceConnection interface.  Do not call.
      */
+    @Override
+    public void onServiceConnected(ComponentName className, IBinder service) {
+        // No longer needed
+    }
+
+    /**
+     * Part of the ServiceConnection interface.  Do not call.
+     */
+    @Override
     public void onServiceDisconnected(ComponentName className) {
-        if (false) {
-            Log.v(TAG, "Disconnected from Media Scanner");
-        }
-        synchronized (this) {
-            mService = null;
-        }
+        // No longer needed
     }
 }
diff --git a/media/java/android/media/Metadata.java b/media/java/android/media/Metadata.java
index be0d966..792a2ba 100644
--- a/media/java/android/media/Metadata.java
+++ b/media/java/android/media/Metadata.java
@@ -272,15 +272,6 @@
     @UnsupportedAppUsage
     public Metadata() { }
 
-    // Have to declare protected for finalize() since it is protected
-    // in the base class Object.
-    @Override
-    protected void finalize() throws Throwable {
-        if (mParcel != null) {
-            mParcel.recycle();
-        }
-    }
-
     /**
      * Go over all the records, collecting metadata keys and records'
      * type field offset in the Parcel. These are stored in
@@ -427,10 +418,6 @@
             parcel.setDataPosition(pin);
             return false;
         }
-
-        if (mParcel != null) {
-            mParcel.recycle();
-        }
         mParcel = parcel;
         return true;
     }
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 97fbea6..7ea83f5 100644
--- a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
+++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
@@ -32,7 +32,10 @@
 import android.graphics.PixelFormat;
 import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
+import android.inputmethodservice.InputMethodService;
+import android.os.IBinder;
 import android.util.Log;
+import android.view.Display;
 import android.view.GestureDetector;
 import android.view.Gravity;
 import android.view.MotionEvent;
@@ -87,8 +90,7 @@
 /**
  * A status bar (and navigation bar) tailored for the automotive use case.
  */
-public class CarStatusBar extends StatusBar implements
-        CarBatteryController.BatteryViewHandler {
+public class CarStatusBar extends StatusBar implements CarBatteryController.BatteryViewHandler {
     private static final String TAG = "CarStatusBar";
     // used to calculate how fast to open or close the window
     private static final float DEFAULT_FLING_VELOCITY = 0;
@@ -169,6 +171,9 @@
     private boolean mIsSwipingVerticallyToClose;
     // Whether heads-up notifications should be shown when shade is open.
     private boolean mEnableHeadsUpNotificationWhenNotificationShadeOpen;
+    // If the nav bar should be hidden when the soft keyboard is visible.
+    private boolean mHideNavBarForKeyboard;
+    private boolean mBottomNavBarVisible;
 
     private final CarPowerStateListener mCarPowerStateListener =
             (int state) -> {
@@ -190,6 +195,17 @@
         // builds the nav bar
         mDeviceProvisionedController = Dependency.get(DeviceProvisionedController.class);
         mDeviceIsProvisioned = mDeviceProvisionedController.isDeviceProvisioned();
+
+        // Keyboard related setup, before nav bars are created.
+        mHideNavBarForKeyboard = mContext.getResources().getBoolean(
+                com.android.internal.R.bool.config_automotiveHideNavBarForKeyboard);
+        mBottomNavBarVisible = false;
+
+        // Need to initialize screen lifecycle before calling super.start - before switcher is
+        // created.
+        mScreenLifecycle = Dependency.get(ScreenLifecycle.class);
+        mScreenLifecycle.addObserver(mScreenObserver);
+
         super.start();
         mTaskStackListener = new TaskStackListenerImpl();
         mActivityManagerWrapper = ActivityManagerWrapper.getInstance();
@@ -236,9 +252,6 @@
         mPowerManagerHelper.connectToCarService();
 
         mSwitchToGuestTimer = new SwitchToGuestTimer(mContext);
-
-        mScreenLifecycle = Dependency.get(ScreenLifecycle.class);
-        mScreenLifecycle.addObserver(mScreenObserver);
     }
 
     /**
@@ -720,6 +733,13 @@
         buildNavBarContent();
         attachNavBarWindows();
 
+        // Try setting up the initial state of the nav bar if applicable.
+        if (result != null) {
+            setImeWindowStatus(Display.DEFAULT_DISPLAY, result.mImeToken,
+                    result.mImeWindowVis, result.mImeBackDisposition,
+                    result.mShowImeSwitcher);
+        }
+
         // There has been a car customized nav bar on the default display, so just create nav bars
         // on external displays.
         mNavigationBarController.createNavigationBars(false /* includeDefaultDisplay */, result);
@@ -758,22 +778,33 @@
 
     }
 
-    private void attachNavBarWindows() {
-
-        if (mShowBottom) {
-            WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
-                    LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT,
-                    WindowManager.LayoutParams.TYPE_NAVIGATION_BAR,
-                    WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
-                            | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
-                            | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
-                            | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH,
-                    PixelFormat.TRANSLUCENT);
-            lp.setTitle("CarNavigationBar");
-            lp.windowAnimations = 0;
-            mWindowManager.addView(mNavigationBarWindow, lp);
+    /**
+     * We register for soft keyboard visibility events such that we can hide the navigation bar
+     * giving more screen space to the IME. Note: this is optional and controlled by
+     * {@code com.android.internal.R.bool.config_automotiveHideNavBarForKeyboard}.
+     */
+    @Override
+    public void setImeWindowStatus(int displayId, IBinder token, int vis, int backDisposition,
+            boolean showImeSwitcher) {
+        if (!mHideNavBarForKeyboard) {
+            return;
         }
 
+        if (mContext.getDisplay().getDisplayId() != displayId) {
+            return;
+        }
+
+        boolean isKeyboardVisible = (vis & InputMethodService.IME_VISIBLE) != 0;
+        if (!isKeyboardVisible) {
+            attachBottomNavBarWindow();
+        } else {
+            detachBottomNavBarWindow();
+        }
+    }
+
+    private void attachNavBarWindows() {
+        attachBottomNavBarWindow();
+
         if (mShowLeft) {
             int width = mContext.getResources().getDimensionPixelSize(
                     R.dimen.car_left_navigation_bar_width);
@@ -808,7 +839,49 @@
             rightlp.gravity = Gravity.RIGHT;
             mWindowManager.addView(mRightNavigationBarWindow, rightlp);
         }
+    }
 
+    /**
+     * Attaches the bottom nav bar window. Can be extended to modify the specific behavior of
+     * attaching the bottom nav bar.
+     */
+    protected void attachBottomNavBarWindow() {
+        if (!mShowBottom) {
+            return;
+        }
+
+        if (mBottomNavBarVisible) {
+            return;
+        }
+        mBottomNavBarVisible = true;
+
+        WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
+                LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT,
+                WindowManager.LayoutParams.TYPE_NAVIGATION_BAR,
+                WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
+                        | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
+                        | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
+                        | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH,
+                PixelFormat.TRANSLUCENT);
+        lp.setTitle("CarNavigationBar");
+        lp.windowAnimations = 0;
+        mWindowManager.addView(mNavigationBarWindow, lp);
+    }
+
+    /**
+     * Detaches the bottom nav bar window. Can be extended to modify the specific behavior of
+     * detaching the bottom nav bar.
+     */
+    protected void detachBottomNavBarWindow() {
+        if (!mShowBottom) {
+            return;
+        }
+
+        if (!mBottomNavBarVisible) {
+            return;
+        }
+        mBottomNavBarVisible = false;
+        mWindowManager.removeView(mNavigationBarWindow);
     }
 
     private void buildBottomBar(int layout) {
diff --git a/packages/DynamicSystemInstallationService/src/com/android/dynsystem/InstallationAsyncTask.java b/packages/DynamicSystemInstallationService/src/com/android/dynsystem/InstallationAsyncTask.java
index 077f7ec..cf286bd 100644
--- a/packages/DynamicSystemInstallationService/src/com/android/dynsystem/InstallationAsyncTask.java
+++ b/packages/DynamicSystemInstallationService/src/com/android/dynsystem/InstallationAsyncTask.java
@@ -20,6 +20,8 @@
 import android.gsi.GsiProgress;
 import android.net.Uri;
 import android.os.AsyncTask;
+import android.os.MemoryFile;
+import android.os.ParcelFileDescriptor;
 import android.os.image.DynamicSystemManager;
 import android.util.Log;
 import android.webkit.URLUtil;
@@ -28,11 +30,9 @@
 import java.io.IOException;
 import java.io.InputStream;
 import java.net.URL;
-import java.util.Arrays;
 import java.util.Locale;
 import java.util.zip.GZIPInputStream;
 
-
 class InstallationAsyncTask extends AsyncTask<String, Long, Throwable> {
 
     private static final String TAG = "InstallationAsyncTask";
@@ -125,28 +125,26 @@
                 Thread.sleep(10);
             }
 
-
             if (mInstallationSession == null) {
-                throw new IOException("Failed to start installation with requested size: "
-                        + (mSystemSize + mUserdataSize));
+                throw new IOException(
+                        "Failed to start installation with requested size: "
+                                + (mSystemSize + mUserdataSize));
             }
 
             installedSize = mUserdataSize;
 
+            MemoryFile memoryFile = new MemoryFile("dsu", READ_BUFFER_SIZE);
             byte[] bytes = new byte[READ_BUFFER_SIZE];
-
+            mInstallationSession.setAshmem(
+                    new ParcelFileDescriptor(memoryFile.getFileDescriptor()), READ_BUFFER_SIZE);
             int numBytesRead;
-
             Log.d(TAG, "Start installation loop");
             while ((numBytesRead = mStream.read(bytes, 0, READ_BUFFER_SIZE)) != -1) {
+                memoryFile.writeBytes(bytes, 0, 0, numBytesRead);
                 if (isCancelled()) {
                     break;
                 }
-
-                byte[] writeBuffer = numBytesRead == READ_BUFFER_SIZE
-                        ? bytes : Arrays.copyOf(bytes, numBytesRead);
-
-                if (!mInstallationSession.write(writeBuffer)) {
+                if (!mInstallationSession.submitFromAshmem(numBytesRead)) {
                     throw new IOException("Failed write() to DynamicSystem");
                 }
 
@@ -157,7 +155,6 @@
                     reportedInstalledSize = installedSize;
                 }
             }
-
             return null;
 
         } catch (Exception e) {
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java
index 2286f4c..36e945f 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java
@@ -34,7 +34,7 @@
 import android.os.ParcelFileDescriptor;
 import android.os.UserHandle;
 import android.provider.Settings;
-import android.provider.SettingsValidators.Validator;
+import android.provider.settings.validators.Validator;
 import android.util.ArrayMap;
 import android.util.ArraySet;
 import android.util.BackupUtils;
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
index a3b0e6b..00b2563 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
@@ -1538,9 +1538,6 @@
                 Settings.Global.WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON,
                 GlobalSettingsProto.Wifi.NETWORKS_AVAILABLE_NOTIFICATION_ON);
         dumpSetting(s, p,
-                Settings.Global.WIFI_CARRIER_NETWORKS_AVAILABLE_NOTIFICATION_ON,
-                GlobalSettingsProto.Wifi.CARRIER_NETWORKS_AVAILABLE_NOTIFICATION_ON);
-        dumpSetting(s, p,
                 Settings.Global.WIFI_NETWORKS_AVAILABLE_REPEAT_DELAY,
                 GlobalSettingsProto.Wifi.NETWORKS_AVAILABLE_REPEAT_DELAY);
         dumpSetting(s, p,
@@ -1607,9 +1604,6 @@
                 Settings.Global.WIFI_P2P_DEVICE_NAME,
                 GlobalSettingsProto.Wifi.P2P_DEVICE_NAME);
         dumpSetting(s, p,
-                Settings.Global.WIFI_REENABLE_DELAY_MS,
-                GlobalSettingsProto.Wifi.REENABLE_DELAY_MS);
-        dumpSetting(s, p,
                 Settings.Global.WIFI_EPHEMERAL_OUT_OF_RANGE_TIMEOUT_MS,
                 GlobalSettingsProto.Wifi.EPHEMERAL_OUT_OF_RANGE_TIMEOUT_MS);
         dumpSetting(s, p,
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index 7016d30..e492e28 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -70,7 +70,7 @@
 import android.provider.Settings;
 import android.provider.Settings.Global;
 import android.provider.Settings.Secure;
-import android.provider.SettingsValidators;
+import android.provider.settings.validators.Validator;
 import android.text.TextUtils;
 import android.util.ArrayMap;
 import android.util.ArraySet;
@@ -314,11 +314,6 @@
     public boolean onCreate() {
         Settings.setInSystemServer();
 
-        // fail to boot if there're any backed up settings that don't have a non-null validator
-        ensureAllBackedUpSystemSettingsHaveValidators();
-        ensureAllBackedUpGlobalSettingsHaveValidators();
-        ensureAllBackedUpSecureSettingsHaveValidators();
-
         synchronized (mLock) {
             mUserManager = UserManager.get(getContext());
             mUserManagerInternal = LocalServices.getService(UserManagerInternal.class);
@@ -338,57 +333,6 @@
         return true;
     }
 
-    private void ensureAllBackedUpSystemSettingsHaveValidators() {
-        String offenders = getOffenders(concat(Settings.System.SETTINGS_TO_BACKUP,
-                Settings.System.LEGACY_RESTORE_SETTINGS), Settings.System.VALIDATORS);
-
-        failToBootIfOffendersPresent(offenders, "Settings.System");
-    }
-
-    private void ensureAllBackedUpGlobalSettingsHaveValidators() {
-        String offenders = getOffenders(concat(Settings.Global.SETTINGS_TO_BACKUP,
-                Settings.Global.LEGACY_RESTORE_SETTINGS), Settings.Global.VALIDATORS);
-
-        failToBootIfOffendersPresent(offenders, "Settings.Global");
-    }
-
-    private void ensureAllBackedUpSecureSettingsHaveValidators() {
-        String offenders = getOffenders(concat(Settings.Secure.SETTINGS_TO_BACKUP,
-                Settings.Secure.LEGACY_RESTORE_SETTINGS), Settings.Secure.VALIDATORS);
-
-        failToBootIfOffendersPresent(offenders, "Settings.Secure");
-    }
-
-    private void failToBootIfOffendersPresent(String offenders, String settingsType) {
-        if (offenders.length() > 0) {
-            throw new RuntimeException("All " + settingsType + " settings that are backed up"
-                    + " have to have a non-null validator, but those don't: " + offenders);
-        }
-    }
-
-    private String getOffenders(String[] settingsToBackup, Map<String,
-            SettingsValidators.Validator> validators) {
-        StringBuilder offenders = new StringBuilder();
-        for (String setting : settingsToBackup) {
-            if (validators.get(setting) == null) {
-                offenders.append(setting).append(" ");
-            }
-        }
-        return offenders.toString();
-    }
-
-    private final String[] concat(String[] first, String[] second) {
-        if (second == null || second.length == 0) {
-            return first;
-        }
-        final int firstLen = first.length;
-        final int secondLen = second.length;
-        String[] both = new String[firstLen + secondLen];
-        System.arraycopy(first, 0, both, 0, firstLen);
-        System.arraycopy(second, 0, both, firstLen, secondLen);
-        return both;
-    }
-
     @Override
     public Bundle call(String method, String name, Bundle args) {
         final int requestingUserId = getRequestingUserId(args);
@@ -1773,7 +1717,7 @@
     }
 
     private void validateSystemSettingValue(String name, String value) {
-        SettingsValidators.Validator validator = Settings.System.VALIDATORS.get(name);
+        Validator validator = Settings.System.VALIDATORS.get(name);
         if (validator != null && !validator.validate(value)) {
             throw new IllegalArgumentException("Invalid value: " + value
                     + " for setting: " + name);
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index bb9a5e4..b2ff4b3 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -169,6 +169,9 @@
     <uses-permission android:name="android.permission.MANAGE_SENSORS" />
     <uses-permission android:name="android.permission.MANAGE_AUDIO_POLICY" />
     <uses-permission android:name="android.permission.MANAGE_CAMERA" />
+    <!-- Permissions needed to test system only camera devices -->
+    <uses-permission android:name="android.permission.CAMERA" />
+    <uses-permission android:name="android.permission.SYSTEM_CAMERA" />
     <!-- Permission needed to enable/disable Bluetooth/Wifi -->
     <uses-permission android:name="android.permission.MANAGE_BLUETOOTH_WHEN_WIRELESS_CONSENT_REQUIRED" />
     <uses-permission android:name="android.permission.MANAGE_WIFI_WHEN_WIRELESS_CONSENT_REQUIRED" />
@@ -235,6 +238,7 @@
 
         <activity
             android:name=".BugreportWarningActivity"
+            android:theme="@android:style/Theme.DeviceDefault.Dialog.Alert"
             android:finishOnCloseSystemDialogs="true"
             android:excludeFromRecents="true"
             android:exported="false" />
diff --git a/packages/Shell/src/com/android/shell/BugreportProgressService.java b/packages/Shell/src/com/android/shell/BugreportProgressService.java
index 58e1d47..8c0108d 100644
--- a/packages/Shell/src/com/android/shell/BugreportProgressService.java
+++ b/packages/Shell/src/com/android/shell/BugreportProgressService.java
@@ -352,10 +352,10 @@
 
         private final BugreportInfo mInfo;
 
-        BugreportCallbackImpl(String name) {
+        BugreportCallbackImpl(String name, @Nullable String title, @Nullable String description) {
             // pid not used in this workflow, so setting default = 0
             mInfo = new BugreportInfo(mContext, 0 /* pid */, name,
-                    100 /* max progress*/);
+                    100 /* max progress*/, title, description);
         }
 
         @Override
@@ -578,6 +578,8 @@
         }
         int bugreportType = intent.getIntExtra(EXTRA_BUGREPORT_TYPE,
                 BugreportParams.BUGREPORT_MODE_INTERACTIVE);
+        String shareTitle = intent.getStringExtra(EXTRA_TITLE);
+        String shareDescription = intent.getStringExtra(EXTRA_DESCRIPTION);
 
         ParcelFileDescriptor screenshotFd = createReadWriteFile(BUGREPORT_DIR,
                 bugreportName + ".png");
@@ -595,7 +597,8 @@
                 + " bugreport file fd: " + bugreportFd
                 + " screenshot file fd: " + screenshotFd);
 
-        BugreportCallbackImpl bugreportCallback = new BugreportCallbackImpl(bugreportName);
+        BugreportCallbackImpl bugreportCallback = new BugreportCallbackImpl(bugreportName,
+                shareTitle, shareDescription);
         try {
             mBugreportManager.startBugreport(bugreportFd, screenshotFd,
                     new BugreportParams(bugreportType), executor, bugreportCallback);
@@ -982,7 +985,10 @@
             }
             screenshotFile = null;
         }
-        onBugreportFinished(id, bugreportFile, screenshotFile, info.title, info.description, max);
+        // TODO: Since we are passing id to the function, it should be able to find the info linked
+        // to the id and therefore use the value of shareTitle and shareDescription.
+        onBugreportFinished(id, bugreportFile, screenshotFile, info.shareTitle,
+                info.shareDescription, max);
     }
 
 
@@ -1844,6 +1850,14 @@
         String title;
 
         /**
+         * One-line summary of the bug; when set, will be used as the subject of the
+         * {@link Intent#ACTION_SEND_MULTIPLE} intent. This is the predefined title which is
+         * set initially when the request to take a bugreport is made. This overrides any changes
+         * in the title that the user makes after the bugreport starts.
+         */
+        String shareTitle;
+
+        /**
          * User-provided, detailed description of the bugreport; when set, will be added to the body
          * of the {@link Intent#ACTION_SEND_MULTIPLE} intent.
          */
@@ -1906,7 +1920,9 @@
         int screenshotCounter;
 
         /**
-         * Descriptive text that will be shown to the user in the notification message.
+         * Descriptive text that will be shown to the user in the notification message. This is the
+         * predefined description which is set initially when the request to take a bugreport is
+         * made.
          */
         String shareDescription;
 
@@ -1914,18 +1930,21 @@
          * Constructor for tracked bugreports - typically called upon receiving BUGREPORT_STARTED.
          */
         BugreportInfo(Context context, int id, int pid, String name, int max) {
-            this(context, pid, name, max);
+            this(context, pid, name, max, null, null);
             this.id = id;
         }
 
         /**
          * Constructor for tracked bugreports - typically called upon receiving BUGREPORT_REQUESTED.
          */
-        BugreportInfo(Context context, int pid, String name, int max) {
+        BugreportInfo(Context context, int pid, String name, int max, @Nullable String shareTitle,
+                @Nullable String shareDescription) {
             this.context = context;
             this.pid = pid;
             this.name = name;
             this.max = this.realMax = max;
+            this.shareTitle = shareTitle == null ? "" : shareTitle;
+            this.shareDescription = shareDescription == null ? "" : shareDescription;
         }
 
         /**
@@ -2019,6 +2038,7 @@
                 .append("\n\taddingDetailsToZip: ").append(addingDetailsToZip)
                 .append(" addedDetailsToZip: ").append(addedDetailsToZip)
                 .append("\n\tshareDescription: ").append(shareDescription)
+                .append("\n\tshareTitle: ").append(shareTitle)
                 .toString();
         }
 
@@ -2046,6 +2066,7 @@
             finished = in.readInt() == 1;
             screenshotCounter = in.readInt();
             shareDescription = in.readString();
+            shareTitle = in.readString();
         }
 
         @Override
@@ -2071,6 +2092,7 @@
             dest.writeInt(finished ? 1 : 0);
             dest.writeInt(screenshotCounter);
             dest.writeString(shareDescription);
+            dest.writeString(shareTitle);
         }
 
         @Override
diff --git a/packages/SoundPicker/Android.bp b/packages/SoundPicker/Android.bp
new file mode 100644
index 0000000..3be7ca9
--- /dev/null
+++ b/packages/SoundPicker/Android.bp
@@ -0,0 +1,18 @@
+android_app {
+    name: "SoundPicker",
+    manifest: "AndroidManifest.xml",
+
+    static_libs: [
+        "androidx.appcompat_appcompat",
+    ],
+    resource_dirs: [
+        "res",
+    ],
+    srcs: [
+        "src/**/*.java",
+    ],
+
+    platform_apis: true,
+    certificate: "media",
+    privileged: true,
+}
diff --git a/packages/SoundPicker/AndroidManifest.xml b/packages/SoundPicker/AndroidManifest.xml
new file mode 100644
index 0000000..9d08182
--- /dev/null
+++ b/packages/SoundPicker/AndroidManifest.xml
@@ -0,0 +1,33 @@
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+        package="com.android.soundpicker"
+        android:sharedUserId="android.media">
+
+    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
+    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
+
+    <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
+    <uses-permission android:name="android.permission.RECEIVE_DEVICE_CUSTOMIZATION_READY" />
+    <uses-permission android:name="android.permission.WRITE_SETTINGS" />
+
+    <application
+            android:allowBackup="false"
+            android:supportsRtl="true">
+        <receiver android:name="RingtoneReceiver">
+            <intent-filter>
+                <action android:name="android.intent.action.DEVICE_CUSTOMIZATION_READY"/>
+            </intent-filter>
+        </receiver>
+
+        <service android:name="RingtoneOverlayService" />
+
+        <activity android:name="RingtonePickerActivity"
+                android:theme="@style/PickerDialogTheme"
+                android:enabled="@*android:bool/config_defaultRingtonePickerEnabled"
+                android:excludeFromRecents="true">
+            <intent-filter>
+                <action android:name="android.intent.action.RINGTONE_PICKER" />
+                <category android:name="android.intent.category.DEFAULT" />
+            </intent-filter>
+        </activity>
+    </application>
+</manifest>
diff --git a/packages/SoundPicker/res/drawable/ic_add.xml b/packages/SoundPicker/res/drawable/ic_add.xml
new file mode 100644
index 0000000..22b3fe9
--- /dev/null
+++ b/packages/SoundPicker/res/drawable/ic_add.xml
@@ -0,0 +1,24 @@
+<!--
+    Copyright (C) 2016 The Android Open Source Project
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24.0dp"
+        android:height="24.0dp"
+        android:viewportWidth="48.0"
+        android:viewportHeight="48.0">
+    <path
+        android:fillColor="?android:attr/colorAccent"
+        android:pathData="M38.0,26.0L26.0,26.0l0.0,12.0l-4.0,0.0L22.0,26.0L10.0,26.0l0.0,-4.0l12.0,0.0L22.0,10.0l4.0,0.0l0.0,12.0l12.0,0.0l0.0,4.0z"/>
+</vector>
\ No newline at end of file
diff --git a/packages/SoundPicker/res/drawable/ic_add_padded.xml b/packages/SoundPicker/res/drawable/ic_add_padded.xml
new file mode 100644
index 0000000..c376867
--- /dev/null
+++ b/packages/SoundPicker/res/drawable/ic_add_padded.xml
@@ -0,0 +1,22 @@
+<!--
+    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.
+-->
+
+<inset xmlns:android="http://schemas.android.com/apk/res/android"
+        android:drawable="@drawable/ic_add"
+        android:insetTop="4dp"
+        android:insetRight="4dp"
+        android:insetBottom="4dp"
+        android:insetLeft="4dp"/>
diff --git a/packages/SoundPicker/res/layout-watch/add_new_sound_item.xml b/packages/SoundPicker/res/layout-watch/add_new_sound_item.xml
new file mode 100644
index 0000000..6f91d77
--- /dev/null
+++ b/packages/SoundPicker/res/layout-watch/add_new_sound_item.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     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.
+-->
+
+<!--
+     Currently, no file manager app on watch could handle ACTION_GET_CONTENT intent.
+     Make the visibility to "gone" to prevent failures.
+ -->
+<TextView xmlns:android="http://schemas.android.com/apk/res/android"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:minHeight="?android:attr/listPreferredItemHeightSmall"
+        android:textAppearance="?android:attr/textAppearanceMedium"
+        android:text="@null"
+        android:textColor="?android:attr/colorAccent"
+        android:gravity="center_vertical"
+        android:paddingStart="?android:attr/listPreferredItemPaddingStart"
+        android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
+        android:drawableStart="@drawable/ic_add_padded"
+        android:drawablePadding="8dp"
+        android:ellipsize="marquee"
+        android:visibility="gone" />
diff --git a/packages/SoundPicker/res/layout-watch/radio_with_work_badge.xml b/packages/SoundPicker/res/layout-watch/radio_with_work_badge.xml
new file mode 100644
index 0000000..0e11621
--- /dev/null
+++ b/packages/SoundPicker/res/layout-watch/radio_with_work_badge.xml
@@ -0,0 +1,47 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<com.android.providers.media.CheckedListItem xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="fill_parent"
+    android:layout_height="wrap_content"
+    android:gravity="center_vertical"
+    android:background="?android:attr/selectableItemBackground"
+    >
+
+    <CheckedTextView
+        android:id="@+id/checked_text_view"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:minHeight="?android:attr/listPreferredItemHeightSmall"
+        android:textAppearance="?android:attr/textAppearanceMedium"
+        android:textColor="?android:attr/textColorAlertDialogListItem"
+        android:gravity="center_vertical"
+        android:paddingStart="?android:attr/listPreferredItemPaddingStart"
+        android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
+        android:drawableStart="?android:attr/listChoiceIndicatorSingle"
+        android:drawablePadding="8dp"
+        android:ellipsize="marquee"
+        android:layout_toLeftOf="@+id/work_icon"
+        android:maxLines="3" />
+
+    <ImageView
+        android:id="@id/work_icon"
+        android:layout_width="18dp"
+        android:layout_height="18dp"
+        android:layout_alignParentRight="true"
+        android:layout_centerVertical="true"
+        android:scaleType="centerCrop"
+        android:layout_marginRight="20dp" />
+</com.android.providers.media.CheckedListItem>
diff --git a/packages/SoundPicker/res/layout/add_new_sound_item.xml b/packages/SoundPicker/res/layout/add_new_sound_item.xml
new file mode 100644
index 0000000..14421c9
--- /dev/null
+++ b/packages/SoundPicker/res/layout/add_new_sound_item.xml
@@ -0,0 +1,47 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2016 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="fill_parent"
+    android:layout_height="wrap_content"
+    android:gravity="center_vertical"
+    android:background="?android:attr/selectableItemBackground">
+
+<ImageView
+        android:layout_width="24dp"
+        android:layout_height="24dp"
+        android:layout_alignParentRight="true"
+        android:layout_centerVertical="true"
+        android:scaleType="centerCrop"
+        android:layout_marginRight="24dp"
+        android:layout_marginLeft="24dp"
+        android:src="@drawable/ic_add" />
+
+<TextView xmlns:android="http://schemas.android.com/apk/res/android"
+        android:id="@+id/add_new_sound_text"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:minHeight="?android:attr/listPreferredItemHeightSmall"
+        android:text="@null"
+        android:textColor="?android:attr/colorAccent"
+        android:textAppearance="?android:attr/textAppearanceMedium"
+        android:maxLines="3"
+        android:gravity="center_vertical"
+        android:paddingEnd="?android:attr/dialogPreferredPadding"
+        android:drawablePadding="20dp"
+        android:ellipsize="marquee" />
+</LinearLayout>
\ No newline at end of file
diff --git a/packages/SoundPicker/res/layout/radio_with_work_badge.xml b/packages/SoundPicker/res/layout/radio_with_work_badge.xml
new file mode 100644
index 0000000..e7d37ea
--- /dev/null
+++ b/packages/SoundPicker/res/layout/radio_with_work_badge.xml
@@ -0,0 +1,48 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2016 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<com.android.providers.media.CheckedListItem xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="fill_parent"
+    android:layout_height="wrap_content"
+    android:gravity="center_vertical"
+    android:background="?android:attr/selectableItemBackground"
+    >
+
+    <CheckedTextView
+        android:id="@+id/checked_text_view"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:minHeight="?android:attr/listPreferredItemHeightSmall"
+        android:textAppearance="?android:attr/textAppearanceMedium"
+        android:textColor="?android:attr/textColorAlertDialogListItem"
+        android:gravity="center_vertical"
+        android:paddingStart="20dp"
+        android:paddingEnd="?android:attr/dialogPreferredPadding"
+        android:drawableStart="?android:attr/listChoiceIndicatorSingle"
+        android:drawablePadding="20dp"
+        android:ellipsize="marquee"
+        android:layout_toLeftOf="@+id/work_icon"
+        android:maxLines="3" />
+
+    <ImageView
+        android:id="@id/work_icon"
+        android:layout_width="18dp"
+        android:layout_height="18dp"
+        android:layout_alignParentRight="true"
+        android:layout_centerVertical="true"
+        android:scaleType="centerCrop"
+        android:layout_marginRight="20dp" />
+</com.android.providers.media.CheckedListItem>
diff --git a/packages/SoundPicker/res/raw/default_alarm_alert.ogg b/packages/SoundPicker/res/raw/default_alarm_alert.ogg
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/packages/SoundPicker/res/raw/default_alarm_alert.ogg
diff --git a/packages/SoundPicker/res/raw/default_notification_sound.ogg b/packages/SoundPicker/res/raw/default_notification_sound.ogg
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/packages/SoundPicker/res/raw/default_notification_sound.ogg
diff --git a/packages/SoundPicker/res/raw/default_ringtone.ogg b/packages/SoundPicker/res/raw/default_ringtone.ogg
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/packages/SoundPicker/res/raw/default_ringtone.ogg
diff --git a/packages/SoundPicker/res/values-watch/config.xml b/packages/SoundPicker/res/values-watch/config.xml
new file mode 100644
index 0000000..0bc24fa
--- /dev/null
+++ b/packages/SoundPicker/res/values-watch/config.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2016 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.
+-->
+
+<!-- These resources are around just to allow their values to be customized
+     for different hardware and product builds.  Do not translate.
+
+     NOTE: The naming convention is "config_camelCaseValue".  -->
+<resources xmlns:android="http://schemas.android.com/apk/res/android">
+    <!-- True if the ringtone picker should show the ok/cancel buttons. If it is not shown, the
+    ringtone will be automatically selected when the picker is closed. -->
+    <bool name="config_showOkCancelButtons">false</bool>
+</resources>
diff --git a/packages/SoundPicker/res/values/config.xml b/packages/SoundPicker/res/values/config.xml
new file mode 100644
index 0000000..4e237a2
--- /dev/null
+++ b/packages/SoundPicker/res/values/config.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2016 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.
+-->
+
+<!-- These resources are around just to allow their values to be customized
+     for different hardware and product builds.  Do not translate.
+
+     NOTE: The naming convention is "config_camelCaseValue".  -->
+<resources xmlns:android="http://schemas.android.com/apk/res/android">
+    <!-- True if the ringtone picker should show the ok/cancel buttons. If it is not shown, the
+    ringtone will be automatically selected when the picker is closed. -->
+    <bool name="config_showOkCancelButtons">true</bool>
+</resources>
diff --git a/packages/SoundPicker/res/values/strings.xml b/packages/SoundPicker/res/values/strings.xml
new file mode 100644
index 0000000..56ed5fd
--- /dev/null
+++ b/packages/SoundPicker/res/values/strings.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2009 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.
+-->
+
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <!-- Choice in the ringtone picker.  If chosen, the default ringtone will be used. -->
+    <string name="ringtone_default">Default ringtone</string>
+
+    <!-- Choice in the notification sound picker.  If chosen, the default notification sound will be
+         used. -->
+    <string name="notification_sound_default">Default notification sound</string>
+
+    <!-- Choice in the alarm sound picker.  If chosen, the default alarm sound will be used. -->
+    <string name="alarm_sound_default">Default alarm sound</string>
+
+    <!-- Text for the RingtonePicker item that allows adding a new ringtone. -->
+    <string name="add_ringtone_text">Add ringtone</string>
+    <!-- Text for the RingtonePicker item that allows adding a new alarm. -->
+    <string name="add_alarm_text">Add alarm</string>
+    <!-- Text for the RingtonePicker item that allows adding a new notification. -->
+    <string name="add_notification_text">Add notification</string>
+    <!-- Text for the RingtonePicker item ContextMenu that allows deleting a custom ringtone. -->
+    <string name="delete_ringtone_text">Delete</string>
+    <!-- Text for the Toast displayed when adding a custom ringtone fails. -->
+    <string name="unable_to_add_ringtone">Unable to add custom ringtone</string>
+    <!-- Text for the Toast displayed when deleting a custom ringtone fails. -->
+    <string name="unable_to_delete_ringtone">Unable to delete custom ringtone</string>
+</resources>
diff --git a/packages/SoundPicker/res/values/styles.xml b/packages/SoundPicker/res/values/styles.xml
new file mode 100644
index 0000000..d22d9c4
--- /dev/null
+++ b/packages/SoundPicker/res/values/styles.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2014 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.
+-->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android">
+
+    <style name="PickerDialogTheme" parent="@*android:style/Theme.DeviceDefault.Settings.Dialog">
+    </style>
+
+</resources>
diff --git a/packages/SoundPicker/src/com/android/soundpicker/RingtoneOverlayService.java b/packages/SoundPicker/src/com/android/soundpicker/RingtoneOverlayService.java
new file mode 100644
index 0000000..2d37b4c
--- /dev/null
+++ b/packages/SoundPicker/src/com/android/soundpicker/RingtoneOverlayService.java
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.soundpicker;
+
+import android.app.Service;
+import android.content.Intent;
+import android.media.RingtoneManager;
+import android.net.Uri;
+import android.os.AsyncTask;
+import android.os.Environment;
+import android.os.FileUtils;
+import android.os.IBinder;
+import android.provider.MediaStore;
+import android.provider.Settings.System;
+import android.util.Log;
+
+import androidx.annotation.IdRes;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * Service to copy and set customization of default sounds
+ */
+public class RingtoneOverlayService extends Service {
+    private static final String TAG = "RingtoneOverlayService";
+    private static final boolean DEBUG = false;
+
+    @Override
+    public int onStartCommand(@Nullable final Intent intent, final int flags, final int startId) {
+        AsyncTask.execute(() -> {
+            updateRingtones();
+            stopSelf();
+        });
+
+        // Try again later if we are killed before we finish.
+        return Service.START_REDELIVER_INTENT;
+    }
+
+    @Override
+    public IBinder onBind(@Nullable final Intent intent) {
+        return null;
+    }
+
+    private void updateRingtones() {
+        copyResourceAndSetAsSound(R.raw.default_ringtone,
+                System.RINGTONE, Environment.DIRECTORY_RINGTONES);
+        copyResourceAndSetAsSound(R.raw.default_notification_sound,
+                System.NOTIFICATION_SOUND, Environment.DIRECTORY_NOTIFICATIONS);
+        copyResourceAndSetAsSound(R.raw.default_alarm_alert,
+                System.ALARM_ALERT, Environment.DIRECTORY_ALARMS);
+    }
+
+    /* If the resource contains any data, copy a resource to the file system, scan it, and set the
+     * file URI as the default for a sound. */
+    private void copyResourceAndSetAsSound(@IdRes final int id, @NonNull final String name,
+            @NonNull final String subPath) {
+        final File destDir = Environment.getExternalStoragePublicDirectory(subPath);
+        if (!destDir.exists() && !destDir.mkdirs()) {
+            Log.e(TAG, "can't create " + destDir.getAbsolutePath());
+            return;
+        }
+
+        final File dest = new File(destDir, "default_" + name + ".ogg");
+        try (
+            InputStream is = getResources().openRawResource(id);
+            FileOutputStream os = new FileOutputStream(dest);
+        ) {
+            if (is.available() > 0) {
+                FileUtils.copy(is, os);
+                final Uri uri = scanFile(dest);
+                if (uri != null) {
+                    set(name, uri);
+                }
+            } else {
+                // TODO Shall we remove any former copied resource in this case and unset
+                // the defaults if we use this event a second time to clear the data?
+                if (DEBUG) Log.d(TAG, "Resource for " + name + " has no overlay");
+            }
+        } catch (IOException e) {
+            Log.e(TAG, "Unable to open resource for " + name + ": " + e);
+        }
+    }
+
+    private Uri scanFile(@NonNull final File file) {
+        return MediaStore.scanFile(this, file);
+    }
+
+    private void set(@NonNull final String name, @NonNull final Uri uri) {
+        final Uri settingUri = System.getUriFor(name);
+        RingtoneManager.setActualDefaultRingtoneUri(this,
+                RingtoneManager.getDefaultType(settingUri), uri);
+        System.putInt(getContentResolver(), name + "_set", 1);
+    }
+}
diff --git a/packages/SoundPicker/src/com/android/soundpicker/RingtonePickerActivity.java b/packages/SoundPicker/src/com/android/soundpicker/RingtonePickerActivity.java
new file mode 100644
index 0000000..4ba5146
--- /dev/null
+++ b/packages/SoundPicker/src/com/android/soundpicker/RingtonePickerActivity.java
@@ -0,0 +1,762 @@
+/*
+ * Copyright (C) 2007 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.soundpicker;
+
+import android.content.ContentProvider;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.res.Resources;
+import android.content.res.Resources.NotFoundException;
+import android.database.Cursor;
+import android.database.CursorWrapper;
+import android.media.AudioAttributes;
+import android.media.Ringtone;
+import android.media.RingtoneManager;
+import android.net.Uri;
+import android.os.AsyncTask;
+import android.os.Bundle;
+import android.os.Environment;
+import android.os.Handler;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.provider.MediaStore;
+import android.provider.Settings;
+import android.util.Log;
+import android.util.TypedValue;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.AdapterView;
+import android.widget.CursorAdapter;
+import android.widget.ImageView;
+import android.widget.ListView;
+import android.widget.TextView;
+import android.widget.Toast;
+
+import com.android.internal.app.AlertActivity;
+import com.android.internal.app.AlertController;
+
+import java.io.IOException;
+import java.util.Objects;
+import java.util.regex.Pattern;
+
+/**
+ * The {@link RingtonePickerActivity} allows the user to choose one from all of the
+ * available ringtones. The chosen ringtone's URI will be persisted as a string.
+ *
+ * @see RingtoneManager#ACTION_RINGTONE_PICKER
+ */
+public final class RingtonePickerActivity extends AlertActivity implements
+        AdapterView.OnItemSelectedListener, Runnable, DialogInterface.OnClickListener,
+        AlertController.AlertParams.OnPrepareListViewListener {
+
+    private static final int POS_UNKNOWN = -1;
+
+    private static final String TAG = "RingtonePickerActivity";
+
+    private static final int DELAY_MS_SELECTION_PLAYED = 300;
+
+    private static final String COLUMN_LABEL = MediaStore.Audio.Media.TITLE;
+
+    private static final String SAVE_CLICKED_POS = "clicked_pos";
+
+    private static final String SOUND_NAME_RES_PREFIX = "sound_name_";
+
+    private static final int ADD_FILE_REQUEST_CODE = 300;
+
+    private RingtoneManager mRingtoneManager;
+    private int mType;
+
+    private Cursor mCursor;
+    private Handler mHandler;
+    private BadgedRingtoneAdapter mAdapter;
+
+    /** The position in the list of the 'Silent' item. */
+    private int mSilentPos = POS_UNKNOWN;
+
+    /** The position in the list of the 'Default' item. */
+    private int mDefaultRingtonePos = POS_UNKNOWN;
+
+    /** The position in the list of the ringtone to sample. */
+    private int mSampleRingtonePos = POS_UNKNOWN;
+
+    /** Whether this list has the 'Silent' item. */
+    private boolean mHasSilentItem;
+
+    /** The Uri to place a checkmark next to. */
+    private Uri mExistingUri;
+
+    /** The number of static items in the list. */
+    private int mStaticItemCount;
+
+    /** Whether this list has the 'Default' item. */
+    private boolean mHasDefaultItem;
+
+    /** The Uri to play when the 'Default' item is clicked. */
+    private Uri mUriForDefaultItem;
+
+    /** Id of the user to which the ringtone picker should list the ringtones */
+    private int mPickerUserId;
+
+    /** Context of the user specified by mPickerUserId */
+    private Context mTargetContext;
+
+    /**
+     * A Ringtone for the default ringtone. In most cases, the RingtoneManager
+     * will stop the previous ringtone. However, the RingtoneManager doesn't
+     * manage the default ringtone for us, so we should stop this one manually.
+     */
+    private Ringtone mDefaultRingtone;
+
+    /**
+     * The ringtone that's currently playing, unless the currently playing one is the default
+     * ringtone.
+     */
+    private Ringtone mCurrentRingtone;
+
+    /**
+     * Stable ID for the ringtone that is currently checked (may be -1 if no ringtone is checked).
+     */
+    private long mCheckedItemId = -1;
+
+    private int mAttributesFlags;
+
+    private boolean mShowOkCancelButtons;
+
+    /**
+     * Keep the currently playing ringtone around when changing orientation, so that it
+     * can be stopped later, after the activity is recreated.
+     */
+    private static Ringtone sPlayingRingtone;
+
+    private DialogInterface.OnClickListener mRingtoneClickListener =
+            new DialogInterface.OnClickListener() {
+
+        /*
+         * On item clicked
+         */
+        public void onClick(DialogInterface dialog, int which) {
+            if (which == mCursor.getCount() + mStaticItemCount) {
+                // The "Add new ringtone" item was clicked. Start a file picker intent to select
+                // only audio files (MIME type "audio/*")
+                final Intent chooseFile = new Intent(Intent.ACTION_GET_CONTENT);
+                chooseFile.setType("audio/*");
+                chooseFile.putExtra(Intent.EXTRA_MIME_TYPES,
+                        new String[] { "audio/*", "application/ogg" });
+                startActivityForResult(chooseFile, ADD_FILE_REQUEST_CODE);
+                return;
+            }
+
+            // Save the position of most recently clicked item
+            setCheckedItem(which);
+
+            // In the buttonless (watch-only) version, preemptively set our result since we won't
+            // have another chance to do so before the activity closes.
+            if (!mShowOkCancelButtons) {
+                setSuccessResultWithRingtone(getCurrentlySelectedRingtoneUri());
+            }
+
+            // Play clip
+            playRingtone(which, 0);
+        }
+
+    };
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        mHandler = new Handler();
+
+        Intent intent = getIntent();
+        mPickerUserId = UserHandle.myUserId();
+        mTargetContext = this;
+
+        // Get the types of ringtones to show
+        mType = intent.getIntExtra(RingtoneManager.EXTRA_RINGTONE_TYPE, -1);
+        initRingtoneManager();
+
+        /*
+         * Get whether to show the 'Default' item, and the URI to play when the
+         * default is clicked
+         */
+        mHasDefaultItem = intent.getBooleanExtra(RingtoneManager.EXTRA_RINGTONE_SHOW_DEFAULT, true);
+        mUriForDefaultItem = intent.getParcelableExtra(RingtoneManager.EXTRA_RINGTONE_DEFAULT_URI);
+        if (mUriForDefaultItem == null) {
+            if (mType == RingtoneManager.TYPE_NOTIFICATION) {
+                mUriForDefaultItem = Settings.System.DEFAULT_NOTIFICATION_URI;
+            } else if (mType == RingtoneManager.TYPE_ALARM) {
+                mUriForDefaultItem = Settings.System.DEFAULT_ALARM_ALERT_URI;
+            } else if (mType == RingtoneManager.TYPE_RINGTONE) {
+                mUriForDefaultItem = Settings.System.DEFAULT_RINGTONE_URI;
+            } else {
+                // or leave it null for silence.
+                mUriForDefaultItem = Settings.System.DEFAULT_RINGTONE_URI;
+            }
+        }
+
+        // Get whether to show the 'Silent' item
+        mHasSilentItem = intent.getBooleanExtra(RingtoneManager.EXTRA_RINGTONE_SHOW_SILENT, true);
+        // AudioAttributes flags
+        mAttributesFlags |= intent.getIntExtra(
+                RingtoneManager.EXTRA_RINGTONE_AUDIO_ATTRIBUTES_FLAGS,
+                0 /*defaultValue == no flags*/);
+
+        mShowOkCancelButtons = getResources().getBoolean(R.bool.config_showOkCancelButtons);
+
+        // The volume keys will control the stream that we are choosing a ringtone for
+        setVolumeControlStream(mRingtoneManager.inferStreamType());
+
+        // Get the URI whose list item should have a checkmark
+        mExistingUri = intent
+                .getParcelableExtra(RingtoneManager.EXTRA_RINGTONE_EXISTING_URI);
+
+        // Create the list of ringtones and hold on to it so we can update later.
+        mAdapter = new BadgedRingtoneAdapter(this, mCursor,
+                /* isManagedProfile = */ UserManager.get(this).isManagedProfile(mPickerUserId));
+        if (savedInstanceState != null) {
+            setCheckedItem(savedInstanceState.getInt(SAVE_CLICKED_POS, POS_UNKNOWN));
+        }
+
+        final AlertController.AlertParams p = mAlertParams;
+        p.mAdapter = mAdapter;
+        p.mOnClickListener = mRingtoneClickListener;
+        p.mLabelColumn = COLUMN_LABEL;
+        p.mIsSingleChoice = true;
+        p.mOnItemSelectedListener = this;
+        if (mShowOkCancelButtons) {
+            p.mPositiveButtonText = getString(com.android.internal.R.string.ok);
+            p.mPositiveButtonListener = this;
+            p.mNegativeButtonText = getString(com.android.internal.R.string.cancel);
+            p.mPositiveButtonListener = this;
+        }
+        p.mOnPrepareListViewListener = this;
+
+        p.mTitle = intent.getCharSequenceExtra(RingtoneManager.EXTRA_RINGTONE_TITLE);
+        if (p.mTitle == null) {
+          if (mType == RingtoneManager.TYPE_ALARM) {
+              p.mTitle = getString(com.android.internal.R.string.ringtone_picker_title_alarm);
+          } else if (mType == RingtoneManager.TYPE_NOTIFICATION) {
+              p.mTitle =
+                  getString(com.android.internal.R.string.ringtone_picker_title_notification);
+          } else {
+              p.mTitle = getString(com.android.internal.R.string.ringtone_picker_title);
+          }
+        }
+
+        setupAlert();
+    }
+    @Override
+    public void onSaveInstanceState(Bundle outState) {
+        super.onSaveInstanceState(outState);
+        outState.putInt(SAVE_CLICKED_POS, getCheckedItem());
+    }
+
+    @Override
+    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+        super.onActivityResult(requestCode, resultCode, data);
+
+        if (requestCode == ADD_FILE_REQUEST_CODE && resultCode == RESULT_OK) {
+            // Add the custom ringtone in a separate thread
+            final AsyncTask<Uri, Void, Uri> installTask = new AsyncTask<Uri, Void, Uri>() {
+                @Override
+                protected Uri doInBackground(Uri... params) {
+                    try {
+                        return mRingtoneManager.addCustomExternalRingtone(params[0], mType);
+                    } catch (IOException | IllegalArgumentException e) {
+                        Log.e(TAG, "Unable to add new ringtone", e);
+                    }
+                    return null;
+                }
+
+                @Override
+                protected void onPostExecute(Uri ringtoneUri) {
+                    if (ringtoneUri != null) {
+                        requeryForAdapter();
+                    } else {
+                        // Ringtone was not added, display error Toast
+                        Toast.makeText(RingtonePickerActivity.this, R.string.unable_to_add_ringtone,
+                                Toast.LENGTH_SHORT).show();
+                    }
+                }
+            };
+            installTask.execute(data.getData());
+        }
+    }
+
+    // Disabled because context menus aren't Material Design :(
+    /*
+    @Override
+    public void onCreateContextMenu(ContextMenu menu, View view, ContextMenuInfo menuInfo) {
+        int position = ((AdapterContextMenuInfo) menuInfo).position;
+
+        Ringtone ringtone = getRingtone(getRingtoneManagerPosition(position));
+        if (ringtone != null && mRingtoneManager.isCustomRingtone(ringtone.getUri())) {
+            // It's a custom ringtone so we display the context menu
+            menu.setHeaderTitle(ringtone.getTitle(this));
+            menu.add(Menu.NONE, Menu.FIRST, Menu.NONE, R.string.delete_ringtone_text);
+        }
+    }
+
+    @Override
+    public boolean onContextItemSelected(MenuItem item) {
+        switch (item.getItemId()) {
+            case Menu.FIRST: {
+                int deletedRingtonePos = ((AdapterContextMenuInfo) item.getMenuInfo()).position;
+                Uri deletedRingtoneUri = getRingtone(
+                        getRingtoneManagerPosition(deletedRingtonePos)).getUri();
+                if(mRingtoneManager.deleteExternalRingtone(deletedRingtoneUri)) {
+                    requeryForAdapter();
+                } else {
+                    Toast.makeText(this, R.string.unable_to_delete_ringtone, Toast.LENGTH_SHORT)
+                            .show();
+                }
+                return true;
+            }
+            default: {
+                return false;
+            }
+        }
+    }
+    */
+
+    @Override
+    public void onDestroy() {
+        if (mCursor != null) {
+            mCursor.close();
+            mCursor = null;
+        }
+        super.onDestroy();
+    }
+
+    public void onPrepareListView(ListView listView) {
+        // Reset the static item count, as this method can be called multiple times
+        mStaticItemCount = 0;
+
+        if (mHasDefaultItem) {
+            mDefaultRingtonePos = addDefaultRingtoneItem(listView);
+
+            if (getCheckedItem() == POS_UNKNOWN && RingtoneManager.isDefault(mExistingUri)) {
+                setCheckedItem(mDefaultRingtonePos);
+            }
+        }
+
+        if (mHasSilentItem) {
+            mSilentPos = addSilentItem(listView);
+
+            // The 'Silent' item should use a null Uri
+            if (getCheckedItem() == POS_UNKNOWN && mExistingUri == null) {
+                setCheckedItem(mSilentPos);
+            }
+        }
+
+        if (getCheckedItem() == POS_UNKNOWN) {
+            setCheckedItem(getListPosition(mRingtoneManager.getRingtonePosition(mExistingUri)));
+        }
+
+        // In the buttonless (watch-only) version, preemptively set our result since we won't
+        // have another chance to do so before the activity closes.
+        if (!mShowOkCancelButtons) {
+            setSuccessResultWithRingtone(getCurrentlySelectedRingtoneUri());
+        }
+        // If external storage is available, add a button to install sounds from storage.
+        if(Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
+            addNewSoundItem(listView);
+        }
+
+        // Enable context menu in ringtone items
+        registerForContextMenu(listView);
+    }
+
+    /**
+     * Re-query RingtoneManager for the most recent set of installed ringtones. May move the
+     * selected item position to match the new position of the chosen sound.
+     *
+     * This should only need to happen after adding or removing a ringtone.
+     */
+    private void requeryForAdapter() {
+        // Refresh and set a new cursor, closing the old one.
+        initRingtoneManager();
+        mAdapter.changeCursor(mCursor);
+
+        // Update checked item location.
+        int checkedPosition = POS_UNKNOWN;
+        for (int i = 0; i < mAdapter.getCount(); i++) {
+            if (mAdapter.getItemId(i) == mCheckedItemId) {
+                checkedPosition = getListPosition(i);
+                break;
+            }
+        }
+        if (mHasSilentItem && checkedPosition == POS_UNKNOWN) {
+            checkedPosition = mSilentPos;
+        }
+        setCheckedItem(checkedPosition);
+        setupAlert();
+    }
+
+    /**
+     * Adds a static item to the top of the list. A static item is one that is not from the
+     * RingtoneManager.
+     *
+     * @param listView The ListView to add to.
+     * @param textResId The resource ID of the text for the item.
+     * @return The position of the inserted item.
+     */
+    private int addStaticItem(ListView listView, int textResId) {
+        TextView textView = (TextView) getLayoutInflater().inflate(
+                com.android.internal.R.layout.select_dialog_singlechoice_material, listView, false);
+        textView.setText(textResId);
+        listView.addHeaderView(textView);
+        mStaticItemCount++;
+        return listView.getHeaderViewsCount() - 1;
+    }
+
+    private int addDefaultRingtoneItem(ListView listView) {
+        if (mType == RingtoneManager.TYPE_NOTIFICATION) {
+            return addStaticItem(listView, R.string.notification_sound_default);
+        } else if (mType == RingtoneManager.TYPE_ALARM) {
+            return addStaticItem(listView, R.string.alarm_sound_default);
+        }
+
+        return addStaticItem(listView, R.string.ringtone_default);
+    }
+
+    private int addSilentItem(ListView listView) {
+        return addStaticItem(listView, com.android.internal.R.string.ringtone_silent);
+    }
+
+    private void addNewSoundItem(ListView listView) {
+        View view = getLayoutInflater().inflate(R.layout.add_new_sound_item, listView,
+                false /* attachToRoot */);
+        TextView text = (TextView)view.findViewById(R.id.add_new_sound_text);
+
+        if (mType == RingtoneManager.TYPE_ALARM) {
+            text.setText(R.string.add_alarm_text);
+        } else if (mType == RingtoneManager.TYPE_NOTIFICATION) {
+            text.setText(R.string.add_notification_text);
+        } else {
+            text.setText(R.string.add_ringtone_text);
+        }
+        listView.addFooterView(view);
+    }
+
+    private void initRingtoneManager() {
+        // Reinstantiate the RingtoneManager. Cursor.requery() was deprecated and calling it
+        // causes unexpected behavior.
+        mRingtoneManager = new RingtoneManager(mTargetContext, /* includeParentRingtones */ true);
+        if (mType != -1) {
+            mRingtoneManager.setType(mType);
+        }
+        mCursor = new LocalizedCursor(mRingtoneManager.getCursor(), getResources(), COLUMN_LABEL);
+    }
+
+    private Ringtone getRingtone(int ringtoneManagerPosition) {
+        if (ringtoneManagerPosition < 0) {
+            return null;
+        }
+        return mRingtoneManager.getRingtone(ringtoneManagerPosition);
+    }
+
+    private int getCheckedItem() {
+        return mAlertParams.mCheckedItem;
+    }
+
+    private void setCheckedItem(int pos) {
+        mAlertParams.mCheckedItem = pos;
+        mCheckedItemId = mAdapter.getItemId(getRingtoneManagerPosition(pos));
+    }
+
+    /*
+     * On click of Ok/Cancel buttons
+     */
+    public void onClick(DialogInterface dialog, int which) {
+        boolean positiveResult = which == DialogInterface.BUTTON_POSITIVE;
+
+        // Stop playing the previous ringtone
+        mRingtoneManager.stopPreviousRingtone();
+
+        if (positiveResult) {
+            setSuccessResultWithRingtone(getCurrentlySelectedRingtoneUri());
+        } else {
+            setResult(RESULT_CANCELED);
+        }
+
+        finish();
+    }
+
+    /*
+     * On item selected via keys
+     */
+    public void onItemSelected(AdapterView parent, View view, int position, long id) {
+        // footer view
+        if (position >= mCursor.getCount() + mStaticItemCount) {
+            return;
+        }
+
+        playRingtone(position, DELAY_MS_SELECTION_PLAYED);
+
+        // In the buttonless (watch-only) version, preemptively set our result since we won't
+        // have another chance to do so before the activity closes.
+        if (!mShowOkCancelButtons) {
+            setSuccessResultWithRingtone(getCurrentlySelectedRingtoneUri());
+        }
+    }
+
+    public void onNothingSelected(AdapterView parent) {
+    }
+
+    private void playRingtone(int position, int delayMs) {
+        mHandler.removeCallbacks(this);
+        mSampleRingtonePos = position;
+        mHandler.postDelayed(this, delayMs);
+    }
+
+    public void run() {
+        stopAnyPlayingRingtone();
+        if (mSampleRingtonePos == mSilentPos) {
+            return;
+        }
+
+        Ringtone ringtone;
+        if (mSampleRingtonePos == mDefaultRingtonePos) {
+            if (mDefaultRingtone == null) {
+                mDefaultRingtone = RingtoneManager.getRingtone(this, mUriForDefaultItem);
+            }
+           /*
+            * Stream type of mDefaultRingtone is not set explicitly here.
+            * It should be set in accordance with mRingtoneManager of this Activity.
+            */
+            if (mDefaultRingtone != null) {
+                mDefaultRingtone.setStreamType(mRingtoneManager.inferStreamType());
+            }
+            ringtone = mDefaultRingtone;
+            mCurrentRingtone = null;
+        } else {
+            ringtone = mRingtoneManager.getRingtone(getRingtoneManagerPosition(mSampleRingtonePos));
+            mCurrentRingtone = ringtone;
+        }
+
+        if (ringtone != null) {
+            if (mAttributesFlags != 0) {
+                ringtone.setAudioAttributes(
+                        new AudioAttributes.Builder(ringtone.getAudioAttributes())
+                                .setFlags(mAttributesFlags)
+                                .build());
+            }
+            ringtone.play();
+        }
+    }
+
+    @Override
+    protected void onStop() {
+        super.onStop();
+
+        if (!isChangingConfigurations()) {
+            stopAnyPlayingRingtone();
+        } else {
+            saveAnyPlayingRingtone();
+        }
+    }
+
+    @Override
+    protected void onPause() {
+        super.onPause();
+        if (!isChangingConfigurations()) {
+            stopAnyPlayingRingtone();
+        }
+    }
+
+    private void setSuccessResultWithRingtone(Uri ringtoneUri) {
+      setResult(RESULT_OK,
+          new Intent().putExtra(RingtoneManager.EXTRA_RINGTONE_PICKED_URI, ringtoneUri));
+    }
+
+    private Uri getCurrentlySelectedRingtoneUri() {
+      if (getCheckedItem() == mDefaultRingtonePos) {
+        // Use the default Uri that they originally gave us.
+        return mUriForDefaultItem;
+      } else if (getCheckedItem() == mSilentPos) {
+        // Use a null Uri for the 'Silent' item.
+        return null;
+      } else {
+        return mRingtoneManager.getRingtoneUri(getRingtoneManagerPosition(getCheckedItem()));
+      }
+    }
+
+    private void saveAnyPlayingRingtone() {
+        if (mDefaultRingtone != null && mDefaultRingtone.isPlaying()) {
+            sPlayingRingtone = mDefaultRingtone;
+        } else if (mCurrentRingtone != null && mCurrentRingtone.isPlaying()) {
+            sPlayingRingtone = mCurrentRingtone;
+        }
+    }
+
+    private void stopAnyPlayingRingtone() {
+        if (sPlayingRingtone != null && sPlayingRingtone.isPlaying()) {
+            sPlayingRingtone.stop();
+        }
+        sPlayingRingtone = null;
+
+        if (mDefaultRingtone != null && mDefaultRingtone.isPlaying()) {
+            mDefaultRingtone.stop();
+        }
+
+        if (mRingtoneManager != null) {
+            mRingtoneManager.stopPreviousRingtone();
+        }
+    }
+
+    private int getRingtoneManagerPosition(int listPos) {
+        return listPos - mStaticItemCount;
+    }
+
+    private int getListPosition(int ringtoneManagerPos) {
+
+        // If the manager position is -1 (for not found), return that
+        if (ringtoneManagerPos < 0) return ringtoneManagerPos;
+
+        return ringtoneManagerPos + mStaticItemCount;
+    }
+
+    private static class LocalizedCursor extends CursorWrapper {
+
+        final int mTitleIndex;
+        final Resources mResources;
+        String mNamePrefix;
+        final Pattern mSanitizePattern;
+
+        LocalizedCursor(Cursor cursor, Resources resources, String columnLabel) {
+            super(cursor);
+            mTitleIndex = mCursor.getColumnIndex(columnLabel);
+            mResources = resources;
+            mSanitizePattern = Pattern.compile("[^a-zA-Z0-9]");
+            if (mTitleIndex == -1) {
+                Log.e(TAG, "No index for column " + columnLabel);
+                mNamePrefix = null;
+            } else {
+                try {
+                    // Build the prefix for the name of the resource to look up
+                    // format is: "ResourcePackageName::ResourceTypeName/"
+                    // (the type name is expected to be "string" but let's not hardcode it).
+                    // Here we use an existing resource "notification_sound_default" which is
+                    // always expected to be found.
+                    mNamePrefix = String.format("%s:%s/%s",
+                            mResources.getResourcePackageName(R.string.notification_sound_default),
+                            mResources.getResourceTypeName(R.string.notification_sound_default),
+                            SOUND_NAME_RES_PREFIX);
+                } catch (NotFoundException e) {
+                    mNamePrefix = null;
+                }
+            }
+        }
+
+        /**
+         * Process resource name to generate a valid resource name.
+         * @param input
+         * @return a non-null String
+         */
+        private String sanitize(String input) {
+            if (input == null) {
+                return "";
+            }
+            return mSanitizePattern.matcher(input).replaceAll("_").toLowerCase();
+        }
+
+        @Override
+        public String getString(int columnIndex) {
+            final String defaultName = mCursor.getString(columnIndex);
+            if ((columnIndex != mTitleIndex) || (mNamePrefix == null)) {
+                return defaultName;
+            }
+            TypedValue value = new TypedValue();
+            try {
+                // the name currently in the database is used to derive a name to match
+                // against resource names in this package
+                mResources.getValue(mNamePrefix + sanitize(defaultName), value, false);
+            } catch (NotFoundException e) {
+                // no localized string, use the default string
+                return defaultName;
+            }
+            if ((value != null) && (value.type == TypedValue.TYPE_STRING)) {
+                Log.d(TAG, String.format("Replacing name %s with %s",
+                        defaultName, value.string.toString()));
+                return value.string.toString();
+            } else {
+                Log.e(TAG, "Invalid value when looking up localized name, using " + defaultName);
+                return defaultName;
+            }
+        }
+    }
+
+    private class BadgedRingtoneAdapter extends CursorAdapter {
+        private final boolean mIsManagedProfile;
+
+        public BadgedRingtoneAdapter(Context context, Cursor cursor, boolean isManagedProfile) {
+            super(context, cursor);
+            mIsManagedProfile = isManagedProfile;
+        }
+
+        @Override
+        public long getItemId(int position) {
+            if (position < 0) {
+                return position;
+            }
+            return super.getItemId(position);
+        }
+
+        @Override
+        public View newView(Context context, Cursor cursor, ViewGroup parent) {
+            LayoutInflater inflater = LayoutInflater.from(context);
+            return inflater.inflate(R.layout.radio_with_work_badge, parent, false);
+        }
+
+        @Override
+        public void bindView(View view, Context context, Cursor cursor) {
+            // Set text as the title of the ringtone
+            ((TextView) view.findViewById(R.id.checked_text_view))
+                    .setText(cursor.getString(RingtoneManager.TITLE_COLUMN_INDEX));
+
+            boolean isWorkRingtone = false;
+            if (mIsManagedProfile) {
+                /*
+                 * Display the work icon if the ringtone belongs to a work profile. We can tell that
+                 * a ringtone belongs to a work profile if the picker user is a managed profile, the
+                 * ringtone Uri is in external storage, and either the uri has no user id or has the
+                 * id of the picker user
+                 */
+                Uri currentUri = mRingtoneManager.getRingtoneUri(cursor.getPosition());
+                int uriUserId = ContentProvider.getUserIdFromUri(currentUri, mPickerUserId);
+                Uri uriWithoutUserId = ContentProvider.getUriWithoutUserId(currentUri);
+
+                if (uriUserId == mPickerUserId && uriWithoutUserId.toString()
+                        .startsWith(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI.toString())) {
+                    isWorkRingtone = true;
+                }
+            }
+
+            ImageView workIcon = (ImageView) view.findViewById(R.id.work_icon);
+            if(isWorkRingtone) {
+                workIcon.setImageDrawable(getPackageManager().getUserBadgeForDensityNoBackground(
+                        UserHandle.of(mPickerUserId), -1 /* density */));
+                workIcon.setVisibility(View.VISIBLE);
+            } else {
+                workIcon.setVisibility(View.GONE);
+            }
+        }
+    }
+}
diff --git a/packages/SoundPicker/src/com/android/soundpicker/RingtoneReceiver.java b/packages/SoundPicker/src/com/android/soundpicker/RingtoneReceiver.java
new file mode 100644
index 0000000..9e1ba3a
--- /dev/null
+++ b/packages/SoundPicker/src/com/android/soundpicker/RingtoneReceiver.java
@@ -0,0 +1,37 @@
+/* //device/content/providers/media/src/com/android/providers/media/MediaScannerReceiver.java
+**
+** Copyright 2007, 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.soundpicker;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+
+public class RingtoneReceiver extends BroadcastReceiver {
+    @Override
+    public void onReceive(Context context, Intent intent) {
+        final String action = intent.getAction();
+        if (Intent.ACTION_DEVICE_CUSTOMIZATION_READY.equals(action)) {
+            initResourceRingtones(context);
+        }
+    }
+
+    private void initResourceRingtones(Context context) {
+        context.startService(
+                new Intent(context, RingtoneOverlayService.class));
+    }
+}
diff --git a/packages/SystemUI/src/com/android/keyguard/CarrierTextController.java b/packages/SystemUI/src/com/android/keyguard/CarrierTextController.java
index 10d132a..45126f3 100644
--- a/packages/SystemUI/src/com/android/keyguard/CarrierTextController.java
+++ b/packages/SystemUI/src/com/android/keyguard/CarrierTextController.java
@@ -19,16 +19,12 @@
 import static android.telephony.PhoneStateListener.LISTEN_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGE;
 import static android.telephony.PhoneStateListener.LISTEN_NONE;
 
-import static com.android.internal.telephony.PhoneConstants.MAX_PHONE_COUNT_DUAL_SIM;
-
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.net.ConnectivityManager;
 import android.net.wifi.WifiManager;
 import android.os.Handler;
-import android.os.SystemProperties;
-import android.telephony.CarrierConfigManager;
 import android.telephony.PhoneStateListener;
 import android.telephony.ServiceState;
 import android.telephony.SubscriptionInfo;
@@ -37,19 +33,17 @@
 import android.text.TextUtils;
 import android.util.Log;
 
-import androidx.annotation.VisibleForTesting;
-
 import com.android.internal.telephony.IccCardConstants;
 import com.android.internal.telephony.TelephonyIntents;
-import com.android.internal.telephony.TelephonyProperties;
 import com.android.settingslib.WirelessUtils;
 import com.android.systemui.Dependency;
 import com.android.systemui.keyguard.WakefulnessLifecycle;
 
-import java.util.ArrayList;
 import java.util.List;
 import java.util.Objects;
 
+import androidx.annotation.VisibleForTesting;
+
 /**
  * Controller that generates text including the carrier names and/or the status of all the SIM
  * interfaces in the device. Through a callback, the updates can be retrieved either as a list or
@@ -72,8 +66,6 @@
     private Context mContext;
     private CharSequence mSeparator;
     private WakefulnessLifecycle mWakefulnessLifecycle;
-    @VisibleForTesting
-    protected boolean mDisplayOpportunisticSubscriptionCarrierText;
     private final WakefulnessLifecycle.Observer mWakefulnessObserver =
             new WakefulnessLifecycle.Observer() {
                 @Override
@@ -174,9 +166,6 @@
         mSimSlotsNumber = ((TelephonyManager) context.getSystemService(
                 Context.TELEPHONY_SERVICE)).getPhoneCount();
         mSimErrorState = new boolean[mSimSlotsNumber];
-        updateDisplayOpportunisticSubscriptionCarrierText(SystemProperties.getBoolean(
-                TelephonyProperties.DISPLAY_OPPORTUNISTIC_SUBSCRIPTION_CARRIER_TEXT_PROPERTY_NAME,
-                false));
     }
 
     /**
@@ -253,63 +242,8 @@
         }
     }
 
-    /**
-     * @param subscriptions
-     */
-    private void filterMobileSubscriptionInSameGroup(List<SubscriptionInfo> subscriptions) {
-        if (subscriptions.size() == MAX_PHONE_COUNT_DUAL_SIM) {
-            SubscriptionInfo info1 = subscriptions.get(0);
-            SubscriptionInfo info2 = subscriptions.get(1);
-            if (info1.getGroupUuid() != null && info1.getGroupUuid().equals(info2.getGroupUuid())) {
-                // If both subscriptions are primary, show both.
-                if (!info1.isOpportunistic() && !info2.isOpportunistic()) return;
-
-                // If carrier required, always show signal bar of primary subscription.
-                // Otherwise, show whichever subscription is currently active for Internet.
-                boolean alwaysShowPrimary = CarrierConfigManager.getDefaultConfig()
-                        .getBoolean(CarrierConfigManager
-                        .KEY_ALWAYS_SHOW_PRIMARY_SIGNAL_BAR_IN_OPPORTUNISTIC_NETWORK_BOOLEAN);
-                if (alwaysShowPrimary) {
-                    subscriptions.remove(info1.isOpportunistic() ? info1 : info2);
-                } else {
-                    subscriptions.remove(info1.getSubscriptionId() == mActiveMobileDataSubscription
-                            ? info2 : info1);
-                }
-
-            }
-        }
-    }
-
-    /**
-     * updates if opportunistic sub carrier text should be displayed or not
-     *
-     */
-    @VisibleForTesting
-    public void updateDisplayOpportunisticSubscriptionCarrierText(boolean isEnable) {
-        mDisplayOpportunisticSubscriptionCarrierText = isEnable;
-    }
-
     protected List<SubscriptionInfo> getSubscriptionInfo() {
-        List<SubscriptionInfo> subs;
-        if (mDisplayOpportunisticSubscriptionCarrierText) {
-            SubscriptionManager subscriptionManager = ((SubscriptionManager) mContext
-                    .getSystemService(
-                    Context.TELEPHONY_SUBSCRIPTION_SERVICE));
-            subs = subscriptionManager.getActiveSubscriptionInfoList(false);
-            if (subs == null) {
-                subs = new ArrayList<>();
-            } else {
-                filterMobileSubscriptionInSameGroup(subs);
-            }
-        } else {
-            subs = mKeyguardUpdateMonitor.getSubscriptionInfo(false);
-            if (subs == null) {
-                subs = new ArrayList<>();
-            } else {
-                filterMobileSubscriptionInSameGroup(subs);
-            }
-        }
-        return subs;
+        return mKeyguardUpdateMonitor.getFilteredSubscriptionInfo(false);
     }
 
     protected void updateCarrierText() {
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index de20972..5d6cc83 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -30,6 +30,7 @@
 import static android.os.BatteryManager.EXTRA_STATUS;
 import static android.telephony.PhoneStateListener.LISTEN_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGE;
 
+import static com.android.internal.telephony.PhoneConstants.MAX_PHONE_COUNT_DUAL_SIM;
 import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_BOOT;
 import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_DPM_LOCK_NOW;
 import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_LOCKOUT;
@@ -77,6 +78,7 @@
 import android.provider.Settings;
 import android.service.dreams.DreamService;
 import android.service.dreams.IDreamManager;
+import android.telephony.CarrierConfigManager;
 import android.telephony.PhoneStateListener;
 import android.telephony.ServiceState;
 import android.telephony.SubscriptionInfo;
@@ -261,6 +263,7 @@
     private boolean mLogoutEnabled;
     // If the user long pressed the lock icon, disabling face auth for the current session.
     private boolean mLockIconPressed;
+    private int mActiveMobileDataSubscription = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
 
     /**
      * Short delay before restarting biometric authentication after a successful try
@@ -406,9 +409,11 @@
                 }
             };
 
-    private PhoneStateListener mPhoneStateListener = new PhoneStateListener() {
+    @VisibleForTesting
+    public PhoneStateListener mPhoneStateListener = new PhoneStateListener() {
         @Override
         public void onActiveDataSubscriptionIdChanged(int subId) {
+            mActiveMobileDataSubscription = subId;
             mHandler.sendEmptyMessage(MSG_SIM_SUBSCRIPTION_INFO_CHANGED);
         }
     };
@@ -511,7 +516,9 @@
         }
     }
 
-    /** @return List of SubscriptionInfo records, maybe empty but never null */
+    /**
+     * @return List of SubscriptionInfo records, maybe empty but never null.
+     */
     public List<SubscriptionInfo> getSubscriptionInfo(boolean forceReload) {
         List<SubscriptionInfo> sil = mSubscriptionInfo;
         if (sil == null || forceReload) {
@@ -523,7 +530,42 @@
         } else {
             mSubscriptionInfo = sil;
         }
-        return mSubscriptionInfo;
+        return new ArrayList<>(mSubscriptionInfo);
+    }
+
+    /**
+     * This method returns filtered list of SubscriptionInfo from {@link #getSubscriptionInfo}.
+     * above. Maybe empty but never null.
+     *
+     * In DSDS mode if both subscriptions are grouped and one is opportunistic, we filter out one
+     * of them based on carrier config. e.g. In this case we should only show one carrier name
+     * on the status bar and quick settings.
+     */
+    public List<SubscriptionInfo> getFilteredSubscriptionInfo(boolean forceReload) {
+        List<SubscriptionInfo> subscriptions = getSubscriptionInfo(false);
+        if (subscriptions.size() == MAX_PHONE_COUNT_DUAL_SIM) {
+            SubscriptionInfo info1 = subscriptions.get(0);
+            SubscriptionInfo info2 = subscriptions.get(1);
+            if (info1.getGroupUuid() != null && info1.getGroupUuid().equals(info2.getGroupUuid())) {
+                // If both subscriptions are primary, show both.
+                if (!info1.isOpportunistic() && !info2.isOpportunistic()) return subscriptions;
+
+                // If carrier required, always show signal bar of primary subscription.
+                // Otherwise, show whichever subscription is currently active for Internet.
+                boolean alwaysShowPrimary = CarrierConfigManager.getDefaultConfig()
+                        .getBoolean(CarrierConfigManager
+                        .KEY_ALWAYS_SHOW_PRIMARY_SIGNAL_BAR_IN_OPPORTUNISTIC_NETWORK_BOOLEAN);
+                if (alwaysShowPrimary) {
+                    subscriptions.remove(info1.isOpportunistic() ? info1 : info2);
+                } else {
+                    subscriptions.remove(info1.getSubscriptionId() == mActiveMobileDataSubscription
+                            ? info2 : info1);
+                }
+
+            }
+        }
+
+        return subscriptions;
     }
 
     @Override
@@ -1446,6 +1488,7 @@
 
     protected void handleStartedGoingToSleep(int arg1) {
         checkIsHandlerThread();
+        mLockIconPressed = false;
         clearBiometricRecognized();
         for (int i = 0; i < mCallbacks.size(); i++) {
             KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
@@ -1481,7 +1524,6 @@
 
     private void handleScreenTurnedOff() {
         checkIsHandlerThread();
-        mLockIconPressed = false;
         mHardwareFingerprintUnavailableRetryCount = 0;
         mHardwareFaceUnavailableRetryCount = 0;
         for (int i = 0; i < mCallbacks.size(); i++) {
@@ -2700,6 +2742,7 @@
                 pw.println("    " + mSubscriptionInfo.get(i));
             }
         }
+        pw.println("  Current active data subId=" + mActiveMobileDataSubscription);
         pw.println("  Service states:");
         for (int subId : mServiceStates.keySet()) {
             pw.println("    " + subId + "=" + mServiceStates.get(subId));
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
index 7e3b423..d5c928b 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
@@ -140,11 +140,12 @@
             LockPatternUtils lockPatternUtils,  ViewGroup container,
             DismissCallbackRegistry dismissCallbackRegistry,
             KeyguardBouncer.BouncerExpansionCallback expansionCallback,
-            FalsingManager falsingManager) {
+            FalsingManager falsingManager, KeyguardBypassController bypassController) {
         return new KeyguardBouncer(context, callback, lockPatternUtils, container,
                 dismissCallbackRegistry, falsingManager,
                 expansionCallback, UnlockMethodCache.getInstance(context),
-                KeyguardUpdateMonitor.getInstance(context), new Handler(Looper.getMainLooper()));
+                KeyguardUpdateMonitor.getInstance(context), bypassController,
+                new Handler(Looper.getMainLooper()));
     }
 
     public ScrimController createScrimController(ScrimView scrimBehind, ScrimView scrimInFront,
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDialog.java b/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDialog.java
new file mode 100644
index 0000000..d4baefd
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDialog.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.biometrics;
+
+import android.hardware.biometrics.BiometricPrompt;
+import android.os.Bundle;
+import android.view.WindowManager;
+
+import com.android.systemui.biometrics.ui.BiometricDialogView;
+
+/**
+ * Interface for the biometric dialog UI.
+ */
+public interface BiometricDialog {
+
+    // TODO: Clean up save/restore state
+    String[] KEYS_TO_BACKUP = {
+            BiometricPrompt.KEY_TITLE,
+            BiometricPrompt.KEY_USE_DEFAULT_TITLE,
+            BiometricPrompt.KEY_SUBTITLE,
+            BiometricPrompt.KEY_DESCRIPTION,
+            BiometricPrompt.KEY_POSITIVE_TEXT,
+            BiometricPrompt.KEY_NEGATIVE_TEXT,
+            BiometricPrompt.KEY_REQUIRE_CONFIRMATION,
+            BiometricPrompt.KEY_ALLOW_DEVICE_CREDENTIAL,
+            BiometricPrompt.KEY_FROM_CONFIRM_DEVICE_CREDENTIAL,
+
+            BiometricDialogView.KEY_TRY_AGAIN_VISIBILITY,
+            BiometricDialogView.KEY_CONFIRM_VISIBILITY,
+            BiometricDialogView.KEY_CONFIRM_ENABLED,
+            BiometricDialogView.KEY_STATE,
+            BiometricDialogView.KEY_ERROR_TEXT_VISIBILITY,
+            BiometricDialogView.KEY_ERROR_TEXT_STRING,
+            BiometricDialogView.KEY_ERROR_TEXT_IS_TEMPORARY,
+            BiometricDialogView.KEY_ERROR_TEXT_COLOR,
+    };
+
+    /**
+     * Show the dialog.
+     * @param wm
+     * @param skipIntroAnimation
+     */
+    void show(WindowManager wm, boolean skipIntroAnimation);
+
+    /**
+     * Dismiss the dialog without sending a callback.
+     */
+    void dismissWithoutCallback(boolean animate);
+
+    /**
+     * Dismiss the dialog. Animate away.
+     */
+    void dismissFromSystemServer();
+
+    /**
+     * Biometric authenticated. May be pending user confirmation, or completed.
+     */
+    void onAuthenticationSucceeded();
+
+    /**
+     * Authentication failed (reject, timeout). Dialog stays showing.
+     * @param failureReason
+     */
+    void onAuthenticationFailed(String failureReason);
+
+    /**
+     * Authentication rejected, or help message received.
+     * @param help
+     */
+    void onHelp(String help);
+
+    /**
+     * Authentication failed. Dialog going away.
+     * @param error
+     */
+    void onError(String error);
+
+    /**
+     * Save the current state.
+     * @param outState
+     */
+    void onSaveState(Bundle outState);
+
+    /**
+     * Restore a previous state.
+     * @param savedState
+     */
+    void restoreState(Bundle savedState);
+
+    /**
+     * Get the client's package name
+     */
+    String getOpPackageName();
+}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDialogImpl.java b/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDialogImpl.java
index e66a8fa..a8e5722 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDialogImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDialogImpl.java
@@ -16,137 +16,156 @@
 
 package com.android.systemui.biometrics;
 
+import android.app.ActivityManager;
+import android.app.ActivityTaskManager;
+import android.app.IActivityTaskManager;
+import android.app.TaskStackListener;
 import android.content.Context;
 import android.content.pm.PackageManager;
 import android.content.res.Configuration;
-import android.hardware.biometrics.BiometricAuthenticator;
 import android.hardware.biometrics.BiometricPrompt;
 import android.hardware.biometrics.IBiometricServiceReceiverInternal;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.Looper;
-import android.os.Message;
 import android.os.RemoteException;
 import android.util.Log;
 import android.view.WindowManager;
 
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.os.SomeArgs;
-import com.android.systemui.Dependency;
 import com.android.systemui.SystemUI;
-import com.android.systemui.keyguard.WakefulnessLifecycle;
+import com.android.systemui.biometrics.ui.BiometricDialogView;
 import com.android.systemui.statusbar.CommandQueue;
 
+import java.util.List;
+
 /**
- * Receives messages sent from AuthenticationClient and shows the appropriate biometric UI (e.g.
- * BiometricDialogView).
+ * Receives messages sent from {@link com.android.server.biometrics.BiometricService} and shows the
+ * appropriate biometric UI (e.g. BiometricDialogView).
  */
-public class BiometricDialogImpl extends SystemUI implements CommandQueue.Callbacks {
+public class BiometricDialogImpl extends SystemUI implements CommandQueue.Callbacks,
+        DialogViewCallback {
     private static final String TAG = "BiometricDialogImpl";
     private static final boolean DEBUG = true;
 
-    private static final int MSG_SHOW_DIALOG = 1;
-    private static final int MSG_BIOMETRIC_AUTHENTICATED = 2;
-    private static final int MSG_BIOMETRIC_HELP = 3;
-    private static final int MSG_BIOMETRIC_ERROR = 4;
-    private static final int MSG_HIDE_DIALOG = 5;
-    private static final int MSG_BUTTON_NEGATIVE = 6;
-    private static final int MSG_USER_CANCELED = 7;
-    private static final int MSG_BUTTON_POSITIVE = 8;
-    private static final int MSG_TRY_AGAIN_PRESSED = 9;
+    private final Injector mInjector;
 
+    // TODO: These should just be saved from onSaveState
     private SomeArgs mCurrentDialogArgs;
-    private BiometricDialogView mCurrentDialog;
+    @VisibleForTesting
+    BiometricDialog mCurrentDialog;
+
+    private Handler mHandler = new Handler(Looper.getMainLooper());
     private WindowManager mWindowManager;
-    private IBiometricServiceReceiverInternal mReceiver;
-    private boolean mDialogShowing;
-    private Callback mCallback = new Callback();
-    private WakefulnessLifecycle mWakefulnessLifecycle;
+    @VisibleForTesting
+    IActivityTaskManager mActivityTaskManager;
+    @VisibleForTesting
+    BiometricTaskStackListener mTaskStackListener;
+    @VisibleForTesting
+    IBiometricServiceReceiverInternal mReceiver;
 
-    private Handler mHandler = new Handler(Looper.getMainLooper()) {
+    public class BiometricTaskStackListener extends TaskStackListener {
         @Override
-        public void handleMessage(Message msg) {
-            switch(msg.what) {
-                case MSG_SHOW_DIALOG:
-                    handleShowDialog((SomeArgs) msg.obj, false /* skipAnimation */,
-                            null /* savedState */);
-                    break;
-                case MSG_BIOMETRIC_AUTHENTICATED: {
-                    SomeArgs args = (SomeArgs) msg.obj;
-                    handleBiometricAuthenticated((boolean) args.arg1 /* authenticated */,
-                            (String) args.arg2 /* failureReason */);
-                    args.recycle();
-                    break;
-                }
-                case MSG_BIOMETRIC_HELP: {
-                    SomeArgs args = (SomeArgs) msg.obj;
-                    handleBiometricHelp((String) args.arg1 /* message */);
-                    args.recycle();
-                    break;
-                }
-                case MSG_BIOMETRIC_ERROR:
-                    handleBiometricError((String) msg.obj);
-                    break;
-                case MSG_HIDE_DIALOG:
-                    handleHideDialog((Boolean) msg.obj);
-                    break;
-                case MSG_BUTTON_NEGATIVE:
-                    handleButtonNegative();
-                    break;
-                case MSG_USER_CANCELED:
-                    handleUserCanceled();
-                    break;
-                case MSG_BUTTON_POSITIVE:
-                    handleButtonPositive();
-                    break;
-                case MSG_TRY_AGAIN_PRESSED:
-                    handleTryAgainPressed();
-                    break;
-                default:
-                    Log.w(TAG, "Unknown message: " + msg.what);
-                    break;
-            }
-        }
-    };
-
-    private class Callback implements DialogViewCallback {
-        @Override
-        public void onUserCanceled() {
-            mHandler.obtainMessage(MSG_USER_CANCELED).sendToTarget();
-        }
-
-        @Override
-        public void onErrorShown() {
-            mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_HIDE_DIALOG,
-                    false /* userCanceled */), BiometricPrompt.HIDE_DIALOG_DELAY);
-        }
-
-        @Override
-        public void onNegativePressed() {
-            mHandler.obtainMessage(MSG_BUTTON_NEGATIVE).sendToTarget();
-        }
-
-        @Override
-        public void onPositivePressed() {
-            mHandler.obtainMessage(MSG_BUTTON_POSITIVE).sendToTarget();
-        }
-
-        @Override
-        public void onTryAgainPressed() {
-            mHandler.obtainMessage(MSG_TRY_AGAIN_PRESSED).sendToTarget();
+        public void onTaskStackChanged() {
+            mHandler.post(mTaskStackChangedRunnable);
         }
     }
 
-    final WakefulnessLifecycle.Observer mWakefulnessObserver = new WakefulnessLifecycle.Observer() {
-        @Override
-        public void onStartedGoingToSleep() {
-            if (mDialogShowing) {
-                if (DEBUG) Log.d(TAG, "User canceled due to screen off");
-                mHandler.obtainMessage(MSG_USER_CANCELED).sendToTarget();
+    private final Runnable mTaskStackChangedRunnable = () -> {
+        if (mCurrentDialog != null) {
+            try {
+                final String clientPackage = mCurrentDialog.getOpPackageName();
+                Log.w(TAG, "Task stack changed, current client: " + clientPackage);
+                final List<ActivityManager.RunningTaskInfo> runningTasks =
+                        mActivityTaskManager.getTasks(1);
+                if (!runningTasks.isEmpty()) {
+                    final String topPackage = runningTasks.get(0).topActivity.getPackageName();
+                    if (!topPackage.contentEquals(clientPackage)) {
+                        Log.w(TAG, "Evicting client due to: " + topPackage);
+                        mCurrentDialog.dismissWithoutCallback(true /* animate */);
+                        mCurrentDialog = null;
+                        mReceiver.onDialogDismissed(BiometricPrompt.DISMISSED_REASON_USER_CANCEL);
+                        mReceiver = null;
+                    }
+                }
+            } catch (RemoteException e) {
+                Log.e(TAG, "Remote exception", e);
             }
         }
     };
 
     @Override
+    public void onTryAgainPressed() {
+        try {
+            mReceiver.onTryAgainPressed();
+        } catch (RemoteException e) {
+            Log.e(TAG, "RemoteException when handling try again", e);
+        }
+    }
+
+    @Override
+    public void onDismissed(@DismissedReason int reason) {
+        switch (reason) {
+            case DialogViewCallback.DISMISSED_USER_CANCELED:
+                sendResultAndCleanUp(BiometricPrompt.DISMISSED_REASON_USER_CANCEL);
+                break;
+
+            case DialogViewCallback.DISMISSED_BUTTON_NEGATIVE:
+                sendResultAndCleanUp(BiometricPrompt.DISMISSED_REASON_NEGATIVE);
+                break;
+
+            case DialogViewCallback.DISMISSED_BUTTON_POSITIVE:
+                sendResultAndCleanUp(BiometricPrompt.DISMISSED_REASON_CONFIRMED);
+                break;
+
+            case DialogViewCallback.DISMISSED_AUTHENTICATED:
+                sendResultAndCleanUp(BiometricPrompt.DISMISSED_REASON_CONFIRM_NOT_REQUIRED);
+                break;
+
+            case DialogViewCallback.DISMISSED_ERROR:
+                sendResultAndCleanUp(BiometricPrompt.DISMISSED_REASON_ERROR);
+                break;
+
+            case DialogViewCallback.DISMISSED_BY_SYSTEM_SERVER:
+                sendResultAndCleanUp(BiometricPrompt.DISMISSED_REASON_SERVER_REQUESTED);
+                break;
+
+            default:
+                Log.e(TAG, "Unhandled reason: " + reason);
+                break;
+        }
+    }
+
+    private void sendResultAndCleanUp(@DismissedReason int reason) {
+        if (mReceiver == null) {
+            Log.e(TAG, "Receiver is null");
+            return;
+        }
+        try {
+            mReceiver.onDialogDismissed(reason);
+        } catch (RemoteException e) {
+            Log.w(TAG, "Remote exception", e);
+        }
+        onDialogDismissed(reason);
+    }
+
+    public static class Injector {
+        IActivityTaskManager getActivityTaskManager() {
+            return ActivityTaskManager.getService();
+        }
+    }
+
+    public BiometricDialogImpl() {
+        this(new Injector());
+    }
+
+    @VisibleForTesting
+    BiometricDialogImpl(Injector injector) {
+        mInjector = injector;
+    }
+
+    @Override
     public void start() {
         final PackageManager pm = mContext.getPackageManager();
         if (pm.hasSystemFeature(PackageManager.FEATURE_FINGERPRINT)
@@ -154,30 +173,38 @@
                 || pm.hasSystemFeature(PackageManager.FEATURE_IRIS)) {
             getComponent(CommandQueue.class).addCallback(this);
             mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
-            mWakefulnessLifecycle = Dependency.get(WakefulnessLifecycle.class);
-            mWakefulnessLifecycle.addObserver(mWakefulnessObserver);
+            mActivityTaskManager = mInjector.getActivityTaskManager();
+
+            try {
+                mTaskStackListener = new BiometricTaskStackListener();
+                mActivityTaskManager.registerTaskStackListener(mTaskStackListener);
+            } catch (RemoteException e) {
+                Log.w(TAG, "Unable to register task stack listener", e);
+            }
         }
     }
 
     @Override
     public void showBiometricDialog(Bundle bundle, IBiometricServiceReceiverInternal receiver,
-            int type, boolean requireConfirmation, int userId) {
+            int type, boolean requireConfirmation, int userId, String opPackageName) {
         if (DEBUG) {
             Log.d(TAG, "showBiometricDialog, type: " + type
                     + ", requireConfirmation: " + requireConfirmation);
         }
-        // Remove these messages as they are part of the previous client
-        mHandler.removeMessages(MSG_BIOMETRIC_ERROR);
-        mHandler.removeMessages(MSG_BIOMETRIC_HELP);
-        mHandler.removeMessages(MSG_BIOMETRIC_AUTHENTICATED);
-        mHandler.removeMessages(MSG_HIDE_DIALOG);
         SomeArgs args = SomeArgs.obtain();
         args.arg1 = bundle;
         args.arg2 = receiver;
         args.argi1 = type;
         args.arg3 = requireConfirmation;
         args.argi2 = userId;
-        mHandler.obtainMessage(MSG_SHOW_DIALOG, args).sendToTarget();
+        args.arg4 = opPackageName;
+
+        boolean skipAnimation = false;
+        if (mCurrentDialog != null) {
+            Log.w(TAG, "mCurrentDialog: " + mCurrentDialog);
+            skipAnimation = true;
+        }
+        showDialog(args, skipAnimation, null /* savedState */);
     }
 
     @Override
@@ -185,185 +212,111 @@
         if (DEBUG) Log.d(TAG, "onBiometricAuthenticated: " + authenticated
                 + " reason: " + failureReason);
 
-        SomeArgs args = SomeArgs.obtain();
-        args.arg1 = authenticated;
-        args.arg2 = failureReason;
-        mHandler.obtainMessage(MSG_BIOMETRIC_AUTHENTICATED, args).sendToTarget();
-    }
-
-    @Override
-    public void onBiometricHelp(String message) {
-        if (DEBUG) Log.d(TAG, "onBiometricHelp: " + message);
-        SomeArgs args = SomeArgs.obtain();
-        args.arg1 = message;
-        mHandler.obtainMessage(MSG_BIOMETRIC_HELP, args).sendToTarget();
-    }
-
-    @Override
-    public void onBiometricError(String error) {
-        if (DEBUG) Log.d(TAG, "onBiometricError: " + error);
-        mHandler.obtainMessage(MSG_BIOMETRIC_ERROR, error).sendToTarget();
-    }
-
-    @Override
-    public void hideBiometricDialog() {
-        if (DEBUG) Log.d(TAG, "hideBiometricDialog");
-        mHandler.obtainMessage(MSG_HIDE_DIALOG, false /* userCanceled */).sendToTarget();
-    }
-
-    private void handleShowDialog(SomeArgs args, boolean skipAnimation, Bundle savedState) {
-        mCurrentDialogArgs = args;
-        final int type = args.argi1;
-
-        // Create a new dialog but do not replace the current one yet.
-        BiometricDialogView newDialog;
-        if (type == BiometricAuthenticator.TYPE_FINGERPRINT) {
-            newDialog = new FingerprintDialogView(mContext, mCallback);
-        } else if (type == BiometricAuthenticator.TYPE_FACE) {
-            newDialog = new FaceDialogView(mContext, mCallback);
-        } else {
-            Log.e(TAG, "Unsupported type: " + type);
-            return;
-        }
-
-        if (DEBUG) Log.d(TAG, "handleShowDialog, "
-                + " savedState: " + savedState
-                + " mCurrentDialog: " + mCurrentDialog
-                + " newDialog: " + newDialog
-                + " type: " + type);
-
-        if (savedState != null) {
-            // SavedState is only non-null if it's from onConfigurationChanged. Restore the state
-            // even though it may be removed / re-created again
-            newDialog.restoreState(savedState);
-        } else if (mCurrentDialog != null && mDialogShowing) {
-            // If somehow we're asked to show a dialog, the old one doesn't need to be animated
-            // away. This can happen if the app cancels and re-starts auth during configuration
-            // change. This is ugly because we also have to do things on onConfigurationChanged
-            // here.
-            mCurrentDialog.forceRemove();
-        }
-
-        mReceiver = (IBiometricServiceReceiverInternal) args.arg2;
-        newDialog.setBundle((Bundle) args.arg1);
-        newDialog.setRequireConfirmation((boolean) args.arg3);
-        newDialog.setUserId(args.argi2);
-        newDialog.setSkipIntro(skipAnimation);
-        mCurrentDialog = newDialog;
-        mWindowManager.addView(mCurrentDialog, mCurrentDialog.getLayoutParams());
-        mDialogShowing = true;
-    }
-
-    private void handleBiometricAuthenticated(boolean authenticated, String failureReason) {
-        if (DEBUG) Log.d(TAG, "handleBiometricAuthenticated: " + authenticated);
-
         if (authenticated) {
-            mCurrentDialog.announceForAccessibility(
-                    mContext.getResources()
-                            .getText(mCurrentDialog.getAuthenticatedAccessibilityResourceId()));
-            if (mCurrentDialog.requiresConfirmation()) {
-                mCurrentDialog.updateState(BiometricDialogView.STATE_PENDING_CONFIRMATION);
-            } else {
-                mCurrentDialog.updateState(BiometricDialogView.STATE_AUTHENTICATED);
-                mHandler.postDelayed(() -> {
-                    handleHideDialog(false /* userCanceled */);
-                }, mCurrentDialog.getDelayAfterAuthenticatedDurationMs());
-            }
+            mCurrentDialog.onAuthenticationSucceeded();
         } else {
             mCurrentDialog.onAuthenticationFailed(failureReason);
         }
     }
 
-    private void handleBiometricHelp(String message) {
-        if (DEBUG) Log.d(TAG, "handleBiometricHelp: " + message);
-        mCurrentDialog.onHelpReceived(message);
+    @Override
+    public void onBiometricHelp(String message) {
+        if (DEBUG) Log.d(TAG, "onBiometricHelp: " + message);
+
+        mCurrentDialog.onHelp(message);
     }
 
-    private void handleBiometricError(String error) {
-        if (DEBUG) Log.d(TAG, "handleBiometricError: " + error);
-        if (!mDialogShowing) {
-            if (DEBUG) Log.d(TAG, "Dialog already dismissed");
-            return;
-        }
-        mCurrentDialog.onErrorReceived(error);
+    @Override
+    public void onBiometricError(String error) {
+        if (DEBUG) Log.d(TAG, "onBiometricError: " + error);
+        mCurrentDialog.onError(error);
     }
 
-    private void handleHideDialog(boolean userCanceled) {
-        if (DEBUG) Log.d(TAG, "handleHideDialog, userCanceled: " + userCanceled);
-        if (!mDialogShowing) {
-            // This can happen if there's a race and we get called from both
-            // onAuthenticated and onError, etc.
-            Log.w(TAG, "Dialog already dismissed, userCanceled: " + userCanceled);
+    @Override
+    public void hideBiometricDialog() {
+        if (DEBUG) Log.d(TAG, "hideBiometricDialog");
+
+        mCurrentDialog.dismissFromSystemServer();
+    }
+
+    private void showDialog(SomeArgs args, boolean skipAnimation, Bundle savedState) {
+        mCurrentDialogArgs = args;
+        final int type = args.argi1;
+        final Bundle biometricPromptBundle = (Bundle) args.arg1;
+        final boolean requireConfirmation = (boolean) args.arg3;
+        final int userId = args.argi2;
+        final String opPackageName = (String) args.arg4;
+
+        // Create a new dialog but do not replace the current one yet.
+        final BiometricDialog newDialog = buildDialog(
+                biometricPromptBundle,
+                requireConfirmation,
+                userId,
+                type,
+                opPackageName);
+
+        if (newDialog == null) {
+            Log.e(TAG, "Unsupported type: " + type);
             return;
         }
-        if (userCanceled) {
-            try {
-                mReceiver.onDialogDismissed(BiometricPrompt.DISMISSED_REASON_USER_CANCEL);
-            } catch (RemoteException e) {
-                Log.e(TAG, "RemoteException when hiding dialog", e);
-            }
+
+        if (DEBUG) {
+            Log.d(TAG, "showDialog, "
+                    + " savedState: " + savedState
+                    + " mCurrentDialog: " + mCurrentDialog
+                    + " newDialog: " + newDialog
+                    + " type: " + type);
+        }
+
+        if (savedState != null) {
+            // SavedState is only non-null if it's from onConfigurationChanged. Restore the state
+            // even though it may be removed / re-created again
+            newDialog.restoreState(savedState);
+        } else if (mCurrentDialog != null) {
+            // If somehow we're asked to show a dialog, the old one doesn't need to be animated
+            // away. This can happen if the app cancels and re-starts auth during configuration
+            // change. This is ugly because we also have to do things on onConfigurationChanged
+            // here.
+            mCurrentDialog.dismissWithoutCallback(false /* animate */);
+        }
+
+        mReceiver = (IBiometricServiceReceiverInternal) args.arg2;
+        mCurrentDialog = newDialog;
+        mCurrentDialog.show(mWindowManager, skipAnimation);
+    }
+
+    private void onDialogDismissed(@DismissedReason int reason) {
+        if (DEBUG) Log.d(TAG, "onDialogDismissed: " + reason);
+        if (mCurrentDialog == null) {
+            Log.w(TAG, "Dialog already dismissed");
         }
         mReceiver = null;
-        mDialogShowing = false;
-        mCurrentDialog.startDismiss();
-    }
-
-    private void handleButtonNegative() {
-        if (mReceiver == null) {
-            Log.e(TAG, "Receiver is null");
-            return;
-        }
-        try {
-            mReceiver.onDialogDismissed(BiometricPrompt.DISMISSED_REASON_NEGATIVE);
-        } catch (RemoteException e) {
-            Log.e(TAG, "Remote exception when handling negative button", e);
-        }
-        handleHideDialog(false /* userCanceled */);
-    }
-
-    private void handleButtonPositive() {
-        if (mReceiver == null) {
-            Log.e(TAG, "Receiver is null");
-            return;
-        }
-        try {
-            mReceiver.onDialogDismissed(BiometricPrompt.DISMISSED_REASON_POSITIVE);
-        } catch (RemoteException e) {
-            Log.e(TAG, "Remote exception when handling positive button", e);
-        }
-        handleHideDialog(false /* userCanceled */);
-    }
-
-    private void handleUserCanceled() {
-        handleHideDialog(true /* userCanceled */);
-    }
-
-    private void handleTryAgainPressed() {
-        try {
-            mReceiver.onTryAgainPressed();
-        } catch (RemoteException e) {
-            Log.e(TAG, "RemoteException when handling try again", e);
-        }
+        mCurrentDialog = null;
     }
 
     @Override
     protected void onConfigurationChanged(Configuration newConfig) {
         super.onConfigurationChanged(newConfig);
-        final boolean wasShowing = mDialogShowing;
 
         // Save the state of the current dialog (buttons showing, etc)
-        final Bundle savedState = new Bundle();
         if (mCurrentDialog != null) {
+            final Bundle savedState = new Bundle();
             mCurrentDialog.onSaveState(savedState);
-        }
+            mCurrentDialog.dismissWithoutCallback(false /* animate */);
+            mCurrentDialog = null;
 
-        if (mDialogShowing) {
-            mCurrentDialog.forceRemove();
-            mDialogShowing = false;
+            showDialog(mCurrentDialogArgs, true /* skipAnimation */, savedState);
         }
+    }
 
-        if (wasShowing) {
-            handleShowDialog(mCurrentDialogArgs, true /* skipAnimation */, savedState);
-        }
+    protected BiometricDialog buildDialog(Bundle biometricPromptBundle,
+            boolean requireConfirmation, int userId, int type, String opPackageName) {
+        return new BiometricDialogView.Builder(mContext)
+                .setCallback(this)
+                .setBiometricPromptBundle(biometricPromptBundle)
+                .setRequireConfirmation(requireConfirmation)
+                .setUserId(userId)
+                .setOpPackageName(opPackageName)
+                .build(type);
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/DialogViewCallback.java b/packages/SystemUI/src/com/android/systemui/biometrics/DialogViewCallback.java
index 24fd22e..b65d1e8 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/DialogViewCallback.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/DialogViewCallback.java
@@ -16,36 +16,38 @@
 
 package com.android.systemui.biometrics;
 
+import android.annotation.IntDef;
+
 /**
  * Callback interface for dialog views. These should be implemented by the controller (e.g.
  * FingerprintDialogImpl) and passed into their views (e.g. FingerprintDialogView).
  */
 public interface DialogViewCallback {
-    /**
-     * Invoked when the user cancels authentication by tapping outside the prompt, etc. The dialog
-     * should be dismissed.
-     */
-    void onUserCanceled();
+
+    int DISMISSED_USER_CANCELED = 1;
+    int DISMISSED_BUTTON_NEGATIVE = 2;
+    int DISMISSED_BUTTON_POSITIVE = 3;
+
+    int DISMISSED_AUTHENTICATED = 4;
+    int DISMISSED_ERROR = 5;
+    int DISMISSED_BY_SYSTEM_SERVER = 6;
+
+    @IntDef({DISMISSED_USER_CANCELED,
+            DISMISSED_BUTTON_NEGATIVE,
+            DISMISSED_BUTTON_POSITIVE,
+            DISMISSED_AUTHENTICATED,
+            DISMISSED_ERROR,
+            DISMISSED_BY_SYSTEM_SERVER})
+    @interface DismissedReason {}
 
     /**
-     * Invoked when an error is shown. The dialog should be dismissed after a set amount of time.
+     * Invoked when the dialog is dismissed
+     * @param reason
      */
-    void onErrorShown();
+    void onDismissed(@DismissedReason int reason);
 
     /**
-     * Invoked when the negative button is pressed. The client should be notified and the dialog
-     * should be dismissed.
-     */
-    void onNegativePressed();
-
-    /**
-     * Invoked when the positive button is pressed. The client should be notified and the dialog
-     * should be dismissed.
-     */
-    void onPositivePressed();
-
-    /**
-     * Invoked when the "try again" button is pressed.
+     * Invoked when the "try again" button is clicked
      */
     void onTryAgainPressed();
 }
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDialogView.java b/packages/SystemUI/src/com/android/systemui/biometrics/ui/BiometricDialogView.java
similarity index 70%
rename from packages/SystemUI/src/com/android/systemui/biometrics/BiometricDialogView.java
rename to packages/SystemUI/src/com/android/systemui/biometrics/ui/BiometricDialogView.java
index ce67577..2b4dde5 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDialogView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/BiometricDialogView.java
@@ -11,10 +11,10 @@
  * 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
+ * limitations under the License.
  */
 
-package com.android.systemui.biometrics;
+package com.android.systemui.biometrics.ui;
 
 import static android.view.accessibility.AccessibilityEvent.CONTENT_CHANGE_TYPE_SUBTREE;
 
@@ -23,6 +23,7 @@
 import android.graphics.PixelFormat;
 import android.graphics.PorterDuff;
 import android.graphics.drawable.Drawable;
+import android.hardware.biometrics.BiometricAuthenticator;
 import android.hardware.biometrics.BiometricPrompt;
 import android.os.Binder;
 import android.os.Bundle;
@@ -46,25 +47,30 @@
 import android.widget.LinearLayout;
 import android.widget.TextView;
 
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.systemui.Dependency;
 import com.android.systemui.Interpolators;
 import com.android.systemui.R;
+import com.android.systemui.biometrics.BiometricDialog;
+import com.android.systemui.biometrics.DialogViewCallback;
+import com.android.systemui.keyguard.WakefulnessLifecycle;
 import com.android.systemui.util.leak.RotationUtils;
 
 /**
  * Abstract base class. Shows a dialog for BiometricPrompt.
  */
-public abstract class BiometricDialogView extends LinearLayout {
+public abstract class BiometricDialogView extends LinearLayout implements BiometricDialog {
 
     private static final String TAG = "BiometricDialogView";
 
-    private static final String KEY_TRY_AGAIN_VISIBILITY = "key_try_again_visibility";
-    private static final String KEY_CONFIRM_VISIBILITY = "key_confirm_visibility";
-    private static final String KEY_CONFIRM_ENABLED = "key_confirm_enabled";
-    private static final String KEY_STATE = "key_state";
-    private static final String KEY_ERROR_TEXT_VISIBILITY = "key_error_text_visibility";
-    private static final String KEY_ERROR_TEXT_STRING = "key_error_text_string";
-    private static final String KEY_ERROR_TEXT_IS_TEMPORARY = "key_error_text_is_temporary";
-    private static final String KEY_ERROR_TEXT_COLOR = "key_error_text_color";
+    public static final String KEY_TRY_AGAIN_VISIBILITY = "key_try_again_visibility";
+    public static final String KEY_CONFIRM_VISIBILITY = "key_confirm_visibility";
+    public static final String KEY_CONFIRM_ENABLED = "key_confirm_enabled";
+    public static final String KEY_STATE = "key_state";
+    public static final String KEY_ERROR_TEXT_VISIBILITY = "key_error_text_visibility";
+    public static final String KEY_ERROR_TEXT_STRING = "key_error_text_string";
+    public static final String KEY_ERROR_TEXT_IS_TEMPORARY = "key_error_text_is_temporary";
+    public static final String KEY_ERROR_TEXT_COLOR = "key_error_text_color";
 
     private static final int ANIMATION_DURATION_SHOW = 250; // ms
     private static final int ANIMATION_DURATION_AWAY = 350; // ms
@@ -77,6 +83,8 @@
     protected static final int STATE_PENDING_CONFIRMATION = 3;
     protected static final int STATE_AUTHENTICATED = 4;
 
+    @VisibleForTesting
+    final WakefulnessLifecycle mWakefulnessLifecycle;
     private final AccessibilityManager mAccessibilityManager;
     private final IBinder mWindowToken = new Binder();
     private final Interpolator mLinearOutSlowIn;
@@ -90,22 +98,30 @@
 
     protected final ViewGroup mLayout;
     protected final LinearLayout mDialog;
-    protected final TextView mTitleText;
-    protected final TextView mSubtitleText;
-    protected final TextView mDescriptionText;
-    protected final ImageView mBiometricIcon;
-    protected final TextView mErrorText;
-    protected final Button mPositiveButton;
-    protected final Button mNegativeButton;
-    protected final Button mTryAgainButton;
+    @VisibleForTesting
+    final TextView mTitleText;
+    @VisibleForTesting
+    final TextView mSubtitleText;
+    @VisibleForTesting
+    final TextView mDescriptionText;
+    @VisibleForTesting
+    final ImageView mBiometricIcon;
+    @VisibleForTesting
+    final TextView mErrorText;
+    @VisibleForTesting
+    final Button mPositiveButton;
+    @VisibleForTesting
+    final Button mNegativeButton;
+    @VisibleForTesting
+    final Button mTryAgainButton;
 
     protected final int mTextColor;
 
     private Bundle mBundle;
     private Bundle mRestoredState;
+    private String mOpPackageName;
 
     private int mState = STATE_IDLE;
-    private boolean mAnimatingAway;
     private boolean mWasForceRemoved;
     private boolean mSkipIntro;
     protected boolean mRequireConfirmation;
@@ -141,6 +157,15 @@
         }
     };
 
+    @VisibleForTesting
+    final WakefulnessLifecycle.Observer mWakefulnessObserver =
+            new WakefulnessLifecycle.Observer() {
+                @Override
+                public void onStartedGoingToSleep() {
+                    animateAway(DialogViewCallback.DISMISSED_USER_CANCELED);
+                }
+            };
+
     protected Handler mHandler = new Handler() {
         @Override
         public void handleMessage(Message msg) {
@@ -155,8 +180,80 @@
         }
     };
 
-    public BiometricDialogView(Context context, DialogViewCallback callback) {
+    /**
+     * Builds the dialog with specified parameters.
+     */
+    public static class Builder {
+        public static final int TYPE_FINGERPRINT = BiometricAuthenticator.TYPE_FINGERPRINT;
+        public static final int TYPE_FACE = BiometricAuthenticator.TYPE_FACE;
+
+        private Context mContext;
+        private DialogViewCallback mCallback;
+        private Bundle mBundle;
+        private boolean mRequireConfirmation;
+        private int mUserId;
+        private String mOpPackageName;
+
+        public Builder(Context context) {
+            mContext = context;
+        }
+
+        public Builder setCallback(DialogViewCallback callback) {
+            mCallback = callback;
+            return this;
+        }
+
+        public Builder setBiometricPromptBundle(Bundle bundle) {
+            mBundle = bundle;
+            return this;
+        }
+
+        public Builder setRequireConfirmation(boolean requireConfirmation) {
+            mRequireConfirmation = requireConfirmation;
+            return this;
+        }
+
+        public Builder setUserId(int userId) {
+            mUserId = userId;
+            return this;
+        }
+
+        public Builder setOpPackageName(String opPackageName) {
+            mOpPackageName = opPackageName;
+            return this;
+        }
+
+        public BiometricDialogView build(int type) {
+            return build(type, new Injector());
+        }
+
+        public BiometricDialogView build(int type, Injector injector) {
+            BiometricDialogView dialog;
+            if (type == TYPE_FINGERPRINT) {
+                dialog = new FingerprintDialogView(mContext, mCallback, injector);
+            } else if (type == TYPE_FACE) {
+                dialog = new FaceDialogView(mContext, mCallback, injector);
+            } else {
+                return null;
+            }
+            dialog.setBundle(mBundle);
+            dialog.setRequireConfirmation(mRequireConfirmation);
+            dialog.setUserId(mUserId);
+            dialog.setOpPackageName(mOpPackageName);
+            return dialog;
+        }
+    }
+
+    public static class Injector {
+        public WakefulnessLifecycle getWakefulnessLifecycle() {
+            return Dependency.get(WakefulnessLifecycle.class);
+        }
+    }
+
+    protected BiometricDialogView(Context context, DialogViewCallback callback, Injector injector) {
         super(context);
+        mWakefulnessLifecycle = injector.getWakefulnessLifecycle();
+
         mCallback = callback;
         mLinearOutSlowIn = Interpolators.LINEAR_OUT_SLOW_IN;
         mAccessibilityManager = mContext.getSystemService(AccessibilityManager.class);
@@ -178,19 +275,13 @@
         addView(mLayout);
 
         mLayout.setOnKeyListener(new View.OnKeyListener() {
-            boolean downPressed = false;
             @Override
             public boolean onKey(View v, int keyCode, KeyEvent event) {
                 if (keyCode != KeyEvent.KEYCODE_BACK) {
                     return false;
                 }
-                if (event.getAction() == KeyEvent.ACTION_DOWN && downPressed == false) {
-                    downPressed = true;
-                } else if (event.getAction() == KeyEvent.ACTION_DOWN) {
-                    downPressed = false;
-                } else if (event.getAction() == KeyEvent.ACTION_UP && downPressed == true) {
-                    downPressed = false;
-                    mCallback.onUserCanceled();
+                if (event.getAction() == KeyEvent.ACTION_UP) {
+                    animateAway(DialogViewCallback.DISMISSED_USER_CANCELED);
                 }
                 return true;
             }
@@ -219,16 +310,16 @@
 
         mNegativeButton.setOnClickListener((View v) -> {
             if (mState == STATE_PENDING_CONFIRMATION || mState == STATE_AUTHENTICATED) {
-                mCallback.onUserCanceled();
+                animateAway(DialogViewCallback.DISMISSED_USER_CANCELED);
             } else {
-                mCallback.onNegativePressed();
+                animateAway(DialogViewCallback.DISMISSED_BUTTON_NEGATIVE);
             }
         });
 
         mPositiveButton.setOnClickListener((View v) -> {
             updateState(STATE_AUTHENTICATED);
             mHandler.postDelayed(() -> {
-                mCallback.onPositivePressed();
+                animateAway(DialogViewCallback.DISMISSED_BUTTON_POSITIVE);
             }, getDelayAfterAuthenticatedDurationMs());
         });
 
@@ -248,21 +339,12 @@
         mLayout.requestFocus();
     }
 
-    public void onSaveState(Bundle bundle) {
-        bundle.putInt(KEY_TRY_AGAIN_VISIBILITY, mTryAgainButton.getVisibility());
-        bundle.putInt(KEY_CONFIRM_VISIBILITY, mPositiveButton.getVisibility());
-        bundle.putBoolean(KEY_CONFIRM_ENABLED, mPositiveButton.isEnabled());
-        bundle.putInt(KEY_STATE, mState);
-        bundle.putInt(KEY_ERROR_TEXT_VISIBILITY, mErrorText.getVisibility());
-        bundle.putCharSequence(KEY_ERROR_TEXT_STRING, mErrorText.getText());
-        bundle.putBoolean(KEY_ERROR_TEXT_IS_TEMPORARY, mHandler.hasMessages(MSG_RESET_MESSAGE));
-        bundle.putInt(KEY_ERROR_TEXT_COLOR, mErrorText.getCurrentTextColor());
-    }
-
     @Override
     public void onAttachedToWindow() {
         super.onAttachedToWindow();
 
+        mWakefulnessLifecycle.addObserver(mWakefulnessObserver);
+
         final ImageView backgroundView = mLayout.findViewById(R.id.background);
 
         if (mUserManager.isManagedProfile(mUserId)) {
@@ -278,6 +360,7 @@
         }
 
         mNegativeButton.setVisibility(View.VISIBLE);
+        mNegativeButton.setText(mBundle.getCharSequence(BiometricPrompt.KEY_NEGATIVE_TEXT));
 
         if (RotationUtils.getRotation(mContext) != RotationUtils.ROTATION_NONE) {
             mDialog.getLayoutParams().width = (int) mDialogWidth;
@@ -285,7 +368,6 @@
 
         if (mRestoredState == null) {
             updateState(STATE_AUTHENTICATING);
-            mNegativeButton.setText(mBundle.getCharSequence(BiometricPrompt.KEY_NEGATIVE_TEXT));
             final int hint = getHintStringResourceId();
             if (hint != 0) {
                 mErrorText.setText(hint);
@@ -346,34 +428,49 @@
         mSkipIntro = false;
     }
 
+    @Override
+    public void onDetachedFromWindow() {
+        super.onDetachedFromWindow();
+
+        mWakefulnessLifecycle.removeObserver(mWakefulnessObserver);
+    }
+
     private void setDismissesDialog(View v) {
         v.setClickable(true);
         v.setOnClickListener(v1 -> {
             if (mState != STATE_AUTHENTICATED && shouldGrayAreaDismissDialog()) {
-                mCallback.onUserCanceled();
+                animateAway(DialogViewCallback.DISMISSED_USER_CANCELED);
             }
         });
     }
 
-    public void startDismiss() {
+    private void animateAway(@DialogViewCallback.DismissedReason int reason) {
+        animateAway(true /* sendReason */, reason);
+    }
+
+    /**
+     * Animate the dialog away
+     * @param reason one of the {@link DialogViewCallback} codes
+     */
+    private void animateAway(boolean sendReason, @DialogViewCallback.DismissedReason int reason) {
         if (!mCompletedAnimatingIn) {
             Log.w(TAG, "startDismiss(): waiting for onDialogAnimatedIn");
             mPendingDismissDialog = true;
             return;
         }
 
-        mAnimatingAway = true;
-
         // This is where final cleanup should occur.
         final Runnable endActionRunnable = new Runnable() {
             @Override
             public void run() {
                 mWindowManager.removeView(BiometricDialogView.this);
-                mAnimatingAway = false;
                 // Set the icons / text back to normal state
                 handleResetMessage();
                 showTryAgainButton(false /* show */);
                 updateState(STATE_IDLE);
+                if (sendReason) {
+                    mCallback.onDismissed(reason);
+                }
             }
         };
 
@@ -398,47 +495,30 @@
     }
 
     /**
-     * Force remove the window, cancelling any animation that's happening. This should only be
-     * called if we want to quickly show the dialog again (e.g. on rotation). Calling this method
-     * will cause the dialog to show without an animation the next time it's attached.
-     */
-    public void forceRemove() {
-        mLayout.animate().cancel();
-        mDialog.animate().cancel();
-        mWindowManager.removeView(BiometricDialogView.this);
-        mAnimatingAway = false;
-        mWasForceRemoved = true;
-    }
-
-    /**
      * Skip the intro animation
      */
-    public void setSkipIntro(boolean skip) {
+    private void setSkipIntro(boolean skip) {
         mSkipIntro = skip;
     }
 
-    public boolean isAnimatingAway() {
-        return mAnimatingAway;
-    }
-
-    public void setBundle(Bundle bundle) {
+    private void setBundle(Bundle bundle) {
         mBundle = bundle;
     }
 
-    public void setRequireConfirmation(boolean requireConfirmation) {
+    private void setRequireConfirmation(boolean requireConfirmation) {
         mRequireConfirmation = requireConfirmation;
     }
 
-    public boolean requiresConfirmation() {
+    protected boolean requiresConfirmation() {
         return mRequireConfirmation;
     }
 
-    public void setUserId(int userId) {
+    private void setUserId(int userId) {
         mUserId = userId;
     }
 
-    public ViewGroup getLayout() {
-        return mLayout;
+    private void setOpPackageName(String opPackageName) {
+        mOpPackageName = opPackageName;
     }
 
     // Shows an error/help message
@@ -452,17 +532,63 @@
                 BiometricPrompt.HIDE_DIALOG_DELAY);
     }
 
+    @Override
+    public void show(WindowManager wm, boolean skipIntroAnimation) {
+        setSkipIntro(skipIntroAnimation);
+        wm.addView(this, getLayoutParams(mWindowToken));
+    }
+
+    /**
+     * Force remove the window, cancelling any animation that's happening. This should only be
+     * called if we want to quickly show the dialog again (e.g. on rotation). Calling this method
+     * will cause the dialog to show without an animation the next time it's attached.
+     */
+    @Override
+    public void dismissWithoutCallback(boolean animate) {
+        if (animate) {
+            animateAway(false /* sendReason */, 0 /* reason */);
+        } else {
+            mLayout.animate().cancel();
+            mDialog.animate().cancel();
+            mWindowManager.removeView(BiometricDialogView.this);
+            mWasForceRemoved = true;
+        }
+    }
+
+    @Override
+    public void dismissFromSystemServer() {
+        animateAway(DialogViewCallback.DISMISSED_BY_SYSTEM_SERVER);
+    }
+
+    @Override
+    public void onAuthenticationSucceeded() {
+        announceForAccessibility(getResources().getText(getAuthenticatedAccessibilityResourceId()));
+
+        if (requiresConfirmation()) {
+            updateState(STATE_PENDING_CONFIRMATION);
+        } else {
+            mHandler.postDelayed(() -> {
+                animateAway(DialogViewCallback.DISMISSED_AUTHENTICATED);
+            }, getDelayAfterAuthenticatedDurationMs());
+
+            updateState(STATE_AUTHENTICATED);
+        }
+    }
+
+
+    @Override
+    public void onAuthenticationFailed(String message) {
+        updateState(STATE_ERROR);
+        showTemporaryMessage(message);
+    }
+
     /**
      * Transient help message (acquire) is received, dialog stays showing. Sensor stays in
      * "authenticating" state.
      * @param message
      */
-    public void onHelpReceived(String message) {
-        updateState(STATE_ERROR);
-        showTemporaryMessage(message);
-    }
-
-    public void onAuthenticationFailed(String message) {
+    @Override
+    public void onHelp(String message) {
         updateState(STATE_ERROR);
         showTemporaryMessage(message);
     }
@@ -471,14 +597,61 @@
      * Hard error is received, dialog will be dismissed soon.
      * @param error
      */
-    public void onErrorReceived(String error) {
+    @Override
+    public void onError(String error) {
         updateState(STATE_ERROR);
         showTemporaryMessage(error);
         showTryAgainButton(false /* show */);
-        mCallback.onErrorShown(); // TODO: Split between fp and face
+
+        mHandler.postDelayed(() -> {
+            animateAway(DialogViewCallback.DISMISSED_ERROR);
+        }, BiometricPrompt.HIDE_DIALOG_DELAY);
     }
 
-    public void updateState(int newState) {
+
+    @Override
+    public void onSaveState(Bundle bundle) {
+        bundle.putInt(KEY_TRY_AGAIN_VISIBILITY, mTryAgainButton.getVisibility());
+        bundle.putInt(KEY_CONFIRM_VISIBILITY, mPositiveButton.getVisibility());
+        bundle.putBoolean(KEY_CONFIRM_ENABLED, mPositiveButton.isEnabled());
+        bundle.putInt(KEY_STATE, mState);
+        bundle.putInt(KEY_ERROR_TEXT_VISIBILITY, mErrorText.getVisibility());
+        bundle.putCharSequence(KEY_ERROR_TEXT_STRING, mErrorText.getText());
+        bundle.putBoolean(KEY_ERROR_TEXT_IS_TEMPORARY, mHandler.hasMessages(MSG_RESET_MESSAGE));
+        bundle.putInt(KEY_ERROR_TEXT_COLOR, mErrorText.getCurrentTextColor());
+    }
+
+    @Override
+    public void restoreState(Bundle bundle) {
+        mRestoredState = bundle;
+        final int tryAgainVisibility = bundle.getInt(KEY_TRY_AGAIN_VISIBILITY);
+        mTryAgainButton.setVisibility(tryAgainVisibility);
+        final int confirmVisibility = bundle.getInt(KEY_CONFIRM_VISIBILITY);
+        mPositiveButton.setVisibility(confirmVisibility);
+        final boolean confirmEnabled = bundle.getBoolean(KEY_CONFIRM_ENABLED);
+        mPositiveButton.setEnabled(confirmEnabled);
+        mState = bundle.getInt(KEY_STATE);
+        mErrorText.setText(bundle.getCharSequence(KEY_ERROR_TEXT_STRING));
+        mErrorText.setContentDescription(bundle.getCharSequence(KEY_ERROR_TEXT_STRING));
+        final int errorTextVisibility = bundle.getInt(KEY_ERROR_TEXT_VISIBILITY);
+        mErrorText.setVisibility(errorTextVisibility);
+        if (errorTextVisibility == View.INVISIBLE || tryAgainVisibility == View.INVISIBLE
+                || confirmVisibility == View.INVISIBLE) {
+            announceAccessibilityEvent();
+        }
+        mErrorText.setTextColor(bundle.getInt(KEY_ERROR_TEXT_COLOR));
+        if (bundle.getBoolean(KEY_ERROR_TEXT_IS_TEMPORARY)) {
+            mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_RESET_MESSAGE),
+                    BiometricPrompt.HIDE_DIALOG_DELAY);
+        }
+    }
+
+    @Override
+    public String getOpPackageName() {
+        return mOpPackageName;
+    }
+
+    protected void updateState(int newState) {
         if (newState == STATE_PENDING_CONFIRMATION) {
             mHandler.removeMessages(MSG_RESET_MESSAGE);
             mErrorText.setTextColor(mTextColor);
@@ -505,48 +678,24 @@
         mState = newState;
     }
 
-    public void showTryAgainButton(boolean show) {
+    protected void showTryAgainButton(boolean show) {
     }
 
-    public void onDialogAnimatedIn() {
+    protected void onDialogAnimatedIn() {
         mCompletedAnimatingIn = true;
 
         if (mPendingDismissDialog) {
             Log.d(TAG, "onDialogAnimatedIn(): mPendingDismissDialog=true, dismissing now");
-            startDismiss();
+            animateAway(false /* sendReason */, 0);
             mPendingDismissDialog = false;
         }
     }
 
-    public void restoreState(Bundle bundle) {
-        mRestoredState = bundle;
-        final int tryAgainVisibility = bundle.getInt(KEY_TRY_AGAIN_VISIBILITY);
-        mTryAgainButton.setVisibility(tryAgainVisibility);
-        final int confirmVisibility = bundle.getInt(KEY_CONFIRM_VISIBILITY);
-        mPositiveButton.setVisibility(confirmVisibility);
-        final boolean confirmEnabled = bundle.getBoolean(KEY_CONFIRM_ENABLED);
-        mPositiveButton.setEnabled(confirmEnabled);
-        mState = bundle.getInt(KEY_STATE);
-        mErrorText.setText(bundle.getCharSequence(KEY_ERROR_TEXT_STRING));
-        mErrorText.setContentDescription(bundle.getCharSequence(KEY_ERROR_TEXT_STRING));
-        final int errorTextVisibility = bundle.getInt(KEY_ERROR_TEXT_VISIBILITY);
-        mErrorText.setVisibility(errorTextVisibility);
-        if (errorTextVisibility == View.INVISIBLE || tryAgainVisibility == View.INVISIBLE
-                || confirmVisibility == View.INVISIBLE) {
-            announceAccessibilityEvent();
-        }
-        mErrorText.setTextColor(bundle.getInt(KEY_ERROR_TEXT_COLOR));
-        if (bundle.getBoolean(KEY_ERROR_TEXT_IS_TEMPORARY)) {
-            mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_RESET_MESSAGE),
-                    BiometricPrompt.HIDE_DIALOG_DELAY);
-        }
-    }
-
-    protected int getState() {
-        return mState;
-    }
-
-    public WindowManager.LayoutParams getLayoutParams() {
+    /**
+     * @param windowToken token for the window
+     * @return
+     */
+    public static WindowManager.LayoutParams getLayoutParams(IBinder windowToken) {
         final WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
                 ViewGroup.LayoutParams.MATCH_PARENT,
                 ViewGroup.LayoutParams.MATCH_PARENT,
@@ -555,7 +704,7 @@
                 PixelFormat.TRANSLUCENT);
         lp.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS;
         lp.setTitle("BiometricDialogView");
-        lp.token = mWindowToken;
+        lp.token = windowToken;
         return lp;
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/FaceDialogView.java b/packages/SystemUI/src/com/android/systemui/biometrics/ui/FaceDialogView.java
similarity index 97%
rename from packages/SystemUI/src/com/android/systemui/biometrics/FaceDialogView.java
rename to packages/SystemUI/src/com/android/systemui/biometrics/ui/FaceDialogView.java
index ae6cb5c..bd87148 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/FaceDialogView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/FaceDialogView.java
@@ -11,10 +11,10 @@
  * 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
+ * limitations under the License.
  */
 
-package com.android.systemui.biometrics;
+package com.android.systemui.biometrics.ui;
 
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
@@ -33,7 +33,9 @@
 import android.view.View;
 import android.view.ViewOutlineProvider;
 
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.systemui.R;
+import com.android.systemui.biometrics.DialogViewCallback;
 
 /**
  * This class loads the view for the system-provided dialog. The view consists of:
@@ -52,7 +54,8 @@
     private static final int TEXT_ANIMATE_DISTANCE = 32; // dp
 
     private static final int SIZE_UNKNOWN = 0;
-    private static final int SIZE_SMALL = 1;
+    @VisibleForTesting
+    static final int SIZE_SMALL = 1;
     private static final int SIZE_GROWING = 2;
     private static final int SIZE_BIG = 3;
 
@@ -152,13 +155,13 @@
         announceAccessibilityEvent();
     };
 
-    public FaceDialogView(Context context,
-            DialogViewCallback callback) {
-        super(context, callback);
+    protected FaceDialogView(Context context, DialogViewCallback callback, Injector injector) {
+        super(context, callback, injector);
         mIconController = new IconController();
     }
 
-    private void updateSize(int newSize) {
+    @VisibleForTesting
+    void updateSize(int newSize) {
         final float padding = dpToPixels(IMPLICIT_Y_PADDING);
         final float iconSmallPositionY = mDialog.getHeight() - mBiometricIcon.getHeight() - padding;
 
@@ -339,8 +342,8 @@
     }
 
     @Override
-    public void onErrorReceived(String error) {
-        super.onErrorReceived(error);
+    public void onError(String error) {
+        super.onError(error);
         // All error messages will cause the dialog to go from small -> big. Error messages
         // are messages such as lockout, auth failed, etc.
         if (mSize == SIZE_SMALL) {
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/FingerprintDialogView.java b/packages/SystemUI/src/com/android/systemui/biometrics/ui/FingerprintDialogView.java
similarity index 93%
rename from packages/SystemUI/src/com/android/systemui/biometrics/FingerprintDialogView.java
rename to packages/SystemUI/src/com/android/systemui/biometrics/ui/FingerprintDialogView.java
index 183933e..e597080 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/FingerprintDialogView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/FingerprintDialogView.java
@@ -11,10 +11,10 @@
  * 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
+ * limitations under the License.
  */
 
-package com.android.systemui.biometrics;
+package com.android.systemui.biometrics.ui;
 
 import android.content.Context;
 import android.graphics.drawable.AnimatedVectorDrawable;
@@ -22,6 +22,7 @@
 import android.util.Log;
 
 import com.android.systemui.R;
+import com.android.systemui.biometrics.DialogViewCallback;
 
 /**
  * This class loads the view for the system-provided dialog. The view consists of:
@@ -32,9 +33,9 @@
 
     private static final String TAG = "FingerprintDialogView";
 
-    public FingerprintDialogView(Context context,
-            DialogViewCallback callback) {
-        super(context, callback);
+    protected FingerprintDialogView(Context context, DialogViewCallback callback,
+            Injector injector) {
+        super(context, callback, injector);
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/glwallpaper/EglHelper.java b/packages/SystemUI/src/com/android/systemui/glwallpaper/EglHelper.java
index d7411260..aac721e 100644
--- a/packages/SystemUI/src/com/android/systemui/glwallpaper/EglHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/glwallpaper/EglHelper.java
@@ -146,7 +146,13 @@
      * @return true if EglSurface is ready.
      */
     public boolean createEglSurface(SurfaceHolder surfaceHolder) {
-        mEglSurface = eglCreateWindowSurface(mEglDisplay, mEglConfig, surfaceHolder, null, 0);
+        if (hasEglDisplay()) {
+            mEglSurface = eglCreateWindowSurface(mEglDisplay, mEglConfig, surfaceHolder, null, 0);
+        } else {
+            Log.w(TAG, "mEglDisplay is null");
+            return false;
+        }
+
         if (mEglSurface == null || mEglSurface == EGL_NO_SURFACE) {
             Log.w(TAG, "createWindowSurface failed: " + GLUtils.getEGLErrorString(eglGetError()));
             return false;
@@ -186,7 +192,13 @@
     public boolean createEglContext() {
         int[] attrib_list = new int[] {EGL_CONTEXT_CLIENT_VERSION, 2,
                 EGL_CONTEXT_PRIORITY_LEVEL_IMG, EGL_CONTEXT_PRIORITY_LOW_IMG, EGL_NONE};
-        mEglContext = eglCreateContext(mEglDisplay, mEglConfig, EGL_NO_CONTEXT, attrib_list, 0);
+        if (hasEglDisplay()) {
+            mEglContext = eglCreateContext(mEglDisplay, mEglConfig, EGL_NO_CONTEXT, attrib_list, 0);
+        } else {
+            Log.w(TAG, "mEglDisplay is null");
+            return false;
+        }
+
         if (mEglContext == EGL_NO_CONTEXT) {
             Log.w(TAG, "eglCreateContext failed: " + GLUtils.getEGLErrorString(eglGetError()));
             return false;
@@ -213,6 +225,14 @@
     }
 
     /**
+     * Check if we have EglDisplay.
+     * @return true if EglDisplay is ready.
+     */
+    public boolean hasEglDisplay() {
+        return mEglDisplay != null;
+    }
+
+    /**
      * Swap buffer to display.
      * @return true if swap successfully.
      */
@@ -235,7 +255,9 @@
         if (hasEglContext()) {
             destroyEglContext();
         }
-        eglTerminate(mEglDisplay);
+        if (hasEglDisplay()) {
+            eglTerminate(mEglDisplay);
+        }
         mEglReady = false;
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
index fa0fe13..134d4b8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
@@ -271,7 +271,7 @@
         default void onRotationProposal(int rotation, boolean isValid) { }
 
         default void showBiometricDialog(Bundle bundle, IBiometricServiceReceiverInternal receiver,
-                int type, boolean requireConfirmation, int userId) { }
+                int type, boolean requireConfirmation, int userId, String opPackageName) { }
         default void onBiometricAuthenticated(boolean authenticated, String failureReason) { }
         default void onBiometricHelp(String message) { }
         default void onBiometricError(String error) { }
@@ -741,7 +741,7 @@
 
     @Override
     public void showBiometricDialog(Bundle bundle, IBiometricServiceReceiverInternal receiver,
-            int type, boolean requireConfirmation, int userId) {
+            int type, boolean requireConfirmation, int userId, String opPackageName) {
         synchronized (mLock) {
             SomeArgs args = SomeArgs.obtain();
             args.arg1 = bundle;
@@ -749,6 +749,7 @@
             args.argi1 = type;
             args.arg3 = requireConfirmation;
             args.argi2 = userId;
+            args.arg4 = opPackageName;
             mHandler.obtainMessage(MSG_BIOMETRIC_SHOW, args)
                     .sendToTarget();
         }
@@ -1036,7 +1037,8 @@
                                 (IBiometricServiceReceiverInternal) someArgs.arg2,
                                 someArgs.argi1 /* type */,
                                 (boolean) someArgs.arg3 /* requireConfirmation */,
-                                someArgs.argi2 /* userId */);
+                                someArgs.argi2 /* userId */,
+                                (String) someArgs.arg4 /* opPackageName */);
                     }
                     someArgs.recycle();
                     break;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/OperatorNameView.java b/packages/SystemUI/src/com/android/systemui/statusbar/OperatorNameView.java
index d1b3c3c..2a5ccdb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/OperatorNameView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/OperatorNameView.java
@@ -134,7 +134,7 @@
 
     private void updateText() {
         CharSequence displayText = null;
-        List<SubscriptionInfo> subs = mKeyguardUpdateMonitor.getSubscriptionInfo(false);
+        List<SubscriptionInfo> subs = mKeyguardUpdateMonitor.getFilteredSubscriptionInfo(false);
         final int N = subs.size();
         for (int i = 0; i < N; i++) {
             int subId = subs.get(i).getSubscriptionId();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/DynamicPrivacyController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/DynamicPrivacyController.java
index d9328fa..e4bd4fa 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/DynamicPrivacyController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/DynamicPrivacyController.java
@@ -107,7 +107,7 @@
      */
     public boolean isInLockedDownShade() {
         if (!mStatusBarKeyguardViewManager.isShowing()
-                || !mStatusBarKeyguardViewManager.isSecure()) {
+                || !mUnlockMethodCache.isMethodSecure()) {
             return false;
         }
         int state = mStateController.getState();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
index c4d346c..dc9b373 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
@@ -77,6 +77,7 @@
                 }
             };
     private final Runnable mRemoveViewRunnable = this::removeView;
+    private final KeyguardBypassController mKeyguardBypassController;
     protected KeyguardHostView mKeyguardView;
     private final Runnable mResetRunnable = ()-> {
         if (mKeyguardView != null) {
@@ -97,7 +98,8 @@
             LockPatternUtils lockPatternUtils, ViewGroup container,
             DismissCallbackRegistry dismissCallbackRegistry, FalsingManager falsingManager,
             BouncerExpansionCallback expansionCallback, UnlockMethodCache unlockMethodCache,
-            KeyguardUpdateMonitor keyguardUpdateMonitor, Handler handler) {
+            KeyguardUpdateMonitor keyguardUpdateMonitor,
+            KeyguardBypassController keyguardBypassController, Handler handler) {
         mContext = context;
         mCallback = callback;
         mLockPatternUtils = lockPatternUtils;
@@ -109,6 +111,7 @@
         mHandler = handler;
         mUnlockMethodCache = unlockMethodCache;
         mKeyguardUpdateMonitor.registerCallback(mUpdateMonitorCallback);
+        mKeyguardBypassController = keyguardBypassController;
     }
 
     public void show(boolean resetSecuritySelection) {
@@ -171,7 +174,8 @@
         // Split up the work over multiple frames.
         DejankUtils.removeCallbacks(mResetRunnable);
         if (mUnlockMethodCache.isFaceAuthEnabled() && !needsFullscreenBouncer()
-                && !mKeyguardUpdateMonitor.userNeedsStrongAuth()) {
+                && !mKeyguardUpdateMonitor.userNeedsStrongAuth()
+                && !mKeyguardBypassController.getBypassEnabled()) {
             mHandler.postDelayed(mShowRunnable, BOUNCER_FACE_DELAY);
         } else {
             DejankUtils.postAfterTraversal(mShowRunnable);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
index 3508c90..ccb85fa 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -221,7 +221,7 @@
         mBiometricUnlockController = biometricUnlockController;
         mBouncer = SystemUIFactory.getInstance().createKeyguardBouncer(mContext,
                 mViewMediatorCallback, mLockPatternUtils, container, dismissCallbackRegistry,
-                mExpansionCallback, falsingManager);
+                mExpansionCallback, falsingManager, bypassController);
         mNotificationPanelView = notificationPanelView;
         notificationPanelView.addExpansionListener(this);
         mBypassController = bypassController;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/EmergencyCryptkeeperText.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/EmergencyCryptkeeperText.java
index 0d6178b..f2c0434 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/EmergencyCryptkeeperText.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/EmergencyCryptkeeperText.java
@@ -97,7 +97,7 @@
         boolean allSimsMissing = true;
         CharSequence displayText = null;
 
-        List<SubscriptionInfo> subs = mKeyguardUpdateMonitor.getSubscriptionInfo(false);
+        List<SubscriptionInfo> subs = mKeyguardUpdateMonitor.getFilteredSubscriptionInfo(false);
         final int N = subs.size();
         for (int i = 0; i < N; i++) {
             int subId = subs.get(i).getSubscriptionId();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java
index e75365e..7acf4fd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java
@@ -537,8 +537,14 @@
                 return mConfig.nr5GIconMap.get(Config.NR_CONNECTED);
             }
         } else if (nrState == NetworkRegistrationInfo.NR_STATE_NOT_RESTRICTED) {
-            if (mConfig.nr5GIconMap.containsKey(Config.NR_NOT_RESTRICTED)) {
-                return mConfig.nr5GIconMap.get(Config.NR_NOT_RESTRICTED);
+            if (mCurrentState.activityDormant) {
+                if (mConfig.nr5GIconMap.containsKey(Config.NR_NOT_RESTRICTED_RRC_IDLE)) {
+                    return mConfig.nr5GIconMap.get(Config.NR_NOT_RESTRICTED_RRC_IDLE);
+                }
+            } else {
+                if (mConfig.nr5GIconMap.containsKey(Config.NR_NOT_RESTRICTED_RRC_CON)) {
+                    return mConfig.nr5GIconMap.get(Config.NR_NOT_RESTRICTED_RRC_CON);
+                }
             }
         } else if (nrState == NetworkRegistrationInfo.NR_STATE_RESTRICTED) {
             if (mConfig.nr5GIconMap.containsKey(Config.NR_RESTRICTED)) {
@@ -559,6 +565,8 @@
                 || activity == TelephonyManager.DATA_ACTIVITY_IN;
         mCurrentState.activityOut = activity == TelephonyManager.DATA_ACTIVITY_INOUT
                 || activity == TelephonyManager.DATA_ACTIVITY_OUT;
+        mCurrentState.activityDormant = activity == TelephonyManager.DATA_ACTIVITY_DORMANT;
+
         notifyListenersIfNecessary();
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
index 04f96a4..292571e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
@@ -1112,8 +1112,9 @@
     static class Config {
         static final int NR_CONNECTED_MMWAVE = 1;
         static final int NR_CONNECTED = 2;
-        static final int NR_NOT_RESTRICTED = 3;
-        static final int NR_RESTRICTED = 4;
+        static final int NR_NOT_RESTRICTED_RRC_IDLE = 3;
+        static final int NR_NOT_RESTRICTED_RRC_CON = 4;
+        static final int NR_RESTRICTED = 5;
 
         Map<Integer, MobileIconGroup> nr5GIconMap = new HashMap<>();
 
@@ -1133,10 +1134,11 @@
          */
         private static final Map<String, Integer> NR_STATUS_STRING_TO_INDEX;
         static {
-            NR_STATUS_STRING_TO_INDEX = new HashMap<>(4);
+            NR_STATUS_STRING_TO_INDEX = new HashMap<>(5);
             NR_STATUS_STRING_TO_INDEX.put("connected_mmwave", NR_CONNECTED_MMWAVE);
             NR_STATUS_STRING_TO_INDEX.put("connected", NR_CONNECTED);
-            NR_STATUS_STRING_TO_INDEX.put("not_restricted", NR_NOT_RESTRICTED);
+            NR_STATUS_STRING_TO_INDEX.put("not_restricted_rrc_idle", NR_NOT_RESTRICTED_RRC_IDLE);
+            NR_STATUS_STRING_TO_INDEX.put("not_restricted_rrc_con", NR_NOT_RESTRICTED_RRC_CON);
             NR_STATUS_STRING_TO_INDEX.put("restricted", NR_RESTRICTED);
         }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SignalController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SignalController.java
index 9ec30d4..abe3f2c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SignalController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SignalController.java
@@ -258,6 +258,7 @@
         boolean enabled;
         boolean activityIn;
         boolean activityOut;
+        public boolean activityDormant;
         int level;
         IconGroup iconGroup;
         int inetCondition;
@@ -274,6 +275,7 @@
             inetCondition = state.inetCondition;
             activityIn = state.activityIn;
             activityOut = state.activityOut;
+            activityDormant = state.activityDormant;
             rssi = state.rssi;
             time = state.time;
         }
@@ -297,6 +299,7 @@
                     .append("iconGroup=").append(iconGroup).append(',')
                     .append("activityIn=").append(activityIn).append(',')
                     .append("activityOut=").append(activityOut).append(',')
+                    .append("activityDormant=").append(activityDormant).append(',')
                     .append("rssi=").append(rssi).append(',')
                     .append("lastModified=").append(DateFormat.format("MM-dd HH:mm:ss", time));
         }
@@ -314,6 +317,7 @@
                     && other.iconGroup == iconGroup
                     && other.activityIn == activityIn
                     && other.activityOut == activityOut
+                    && other.activityDormant == activityDormant
                     && other.rssi == rssi;
         }
     }
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/CarrierTextControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/CarrierTextControllerTest.java
index 0044ca7..1ae1b97 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/CarrierTextControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/CarrierTextControllerTest.java
@@ -70,15 +70,10 @@
     private static final CharSequence AIRPLANE_MODE_TEXT = "Airplane mode";
     private static final String TEST_CARRIER = "TEST_CARRIER";
     private static final String TEST_CARRIER_2 = "TEST_CARRIER_2";
-    private static final String TEST_GROUP_UUID = "59b5c870-fc4c-47a4-a99e-9db826b48b24";
     private static final int TEST_CARRIER_ID = 1;
     private static final SubscriptionInfo TEST_SUBSCRIPTION = new SubscriptionInfo(0, "", 0,
             TEST_CARRIER, TEST_CARRIER, NAME_SOURCE_DEFAULT_SOURCE, 0xFFFFFF, "",
-            DATA_ROAMING_DISABLE, null, null, null, null, false, null, "", false, TEST_GROUP_UUID,
-            TEST_CARRIER_ID, 0);
-    private static final SubscriptionInfo TEST_SUBSCRIPTION_2 = new SubscriptionInfo(0, "", 0,
-            TEST_CARRIER, TEST_CARRIER_2, NAME_SOURCE_DEFAULT_SOURCE, 0xFFFFFF, "",
-            DATA_ROAMING_DISABLE, null, null, null, null, false, null, "", true, TEST_GROUP_UUID,
+            DATA_ROAMING_DISABLE, null, null, null, null, false, null, "", false, null,
             TEST_CARRIER_ID, 0);
     private static final SubscriptionInfo TEST_SUBSCRIPTION_ROAMING = new SubscriptionInfo(0, "", 0,
             TEST_CARRIER, TEST_CARRIER, NAME_SOURCE_DEFAULT_SOURCE, 0xFFFFFF, "",
@@ -125,7 +120,6 @@
                 mKeyguardUpdateMonitor);
         // This should not start listening on any of the real dependencies
         mCarrierTextController.setListening(mCarrierTextCallback);
-        mCarrierTextController.updateDisplayOpportunisticSubscriptionCarrierText(false);
     }
 
     @Test
@@ -134,7 +128,7 @@
         reset(mCarrierTextCallback);
         List<SubscriptionInfo> list = new ArrayList<>();
         list.add(TEST_SUBSCRIPTION);
-        when(mKeyguardUpdateMonitor.getSubscriptionInfo(anyBoolean())).thenReturn(list);
+        when(mKeyguardUpdateMonitor.getFilteredSubscriptionInfo(anyBoolean())).thenReturn(list);
         when(mKeyguardUpdateMonitor.getSimState(0)).thenReturn(IccCardConstants.State.READY);
         mKeyguardUpdateMonitor.mServiceStates = new HashMap<>();
 
@@ -154,7 +148,7 @@
         reset(mCarrierTextCallback);
         List<SubscriptionInfo> list = new ArrayList<>();
         list.add(TEST_SUBSCRIPTION);
-        when(mKeyguardUpdateMonitor.getSubscriptionInfo(anyBoolean())).thenReturn(list);
+        when(mKeyguardUpdateMonitor.getFilteredSubscriptionInfo(anyBoolean())).thenReturn(list);
         when(mKeyguardUpdateMonitor.getSimState(0)).thenReturn(IccCardConstants.State.READY);
         when(mKeyguardUpdateMonitor.getSimState(1)).thenReturn(
                 IccCardConstants.State.CARD_IO_ERROR);
@@ -178,7 +172,7 @@
     @Test
     public void testWrongSlots() {
         reset(mCarrierTextCallback);
-        when(mKeyguardUpdateMonitor.getSubscriptionInfo(anyBoolean())).thenReturn(
+        when(mKeyguardUpdateMonitor.getFilteredSubscriptionInfo(anyBoolean())).thenReturn(
                 new ArrayList<>());
         when(mKeyguardUpdateMonitor.getSimState(anyInt())).thenReturn(
                 IccCardConstants.State.CARD_IO_ERROR);
@@ -192,7 +186,7 @@
     @Test
     public void testMoreSlotsThanSubs() {
         reset(mCarrierTextCallback);
-        when(mKeyguardUpdateMonitor.getSubscriptionInfo(anyBoolean())).thenReturn(
+        when(mKeyguardUpdateMonitor.getFilteredSubscriptionInfo(anyBoolean())).thenReturn(
                 new ArrayList<>());
 
         // STOPSHIP(b/130246708) This line makes sure that SubscriptionManager provides the
@@ -242,7 +236,7 @@
         List<SubscriptionInfo> list = new ArrayList<>();
         list.add(TEST_SUBSCRIPTION);
         when(mKeyguardUpdateMonitor.getSimState(anyInt())).thenReturn(IccCardConstants.State.READY);
-        when(mKeyguardUpdateMonitor.getSubscriptionInfo(anyBoolean())).thenReturn(list);
+        when(mKeyguardUpdateMonitor.getFilteredSubscriptionInfo(anyBoolean())).thenReturn(list);
 
         mKeyguardUpdateMonitor.mServiceStates = new HashMap<>();
 
@@ -266,7 +260,7 @@
         List<SubscriptionInfo> list = new ArrayList<>();
         list.add(TEST_SUBSCRIPTION_ROAMING);
         when(mKeyguardUpdateMonitor.getSimState(anyInt())).thenReturn(IccCardConstants.State.READY);
-        when(mKeyguardUpdateMonitor.getSubscriptionInfo(anyBoolean())).thenReturn(list);
+        when(mKeyguardUpdateMonitor.getFilteredSubscriptionInfo(anyBoolean())).thenReturn(list);
 
         mKeyguardUpdateMonitor.mServiceStates = new HashMap<>();
 
@@ -287,7 +281,7 @@
     @Test
     public void testCreateInfo_noSubscriptions() {
         reset(mCarrierTextCallback);
-        when(mKeyguardUpdateMonitor.getSubscriptionInfo(anyBoolean())).thenReturn(
+        when(mKeyguardUpdateMonitor.getFilteredSubscriptionInfo(anyBoolean())).thenReturn(
                 new ArrayList<>());
 
         ArgumentCaptor<CarrierTextController.CarrierTextCallbackInfo> captor =
@@ -311,7 +305,7 @@
         list.add(TEST_SUBSCRIPTION);
         list.add(TEST_SUBSCRIPTION);
         when(mKeyguardUpdateMonitor.getSimState(anyInt())).thenReturn(IccCardConstants.State.READY);
-        when(mKeyguardUpdateMonitor.getSubscriptionInfo(anyBoolean())).thenReturn(list);
+        when(mKeyguardUpdateMonitor.getFilteredSubscriptionInfo(anyBoolean())).thenReturn(list);
 
         mKeyguardUpdateMonitor.mServiceStates = new HashMap<>();
 
@@ -336,7 +330,7 @@
         when(mKeyguardUpdateMonitor.getSimState(anyInt()))
                 .thenReturn(IccCardConstants.State.READY)
                 .thenReturn(IccCardConstants.State.NOT_READY);
-        when(mKeyguardUpdateMonitor.getSubscriptionInfo(anyBoolean())).thenReturn(list);
+        when(mKeyguardUpdateMonitor.getFilteredSubscriptionInfo(anyBoolean())).thenReturn(list);
 
         mKeyguardUpdateMonitor.mServiceStates = new HashMap<>();
 
@@ -361,7 +355,7 @@
         when(mKeyguardUpdateMonitor.getSimState(anyInt()))
                 .thenReturn(IccCardConstants.State.NOT_READY)
                 .thenReturn(IccCardConstants.State.READY);
-        when(mKeyguardUpdateMonitor.getSubscriptionInfo(anyBoolean())).thenReturn(list);
+        when(mKeyguardUpdateMonitor.getFilteredSubscriptionInfo(anyBoolean())).thenReturn(list);
 
         mKeyguardUpdateMonitor.mServiceStates = new HashMap<>();
 
@@ -388,7 +382,7 @@
                 .thenReturn(IccCardConstants.State.READY)
                 .thenReturn(IccCardConstants.State.NOT_READY)
                 .thenReturn(IccCardConstants.State.READY);
-        when(mKeyguardUpdateMonitor.getSubscriptionInfo(anyBoolean())).thenReturn(list);
+        when(mKeyguardUpdateMonitor.getFilteredSubscriptionInfo(anyBoolean())).thenReturn(list);
         mKeyguardUpdateMonitor.mServiceStates = new HashMap<>();
 
         ArgumentCaptor<CarrierTextController.CarrierTextCallbackInfo> captor =
@@ -403,30 +397,6 @@
                 captor.getValue().carrierText);
     }
 
-    @Test
-    public void testCarrierText_GroupedSubWithOpportunisticCarrierText() {
-        reset(mCarrierTextCallback);
-        List<SubscriptionInfo> list = new ArrayList<>();
-        list.add(TEST_SUBSCRIPTION);
-        list.add(TEST_SUBSCRIPTION_2);
-        when(mKeyguardUpdateMonitor.getSimState(anyInt()))
-            .thenReturn(IccCardConstants.State.READY);
-
-        mKeyguardUpdateMonitor.mServiceStates = new HashMap<>();
-        mCarrierTextController.updateDisplayOpportunisticSubscriptionCarrierText(true);
-        when(mSubscriptionManager.getActiveSubscriptionInfoList(anyBoolean())).thenReturn(list);
-
-        ArgumentCaptor<CarrierTextController.CarrierTextCallbackInfo> captor =
-                ArgumentCaptor.forClass(
-                CarrierTextController.CarrierTextCallbackInfo.class);
-
-        mCarrierTextController.updateCarrierText();
-        mTestableLooper.processAllMessages();
-        verify(mCarrierTextCallback).updateCarrierInfo(captor.capture());
-
-        assertEquals(TEST_CARRIER_2, captor.getValue().carrierText);
-    }
-
     public static class TestCarrierTextController extends CarrierTextController {
         private KeyguardUpdateMonitor mKUM;
 
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
index db6177a..a3cb6c0 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
@@ -16,15 +16,18 @@
 
 package com.android.keyguard;
 
+import static android.telephony.SubscriptionManager.DATA_ROAMING_DISABLE;
+import static android.telephony.SubscriptionManager.NAME_SOURCE_DEFAULT_SOURCE;
+
 import static com.google.common.truth.Truth.assertThat;
 
 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.clearInvocations;
 import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.reset;
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
@@ -42,6 +45,7 @@
 import android.os.Bundle;
 import android.os.UserManager;
 import android.telephony.ServiceState;
+import android.telephony.SubscriptionInfo;
 import android.telephony.SubscriptionManager;
 import android.test.suitebuilder.annotation.SmallTest;
 import android.testing.AndroidTestingRunner;
@@ -62,6 +66,8 @@
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
+import java.util.ArrayList;
+import java.util.List;
 import java.util.concurrent.atomic.AtomicBoolean;
 
 @SmallTest
@@ -73,7 +79,18 @@
 // new tests.
 @RunWithLooper(setAsMainLooper = true)
 public class KeyguardUpdateMonitorTest extends SysuiTestCase {
-
+    private static final String TEST_CARRIER = "TEST_CARRIER";
+    private static final String TEST_CARRIER_2 = "TEST_CARRIER_2";
+    private static final int TEST_CARRIER_ID = 1;
+    private static final String TEST_GROUP_UUID = "59b5c870-fc4c-47a4-a99e-9db826b48b24";
+    private static final SubscriptionInfo TEST_SUBSCRIPTION = new SubscriptionInfo(1, "", 0,
+            TEST_CARRIER, TEST_CARRIER, NAME_SOURCE_DEFAULT_SOURCE, 0xFFFFFF, "",
+            DATA_ROAMING_DISABLE, null, null, null, null, false, null, "", false, TEST_GROUP_UUID,
+            TEST_CARRIER_ID, 0);
+    private static final SubscriptionInfo TEST_SUBSCRIPTION_2 = new SubscriptionInfo(2, "", 0,
+            TEST_CARRIER, TEST_CARRIER_2, NAME_SOURCE_DEFAULT_SOURCE, 0xFFFFFF, "",
+            DATA_ROAMING_DISABLE, null, null, null, null, false, null, "", true, TEST_GROUP_UUID,
+            TEST_CARRIER_ID, 0);
     @Mock
     private KeyguardUpdateMonitor.StrongAuthTracker mStrongAuthTracker;
     @Mock
@@ -92,6 +109,8 @@
     private DevicePolicyManager mDevicePolicyManager;
     @Mock
     private KeyguardBypassController mKeyguardBypassController;
+    @Mock
+    private SubscriptionManager mSubscriptionManager;
     private TestableLooper mTestableLooper;
     private TestableKeyguardUpdateMonitor mKeyguardUpdateMonitor;
 
@@ -119,6 +138,7 @@
         context.addMockSystemService(FaceManager.class, mFaceManager);
         context.addMockSystemService(UserManager.class, mUserManager);
         context.addMockSystemService(DevicePolicyManager.class, mDevicePolicyManager);
+        context.addMockSystemService(SubscriptionManager.class, mSubscriptionManager);
 
         mTestableLooper = TestableLooper.get(this);
         mKeyguardUpdateMonitor = new TestableKeyguardUpdateMonitor(context);
@@ -441,6 +461,22 @@
         assertThat(mKeyguardUpdateMonitor.getUserCanSkipBouncer(user)).isTrue();
     }
 
+    @Test
+    public void testGetSubscriptionInfo_whenInGroupedSubWithOpportunistic() {
+        List<SubscriptionInfo> list = new ArrayList<>();
+        list.add(TEST_SUBSCRIPTION);
+        list.add(TEST_SUBSCRIPTION_2);
+        when(mSubscriptionManager.getActiveSubscriptionInfoList(anyBoolean())).thenReturn(list);
+        mKeyguardUpdateMonitor.mPhoneStateListener.onActiveDataSubscriptionIdChanged(
+                TEST_SUBSCRIPTION_2.getSubscriptionId());
+        mTestableLooper.processAllMessages();
+
+        List<SubscriptionInfo> listToVerify = mKeyguardUpdateMonitor
+                .getFilteredSubscriptionInfo(false);
+        assertThat(listToVerify.size()).isEqualTo(1);
+        assertThat(listToVerify.get(0)).isEqualTo(TEST_SUBSCRIPTION_2);
+    }
+
     private Intent putPhoneInfo(Intent intent, Bundle data, Boolean simInited) {
         int subscription = simInited
                 ? 1/* mock subid=1 */ : SubscriptionManager.DUMMY_SUBSCRIPTION_ID_BASE;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/BiometricDialogImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/BiometricDialogImplTest.java
new file mode 100644
index 0000000..8f2f8b1
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/BiometricDialogImplTest.java
@@ -0,0 +1,329 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.biometrics;
+
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertNull;
+import static junit.framework.TestCase.assertNotNull;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.app.ActivityManager;
+import android.app.IActivityTaskManager;
+import android.content.ComponentName;
+import android.content.pm.PackageManager;
+import android.content.res.Configuration;
+import android.hardware.biometrics.BiometricPrompt;
+import android.hardware.biometrics.IBiometricServiceReceiverInternal;
+import android.os.Bundle;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableContext;
+import android.testing.TestableLooper.RunWithLooper;
+
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.statusbar.CommandQueue;
+import com.android.systemui.statusbar.phone.StatusBar;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@RunWith(AndroidTestingRunner.class)
+@RunWithLooper
+@SmallTest
+public class BiometricDialogImplTest extends SysuiTestCase {
+
+    @Mock
+    private PackageManager mPackageManager;
+    @Mock
+    private IBiometricServiceReceiverInternal mReceiver;
+    @Mock
+    private BiometricDialog mDialog1;
+    @Mock
+    private BiometricDialog mDialog2;
+
+    private TestableBiometricDialogImpl mBiometricDialogImpl;
+
+
+    @Before
+    public void setup() {
+        MockitoAnnotations.initMocks(this);
+
+        TestableContext context = spy(mContext);
+
+        mContext.putComponent(StatusBar.class, mock(StatusBar.class));
+        mContext.putComponent(CommandQueue.class, mock(CommandQueue.class));
+
+        when(context.getPackageManager()).thenReturn(mPackageManager);
+        when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_FACE))
+            .thenReturn(true);
+        when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_FINGERPRINT))
+            .thenReturn(true);
+
+        when(mDialog1.getOpPackageName()).thenReturn("Dialog1");
+        when(mDialog2.getOpPackageName()).thenReturn("Dialog2");
+
+        mBiometricDialogImpl = new TestableBiometricDialogImpl(new MockInjector());
+        mBiometricDialogImpl.mContext = context;
+        mBiometricDialogImpl.mComponents = mContext.getComponents();
+
+        mBiometricDialogImpl.start();
+    }
+
+    // Callback tests
+
+    @Test
+    public void testSendsReasonUserCanceled_whenDismissedByUserCancel() throws Exception {
+        showDialog(BiometricPrompt.TYPE_FACE);
+        mBiometricDialogImpl.onDismissed(DialogViewCallback.DISMISSED_USER_CANCELED);
+        verify(mReceiver).onDialogDismissed(BiometricPrompt.DISMISSED_REASON_USER_CANCEL);
+    }
+
+    @Test
+    public void testSendsReasonNegative_whenDismissedByButtonNegative() throws Exception {
+        showDialog(BiometricPrompt.TYPE_FACE);
+        mBiometricDialogImpl.onDismissed(DialogViewCallback.DISMISSED_BUTTON_NEGATIVE);
+        verify(mReceiver).onDialogDismissed(BiometricPrompt.DISMISSED_REASON_NEGATIVE);
+    }
+
+    @Test
+    public void testSendsReasonConfirmed_whenDismissedByButtonPositive() throws Exception {
+        showDialog(BiometricPrompt.TYPE_FACE);
+        mBiometricDialogImpl.onDismissed(DialogViewCallback.DISMISSED_BUTTON_POSITIVE);
+        verify(mReceiver).onDialogDismissed(BiometricPrompt.DISMISSED_REASON_CONFIRMED);
+    }
+
+    @Test
+    public void testSendsReasonConfirmNotRequired_whenDismissedByAuthenticated() throws Exception {
+        showDialog(BiometricPrompt.TYPE_FACE);
+        mBiometricDialogImpl.onDismissed(DialogViewCallback.DISMISSED_AUTHENTICATED);
+        verify(mReceiver).onDialogDismissed(BiometricPrompt.DISMISSED_REASON_CONFIRM_NOT_REQUIRED);
+    }
+
+    @Test
+    public void testSendsReasonError_whenDismissedByError() throws Exception {
+        showDialog(BiometricPrompt.TYPE_FACE);
+        mBiometricDialogImpl.onDismissed(DialogViewCallback.DISMISSED_ERROR);
+        verify(mReceiver).onDialogDismissed(BiometricPrompt.DISMISSED_REASON_ERROR);
+    }
+
+    @Test
+    public void testSendsReasonDismissedBySystemServer_whenDismissedByServer() throws Exception {
+        showDialog(BiometricPrompt.TYPE_FACE);
+        mBiometricDialogImpl.onDismissed(DialogViewCallback.DISMISSED_BY_SYSTEM_SERVER);
+        verify(mReceiver).onDialogDismissed(BiometricPrompt.DISMISSED_REASON_SERVER_REQUESTED);
+    }
+
+    // Statusbar tests
+
+    @Test
+    public void testShowInvoked_whenSystemRequested()
+            throws Exception {
+        showDialog(BiometricPrompt.TYPE_FACE);
+        verify(mDialog1).show(any(), eq(false) /* skipIntro */);
+    }
+
+    @Test
+    public void testOnAuthenticationSucceededInvoked_whenSystemRequested() throws Exception {
+        showDialog(BiometricPrompt.TYPE_FACE);
+        mBiometricDialogImpl.onBiometricAuthenticated(true, null /* failureReason */);
+        verify(mDialog1).onAuthenticationSucceeded();
+    }
+
+    @Test
+    public void testOnAuthenticationFailedInvoked_whenSystemRequested() throws Exception {
+        showDialog(BiometricPrompt.TYPE_FACE);
+        final String failureReason = "failure reason";
+        mBiometricDialogImpl.onBiometricAuthenticated(false, failureReason);
+
+        ArgumentCaptor<String> captor = ArgumentCaptor.forClass(String.class);
+        verify(mDialog1).onAuthenticationFailed(captor.capture());
+
+        assertEquals(captor.getValue(), failureReason);
+    }
+
+    @Test
+    public void testOnHelpInvoked_whenSystemRequested() throws Exception {
+        showDialog(BiometricPrompt.TYPE_FACE);
+        final String helpMessage = "help";
+        mBiometricDialogImpl.onBiometricHelp(helpMessage);
+
+        ArgumentCaptor<String> captor = ArgumentCaptor.forClass(String.class);
+        verify(mDialog1).onHelp(captor.capture());
+
+        assertEquals(captor.getValue(), helpMessage);
+    }
+
+    @Test
+    public void testOnErrorInvoked_whenSystemRequested() throws Exception {
+        showDialog(BiometricPrompt.TYPE_FACE);
+        final String errMessage = "error message";
+        mBiometricDialogImpl.onBiometricError(errMessage);
+
+        ArgumentCaptor<String> captor = ArgumentCaptor.forClass(String.class);
+        verify(mDialog1).onError(captor.capture());
+
+        assertEquals(captor.getValue(), errMessage);
+    }
+
+    @Test
+    public void testDismissWithoutCallbackInvoked_whenSystemRequested() throws Exception {
+        showDialog(BiometricPrompt.TYPE_FACE);
+        mBiometricDialogImpl.hideBiometricDialog();
+        verify(mDialog1).dismissFromSystemServer();
+    }
+
+    @Test
+    public void testClientNotified_whenDismissedBySystemServer() throws Exception {
+        showDialog(BiometricPrompt.TYPE_FACE);
+        mBiometricDialogImpl.hideBiometricDialog();
+        verify(mDialog1).dismissFromSystemServer();
+
+        assertNotNull(mBiometricDialogImpl.mCurrentDialog);
+        assertNotNull(mBiometricDialogImpl.mReceiver);
+    }
+
+    // Corner case tests
+
+    @Test
+    public void testShowNewDialog_beforeOldDialogDismissed_SkipsAnimations() throws Exception {
+        showDialog(BiometricPrompt.TYPE_FACE);
+        verify(mDialog1).show(any(), eq(false) /* skipIntro */);
+
+        showDialog(BiometricPrompt.TYPE_FACE);
+
+        // First dialog should be dismissed without animation
+        verify(mDialog1).dismissWithoutCallback(eq(false) /* animate */);
+
+        // Second dialog should be shown without animation
+        verify(mDialog2).show(any(), eq(true)) /* skipIntro */;
+    }
+
+    @Test
+    public void testConfigurationPersists_whenOnConfigurationChanged() throws Exception {
+        showDialog(BiometricPrompt.TYPE_FACE);
+        verify(mDialog1).show(any(), eq(false) /* skipIntro */);
+
+        mBiometricDialogImpl.onConfigurationChanged(new Configuration());
+
+        ArgumentCaptor<Bundle> captor = ArgumentCaptor.forClass(Bundle.class);
+        verify(mDialog1).onSaveState(captor.capture());
+
+        // Old dialog doesn't animate
+        verify(mDialog1).dismissWithoutCallback(eq(false /* animate */));
+
+        // Saved state is restored into new dialog
+        ArgumentCaptor<Bundle> captor2 = ArgumentCaptor.forClass(Bundle.class);
+        verify(mDialog2).restoreState(captor2.capture());
+
+        // Dialog for new configuration skips intro
+        verify(mDialog2).show(any(), eq(true) /* skipIntro */);
+
+        // TODO: This should check all values we want to save/restore
+        assertEquals(captor.getValue(), captor2.getValue());
+    }
+
+    @Test
+    public void testClientNotified_whenTaskStackChangesDuringAuthentication() throws Exception {
+        showDialog(BiometricPrompt.TYPE_FACE);
+
+        List<ActivityManager.RunningTaskInfo> tasks = new ArrayList<>();
+        ActivityManager.RunningTaskInfo taskInfo = mock(ActivityManager.RunningTaskInfo.class);
+        taskInfo.topActivity = mock(ComponentName.class);
+        when(taskInfo.topActivity.getPackageName()).thenReturn("other_package");
+        tasks.add(taskInfo);
+        when(mBiometricDialogImpl.mActivityTaskManager.getTasks(anyInt())).thenReturn(tasks);
+
+        mBiometricDialogImpl.mTaskStackListener.onTaskStackChanged();
+        waitForIdleSync();
+
+        assertNull(mBiometricDialogImpl.mCurrentDialog);
+        assertNull(mBiometricDialogImpl.mReceiver);
+        verify(mDialog1).dismissWithoutCallback(true /* animate */);
+        verify(mReceiver).onDialogDismissed(eq(BiometricPrompt.DISMISSED_REASON_USER_CANCEL));
+    }
+
+    // Helpers
+
+    private void showDialog(int type) {
+        mBiometricDialogImpl.showBiometricDialog(createTestDialogBundle(),
+                mReceiver /* receiver */,
+                type,
+                true /* requireConfirmation */,
+                0 /* userId */,
+                "testPackage");
+    }
+
+    private Bundle createTestDialogBundle() {
+        Bundle bundle = new Bundle();
+
+        bundle.putCharSequence(BiometricPrompt.KEY_TITLE, "Title");
+        bundle.putCharSequence(BiometricPrompt.KEY_SUBTITLE, "Subtitle");
+        bundle.putCharSequence(BiometricPrompt.KEY_DESCRIPTION, "Description");
+        bundle.putCharSequence(BiometricPrompt.KEY_NEGATIVE_TEXT, "Negative Button");
+
+        // RequireConfirmation is a hint to BiometricService. This can be forced to be required
+        // by user settings, and should be tested in BiometricService.
+        bundle.putBoolean(BiometricPrompt.KEY_REQUIRE_CONFIRMATION, true);
+
+        return bundle;
+    }
+
+    private final class TestableBiometricDialogImpl extends BiometricDialogImpl {
+        private int mBuildCount = 0;
+
+        public TestableBiometricDialogImpl(Injector injector) {
+            super(injector);
+        }
+
+        @Override
+        protected BiometricDialog buildDialog(Bundle biometricPromptBundle,
+                boolean requireConfirmation, int userId, int type, String opPackageName) {
+            BiometricDialog dialog;
+            if (mBuildCount == 0) {
+                dialog = mDialog1;
+            } else if (mBuildCount == 1) {
+                dialog = mDialog2;
+            } else {
+                dialog = null;
+            }
+            mBuildCount++;
+            return dialog;
+        }
+    }
+
+    private final class MockInjector extends BiometricDialogImpl.Injector {
+        @Override
+        IActivityTaskManager getActivityTaskManager() {
+            return mock(IActivityTaskManager.class);
+        }
+    }
+}
+
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/BiometricDialogViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/BiometricDialogViewTest.java
new file mode 100644
index 0000000..bbdd837
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/BiometricDialogViewTest.java
@@ -0,0 +1,204 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.biometrics.ui;
+
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertNotSame;
+import static junit.framework.Assert.assertTrue;
+
+import static org.mockito.Mockito.spy;
+
+import android.app.admin.DevicePolicyManager;
+import android.content.Context;
+import android.hardware.biometrics.BiometricPrompt;
+import android.os.Bundle;
+import android.os.UserManager;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableContext;
+import android.testing.TestableLooper.RunWithLooper;
+import android.view.View;
+import android.view.WindowManager;
+import android.view.accessibility.AccessibilityManager;
+
+import com.android.systemui.R;
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.biometrics.DialogViewCallback;
+import com.android.systemui.keyguard.WakefulnessLifecycle;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@RunWith(AndroidTestingRunner.class)
+@RunWithLooper
+@SmallTest
+public class BiometricDialogViewTest extends SysuiTestCase {
+
+    FaceDialogView mFaceDialogView;
+
+    private static final String TITLE = "Title";
+    private static final String SUBTITLE = "Subtitle";
+    private static final String DESCRIPTION = "Description";
+    private static final String NEGATIVE_BUTTON = "Negative Button";
+
+    private static final String TEST_HELP = "Help";
+
+    TestableContext mTestableContext;
+    @Mock
+    private DialogViewCallback mCallback;
+    @Mock
+    private UserManager mUserManager;
+    @Mock
+    private DevicePolicyManager mDpm;
+
+    private static class Injector extends BiometricDialogView.Injector {
+        @Override
+        public WakefulnessLifecycle getWakefulnessLifecycle() {
+            final WakefulnessLifecycle lifecycle = new WakefulnessLifecycle();
+            lifecycle.dispatchFinishedWakingUp();
+            return lifecycle;
+        }
+    }
+
+    @Before
+    public void setup() {
+        MockitoAnnotations.initMocks(this);
+        mTestableContext = spy(mContext);
+        mTestableContext.addMockSystemService(UserManager.class, mUserManager);
+        mTestableContext.addMockSystemService(DevicePolicyManager.class, mDpm);
+    }
+
+    @Test
+    public void testContentStates_confirmationRequired_authenticated() {
+        mFaceDialogView = buildFaceDialogView(mTestableContext, mCallback,
+                true /* requireConfirmation */);
+        mFaceDialogView.onAttachedToWindow();
+
+        // When starting authentication
+        assertEquals(View.VISIBLE, mFaceDialogView.mTitleText.getVisibility());
+        assertEquals(View.VISIBLE, mFaceDialogView.mSubtitleText.getVisibility());
+        assertEquals(View.VISIBLE, mFaceDialogView.mDescriptionText.getVisibility());
+        assertEquals(View.INVISIBLE, mFaceDialogView.mErrorText.getVisibility());
+        assertEquals(View.VISIBLE, mFaceDialogView.mPositiveButton.getVisibility());
+        assertEquals(View.VISIBLE, mFaceDialogView.mNegativeButton.getVisibility());
+        assertEquals(View.GONE, mFaceDialogView.mTryAgainButton.getVisibility());
+
+        // Contents are as expected
+        assertTrue(TITLE.contentEquals(mFaceDialogView.mTitleText.getText()));
+        assertTrue(SUBTITLE.contentEquals(mFaceDialogView.mSubtitleText.getText()));
+        assertTrue(DESCRIPTION.contentEquals(mFaceDialogView.mDescriptionText.getText()));
+        assertTrue(mFaceDialogView.mPositiveButton.getText().toString()
+                .contentEquals(mContext.getString(R.string.biometric_dialog_confirm)));
+        assertTrue(NEGATIVE_BUTTON.contentEquals(mFaceDialogView.mNegativeButton.getText()));
+        assertTrue(mFaceDialogView.mTryAgainButton.getText().toString()
+                .contentEquals(mContext.getString(R.string.biometric_dialog_try_again)));
+
+        // When help message is received
+        mFaceDialogView.onHelp(TEST_HELP);
+        assertEquals(mFaceDialogView.mErrorText.getVisibility(), View.VISIBLE);
+        assertTrue(TEST_HELP.contentEquals(mFaceDialogView.mErrorText.getText()));
+
+        // When authenticated, confirm button comes out
+        mFaceDialogView.onAuthenticationSucceeded();
+        assertEquals(View.VISIBLE, mFaceDialogView.mPositiveButton.getVisibility());
+        assertEquals(true, mFaceDialogView.mPositiveButton.isEnabled());
+    }
+
+    @Test
+    public void testContentStates_confirmationNotRequired_authenticated() {
+        mFaceDialogView = buildFaceDialogView(mTestableContext, mCallback,
+                false /* requireConfirmation */);
+        mFaceDialogView.onAttachedToWindow();
+        mFaceDialogView.updateSize(FaceDialogView.SIZE_SMALL);
+
+        assertEquals(View.INVISIBLE, mFaceDialogView.mTitleText.getVisibility());
+        assertNotSame(View.VISIBLE, mFaceDialogView.mSubtitleText.getVisibility());
+        assertNotSame(View.VISIBLE, mFaceDialogView.mDescriptionText.getVisibility());
+        assertEquals(View.INVISIBLE, mFaceDialogView.mErrorText.getVisibility());
+        assertEquals(View.GONE, mFaceDialogView.mPositiveButton.getVisibility());
+        assertEquals(View.GONE, mFaceDialogView.mTryAgainButton.getVisibility());
+        assertEquals(View.GONE, mFaceDialogView.mTryAgainButton.getVisibility());
+    }
+
+    @Test
+    public void testContentStates_confirmationNotRequired_help() {
+        mFaceDialogView = buildFaceDialogView(mTestableContext, mCallback,
+                false /* requireConfirmation */);
+        mFaceDialogView.onAttachedToWindow();
+
+        mFaceDialogView.onHelp(TEST_HELP);
+        assertEquals(mFaceDialogView.mErrorText.getVisibility(), View.VISIBLE);
+        assertTrue(TEST_HELP.contentEquals(mFaceDialogView.mErrorText.getText()));
+    }
+
+    @Test
+    public void testBack_sendsUserCanceled() {
+        // TODO: Need robolectric framework to wait for handler to complete
+    }
+
+    @Test
+    public void testScreenOff_sendsUserCanceled() {
+        // TODO: Need robolectric framework to wait for handler to complete
+    }
+
+    @Test
+    public void testRestoreState_contentStatesCorrect() {
+        mFaceDialogView = buildFaceDialogView(mTestableContext, mCallback,
+                false /* requireConfirmation */);
+        mFaceDialogView.onAttachedToWindow();
+        mFaceDialogView.onAuthenticationFailed(TEST_HELP);
+
+        final Bundle bundle = new Bundle();
+        mFaceDialogView.onSaveState(bundle);
+
+        mFaceDialogView = buildFaceDialogView(mTestableContext, mCallback,
+                false /* requireConfirmation */);
+        mFaceDialogView.restoreState(bundle);
+        mFaceDialogView.onAttachedToWindow();
+
+        assertEquals(View.VISIBLE, mFaceDialogView.mTryAgainButton.getVisibility());
+    }
+
+    private FaceDialogView buildFaceDialogView(Context context, DialogViewCallback callback,
+            boolean requireConfirmation) {
+        return (FaceDialogView) new BiometricDialogView.Builder(context)
+                .setCallback(callback)
+                .setBiometricPromptBundle(createTestDialogBundle())
+                .setRequireConfirmation(requireConfirmation)
+                .setUserId(0)
+                .setOpPackageName("test_package")
+                .build(BiometricDialogView.Builder.TYPE_FACE, new Injector());
+    }
+
+    private Bundle createTestDialogBundle() {
+        Bundle bundle = new Bundle();
+
+        bundle.putCharSequence(BiometricPrompt.KEY_TITLE, TITLE);
+        bundle.putCharSequence(BiometricPrompt.KEY_SUBTITLE, SUBTITLE);
+        bundle.putCharSequence(BiometricPrompt.KEY_DESCRIPTION, DESCRIPTION);
+        bundle.putCharSequence(BiometricPrompt.KEY_NEGATIVE_TEXT, NEGATIVE_BUTTON);
+
+        // RequireConfirmation is a hint to BiometricService. This can be forced to be required
+        // by user settings, and should be tested in BiometricService.
+        bundle.putBoolean(BiometricPrompt.KEY_REQUIRE_CONFIRMATION, true);
+
+        return bundle;
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/FalsingManagerProxyTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/FalsingManagerProxyTest.java
deleted file mode 100644
index 7ea6493..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/classifier/FalsingManagerProxyTest.java
+++ /dev/null
@@ -1,103 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.classifier;
-
-import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.BRIGHTLINE_FALSING_MANAGER_ENABLED;
-
-import static org.hamcrest.CoreMatchers.instanceOf;
-import static org.junit.Assert.assertThat;
-
-import android.os.Handler;
-import android.provider.DeviceConfig;
-import android.testing.AndroidTestingRunner;
-import android.testing.TestableLooper;
-
-import androidx.test.filters.SmallTest;
-
-import com.android.systemui.SysuiTestCase;
-import com.android.systemui.classifier.brightline.BrightLineFalsingManager;
-import com.android.systemui.shared.plugins.PluginManager;
-
-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;
-
-@SmallTest
-@RunWith(AndroidTestingRunner.class)
-@TestableLooper.RunWithLooper
-public class FalsingManagerProxyTest extends SysuiTestCase {
-    @Mock
-    PluginManager mPluginManager;
-    private boolean mDefaultConfigValue;
-    private Handler mHandler;
-    private TestableLooper mTestableLooper;
-
-    @Before
-    public void setup() {
-        MockitoAnnotations.initMocks(this);
-        mTestableLooper = TestableLooper.get(this);
-        mHandler = new Handler(mTestableLooper.getLooper());
-        mDefaultConfigValue = DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_SYSTEMUI,
-                BRIGHTLINE_FALSING_MANAGER_ENABLED, false);
-        // In case it runs on a device where it's been set to true, set it to false by hand.
-        DeviceConfig.setProperty(DeviceConfig.NAMESPACE_SYSTEMUI,
-                BRIGHTLINE_FALSING_MANAGER_ENABLED, "false", false);
-    }
-
-    @After
-    public void tearDown() {
-        DeviceConfig.setProperty(DeviceConfig.NAMESPACE_SYSTEMUI,
-                BRIGHTLINE_FALSING_MANAGER_ENABLED, mDefaultConfigValue ? "true" : "false", false);
-    }
-
-    @Test
-    public void test_brightLineFalsingManagerDisabled() {
-        FalsingManagerProxy proxy = new FalsingManagerProxy(getContext(), mPluginManager, mHandler);
-
-        assertThat(proxy.getInternalFalsingManager(), instanceOf(FalsingManagerImpl.class));
-    }
-
-    @Test
-    public void test_brightLineFalsingManagerEnabled() {
-        DeviceConfig.setProperty(DeviceConfig.NAMESPACE_SYSTEMUI,
-                BRIGHTLINE_FALSING_MANAGER_ENABLED, "true", false);
-        FalsingManagerProxy proxy = new FalsingManagerProxy(getContext(), mPluginManager, mHandler);
-
-        assertThat(proxy.getInternalFalsingManager(), instanceOf(BrightLineFalsingManager.class));
-    }
-
-    @Test
-    public void test_brightLineFalsingManagerToggled() {
-        FalsingManagerProxy proxy = new FalsingManagerProxy(getContext(), mPluginManager, mHandler);
-        assertThat(proxy.getInternalFalsingManager(), instanceOf(FalsingManagerImpl.class));
-
-        DeviceConfig.setProperty(DeviceConfig.NAMESPACE_SYSTEMUI,
-                BRIGHTLINE_FALSING_MANAGER_ENABLED, "true", false);
-        mTestableLooper.processAllMessages();
-        proxy.setupFalsingManager(getContext());
-        assertThat(proxy.getInternalFalsingManager(), instanceOf(BrightLineFalsingManager.class));
-
-        DeviceConfig.setProperty(DeviceConfig.NAMESPACE_SYSTEMUI,
-                BRIGHTLINE_FALSING_MANAGER_ENABLED, "false", false);
-        mTestableLooper.processAllMessages();
-        proxy.setupFalsingManager(getContext());
-        assertThat(proxy.getInternalFalsingManager(), instanceOf(FalsingManagerImpl.class));
-    }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/glwallpaper/EglHelperTest.java b/packages/SystemUI/tests/src/com/android/systemui/glwallpaper/EglHelperTest.java
new file mode 100644
index 0000000..b4a60d6
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/glwallpaper/EglHelperTest.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.systemui.glwallpaper;
+
+import static org.junit.Assert.*;
+import static org.mockito.Mockito.RETURNS_DEFAULTS;
+import static org.mockito.Mockito.mock;
+
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+import android.view.SurfaceHolder;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.systemui.SysuiTestCase;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper
+public class EglHelperTest extends SysuiTestCase {
+
+    @Mock
+    private EglHelper mEglHelper;
+    @Mock
+    private SurfaceHolder mSurfaceHolder;
+
+    @Before
+    public void setUp() throws Exception {
+        mEglHelper = mock(EglHelper.class, RETURNS_DEFAULTS);
+        mSurfaceHolder = mock(SurfaceHolder.class, RETURNS_DEFAULTS);
+    }
+
+    @Test
+    public void testInit_finish() {
+        mEglHelper.init(mSurfaceHolder);
+        mEglHelper.finish();
+    }
+
+    @Test
+    public void testFinish_shouldNotCrash() {
+        assertFalse(mEglHelper.hasEglDisplay());
+        assertFalse(mEglHelper.hasEglSurface());
+        assertFalse(mEglHelper.hasEglContext());
+
+        mEglHelper.finish();
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java
index 1e1f2156..b252a0d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java
@@ -365,4 +365,45 @@
         waitForIdleSync();
         verify(mCallbacks).onRecentsAnimationStateChanged(eq(true));
     }
+
+    @Test
+    public void testShowBiometricDialog() {
+        Bundle bundle = new Bundle();
+        String packageName = "test";
+        mCommandQueue.showBiometricDialog(bundle, null /* receiver */, 1, true, 3, packageName);
+        waitForIdleSync();
+        verify(mCallbacks).showBiometricDialog(eq(bundle), eq(null), eq(1), eq(true), eq(3),
+                eq(packageName));
+    }
+
+    @Test
+    public void testOnBiometricAuthenticated() {
+        String failureReason = "test_failure_reason";
+        mCommandQueue.onBiometricAuthenticated(true /* authenticated */, failureReason);
+        waitForIdleSync();
+        verify(mCallbacks).onBiometricAuthenticated(eq(true), eq(failureReason));
+    }
+
+    @Test
+    public void testOnBiometricHelp() {
+        String helpMessage = "test_help_message";
+        mCommandQueue.onBiometricHelp(helpMessage);
+        waitForIdleSync();
+        verify(mCallbacks).onBiometricHelp(eq(helpMessage));
+    }
+
+    @Test
+    public void testOnBiometricError() {
+        String errorMessage = "test_error_message";
+        mCommandQueue.onBiometricError(errorMessage);
+        waitForIdleSync();
+        verify(mCallbacks).onBiometricError(eq(errorMessage));
+    }
+
+    @Test
+    public void testHideBiometricDialog() {
+        mCommandQueue.hideBiometricDialog();
+        waitForIdleSync();
+        verify(mCallbacks).hideBiometricDialog();
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBouncerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBouncerTest.java
index 907e695..cd60e47 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBouncerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBouncerTest.java
@@ -21,6 +21,7 @@
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyFloat;
 import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyLong;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.never;
@@ -84,6 +85,8 @@
     @Mock
     private UnlockMethodCache mUnlockMethodCache;
     @Mock
+    private KeyguardBypassController mKeyguardBypassController;
+    @Mock
     private Handler mHandler;
 
     private KeyguardBouncer mBouncer;
@@ -98,7 +101,8 @@
         when(mKeyguardHostView.getHeight()).thenReturn(500);
         mBouncer = new KeyguardBouncer(getContext(), mViewMediatorCallback,
                 mLockPatternUtils, container, mDismissCallbackRegistry, mFalsingManager,
-                mExpansionCallback, mUnlockMethodCache, mKeyguardUpdateMonitor, mHandler) {
+                mExpansionCallback, mUnlockMethodCache, mKeyguardUpdateMonitor,
+                mKeyguardBypassController, mHandler) {
             @Override
             protected void inflateView() {
                 super.inflateView();
@@ -391,6 +395,15 @@
     }
 
     @Test
+    public void testShow_delaysIfFaceAuthIsRunning_unlessBypass() {
+        when(mUnlockMethodCache.isFaceAuthEnabled()).thenReturn(true);
+        when(mKeyguardBypassController.getBypassEnabled()).thenReturn(true);
+        mBouncer.show(true /* reset */);
+
+        verify(mHandler, never()).postDelayed(any(), anyLong());
+    }
+
+    @Test
     public void testRegisterUpdateMonitorCallback() {
         verify(mKeyguardUpdateMonitor).registerCallback(any());
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java
index 9ae9ceb..e691b7d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java
@@ -228,6 +228,18 @@
         NetworkControllerImpl.Config.add5GIconMapping("connected:5g", mConfig);
     }
 
+    public void setupNr5GIconConfigurationForNotRestrictedRrcCon() {
+        NetworkControllerImpl.Config.add5GIconMapping("connected_mmwave:5g_plus", mConfig);
+        NetworkControllerImpl.Config.add5GIconMapping("connected:5g_plus", mConfig);
+        NetworkControllerImpl.Config.add5GIconMapping("not_restricted_rrc_con:5g", mConfig);
+    }
+
+    public void setupNr5GIconConfigurationForNotRestrictedRrcIdle() {
+        NetworkControllerImpl.Config.add5GIconMapping("connected_mmwave:5g_plus", mConfig);
+        NetworkControllerImpl.Config.add5GIconMapping("connected:5g_plus", mConfig);
+        NetworkControllerImpl.Config.add5GIconMapping("not_restricted_rrc_idle:5g", mConfig);
+    }
+
     public void setConnectivityViaBroadcast(
         int networkType, boolean validated, boolean isConnected) {
         setConnectivityCommon(networkType, validated, isConnected);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerDataTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerDataTest.java
index 5128675..f0394da 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerDataTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerDataTest.java
@@ -175,6 +175,35 @@
     }
 
     @Test
+    public void testNr5GIcon_NrNotRestrictedRrcCon_show5GIcon() {
+        setupNr5GIconConfigurationForNotRestrictedRrcCon();
+        setupDefaultSignal();
+        updateDataConnectionState(TelephonyManager.DATA_CONNECTED,
+                TelephonyManager.NETWORK_TYPE_LTE);
+        updateDataActivity(TelephonyManager.DATA_ACTIVITY_INOUT);
+        ServiceState ss = Mockito.mock(ServiceState.class);
+        doReturn(NetworkRegistrationInfo.NR_STATE_NOT_RESTRICTED).when(ss).getNrState();
+        mPhoneStateListener.onServiceStateChanged(ss);
+
+        verifyLastMobileDataIndicators(true, DEFAULT_SIGNAL_STRENGTH, TelephonyIcons.ICON_5G,
+                true, DEFAULT_QS_SIGNAL_STRENGTH, TelephonyIcons.ICON_5G, true, true);
+    }
+
+    @Test
+    public void testNr5GIcon_NrNotRestrictedRrcIdle_show5GIcon() {
+        setupNr5GIconConfigurationForNotRestrictedRrcIdle();
+        setupDefaultSignal();
+        updateDataConnectionState(TelephonyManager.DATA_CONNECTED,
+                TelephonyManager.NETWORK_TYPE_LTE);
+        updateDataActivity(TelephonyManager.DATA_ACTIVITY_DORMANT);
+        ServiceState ss = Mockito.mock(ServiceState.class);
+        doReturn(NetworkRegistrationInfo.NR_STATE_NOT_RESTRICTED).when(ss).getNrState();
+        mPhoneStateListener.onServiceStateChanged(ss);
+
+        verifyDataIndicators(TelephonyIcons.ICON_5G);
+    }
+
+    @Test
     public void testNr5GIcon_NrConnectedWithoutMMWave_show5GIcon() {
         setupDefaultNr5GIconConfiguration();
         setupDefaultSignal();
diff --git a/services/Android.bp b/services/Android.bp
index 54157d0..27f8d36 100644
--- a/services/Android.bp
+++ b/services/Android.bp
@@ -43,6 +43,10 @@
         "android.hidl.manager-V1.0-java",
     ],
 
+    plugins: [
+        "compat-changeid-annotation-processor",
+    ],
+
     // Uncomment to enable output of certain warnings (deprecated, unchecked)
     //javacflags: ["-Xlint"],
 
@@ -56,3 +60,9 @@
     defaults: ["libservices.core-libs"],
     whole_static_libs: ["libservices.core"],
 }
+
+platform_compat_config {
+    name: "services-platform-compat-config",
+    prefix: "services",
+    src: ":services",
+}
diff --git a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
index 1356157..5e9c08b 100644
--- a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
+++ b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
@@ -348,13 +348,13 @@
         }
     }
 
-    protected abstract boolean isCalledForCurrentUserLocked();
+    protected abstract boolean hasRightsToCurrentUserLocked();
 
     @Override
     public List<AccessibilityWindowInfo> getWindows() {
         ensureWindowsAvailableTimed();
         synchronized (mLock) {
-            if (!isCalledForCurrentUserLocked()) {
+            if (!hasRightsToCurrentUserLocked()) {
                 return null;
             }
             final boolean permissionGranted =
@@ -387,7 +387,7 @@
     public AccessibilityWindowInfo getWindow(int windowId) {
         ensureWindowsAvailableTimed();
         synchronized (mLock) {
-            if (!isCalledForCurrentUserLocked()) {
+            if (!hasRightsToCurrentUserLocked()) {
                 return null;
             }
             final boolean permissionGranted =
@@ -420,7 +420,7 @@
         MagnificationSpec spec;
         synchronized (mLock) {
             mUsesAccessibilityCache = true;
-            if (!isCalledForCurrentUserLocked()) {
+            if (!hasRightsToCurrentUserLocked()) {
                 return null;
             }
             resolvedWindowId = resolveAccessibilityWindowIdLocked(accessibilityWindowId);
@@ -481,7 +481,7 @@
         MagnificationSpec spec;
         synchronized (mLock) {
             mUsesAccessibilityCache = true;
-            if (!isCalledForCurrentUserLocked()) {
+            if (!hasRightsToCurrentUserLocked()) {
                 return null;
             }
             resolvedWindowId = resolveAccessibilityWindowIdLocked(accessibilityWindowId);
@@ -542,7 +542,7 @@
         MagnificationSpec spec;
         synchronized (mLock) {
             mUsesAccessibilityCache = true;
-            if (!isCalledForCurrentUserLocked()) {
+            if (!hasRightsToCurrentUserLocked()) {
                 return null;
             }
             resolvedWindowId = resolveAccessibilityWindowIdLocked(accessibilityWindowId);
@@ -602,7 +602,7 @@
         Region partialInteractiveRegion = Region.obtain();
         MagnificationSpec spec;
         synchronized (mLock) {
-            if (!isCalledForCurrentUserLocked()) {
+            if (!hasRightsToCurrentUserLocked()) {
                 return null;
             }
             resolvedWindowId = resolveAccessibilityWindowIdForFindFocusLocked(
@@ -663,7 +663,7 @@
         Region partialInteractiveRegion = Region.obtain();
         MagnificationSpec spec;
         synchronized (mLock) {
-            if (!isCalledForCurrentUserLocked()) {
+            if (!hasRightsToCurrentUserLocked()) {
                 return null;
             }
             resolvedWindowId = resolveAccessibilityWindowIdLocked(accessibilityWindowId);
@@ -728,7 +728,7 @@
             throws RemoteException {
         final int resolvedWindowId;
         synchronized (mLock) {
-            if (!isCalledForCurrentUserLocked()) {
+            if (!hasRightsToCurrentUserLocked()) {
                 return false;
             }
             resolvedWindowId = resolveAccessibilityWindowIdLocked(accessibilityWindowId);
@@ -748,7 +748,7 @@
     @Override
     public boolean performGlobalAction(int action) {
         synchronized (mLock) {
-            if (!isCalledForCurrentUserLocked()) {
+            if (!hasRightsToCurrentUserLocked()) {
                 return false;
             }
         }
@@ -771,7 +771,7 @@
     @Override
     public float getMagnificationScale(int displayId) {
         synchronized (mLock) {
-            if (!isCalledForCurrentUserLocked()) {
+            if (!hasRightsToCurrentUserLocked()) {
                 return 1.0f;
             }
         }
@@ -787,7 +787,7 @@
     public Region getMagnificationRegion(int displayId) {
         synchronized (mLock) {
             final Region region = Region.obtain();
-            if (!isCalledForCurrentUserLocked()) {
+            if (!hasRightsToCurrentUserLocked()) {
                 return region;
             }
             MagnificationController magnificationController =
@@ -810,7 +810,7 @@
     @Override
     public float getMagnificationCenterX(int displayId) {
         synchronized (mLock) {
-            if (!isCalledForCurrentUserLocked()) {
+            if (!hasRightsToCurrentUserLocked()) {
                 return 0.0f;
             }
             MagnificationController magnificationController =
@@ -832,7 +832,7 @@
     @Override
     public float getMagnificationCenterY(int displayId) {
         synchronized (mLock) {
-            if (!isCalledForCurrentUserLocked()) {
+            if (!hasRightsToCurrentUserLocked()) {
                 return 0.0f;
             }
             MagnificationController magnificationController =
@@ -864,7 +864,7 @@
     @Override
     public boolean resetMagnification(int displayId, boolean animate) {
         synchronized (mLock) {
-            if (!isCalledForCurrentUserLocked()) {
+            if (!hasRightsToCurrentUserLocked()) {
                 return false;
             }
             if (!mSecurityPolicy.canControlMagnification(this)) {
@@ -886,7 +886,7 @@
     public boolean setMagnificationScaleAndCenter(int displayId, float scale, float centerX,
             float centerY, boolean animate) {
         synchronized (mLock) {
-            if (!isCalledForCurrentUserLocked()) {
+            if (!hasRightsToCurrentUserLocked()) {
                 return false;
             }
             if (!mSecurityPolicy.canControlMagnification(this)) {
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilitySecurityPolicy.java b/services/accessibility/java/com/android/server/accessibility/AccessibilitySecurityPolicy.java
index 315d6fa..b88b24e 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilitySecurityPolicy.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilitySecurityPolicy.java
@@ -468,7 +468,7 @@
         }
     }
 
-    private boolean hasPermission(String permission) {
+    boolean hasPermission(String permission) {
         return mContext.checkCallingPermission(permission) == PackageManager.PERMISSION_GRANTED;
     }
 
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java
index 02f7821..d7f61e5 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java
@@ -18,6 +18,7 @@
 
 import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
 
+import android.Manifest;
 import android.accessibilityservice.AccessibilityServiceInfo;
 import android.accessibilityservice.IAccessibilityServiceClient;
 import android.content.ComponentName;
@@ -27,6 +28,7 @@
 import android.os.Binder;
 import android.os.Handler;
 import android.os.IBinder;
+import android.os.Process;
 import android.os.RemoteException;
 import android.os.UserHandle;
 import android.provider.Settings;
@@ -211,19 +213,31 @@
     }
 
     @Override
-    protected boolean isCalledForCurrentUserLocked() {
+    protected boolean hasRightsToCurrentUserLocked() {
         // We treat calls from a profile as if made by its parent as profiles
         // share the accessibility state of the parent. The call below
         // performs the current profile parent resolution.
-        final int resolvedUserId = mSecurityPolicy
-                .resolveCallingUserIdEnforcingPermissionsLocked(UserHandle.USER_CURRENT);
-        return resolvedUserId == mSystemSupport.getCurrentUserIdLocked();
+        final int callingUid = Binder.getCallingUid();
+        if (callingUid == Process.ROOT_UID
+                || callingUid == Process.SYSTEM_UID
+                || callingUid == Process.SHELL_UID) {
+            return true;
+        }
+        if (mSecurityPolicy.resolveProfileParentLocked(UserHandle.getUserId(callingUid))
+                == mSystemSupport.getCurrentUserIdLocked()) {
+            return true;
+        }
+        if (mSecurityPolicy.hasPermission(Manifest.permission.INTERACT_ACROSS_USERS)
+                || mSecurityPolicy.hasPermission(Manifest.permission.INTERACT_ACROSS_USERS_FULL)) {
+            return true;
+        }
+        return false;
     }
 
     @Override
     public boolean setSoftKeyboardShowMode(int showMode) {
         synchronized (mLock) {
-            if (!isCalledForCurrentUserLocked()) {
+            if (!hasRightsToCurrentUserLocked()) {
                 return false;
             }
             final UserState userState = mUserStateWeakReference.get();
@@ -241,7 +255,7 @@
     @Override
     public boolean isAccessibilityButtonAvailable() {
         synchronized (mLock) {
-            if (!isCalledForCurrentUserLocked()) {
+            if (!hasRightsToCurrentUserLocked()) {
                 return false;
             }
             UserState userState = mUserStateWeakReference.get();
diff --git a/services/accessibility/java/com/android/server/accessibility/UiAutomationManager.java b/services/accessibility/java/com/android/server/accessibility/UiAutomationManager.java
index 2698b72..79d975d 100644
--- a/services/accessibility/java/com/android/server/accessibility/UiAutomationManager.java
+++ b/services/accessibility/java/com/android/server/accessibility/UiAutomationManager.java
@@ -66,6 +66,7 @@
                     mUiAutomationServiceOwner.unlinkToDeath(this, 0);
                     mUiAutomationServiceOwner = null;
                     destroyUiAutomationService();
+                    Slog.v(LOG_TAG, "UiAutomation service owner died");
                 }
             };
 
@@ -263,7 +264,7 @@
         }
 
         @Override
-        protected boolean isCalledForCurrentUserLocked() {
+        protected boolean hasRightsToCurrentUserLocked() {
             // Allow UiAutomation to work for any user
             return true;
         }
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
index a64f4e4..6b7c3e6 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
@@ -807,6 +807,7 @@
                     packageName, versionCode, userId);
             final AutofillOptions options = new AutofillOptions(loggingLevel, compatModeEnabled);
             mAugmentedAutofillState.injectAugmentedAutofillInfo(options, userId, packageName);
+            injectDisableAppInfo(options, userId, packageName);
             return options;
         }
 
@@ -820,6 +821,19 @@
             }
             return false;
         }
+
+        private void injectDisableAppInfo(@NonNull AutofillOptions options, int userId,
+                String packageName) {
+            synchronized (mLock) {
+                final AutofillManagerServiceImpl service = peekServiceForUserLocked(userId);
+                if (service != null) {
+                    options.appDisabledExpiration = service.getAppDisabledExpirationLocked(
+                            packageName);
+                    options.disabledActivities = service.getAppDisabledActivitiesLocked(
+                            packageName);
+                }
+            }
+        }
     }
 
     /**
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
index 1e1e07d..d7ed2e9 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
@@ -980,12 +980,12 @@
             for (int i = 0; i < size; i++) {
                 final String packageName = mDisabledApps.keyAt(i);
                 final long expiration = mDisabledApps.valueAt(i);
-                 builder.append(prefix).append(prefix)
-                     .append(i).append(". ").append(packageName).append(": ");
-                 TimeUtils.formatDuration((expiration - now), builder);
-                 builder.append('\n');
-             }
-             pw.println(builder);
+                builder.append(prefix).append(prefix)
+                        .append(i).append(". ").append(packageName).append(": ");
+                TimeUtils.formatDuration((expiration - now), builder);
+                builder.append('\n');
+            }
+            pw.println(builder);
         }
 
         pw.print(prefix); pw.print("Disabled activities: ");
@@ -1000,12 +1000,12 @@
             for (int i = 0; i < size; i++) {
                 final ComponentName component = mDisabledActivities.keyAt(i);
                 final long expiration = mDisabledActivities.valueAt(i);
-                 builder.append(prefix).append(prefix)
-                     .append(i).append(". ").append(component).append(": ");
-                 TimeUtils.formatDuration((expiration - now), builder);
-                 builder.append('\n');
-             }
-             pw.println(builder);
+                builder.append(prefix).append(prefix)
+                        .append(i).append(". ").append(component).append(": ");
+                TimeUtils.formatDuration((expiration - now), builder);
+                builder.append('\n');
+            }
+            pw.println(builder);
         }
 
         final int size = mSessions.size();
@@ -1418,6 +1418,36 @@
         }
     }
 
+    // Called by AutofillManagerService
+    long getAppDisabledExpirationLocked(@NonNull String packageName) {
+        if (mDisabledApps == null) {
+            return 0;
+        }
+        final Long expiration = mDisabledApps.get(packageName);
+        return expiration != null ? expiration : 0;
+    }
+
+    // Called by AutofillManagerService
+    @Nullable
+    ArrayMap<String, Long> getAppDisabledActivitiesLocked(@NonNull String packageName) {
+        if (mDisabledActivities != null) {
+            final int size = mDisabledActivities.size();
+            ArrayMap<String, Long> disabledList = null;
+            for (int i = 0; i < size; i++) {
+                final ComponentName component = mDisabledActivities.keyAt(i);
+                if (packageName.equals(component.getPackageName())) {
+                    if (disabledList == null) {
+                        disabledList = new ArrayMap<>();
+                    }
+                    final long expiration = mDisabledActivities.valueAt(i);
+                    disabledList.put(component.flattenToShortString(), expiration);
+                }
+            }
+            return disabledList;
+        }
+        return null;
+    }
+
     /**
      * Checks if autofill is disabled by service to the given activity.
      */
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index 4579a3d..48f16ac 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -772,13 +772,19 @@
         final long disableDuration = response.getDisableDuration();
         if (disableDuration > 0) {
             final int flags = response.getFlags();
-            if ((flags & FillResponse.FLAG_DISABLE_ACTIVITY_ONLY) != 0) {
+            final boolean disableActivityOnly =
+                    (flags & FillResponse.FLAG_DISABLE_ACTIVITY_ONLY) != 0;
+            notifyDisableAutofillToClient(disableDuration,
+                    disableActivityOnly ? mComponentName : null);
+
+            if (disableActivityOnly) {
                 mService.disableAutofillForActivity(mComponentName, disableDuration,
                         id, mCompatMode);
             } else {
                 mService.disableAutofillForApp(mComponentName.getPackageName(), disableDuration,
                         id, mCompatMode);
             }
+
             // Although "standard" autofill is disabled, it might still trigger augmented autofill
             if (triggerAugmentedAutofillLocked() != null) {
                 mForAugmentedAutofillOnly = true;
@@ -2567,6 +2573,17 @@
         }
     }
 
+    private void notifyDisableAutofillToClient(long disableDuration, ComponentName componentName) {
+        synchronized (mLock) {
+            if (mCurrentViewId == null) return;
+            try {
+                mClient.notifyDisableAutofill(disableDuration, componentName);
+            } catch (RemoteException e) {
+                Slog.e(TAG, "Error notifying client disable autofill: id=" + mCurrentViewId, e);
+            }
+        }
+    }
+
     @GuardedBy("mLock")
     private void updateTrackedIdsLocked() {
         // Only track the views of the last response as only those are reported back to the
diff --git a/services/backup/java/com/android/server/backup/BackupManagerService.java b/services/backup/java/com/android/server/backup/BackupManagerService.java
index 23afce6..222a6f2 100644
--- a/services/backup/java/com/android/server/backup/BackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/BackupManagerService.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2018 The Android Open Source Project
+ * Copyright (C) 2014 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.
@@ -18,12 +18,56 @@
 
 import static com.android.internal.util.Preconditions.checkNotNull;
 
+import static java.util.Collections.emptySet;
+
+import android.Manifest;
+import android.annotation.Nullable;
+import android.annotation.UserIdInt;
+import android.app.ActivityManager;
+import android.app.admin.DevicePolicyManager;
+import android.app.backup.BackupManager;
+import android.app.backup.IBackupManager;
+import android.app.backup.IBackupManagerMonitor;
+import android.app.backup.IBackupObserver;
+import android.app.backup.IFullBackupRestoreObserver;
+import android.app.backup.IRestoreSession;
+import android.app.backup.ISelectBackupTransportCallback;
+import android.app.job.JobParameters;
+import android.app.job.JobScheduler;
+import android.app.job.JobService;
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
 import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.PackageManager;
+import android.os.Binder;
+import android.os.FileUtils;
+import android.os.Handler;
+import android.os.HandlerThread;
 import android.os.IBinder;
+import android.os.ParcelFileDescriptor;
+import android.os.Process;
+import android.os.RemoteException;
+import android.os.SystemProperties;
+import android.os.Trace;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.util.Slog;
 import android.util.SparseArray;
 
+import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.DumpUtils;
+import com.android.server.SystemConfig;
 import com.android.server.SystemService;
+import com.android.server.backup.utils.RandomAccessFileUtils;
+
+import java.io.File;
+import java.io.FileDescriptor;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.Set;
 
 /**
  * Definition of the system service that performs backup/restore operations.
@@ -31,53 +75,1502 @@
  * <p>This class is responsible for handling user-aware operations and acts as a delegator, routing
  * incoming calls to the appropriate per-user {@link UserBackupManagerService} to handle the
  * corresponding backup/restore operation.
+ *
+ * <p>It also determines whether the backup service is available. It can be disabled in the
+ * following two ways:
+ *
+ * <ul>
+ *  <li>Temporary - call {@link #setBackupServiceActive(int, boolean)}, or
+ *  <li>Permanent - set the system property {@link #BACKUP_DISABLE_PROPERTY} to true.
+ * </ul>
+ *
+ * Temporary disabling is controlled by {@link #setBackupServiceActive(int, boolean)} through
+ * privileged callers (currently {@link DevicePolicyManager}). If called on {@link
+ * UserHandle#USER_SYSTEM}, backup is disabled for all users.
  */
-public class BackupManagerService {
+public class BackupManagerService extends IBackupManager.Stub {
     public static final String TAG = "BackupManagerService";
     public static final boolean DEBUG = true;
     public static final boolean MORE_DEBUG = false;
     public static final boolean DEBUG_SCHEDULING = true;
 
-    private final Context mContext;
-    private final Trampoline mTrampoline;
-    private final SparseArray<UserBackupManagerService> mServiceUsers;
+    @VisibleForTesting
+    static final String DUMP_RUNNING_USERS_MESSAGE = "Backup Manager is running for users:";
 
-    /** Instantiate a new instance of {@link BackupManagerService}. */
-    public BackupManagerService(
-            Context context,
-            Trampoline trampoline,
-            SparseArray<UserBackupManagerService> userServices) {
-        mContext = checkNotNull(context);
-        mTrampoline = checkNotNull(trampoline);
-        // TODO(b/135661048): Remove
-        mServiceUsers = userServices;
+    /**
+     * Name of file that disables the backup service. If this file exists, then backup is disabled
+     * for all users.
+     */
+    private static final String BACKUP_SUPPRESS_FILENAME = "backup-suppress";
+
+    /**
+     * Name of file for non-system users that enables the backup service for the user. Backup is
+     * disabled by default in non-system users.
+     */
+    private static final String BACKUP_ACTIVATED_FILENAME = "backup-activated";
+
+    /**
+     * Name of file for non-system users that remembers whether backup was explicitly activated or
+     * deactivated with a call to setBackupServiceActive.
+     */
+    private static final String REMEMBER_ACTIVATED_FILENAME = "backup-remember-activated";
+
+    // Product-level suppression of backup/restore.
+    private static final String BACKUP_DISABLE_PROPERTY = "ro.backup.disable";
+
+    private static final String BACKUP_THREAD = "backup";
+
+    static BackupManagerService sInstance;
+
+    static BackupManagerService getInstance() {
+        return checkNotNull(sInstance);
+    }
+
+    private final Context mContext;
+    private final UserManager mUserManager;
+
+    private final boolean mGlobalDisable;
+    // Lock to write backup suppress files.
+    // TODD(b/121198006): remove this object and synchronized all methods on "this".
+    private final Object mStateLock = new Object();
+
+    private final Handler mHandler;
+    private final Set<ComponentName> mTransportWhitelist;
+
+    /** Keeps track of all unlocked users registered with this service. Indexed by user id. */
+    private final SparseArray<UserBackupManagerService> mUserServices;
+
+    private final BroadcastReceiver mUserRemovedReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            if (Intent.ACTION_USER_REMOVED.equals(intent.getAction())) {
+                int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL);
+                if (userId > 0) { // for only non system users
+                    mHandler.post(() -> onRemovedNonSystemUser(userId));
+                }
+            }
+        }
+    };
+
+    public BackupManagerService(Context context) {
+        this(context, new SparseArray<>());
+    }
+
+    @VisibleForTesting
+    BackupManagerService(Context context, SparseArray<UserBackupManagerService> userServices) {
+        mContext = context;
+        mGlobalDisable = isBackupDisabled();
+        HandlerThread handlerThread =
+                new HandlerThread(BACKUP_THREAD, Process.THREAD_PRIORITY_BACKGROUND);
+        handlerThread.start();
+        mHandler = new Handler(handlerThread.getLooper());
+        mUserManager = UserManager.get(context);
+        mUserServices = userServices;
+        Set<ComponentName> transportWhitelist =
+                SystemConfig.getInstance().getBackupTransportWhitelist();
+        mTransportWhitelist = (transportWhitelist == null) ? emptySet() : transportWhitelist;
+        mContext.registerReceiver(
+                mUserRemovedReceiver, new IntentFilter(Intent.ACTION_USER_REMOVED));
+    }
+
+    // TODO: Remove this when we implement DI by injecting in the construtor.
+    @VisibleForTesting
+    Handler getBackupHandler() {
+        return mHandler;
+    }
+
+    protected boolean isBackupDisabled() {
+        return SystemProperties.getBoolean(BACKUP_DISABLE_PROPERTY, false);
+    }
+
+    protected int binderGetCallingUserId() {
+        return Binder.getCallingUserHandle().getIdentifier();
+    }
+
+    protected int binderGetCallingUid() {
+        return Binder.getCallingUid();
+    }
+
+    /** Stored in the system user's directory. */
+    protected File getSuppressFileForSystemUser() {
+        return new File(UserBackupManagerFiles.getBaseStateDir(UserHandle.USER_SYSTEM),
+                BACKUP_SUPPRESS_FILENAME);
+    }
+
+    /** Stored in the system user's directory and the file is indexed by the user it refers to. */
+    protected File getRememberActivatedFileForNonSystemUser(int userId) {
+        return UserBackupManagerFiles.getStateFileInSystemDir(REMEMBER_ACTIVATED_FILENAME, userId);
+    }
+
+    /** Stored in the system user's directory and the file is indexed by the user it refers to. */
+    protected File getActivatedFileForNonSystemUser(int userId) {
+        return UserBackupManagerFiles.getStateFileInSystemDir(BACKUP_ACTIVATED_FILENAME, userId);
+    }
+
+    /**
+     * Remove backup state for non system {@code userId} when the user is removed from the device.
+     * For non system users, backup state is stored in both the user's own dir and the system dir.
+     * When the user is removed, the user's own dir gets removed by the OS. This method ensures that
+     * the part of the user backup state which is in the system dir also gets removed.
+     */
+    private void onRemovedNonSystemUser(int userId) {
+        Slog.i(TAG, "Removing state for non system user " + userId);
+        File dir = UserBackupManagerFiles.getStateDirInSystemDir(userId);
+        if (!FileUtils.deleteContentsAndDir(dir)) {
+            Slog.w(TAG, "Failed to delete state dir for removed user: " + userId);
+        }
+    }
+
+    // TODO (b/124359804) move to util method in FileUtils
+    private void createFile(File file) throws IOException {
+        if (file.exists()) {
+            return;
+        }
+
+        file.getParentFile().mkdirs();
+        if (!file.createNewFile()) {
+            Slog.w(TAG, "Failed to create file " + file.getPath());
+        }
+    }
+
+    // TODO (b/124359804) move to util method in FileUtils
+    private void deleteFile(File file) {
+        if (!file.exists()) {
+            return;
+        }
+
+        if (!file.delete()) {
+            Slog.w(TAG, "Failed to delete file " + file.getPath());
+        }
+    }
+
+    /**
+     * Deactivates the backup service for user {@code userId}. If this is the system user, it
+     * creates a suppress file which disables backup for all users. If this is a non-system user, it
+     * only deactivates backup for that user by deleting its activate file.
+     */
+    @GuardedBy("mStateLock")
+    private void deactivateBackupForUserLocked(int userId) throws IOException {
+        if (userId == UserHandle.USER_SYSTEM) {
+            createFile(getSuppressFileForSystemUser());
+        } else {
+            deleteFile(getActivatedFileForNonSystemUser(userId));
+        }
+    }
+
+    /**
+     * Enables the backup service for user {@code userId}. If this is the system user, it deletes
+     * the suppress file. If this is a non-system user, it creates the user's activate file. Note,
+     * deleting the suppress file does not automatically enable backup for non-system users, they
+     * need their own activate file in order to participate in the service.
+     */
+    @GuardedBy("mStateLock")
+    private void activateBackupForUserLocked(int userId) throws IOException {
+        if (userId == UserHandle.USER_SYSTEM) {
+            deleteFile(getSuppressFileForSystemUser());
+        } else {
+            createFile(getActivatedFileForNonSystemUser(userId));
+        }
+    }
+
+    // This method should not perform any I/O (e.g. do not call isBackupActivatedForUser),
+    // it's used in multiple places where I/O waits would cause system lock-ups.
+    private boolean isUserReadyForBackup(int userId) {
+        return mUserServices.get(UserHandle.USER_SYSTEM) != null
+                && mUserServices.get(userId) != null;
+    }
+
+    /**
+     * Backup is activated for the system user if the suppress file does not exist. Backup is
+     * activated for non-system users if the suppress file does not exist AND the user's activated
+     * file exists.
+     */
+    private boolean isBackupActivatedForUser(int userId) {
+        if (getSuppressFileForSystemUser().exists()) {
+            return false;
+        }
+
+        return userId == UserHandle.USER_SYSTEM
+                || getActivatedFileForNonSystemUser(userId).exists();
+    }
+
+    protected Context getContext() {
+        return mContext;
+    }
+
+    protected UserManager getUserManager() {
+        return mUserManager;
+    }
+
+    protected void postToHandler(Runnable runnable) {
+        mHandler.post(runnable);
+    }
+
+    /**
+     * Called from {@link BackupManagerService.Lifecycle} when a user {@code userId} is unlocked.
+     * Starts the backup service for this user if backup is active for this user. Offloads work onto
+     * the handler thread {@link #mHandlerThread} to keep unlock time low since backup is not
+     * essential for device functioning.
+     */
+    void onUnlockUser(int userId) {
+        postToHandler(() -> startServiceForUser(userId));
+    }
+
+    /**
+     * Starts the backup service for user {@code userId} by creating a new instance of {@link
+     * UserBackupManagerService} and registering it with this service.
+     */
+    @VisibleForTesting
+    void startServiceForUser(int userId) {
+        // We know that the user is unlocked here because it is called from setBackupServiceActive
+        // and unlockUser which have these guarantees. So we can check if the file exists.
+        if (mGlobalDisable) {
+            Slog.i(TAG, "Backup service not supported");
+            return;
+        }
+        if (!isBackupActivatedForUser(userId)) {
+            Slog.i(TAG, "Backup not activated for user " + userId);
+            return;
+        }
+        if (mUserServices.get(userId) != null) {
+            Slog.i(TAG, "userId " + userId + " already started, so not starting again");
+            return;
+        }
+        Slog.i(TAG, "Starting service for user: " + userId);
+        UserBackupManagerService userBackupManagerService =
+                UserBackupManagerService.createAndInitializeService(
+                        userId, mContext, this, mTransportWhitelist);
+        startServiceForUser(userId, userBackupManagerService);
+    }
+
+    /**
+     * Starts the backup service for user {@code userId} by registering its instance of {@link
+     * UserBackupManagerService} with this service and setting enabled state.
+     */
+    void startServiceForUser(int userId, UserBackupManagerService userBackupManagerService) {
+        mUserServices.put(userId, userBackupManagerService);
+
+        Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "backup enable");
+        userBackupManagerService.initializeBackupEnableState();
+        Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
+    }
+
+    /** Stops the backup service for user {@code userId} when the user is stopped. */
+    @VisibleForTesting
+    protected void stopServiceForUser(int userId) {
+        UserBackupManagerService userBackupManagerService = mUserServices.removeReturnOld(userId);
+
+        if (userBackupManagerService != null) {
+            userBackupManagerService.tearDownService();
+
+            KeyValueBackupJob.cancel(userId, mContext);
+            FullBackupJob.cancel(userId, mContext);
+        }
+    }
+
+    /**
+     *  Returns a list of users currently unlocked that have a {@link UserBackupManagerService}
+     *  registered.
+     *
+     *  Warning: Do NOT modify returned object as it's used inside.
+     *
+     *  TODO: Return a copy or only expose read-only information through other means.
+     */
+    @VisibleForTesting
+    SparseArray<UserBackupManagerService> getUserServices() {
+        return mUserServices;
+    }
+
+    /**
+     * Called from {@link BackupManagerService.Lifecycle} when a user {@code userId} is stopped.
+     * Offloads work onto the handler thread {@link #mHandlerThread} to keep stopping time low.
+     */
+    void onStopUser(int userId) {
+        postToHandler(
+                () -> {
+                    if (!mGlobalDisable) {
+                        Slog.i(TAG, "Stopping service for user: " + userId);
+                        stopServiceForUser(userId);
+                    }
+                });
+    }
+
+    /** Returns {@link UserBackupManagerService} for user {@code userId}. */
+    @Nullable
+    public UserBackupManagerService getUserService(int userId) {
+        return mUserServices.get(userId);
+    }
+
+    /**
+     * The system user and managed profiles can only be acted on by callers in the system or root
+     * processes. Other users can be acted on by callers who have both android.permission.BACKUP and
+     * android.permission.INTERACT_ACROSS_USERS_FULL permissions.
+     */
+    private void enforcePermissionsOnUser(int userId) throws SecurityException {
+        boolean isRestrictedUser =
+                userId == UserHandle.USER_SYSTEM
+                        || getUserManager().getUserInfo(userId).isManagedProfile();
+
+        if (isRestrictedUser) {
+            int caller = binderGetCallingUid();
+            if (caller != Process.SYSTEM_UID && caller != Process.ROOT_UID) {
+                throw new SecurityException("No permission to configure backup activity");
+            }
+        } else {
+            mContext.enforceCallingOrSelfPermission(
+                    Manifest.permission.BACKUP, "No permission to configure backup activity");
+            mContext.enforceCallingOrSelfPermission(
+                    Manifest.permission.INTERACT_ACROSS_USERS_FULL,
+                    "No permission to configure backup activity");
+        }
+    }
+
+    /**
+     * Only privileged callers should be changing the backup state. Deactivating backup in the
+     * system user also deactivates backup in all users. We are not guaranteed that {@code userId}
+     * is unlocked at this point yet, so handle both cases.
+     */
+    public void setBackupServiceActive(int userId, boolean makeActive) {
+        enforcePermissionsOnUser(userId);
+
+        // In Q, backup is OFF by default for non-system users. In the future, we will change that
+        // to ON unless backup was explicitly deactivated with a (permissioned) call to
+        // setBackupServiceActive.
+        // Therefore, remember this for use in the future. Basically the default in the future will
+        // be: rememberFile.exists() ? rememberFile.value() : ON
+        // Note that this has to be done right after the permission checks and before any other
+        // action since we need to remember that a permissioned call was made irrespective of
+        // whether the call changes the state or not.
+        if (userId != UserHandle.USER_SYSTEM) {
+            try {
+                File rememberFile = getRememberActivatedFileForNonSystemUser(userId);
+                createFile(rememberFile);
+                RandomAccessFileUtils.writeBoolean(rememberFile, makeActive);
+            } catch (IOException e) {
+                Slog.e(TAG, "Unable to persist backup service activity", e);
+            }
+        }
+
+        if (mGlobalDisable) {
+            Slog.i(TAG, "Backup service not supported");
+            return;
+        }
+
+        synchronized (mStateLock) {
+            Slog.i(TAG, "Making backup " + (makeActive ? "" : "in") + "active");
+            if (makeActive) {
+                try {
+                    activateBackupForUserLocked(userId);
+                } catch (IOException e) {
+                    Slog.e(TAG, "Unable to persist backup service activity");
+                }
+
+                // If the user is unlocked, we can start the backup service for it. Otherwise we
+                // will start the service when the user is unlocked as part of its unlock callback.
+                if (getUserManager().isUserUnlocked(userId)) {
+                    // Clear calling identity as initialization enforces the system identity but we
+                    // can be coming from shell.
+                    long oldId = Binder.clearCallingIdentity();
+                    try {
+                        startServiceForUser(userId);
+                    } finally {
+                        Binder.restoreCallingIdentity(oldId);
+                    }
+                }
+            } else {
+                try {
+                    //TODO(b/121198006): what if this throws an exception?
+                    deactivateBackupForUserLocked(userId);
+                } catch (IOException e) {
+                    Slog.e(TAG, "Unable to persist backup service inactivity");
+                }
+                //TODO(b/121198006): loop through active users that have work profile and
+                // stop them as well.
+                onStopUser(userId);
+            }
+        }
+    }
+
+    // IBackupManager binder API
+
+    /**
+     * Querying activity state of backup service.
+     *
+     * @param userId The user in which the activity state of backup service is queried.
+     * @return true if the service is active.
+     */
+    @Override
+    public boolean isBackupServiceActive(int userId) {
+        synchronized (mStateLock) {
+            return !mGlobalDisable && isBackupActivatedForUser(userId);
+        }
+    }
+
+    @Override
+    public void dataChangedForUser(int userId, String packageName) throws RemoteException {
+        if (isUserReadyForBackup(userId)) {
+            dataChanged(userId, packageName);
+        }
+    }
+
+    @Override
+    public void dataChanged(String packageName) throws RemoteException {
+        dataChangedForUser(binderGetCallingUserId(), packageName);
+    }
+
+    /**
+     * An app's backup agent calls this method to let the service know that there's new data to
+     * backup for their app {@code packageName}. Only used for apps participating in key-value
+     * backup.
+     */
+    public void dataChanged(@UserIdInt int userId, String packageName) {
+        UserBackupManagerService userBackupManagerService =
+                getServiceForUserIfCallerHasPermission(userId, "dataChanged()");
+
+        if (userBackupManagerService != null) {
+            userBackupManagerService.dataChanged(packageName);
+        }
+    }
+
+    // ---------------------------------------------
+    // TRANSPORT OPERATIONS
+    // ---------------------------------------------
+
+    @Override
+    public void initializeTransportsForUser(
+            int userId, String[] transportNames, IBackupObserver observer) throws RemoteException {
+        if (isUserReadyForBackup(userId)) {
+            initializeTransports(userId, transportNames, observer);
+        }
+    }
+
+    /** Run an initialize operation for the given transports {@code transportNames}. */
+    public void initializeTransports(
+            @UserIdInt int userId, String[] transportNames, IBackupObserver observer) {
+        UserBackupManagerService userBackupManagerService =
+                getServiceForUserIfCallerHasPermission(userId, "initializeTransports()");
+
+        if (userBackupManagerService != null) {
+            userBackupManagerService.initializeTransports(transportNames, observer);
+        }
+    }
+
+    @Override
+    public void clearBackupDataForUser(int userId, String transportName, String packageName)
+            throws RemoteException {
+        if (isUserReadyForBackup(userId)) {
+            clearBackupData(userId, transportName, packageName);
+        }
+    }
+
+    /**
+     * Clear the given package {@code packageName}'s backup data from the transport {@code
+     * transportName}.
+     */
+    public void clearBackupData(@UserIdInt int userId, String transportName, String packageName) {
+        UserBackupManagerService userBackupManagerService =
+                getServiceForUserIfCallerHasPermission(userId, "clearBackupData()");
+
+        if (userBackupManagerService != null) {
+            userBackupManagerService.clearBackupData(transportName, packageName);
+        }
+    }
+
+    @Override
+    public void clearBackupData(String transportName, String packageName)
+            throws RemoteException {
+        clearBackupDataForUser(binderGetCallingUserId(), transportName, packageName);
+    }
+
+    @Override
+    public void agentConnectedForUser(int userId, String packageName, IBinder agent)
+            throws RemoteException {
+        if (isUserReadyForBackup(userId)) {
+            agentConnected(userId, packageName, agent);
+        }
+    }
+
+    @Override
+    public void agentConnected(String packageName, IBinder agent) throws RemoteException {
+        agentConnectedForUser(binderGetCallingUserId(), packageName, agent);
+    }
+
+    /**
+     * Callback: a requested backup agent has been instantiated. This should only be called from the
+     * {@link ActivityManager}.
+     */
+    public void agentConnected(@UserIdInt int userId, String packageName, IBinder agentBinder) {
+        UserBackupManagerService userBackupManagerService =
+                getServiceForUserIfCallerHasPermission(userId, "agentConnected()");
+
+        if (userBackupManagerService != null) {
+            userBackupManagerService.agentConnected(packageName, agentBinder);
+        }
+    }
+
+    @Override
+    public void agentDisconnectedForUser(int userId, String packageName) throws RemoteException {
+        if (isUserReadyForBackup(userId)) {
+            agentDisconnected(userId, packageName);
+        }
+    }
+
+    @Override
+    public void agentDisconnected(String packageName) throws RemoteException {
+        agentDisconnectedForUser(binderGetCallingUserId(), packageName);
+    }
+
+    /**
+     * Callback: a backup agent has failed to come up, or has unexpectedly quit. This should only be
+     * called from the {@link ActivityManager}.
+     */
+    public void agentDisconnected(@UserIdInt int userId, String packageName) {
+        UserBackupManagerService userBackupManagerService =
+                getServiceForUserIfCallerHasPermission(userId, "agentDisconnected()");
+
+        if (userBackupManagerService != null) {
+            userBackupManagerService.agentDisconnected(packageName);
+        }
+    }
+
+    @Override
+    public void restoreAtInstallForUser(int userId, String packageName, int token)
+            throws RemoteException {
+        if (isUserReadyForBackup(userId)) {
+            restoreAtInstall(userId, packageName, token);
+        }
+    }
+
+    @Override
+    public void restoreAtInstall(String packageName, int token) throws RemoteException {
+        restoreAtInstallForUser(binderGetCallingUserId(), packageName, token);
+    }
+
+    /**
+     * Used to run a restore pass for an application that is being installed. This should only be
+     * called from the {@link PackageManager}.
+     */
+    public void restoreAtInstall(@UserIdInt int userId, String packageName, int token) {
+        UserBackupManagerService userBackupManagerService =
+                getServiceForUserIfCallerHasPermission(userId, "restoreAtInstall()");
+
+        if (userBackupManagerService != null) {
+            userBackupManagerService.restoreAtInstall(packageName, token);
+        }
+    }
+
+    @Override
+    public void setBackupEnabledForUser(@UserIdInt int userId, boolean isEnabled)
+            throws RemoteException {
+        if (isUserReadyForBackup(userId)) {
+            setBackupEnabled(userId, isEnabled);
+        }
+    }
+
+    @Override
+    public void setBackupEnabled(boolean isEnabled) throws RemoteException {
+        setBackupEnabledForUser(binderGetCallingUserId(), isEnabled);
+    }
+
+    /** Enable/disable the backup service. This is user-configurable via backup settings. */
+    public void setBackupEnabled(@UserIdInt int userId, boolean enable) {
+        UserBackupManagerService userBackupManagerService =
+                getServiceForUserIfCallerHasPermission(userId, "setBackupEnabled()");
+
+        if (userBackupManagerService != null) {
+            userBackupManagerService.setBackupEnabled(enable);
+        }
+    }
+
+    @Override
+    public void setAutoRestoreForUser(int userId, boolean doAutoRestore) throws RemoteException {
+        if (isUserReadyForBackup(userId)) {
+            setAutoRestore(userId, doAutoRestore);
+        }
+    }
+
+    @Override
+    public void setAutoRestore(boolean doAutoRestore) throws RemoteException {
+        setAutoRestoreForUser(binderGetCallingUserId(), doAutoRestore);
+    }
+
+    /** Enable/disable automatic restore of app data at install time. */
+    public void setAutoRestore(@UserIdInt int userId, boolean autoRestore) {
+        UserBackupManagerService userBackupManagerService =
+                getServiceForUserIfCallerHasPermission(userId, "setAutoRestore()");
+
+        if (userBackupManagerService != null) {
+            userBackupManagerService.setAutoRestore(autoRestore);
+        }
+    }
+
+    @Override
+    public boolean isBackupEnabledForUser(@UserIdInt int userId) throws RemoteException {
+        return isUserReadyForBackup(userId) && isBackupEnabled(userId);
+    }
+
+    @Override
+    public boolean isBackupEnabled() throws RemoteException {
+        return isBackupEnabledForUser(binderGetCallingUserId());
+    }
+
+    /**
+     * Return {@code true} if the backup mechanism is currently enabled, else returns {@code false}.
+     */
+    public boolean isBackupEnabled(@UserIdInt int userId) {
+        UserBackupManagerService userBackupManagerService =
+                getServiceForUserIfCallerHasPermission(userId, "isBackupEnabled()");
+
+        return userBackupManagerService != null && userBackupManagerService.isBackupEnabled();
+    }
+
+    /** Sets the backup password used when running adb backup. */
+    @Override
+    public boolean setBackupPassword(String currentPassword, String newPassword) {
+        int userId = binderGetCallingUserId();
+        if (!isUserReadyForBackup(userId)) {
+            return false;
+        }
+        UserBackupManagerService userBackupManagerService =
+                getServiceForUserIfCallerHasPermission(
+                        UserHandle.USER_SYSTEM, "setBackupPassword()");
+
+        return userBackupManagerService != null
+                && userBackupManagerService.setBackupPassword(currentPassword, newPassword);
+    }
+
+    /** Returns {@code true} if adb backup was run with a password, else returns {@code false}. */
+    @Override
+    public boolean hasBackupPassword() throws RemoteException {
+        int userId = binderGetCallingUserId();
+        if (!isUserReadyForBackup(userId)) {
+            return false;
+        }
+        UserBackupManagerService userBackupManagerService =
+                getServiceForUserIfCallerHasPermission(
+                        UserHandle.USER_SYSTEM, "hasBackupPassword()");
+
+        return userBackupManagerService != null && userBackupManagerService.hasBackupPassword();
+    }
+
+    @Override
+    public void backupNowForUser(@UserIdInt int userId) throws RemoteException {
+        if (isUserReadyForBackup(userId)) {
+            backupNow(userId);
+        }
+    }
+
+    @Override
+    public void backupNow() throws RemoteException {
+        backupNowForUser(binderGetCallingUserId());
+    }
+
+    /**
+     * Run a backup pass immediately for any key-value backup applications that have declared that
+     * they have pending updates.
+     */
+    public void backupNow(@UserIdInt int userId) {
+        UserBackupManagerService userBackupManagerService =
+                getServiceForUserIfCallerHasPermission(userId, "backupNow()");
+
+        if (userBackupManagerService != null) {
+            userBackupManagerService.backupNow();
+        }
+    }
+
+    /**
+     * Used by 'adb backup' to run a backup pass for packages {@code packageNames} supplied via the
+     * command line, writing the resulting data stream to the supplied {@code fd}. This method is
+     * synchronous and does not return to the caller until the backup has been completed. It
+     * requires on-screen confirmation by the user.
+     */
+    @Override
+    public void adbBackup(
+            @UserIdInt int userId,
+            ParcelFileDescriptor fd,
+            boolean includeApks,
+            boolean includeObbs,
+            boolean includeShared,
+            boolean doWidgets,
+            boolean doAllApps,
+            boolean includeSystem,
+            boolean doCompress,
+            boolean doKeyValue,
+            String[] packageNames) {
+        if (!isUserReadyForBackup(userId)) {
+            return;
+        }
+        UserBackupManagerService userBackupManagerService =
+                getServiceForUserIfCallerHasPermission(userId, "adbBackup()");
+
+        if (userBackupManagerService != null) {
+            userBackupManagerService.adbBackup(
+                    fd,
+                    includeApks,
+                    includeObbs,
+                    includeShared,
+                    doWidgets,
+                    doAllApps,
+                    includeSystem,
+                    doCompress,
+                    doKeyValue,
+                    packageNames);
+        }
+    }
+
+    @Override
+    public void fullTransportBackupForUser(int userId, String[] packageNames)
+            throws RemoteException {
+        if (isUserReadyForBackup(userId)) {
+            fullTransportBackup(userId, packageNames);
+        }
+    }
+
+    /**
+     * Run a full backup pass for the given packages {@code packageNames}. Used by 'adb shell bmgr'.
+     */
+    public void fullTransportBackup(@UserIdInt int userId, String[] packageNames) {
+        UserBackupManagerService userBackupManagerService =
+                getServiceForUserIfCallerHasPermission(userId, "fullTransportBackup()");
+
+        if (userBackupManagerService != null) {
+            userBackupManagerService.fullTransportBackup(packageNames);
+        }
+    }
+
+    /**
+     * Used by 'adb restore' to run a restore pass reading from the supplied {@code fd}. This method
+     * is synchronous and does not return to the caller until the restore has been completed. It
+     * requires on-screen confirmation by the user.
+     */
+    @Override
+    public void adbRestore(@UserIdInt int userId, ParcelFileDescriptor fd) {
+        if (!isUserReadyForBackup(userId)) {
+            return;
+        }
+        UserBackupManagerService userBackupManagerService =
+                getServiceForUserIfCallerHasPermission(userId, "adbRestore()");
+
+        if (userBackupManagerService != null) {
+            userBackupManagerService.adbRestore(fd);
+        }
+    }
+
+    @Override
+    public void acknowledgeFullBackupOrRestoreForUser(
+            int userId,
+            int token,
+            boolean allow,
+            String curPassword,
+            String encryptionPassword,
+            IFullBackupRestoreObserver observer)
+            throws RemoteException {
+        if (isUserReadyForBackup(userId)) {
+            acknowledgeAdbBackupOrRestore(userId, token, allow,
+                    curPassword, encryptionPassword, observer);
+        }
+    }
+
+    /**
+     * Confirm that the previously requested adb backup/restore operation can proceed. This is used
+     * to require a user-facing disclosure about the operation.
+     */
+    public void acknowledgeAdbBackupOrRestore(
+            @UserIdInt int userId,
+            int token,
+            boolean allow,
+            String currentPassword,
+            String encryptionPassword,
+            IFullBackupRestoreObserver observer) {
+        UserBackupManagerService userBackupManagerService =
+                getServiceForUserIfCallerHasPermission(userId, "acknowledgeAdbBackupOrRestore()");
+
+        if (userBackupManagerService != null) {
+            userBackupManagerService.acknowledgeAdbBackupOrRestore(
+                    token, allow, currentPassword, encryptionPassword, observer);
+        }
+    }
+
+    @Override
+    public void acknowledgeFullBackupOrRestore(int token, boolean allow, String curPassword,
+            String encryptionPassword, IFullBackupRestoreObserver observer)
+            throws RemoteException {
+        acknowledgeFullBackupOrRestoreForUser(
+                binderGetCallingUserId(), token, allow, curPassword, encryptionPassword, observer);
+    }
+
+
+    @Override
+    public String getCurrentTransportForUser(int userId) throws RemoteException {
+        return (isUserReadyForBackup(userId)) ? getCurrentTransport(userId) : null;
+    }
+
+    @Override
+    public String getCurrentTransport() throws RemoteException {
+        return getCurrentTransportForUser(binderGetCallingUserId());
+    }
+
+    /** Return the name of the currently active transport. */
+    @Nullable
+    public String getCurrentTransport(@UserIdInt int userId) {
+        UserBackupManagerService userBackupManagerService =
+                getServiceForUserIfCallerHasPermission(userId, "getCurrentTransport()");
+
+        return userBackupManagerService == null
+                ? null
+                : userBackupManagerService.getCurrentTransport();
+    }
+
+    /**
+     * Returns the {@link ComponentName} of the host service of the selected transport or
+     * {@code null} if no transport selected or if the transport selected is not registered.
+     */
+    @Override
+    @Nullable
+    public ComponentName getCurrentTransportComponentForUser(int userId) {
+        return (isUserReadyForBackup(userId)) ? getCurrentTransportComponent(userId) : null;
+    }
+
+    /**
+     * Returns the {@link ComponentName} of the host service of the selected transport or {@code
+     * null} if no transport selected or if the transport selected is not registered.
+     */
+    @Nullable
+    public ComponentName getCurrentTransportComponent(@UserIdInt int userId) {
+        UserBackupManagerService userBackupManagerService =
+                getServiceForUserIfCallerHasPermission(userId, "getCurrentTransportComponent()");
+
+        return userBackupManagerService == null
+                ? null
+                : userBackupManagerService.getCurrentTransportComponent();
+    }
+
+    @Override
+    public String[] listAllTransportsForUser(int userId) throws RemoteException {
+        return (isUserReadyForBackup(userId)) ? listAllTransports(userId) : null;
+    }
+
+    /** Report all known, available backup transports by name. */
+    @Nullable
+    public String[] listAllTransports(@UserIdInt int userId) {
+        UserBackupManagerService userBackupManagerService =
+                getServiceForUserIfCallerHasPermission(userId, "listAllTransports()");
+
+        return userBackupManagerService == null
+                ? null
+                : userBackupManagerService.listAllTransports();
+    }
+
+    @Override
+    public String[] listAllTransports() throws RemoteException {
+        return listAllTransportsForUser(binderGetCallingUserId());
+    }
+
+    @Override
+    public ComponentName[] listAllTransportComponentsForUser(int userId) throws RemoteException {
+        return (isUserReadyForBackup(userId))
+                ? listAllTransportComponents(userId) : null;
+    }
+
+    /** Report all known, available backup transports by {@link ComponentName}. */
+    @Nullable
+    public ComponentName[] listAllTransportComponents(@UserIdInt int userId) {
+        UserBackupManagerService userBackupManagerService =
+                getServiceForUserIfCallerHasPermission(userId, "listAllTransportComponents()");
+
+        return userBackupManagerService == null
+                ? null
+                : userBackupManagerService.listAllTransportComponents();
+    }
+
+    @Override
+    public String[] getTransportWhitelist() {
+        int userId = binderGetCallingUserId();
+        if (!isUserReadyForBackup(userId)) {
+            return null;
+        }
+        // No permission check, intentionally.
+        String[] whitelistedTransports = new String[mTransportWhitelist.size()];
+        int i = 0;
+        for (ComponentName component : mTransportWhitelist) {
+            whitelistedTransports[i] = component.flattenToShortString();
+            i++;
+        }
+        return whitelistedTransports;
+    }
+
+    @Override
+    public void updateTransportAttributesForUser(
+            int userId,
+            ComponentName transportComponent,
+            String name,
+            @Nullable Intent configurationIntent,
+            String currentDestinationString,
+            @Nullable Intent dataManagementIntent,
+            CharSequence dataManagementLabel) {
+        if (isUserReadyForBackup(userId)) {
+            updateTransportAttributes(
+                    userId,
+                    transportComponent,
+                    name,
+                    configurationIntent,
+                    currentDestinationString,
+                    dataManagementIntent,
+                    dataManagementLabel);
+        }
+    }
+
+    /**
+     * Update the attributes of the transport identified by {@code transportComponent}. If the
+     * specified transport has not been bound at least once (for registration), this call will be
+     * ignored. Only the host process of the transport can change its description, otherwise a
+     * {@link SecurityException} will be thrown.
+     *
+     * @param transportComponent The identity of the transport being described.
+     * @param name A {@link String} with the new name for the transport. This is NOT for
+     *     identification. MUST NOT be {@code null}.
+     * @param configurationIntent An {@link Intent} that can be passed to {@link
+     *     Context#startActivity} in order to launch the transport's configuration UI. It may be
+     *     {@code null} if the transport does not offer any user-facing configuration UI.
+     * @param currentDestinationString A {@link String} describing the destination to which the
+     *     transport is currently sending data. MUST NOT be {@code null}.
+     * @param dataManagementIntent An {@link Intent} that can be passed to {@link
+     *     Context#startActivity} in order to launch the transport's data-management UI. It may be
+     *     {@code null} if the transport does not offer any user-facing data management UI.
+     * @param dataManagementLabel A {@link CharSequence} to be used as the label for the transport's
+     *     data management affordance. This MUST be {@code null} when dataManagementIntent is {@code
+     *     null} and MUST NOT be {@code null} when dataManagementIntent is not {@code null}.
+     * @throws SecurityException If the UID of the calling process differs from the package UID of
+     *     {@code transportComponent} or if the caller does NOT have BACKUP permission.
+     */
+    public void updateTransportAttributes(
+            @UserIdInt int userId,
+            ComponentName transportComponent,
+            String name,
+            @Nullable Intent configurationIntent,
+            String currentDestinationString,
+            @Nullable Intent dataManagementIntent,
+            CharSequence dataManagementLabel) {
+        UserBackupManagerService userBackupManagerService =
+                getServiceForUserIfCallerHasPermission(userId, "updateTransportAttributes()");
+
+        if (userBackupManagerService != null) {
+            userBackupManagerService.updateTransportAttributes(
+                    transportComponent,
+                    name,
+                    configurationIntent,
+                    currentDestinationString,
+                    dataManagementIntent,
+                    dataManagementLabel);
+        }
+    }
+
+    @Override
+    public String selectBackupTransportForUser(int userId, String transport)
+            throws RemoteException {
+        return (isUserReadyForBackup(userId))
+                ? selectBackupTransport(userId, transport) : null;
+    }
+
+    @Override
+    public String selectBackupTransport(String transport) throws RemoteException {
+        return selectBackupTransportForUser(binderGetCallingUserId(), transport);
+    }
+
+    /**
+     * Selects transport {@code transportName} and returns the previously selected transport.
+     *
+     * @deprecated Use {@link #selectBackupTransportAsync(ComponentName,
+     *     ISelectBackupTransportCallback)} instead.
+     */
+    @Deprecated
+    @Nullable
+    public String selectBackupTransport(@UserIdInt int userId, String transportName) {
+        UserBackupManagerService userBackupManagerService =
+                getServiceForUserIfCallerHasPermission(userId, "selectBackupTransport()");
+
+        return userBackupManagerService == null
+                ? null
+                : userBackupManagerService.selectBackupTransport(transportName);
+    }
+
+    @Override
+    public void selectBackupTransportAsyncForUser(int userId, ComponentName transport,
+            ISelectBackupTransportCallback listener) throws RemoteException {
+        if (isUserReadyForBackup(userId)) {
+            selectBackupTransportAsync(userId, transport, listener);
+        } else {
+            if (listener != null) {
+                try {
+                    listener.onFailure(BackupManager.ERROR_BACKUP_NOT_ALLOWED);
+                } catch (RemoteException ex) {
+                    // ignore
+                }
+            }
+        }
+    }
+
+    /**
+     * Selects transport {@code transportComponent} asynchronously and notifies {@code listener}
+     * with the result upon completion.
+     */
+    public void selectBackupTransportAsync(
+            @UserIdInt int userId,
+            ComponentName transportComponent,
+            ISelectBackupTransportCallback listener) {
+        UserBackupManagerService userBackupManagerService =
+                getServiceForUserIfCallerHasPermission(userId, "selectBackupTransportAsync()");
+
+        if (userBackupManagerService != null) {
+            userBackupManagerService.selectBackupTransportAsync(transportComponent, listener);
+        }
+    }
+
+    @Override
+    public Intent getConfigurationIntentForUser(int userId, String transport)
+            throws RemoteException {
+        return isUserReadyForBackup(userId) ? getConfigurationIntent(userId, transport)
+                : null;
+    }
+
+    @Override
+    public Intent getConfigurationIntent(String transport)
+            throws RemoteException {
+        return getConfigurationIntentForUser(binderGetCallingUserId(), transport);
+    }
+
+    /**
+     * Supply the configuration intent for the given transport. If the name is not one of the
+     * available transports, or if the transport does not supply any configuration UI, the method
+     * returns {@code null}.
+     */
+    @Nullable
+    public Intent getConfigurationIntent(@UserIdInt int userId, String transportName) {
+        UserBackupManagerService userBackupManagerService =
+                getServiceForUserIfCallerHasPermission(userId, "getConfigurationIntent()");
+
+        return userBackupManagerService == null
+                ? null
+                : userBackupManagerService.getConfigurationIntent(transportName);
+    }
+
+    @Override
+    public String getDestinationStringForUser(int userId, String transport) throws RemoteException {
+        return isUserReadyForBackup(userId) ? getDestinationString(userId, transport)
+                : null;
+    }
+
+    @Override
+    public String getDestinationString(String transport) throws RemoteException {
+        return getDestinationStringForUser(binderGetCallingUserId(), transport);
+    }
+
+    /**
+     * Supply the current destination string for the given transport. If the name is not one of the
+     * registered transports the method will return null.
+     *
+     * <p>This string is used VERBATIM as the summary text of the relevant Settings item.
+     *
+     * @param transportName The name of the registered transport.
+     * @return The current destination string or null if the transport is not registered.
+     */
+    @Nullable
+    public String getDestinationString(@UserIdInt int userId, String transportName) {
+        UserBackupManagerService userBackupManagerService =
+                getServiceForUserIfCallerHasPermission(userId, "getDestinationString()");
+
+        return userBackupManagerService == null
+                ? null
+                : userBackupManagerService.getDestinationString(transportName);
+    }
+
+    @Override
+    public Intent getDataManagementIntentForUser(int userId, String transport)
+            throws RemoteException {
+        return isUserReadyForBackup(userId)
+                ? getDataManagementIntent(userId, transport) : null;
+    }
+
+    @Override
+    public Intent getDataManagementIntent(String transport)
+            throws RemoteException {
+        return getDataManagementIntentForUser(binderGetCallingUserId(), transport);
+    }
+
+    /** Supply the manage-data intent for the given transport. */
+    @Nullable
+    public Intent getDataManagementIntent(@UserIdInt int userId, String transportName) {
+        UserBackupManagerService userBackupManagerService =
+                getServiceForUserIfCallerHasPermission(userId, "getDataManagementIntent()");
+
+        return userBackupManagerService == null
+                ? null
+                : userBackupManagerService.getDataManagementIntent(transportName);
+    }
+
+    @Override
+    public CharSequence getDataManagementLabelForUser(int userId, String transport)
+            throws RemoteException {
+        return isUserReadyForBackup(userId) ? getDataManagementLabel(userId, transport)
+                : null;
+    }
+
+    /**
+     * Supply the menu label for affordances that fire the manage-data intent for the given
+     * transport.
+     */
+    @Nullable
+    public CharSequence getDataManagementLabel(@UserIdInt int userId, String transportName) {
+        UserBackupManagerService userBackupManagerService =
+                getServiceForUserIfCallerHasPermission(userId, "getDataManagementLabel()");
+
+        return userBackupManagerService == null
+                ? null
+                : userBackupManagerService.getDataManagementLabel(transportName);
+    }
+
+    @Override
+    public IRestoreSession beginRestoreSessionForUser(
+            int userId, String packageName, String transportID) throws RemoteException {
+        return isUserReadyForBackup(userId)
+                ? beginRestoreSession(userId, packageName, transportID) : null;
+    }
+
+    /**
+     * Begin a restore for the specified package {@code packageName} using the specified transport
+     * {@code transportName}.
+     */
+    @Nullable
+    public IRestoreSession beginRestoreSession(
+            @UserIdInt int userId, String packageName, String transportName) {
+        UserBackupManagerService userBackupManagerService =
+                getServiceForUserIfCallerHasPermission(userId, "beginRestoreSession()");
+
+        return userBackupManagerService == null
+                ? null
+                : userBackupManagerService.beginRestoreSession(packageName, transportName);
+    }
+
+    @Override
+    public void opCompleteForUser(int userId, int token, long result) throws RemoteException {
+        if (isUserReadyForBackup(userId)) {
+            opComplete(userId, token, result);
+        }
+    }
+
+    @Override
+    public void opComplete(int token, long result) throws RemoteException {
+        opCompleteForUser(binderGetCallingUserId(), token, result);
+    }
+
+    /**
+     * Used by a currently-active backup agent to notify the service that it has completed its given
+     * outstanding asynchronous backup/restore operation.
+     */
+    public void opComplete(@UserIdInt int userId, int token, long result) {
+        UserBackupManagerService userBackupManagerService =
+                getServiceForUserIfCallerHasPermission(userId, "opComplete()");
+
+        if (userBackupManagerService != null) {
+            userBackupManagerService.opComplete(token, result);
+        }
+    }
+
+    @Override
+    public long getAvailableRestoreTokenForUser(int userId, String packageName) {
+        return isUserReadyForBackup(userId) ? getAvailableRestoreToken(userId, packageName) : 0;
+    }
+
+    /**
+     * Get the restore-set token for the best-available restore set for this {@code packageName}:
+     * the active set if possible, else the ancestral one. Returns zero if none available.
+     */
+    public long getAvailableRestoreToken(@UserIdInt int userId, String packageName) {
+        UserBackupManagerService userBackupManagerService =
+                getServiceForUserIfCallerHasPermission(userId, "getAvailableRestoreToken()");
+
+        return userBackupManagerService == null
+                ? 0
+                : userBackupManagerService.getAvailableRestoreToken(packageName);
+    }
+
+    @Override
+    public boolean isAppEligibleForBackupForUser(int userId, String packageName) {
+        return isUserReadyForBackup(userId) && isAppEligibleForBackup(userId,
+                packageName);
+    }
+
+    /** Checks if the given package {@code packageName} is eligible for backup. */
+    public boolean isAppEligibleForBackup(@UserIdInt int userId, String packageName) {
+        UserBackupManagerService userBackupManagerService =
+                getServiceForUserIfCallerHasPermission(userId, "isAppEligibleForBackup()");
+
+        return userBackupManagerService != null
+                && userBackupManagerService.isAppEligibleForBackup(packageName);
+    }
+
+    @Override
+    public String[] filterAppsEligibleForBackupForUser(int userId, String[] packages) {
+        return isUserReadyForBackup(userId) ? filterAppsEligibleForBackup(userId, packages) : null;
+    }
+
+    /**
+     * Returns from the inputted packages {@code packages}, the ones that are eligible for backup.
+     */
+    @Nullable
+    public String[] filterAppsEligibleForBackup(@UserIdInt int userId, String[] packages) {
+        UserBackupManagerService userBackupManagerService =
+                getServiceForUserIfCallerHasPermission(userId, "filterAppsEligibleForBackup()");
+
+        return userBackupManagerService == null
+                ? null
+                : userBackupManagerService.filterAppsEligibleForBackup(packages);
+    }
+
+    @Override
+    public int requestBackupForUser(@UserIdInt int userId, String[] packages, IBackupObserver
+            observer, IBackupManagerMonitor monitor, int flags) throws RemoteException {
+        if (!isUserReadyForBackup(userId)) {
+            return BackupManager.ERROR_BACKUP_NOT_ALLOWED;
+        }
+        return requestBackup(userId, packages, observer, monitor, flags);
+    }
+
+    @Override
+    public int requestBackup(String[] packages, IBackupObserver observer,
+            IBackupManagerMonitor monitor, int flags) throws RemoteException {
+        return requestBackupForUser(binderGetCallingUserId(), packages,
+                observer, monitor, flags);
+    }
+
+    /**
+     * Requests a backup for the inputted {@code packages} with a specified callback {@link
+     * IBackupManagerMonitor} for receiving events during the operation.
+     */
+    public int requestBackup(
+            @UserIdInt int userId,
+            String[] packages,
+            IBackupObserver observer,
+            IBackupManagerMonitor monitor,
+            int flags) {
+        UserBackupManagerService userBackupManagerService =
+                getServiceForUserIfCallerHasPermission(userId, "requestBackup()");
+
+        return userBackupManagerService == null
+                ? BackupManager.ERROR_BACKUP_NOT_ALLOWED
+                : userBackupManagerService.requestBackup(packages, observer, monitor, flags);
+    }
+
+    @Override
+    public void cancelBackupsForUser(@UserIdInt int userId) throws RemoteException {
+        if (isUserReadyForBackup(userId)) {
+            cancelBackups(userId);
+        }
+    }
+
+    @Override
+    public void cancelBackups() throws RemoteException {
+        cancelBackupsForUser(binderGetCallingUserId());
+    }
+
+    /** Cancel all running backup operations. */
+    public void cancelBackups(@UserIdInt int userId) {
+        UserBackupManagerService userBackupManagerService =
+                getServiceForUserIfCallerHasPermission(userId, "cancelBackups()");
+
+        if (userBackupManagerService != null) {
+            userBackupManagerService.cancelBackups();
+        }
+    }
+
+    /**
+     * Returns a {@link UserHandle} for the user that has {@code ancestralSerialNumber} as the
+     * serial number of its ancestral work profile or null if there is no {@link
+     * UserBackupManagerService} associated with that user.
+     *
+     * <p> The ancestral work profile is set by {@link #setAncestralSerialNumber(long)}
+     * and it corresponds to the profile that was used to restore to the callers profile.
+     */
+    @Override
+    @Nullable
+    public UserHandle getUserForAncestralSerialNumber(long ancestralSerialNumber) {
+        if (mGlobalDisable) {
+            return null;
+        }
+        int callingUserId = Binder.getCallingUserHandle().getIdentifier();
+        long oldId = Binder.clearCallingIdentity();
+        final int[] userIds;
+        try {
+            userIds =
+                    mContext
+                            .getSystemService(UserManager.class)
+                            .getProfileIds(callingUserId, false);
+        } finally {
+            Binder.restoreCallingIdentity(oldId);
+        }
+
+        for (int userId : userIds) {
+            UserBackupManagerService userBackupManagerService = mUserServices.get(userId);
+            if (userBackupManagerService != null) {
+                if (userBackupManagerService.getAncestralSerialNumber() == ancestralSerialNumber) {
+                    return UserHandle.of(userId);
+                }
+            }
+        }
+
+        return null;
+    }
+
+    /**
+     * Sets the ancestral work profile for the calling user.
+     *
+     * <p> The ancestral work profile corresponds to the profile that was used to restore to the
+     * callers profile.
+     */
+    @Override
+    public void setAncestralSerialNumber(long ancestralSerialNumber) {
+        if (mGlobalDisable) {
+            return;
+        }
+        UserBackupManagerService userBackupManagerService =
+                getServiceForUserIfCallerHasPermission(
+                        Binder.getCallingUserHandle().getIdentifier(),
+                        "setAncestralSerialNumber()");
+
+        if (userBackupManagerService != null) {
+            userBackupManagerService.setAncestralSerialNumber(ancestralSerialNumber);
+        }
+    }
+
+    @Override
+    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+        if (!DumpUtils.checkDumpAndUsageStatsPermission(mContext, TAG, pw)) {
+            return;
+        }
+        int userId = binderGetCallingUserId();
+        if (!isUserReadyForBackup(userId)) {
+            pw.println("Inactive");
+            return;
+        }
+
+        if (args != null) {
+            for (String arg : args) {
+                if ("users".equals(arg.toLowerCase())) {
+                    pw.print(DUMP_RUNNING_USERS_MESSAGE);
+                    for (int i = 0; i < mUserServices.size(); i++) {
+                        pw.print(" " + mUserServices.keyAt(i));
+                    }
+                    pw.println();
+                    return;
+                }
+            }
+        }
+
+        UserBackupManagerService userBackupManagerService =
+                getServiceForUserIfCallerHasPermission(UserHandle.USER_SYSTEM, "dump()");
+
+        if (userBackupManagerService != null) {
+            userBackupManagerService.dump(fd, pw, args);
+        }
+    }
+
+    /**
+     * Used by the {@link JobScheduler} to run a full backup when conditions are right. The model we
+     * use is to perform one app backup per scheduled job execution, and to reschedule the job with
+     * zero latency as long as conditions remain right and we still have work to do.
+     *
+     * @return Whether ongoing work will continue. The return value here will be passed along as the
+     *     return value to the callback {@link JobService#onStartJob(JobParameters)}.
+     */
+    public boolean beginFullBackup(@UserIdInt int userId, FullBackupJob scheduledJob) {
+        if (!isUserReadyForBackup(userId)) {
+            return false;
+        }
+        UserBackupManagerService userBackupManagerService =
+                getServiceForUserIfCallerHasPermission(userId, "beginFullBackup()");
+
+        return userBackupManagerService != null
+                && userBackupManagerService.beginFullBackup(scheduledJob);
+    }
+
+    /**
+     * Used by the {@link JobScheduler} to end the current full backup task when conditions are no
+     * longer met for running the full backup job.
+     */
+    public void endFullBackup(@UserIdInt int userId) {
+        if (!isUserReadyForBackup(userId)) {
+            return;
+        }
+        UserBackupManagerService userBackupManagerService =
+                getServiceForUserIfCallerHasPermission(userId, "endFullBackup()");
+
+        if (userBackupManagerService != null) {
+            userBackupManagerService.endFullBackup();
+        }
+    }
+
+    /**
+     * Returns the {@link UserBackupManagerService} instance for the specified user {@code userId}.
+     * If the user is not registered with the service (either the user is locked or not eligible for
+     * the backup service) then return {@code null}.
+     *
+     * @param userId The id of the user to retrieve its instance of {@link
+     *     UserBackupManagerService}.
+     * @param caller A {@link String} identifying the caller for logging purposes.
+     * @throws SecurityException if {@code userId} is different from the calling user id and the
+     *     caller does NOT have the android.permission.INTERACT_ACROSS_USERS_FULL permission.
+     */
+    @Nullable
+    @VisibleForTesting
+    UserBackupManagerService getServiceForUserIfCallerHasPermission(
+            @UserIdInt int userId, String caller) {
+        enforceCallingPermissionOnUserId(userId, caller);
+        UserBackupManagerService userBackupManagerService = mUserServices.get(userId);
+        if (userBackupManagerService == null) {
+            Slog.w(TAG, "Called " + caller + " for unknown user: " + userId);
+        }
+        return userBackupManagerService;
+    }
+
+    /**
+     * If {@code userId} is different from the calling user id, then the caller must hold the
+     * android.permission.INTERACT_ACROSS_USERS_FULL permission.
+     *
+     * @param userId User id on which the backup operation is being requested.
+     * @param message A message to include in the exception if it is thrown.
+     */
+    void enforceCallingPermissionOnUserId(@UserIdInt int userId, String message) {
+        if (Binder.getCallingUserHandle().getIdentifier() != userId) {
+            mContext.enforceCallingOrSelfPermission(
+                    Manifest.permission.INTERACT_ACROSS_USERS_FULL, message);
+        }
     }
 
     /** Implementation to receive lifecycle event callbacks for system services. */
     public static class Lifecycle extends SystemService {
         public Lifecycle(Context context) {
-            this(context, new Trampoline(context));
+            this(context, new BackupManagerService(context));
         }
 
         @VisibleForTesting
-        Lifecycle(Context context, Trampoline trampoline) {
+        Lifecycle(Context context, BackupManagerService backupManagerService) {
             super(context);
-            Trampoline.sInstance = trampoline;
+            sInstance = backupManagerService;
         }
 
         @Override
         public void onStart() {
-            publishService(Context.BACKUP_SERVICE, Trampoline.sInstance);
+            publishService(Context.BACKUP_SERVICE, BackupManagerService.sInstance);
         }
 
         @Override
         public void onUnlockUser(int userId) {
-            Trampoline.sInstance.onUnlockUser(userId);
+            sInstance.onUnlockUser(userId);
         }
 
         @Override
         public void onStopUser(int userId) {
-            Trampoline.sInstance.onStopUser(userId);
+            sInstance.onStopUser(userId);
         }
 
         @VisibleForTesting
diff --git a/services/backup/java/com/android/server/backup/FullBackupJob.java b/services/backup/java/com/android/server/backup/FullBackupJob.java
index 19a8543..0bb25e3 100644
--- a/services/backup/java/com/android/server/backup/FullBackupJob.java
+++ b/services/backup/java/com/android/server/backup/FullBackupJob.java
@@ -91,7 +91,7 @@
             mParamsForUser.put(userId, params);
         }
 
-        Trampoline service = Trampoline.getInstance();
+        BackupManagerService service = BackupManagerService.getInstance();
         return service.beginFullBackup(userId, this);
     }
 
@@ -105,7 +105,7 @@
             }
         }
 
-        Trampoline service = Trampoline.getInstance();
+        BackupManagerService service = BackupManagerService.getInstance();
         service.endFullBackup(userId);
 
         return false;
diff --git a/services/backup/java/com/android/server/backup/KeyValueBackupJob.java b/services/backup/java/com/android/server/backup/KeyValueBackupJob.java
index 7b5dbd7..058dcae 100644
--- a/services/backup/java/com/android/server/backup/KeyValueBackupJob.java
+++ b/services/backup/java/com/android/server/backup/KeyValueBackupJob.java
@@ -144,7 +144,7 @@
         }
 
         // Time to run a key/value backup!
-        Trampoline service = Trampoline.getInstance();
+        BackupManagerService service = BackupManagerService.getInstance();
         try {
             service.backupNowForUser(userId);
         } catch (RemoteException e) {}
diff --git a/services/backup/java/com/android/server/backup/Trampoline.java b/services/backup/java/com/android/server/backup/Trampoline.java
deleted file mode 100644
index 4cd1e17..0000000
--- a/services/backup/java/com/android/server/backup/Trampoline.java
+++ /dev/null
@@ -1,1549 +0,0 @@
-/*
- * Copyright (C) 2014 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;
-
-import static com.android.internal.util.Preconditions.checkNotNull;
-import static com.android.server.backup.BackupManagerService.TAG;
-
-import static java.util.Collections.emptySet;
-
-import android.Manifest;
-import android.annotation.Nullable;
-import android.annotation.UserIdInt;
-import android.app.ActivityManager;
-import android.app.admin.DevicePolicyManager;
-import android.app.backup.BackupManager;
-import android.app.backup.IBackupManager;
-import android.app.backup.IBackupManagerMonitor;
-import android.app.backup.IBackupObserver;
-import android.app.backup.IFullBackupRestoreObserver;
-import android.app.backup.IRestoreSession;
-import android.app.backup.ISelectBackupTransportCallback;
-import android.app.job.JobParameters;
-import android.app.job.JobScheduler;
-import android.app.job.JobService;
-import android.content.BroadcastReceiver;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.content.pm.PackageManager;
-import android.os.Binder;
-import android.os.FileUtils;
-import android.os.Handler;
-import android.os.HandlerThread;
-import android.os.IBinder;
-import android.os.ParcelFileDescriptor;
-import android.os.Process;
-import android.os.RemoteException;
-import android.os.SystemProperties;
-import android.os.Trace;
-import android.os.UserHandle;
-import android.os.UserManager;
-import android.util.Slog;
-import android.util.SparseArray;
-
-import com.android.internal.annotations.GuardedBy;
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.util.DumpUtils;
-import com.android.server.SystemConfig;
-import com.android.server.backup.utils.RandomAccessFileUtils;
-
-import java.io.File;
-import java.io.FileDescriptor;
-import java.io.IOException;
-import java.io.PrintWriter;
-import java.util.Set;
-
-/**
- * A proxy to the {@link BackupManagerService} implementation.
- *
- * <p>This is an external interface to the {@link BackupManagerService} which is being accessed via
- * published binder {@link BackupManagerService.Lifecycle}. This lets us turn down the heavy
- * implementation object on the fly without disturbing binders that have been cached somewhere in
- * the system.
- *
- * <p>Trampoline determines whether the backup service is available. It can be disabled in the
- * following two ways:
- *
- * <ul>
- * <li>Temporary - create the file {@link #BACKUP_SUPPRESS_FILENAME}, or
- * <li>Permanent - set the system property {@link #BACKUP_DISABLE_PROPERTY} to true.
- * </ul>
- *
- * Temporary disabling is controlled by {@link #setBackupServiceActive(int, boolean)} through
- * privileged callers (currently {@link DevicePolicyManager}). This is called on {@link
- * UserHandle#USER_SYSTEM} and disables backup for all users.
- */
-public class Trampoline extends IBackupManager.Stub {
-    @VisibleForTesting
-    static final String DUMP_RUNNING_USERS_MESSAGE = "Backup Manager is running for users:";
-
-    /**
-     * Name of file that disables the backup service. If this file exists, then backup is disabled
-     * for all users.
-     */
-    private static final String BACKUP_SUPPRESS_FILENAME = "backup-suppress";
-
-    /**
-     * Name of file for non-system users that enables the backup service for the user. Backup is
-     * disabled by default in non-system users.
-     */
-    private static final String BACKUP_ACTIVATED_FILENAME = "backup-activated";
-
-    /**
-     * Name of file for non-system users that remembers whether backup was explicitly activated or
-     * deactivated with a call to setBackupServiceActive.
-     */
-    private static final String REMEMBER_ACTIVATED_FILENAME = "backup-remember-activated";
-
-    // Product-level suppression of backup/restore.
-    private static final String BACKUP_DISABLE_PROPERTY = "ro.backup.disable";
-
-    private static final String BACKUP_THREAD = "backup";
-
-    static Trampoline sInstance;
-
-    static Trampoline getInstance() {
-        return checkNotNull(sInstance);
-    }
-
-    private final Context mContext;
-    private final UserManager mUserManager;
-
-    private final boolean mGlobalDisable;
-    // Lock to write backup suppress files.
-    // TODD(b/121198006): remove this object and synchronized all methods on "this".
-    private final Object mStateLock = new Object();
-
-    // TODO: This is not marked as final because of test code. Since we'll merge BMS and Trampoline,
-    // it doesn't make sense to refactor for final. It's never null.
-    @VisibleForTesting
-    protected volatile BackupManagerService mService;
-    private final Handler mHandler;
-    private final Set<ComponentName> mTransportWhitelist;
-
-    /** Keeps track of all unlocked users registered with this service. Indexed by user id. */
-    private final SparseArray<UserBackupManagerService> mUserServices;
-
-    private final BroadcastReceiver mUserRemovedReceiver = new BroadcastReceiver() {
-        @Override
-        public void onReceive(Context context, Intent intent) {
-            if (Intent.ACTION_USER_REMOVED.equals(intent.getAction())) {
-                int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL);
-                if (userId > 0) { // for only non system users
-                    mHandler.post(() -> onRemovedNonSystemUser(userId));
-                }
-            }
-        }
-    };
-
-    public Trampoline(Context context) {
-        this(context, new SparseArray<>());
-    }
-
-    @VisibleForTesting
-    Trampoline(Context context, SparseArray<UserBackupManagerService> userServices) {
-        mContext = context;
-        mGlobalDisable = isBackupDisabled();
-        HandlerThread handlerThread =
-                new HandlerThread(BACKUP_THREAD, Process.THREAD_PRIORITY_BACKGROUND);
-        handlerThread.start();
-        mHandler = new Handler(handlerThread.getLooper());
-        mUserManager = UserManager.get(context);
-        mUserServices = userServices;
-        mService = new BackupManagerService(mContext, this, mUserServices);
-        Set<ComponentName> transportWhitelist =
-                SystemConfig.getInstance().getBackupTransportWhitelist();
-        mTransportWhitelist = (transportWhitelist == null) ? emptySet() : transportWhitelist;
-        mContext.registerReceiver(
-                mUserRemovedReceiver, new IntentFilter(Intent.ACTION_USER_REMOVED));
-    }
-
-    // TODO: Remove this when we implement DI by injecting in the construtor.
-    @VisibleForTesting
-    Handler getBackupHandler() {
-        return mHandler;
-    }
-
-    protected boolean isBackupDisabled() {
-        return SystemProperties.getBoolean(BACKUP_DISABLE_PROPERTY, false);
-    }
-
-    protected int binderGetCallingUserId() {
-        return Binder.getCallingUserHandle().getIdentifier();
-    }
-
-    protected int binderGetCallingUid() {
-        return Binder.getCallingUid();
-    }
-
-    /** Stored in the system user's directory. */
-    protected File getSuppressFileForSystemUser() {
-        return new File(UserBackupManagerFiles.getBaseStateDir(UserHandle.USER_SYSTEM),
-                BACKUP_SUPPRESS_FILENAME);
-    }
-
-    /** Stored in the system user's directory and the file is indexed by the user it refers to. */
-    protected File getRememberActivatedFileForNonSystemUser(int userId) {
-        return UserBackupManagerFiles.getStateFileInSystemDir(REMEMBER_ACTIVATED_FILENAME, userId);
-    }
-
-    /** Stored in the system user's directory and the file is indexed by the user it refers to. */
-    protected File getActivatedFileForNonSystemUser(int userId) {
-        return UserBackupManagerFiles.getStateFileInSystemDir(BACKUP_ACTIVATED_FILENAME, userId);
-    }
-
-    /**
-     * Remove backup state for non system {@code userId} when the user is removed from the device.
-     * For non system users, backup state is stored in both the user's own dir and the system dir.
-     * When the user is removed, the user's own dir gets removed by the OS. This method ensures that
-     * the part of the user backup state which is in the system dir also gets removed.
-     */
-    private void onRemovedNonSystemUser(int userId) {
-        Slog.i(TAG, "Removing state for non system user " + userId);
-        File dir = UserBackupManagerFiles.getStateDirInSystemDir(userId);
-        if (!FileUtils.deleteContentsAndDir(dir)) {
-            Slog.w(TAG, "Failed to delete state dir for removed user: " + userId);
-        }
-    }
-
-    // TODO (b/124359804) move to util method in FileUtils
-    private void createFile(File file) throws IOException {
-        if (file.exists()) {
-            return;
-        }
-
-        file.getParentFile().mkdirs();
-        if (!file.createNewFile()) {
-            Slog.w(TAG, "Failed to create file " + file.getPath());
-        }
-    }
-
-    // TODO (b/124359804) move to util method in FileUtils
-    private void deleteFile(File file) {
-        if (!file.exists()) {
-            return;
-        }
-
-        if (!file.delete()) {
-            Slog.w(TAG, "Failed to delete file " + file.getPath());
-        }
-    }
-
-    /**
-     * Deactivates the backup service for user {@code userId}. If this is the system user, it
-     * creates a suppress file which disables backup for all users. If this is a non-system user, it
-     * only deactivates backup for that user by deleting its activate file.
-     */
-    @GuardedBy("mStateLock")
-    private void deactivateBackupForUserLocked(int userId) throws IOException {
-        if (userId == UserHandle.USER_SYSTEM) {
-            createFile(getSuppressFileForSystemUser());
-        } else {
-            deleteFile(getActivatedFileForNonSystemUser(userId));
-        }
-    }
-
-    /**
-     * Enables the backup service for user {@code userId}. If this is the system user, it deletes
-     * the suppress file. If this is a non-system user, it creates the user's activate file. Note,
-     * deleting the suppress file does not automatically enable backup for non-system users, they
-     * need their own activate file in order to participate in the service.
-     */
-    @GuardedBy("mStateLock")
-    private void activateBackupForUserLocked(int userId) throws IOException {
-        if (userId == UserHandle.USER_SYSTEM) {
-            deleteFile(getSuppressFileForSystemUser());
-        } else {
-            createFile(getActivatedFileForNonSystemUser(userId));
-        }
-    }
-
-    // This method should not perform any I/O (e.g. do not call isBackupActivatedForUser),
-    // it's used in multiple places where I/O waits would cause system lock-ups.
-    private boolean isUserReadyForBackup(int userId) {
-        return mUserServices.get(UserHandle.USER_SYSTEM) != null
-                && mUserServices.get(userId) != null;
-    }
-
-    /**
-     * Backup is activated for the system user if the suppress file does not exist. Backup is
-     * activated for non-system users if the suppress file does not exist AND the user's activated
-     * file exists.
-     */
-    private boolean isBackupActivatedForUser(int userId) {
-        if (getSuppressFileForSystemUser().exists()) {
-            return false;
-        }
-
-        return userId == UserHandle.USER_SYSTEM
-                || getActivatedFileForNonSystemUser(userId).exists();
-    }
-
-    protected Context getContext() {
-        return mContext;
-    }
-
-    protected UserManager getUserManager() {
-        return mUserManager;
-    }
-
-    protected void postToHandler(Runnable runnable) {
-        mHandler.post(runnable);
-    }
-
-    /**
-     * Called from {@link BackupManagerService.Lifecycle} when a user {@code userId} is unlocked.
-     * Starts the backup service for this user if backup is active for this user. Offloads work onto
-     * the handler thread {@link #mHandlerThread} to keep unlock time low since backup is not
-     * essential for device functioning.
-     */
-    void onUnlockUser(int userId) {
-        postToHandler(() -> startServiceForUser(userId));
-    }
-
-    /**
-     * Starts the backup service for user {@code userId} by creating a new instance of {@link
-     * UserBackupManagerService} and registering it with this service.
-     */
-    @VisibleForTesting
-    void startServiceForUser(int userId) {
-        // We know that the user is unlocked here because it is called from setBackupServiceActive
-        // and unlockUser which have these guarantees. So we can check if the file exists.
-        if (mGlobalDisable) {
-            Slog.i(TAG, "Backup service not supported");
-            return;
-        }
-        if (!isBackupActivatedForUser(userId)) {
-            Slog.i(TAG, "Backup not activated for user " + userId);
-            return;
-        }
-        if (mUserServices.get(userId) != null) {
-            Slog.i(TAG, "userId " + userId + " already started, so not starting again");
-            return;
-        }
-        Slog.i(TAG, "Starting service for user: " + userId);
-        UserBackupManagerService userBackupManagerService =
-                UserBackupManagerService.createAndInitializeService(
-                        userId, mContext, this, mTransportWhitelist);
-        startServiceForUser(userId, userBackupManagerService);
-    }
-
-    /**
-     * Starts the backup service for user {@code userId} by registering its instance of {@link
-     * UserBackupManagerService} with this service and setting enabled state.
-     */
-    void startServiceForUser(int userId, UserBackupManagerService userBackupManagerService) {
-        mUserServices.put(userId, userBackupManagerService);
-
-        Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "backup enable");
-        userBackupManagerService.initializeBackupEnableState();
-        Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
-    }
-
-    /** Stops the backup service for user {@code userId} when the user is stopped. */
-    @VisibleForTesting
-    protected void stopServiceForUser(int userId) {
-        UserBackupManagerService userBackupManagerService = mUserServices.removeReturnOld(userId);
-
-        if (userBackupManagerService != null) {
-            userBackupManagerService.tearDownService();
-
-            KeyValueBackupJob.cancel(userId, mContext);
-            FullBackupJob.cancel(userId, mContext);
-        }
-    }
-
-    /**
-     *  Returns a list of users currently unlocked that have a {@link UserBackupManagerService}
-     *  registered.
-     *
-     *  Warning: Do NOT modify returned object as it's used inside.
-     *
-     *  TODO: Return a copy or only expose read-only information through other means.
-     */
-    @VisibleForTesting
-    SparseArray<UserBackupManagerService> getUserServices() {
-        return mUserServices;
-    }
-
-    /**
-     * Called from {@link BackupManagerService.Lifecycle} when a user {@code userId} is stopped.
-     * Offloads work onto the handler thread {@link #mHandlerThread} to keep stopping time low.
-     */
-    void onStopUser(int userId) {
-        postToHandler(
-                () -> {
-                    if (!mGlobalDisable) {
-                        Slog.i(TAG, "Stopping service for user: " + userId);
-                        stopServiceForUser(userId);
-                    }
-                });
-    }
-
-    /** Returns {@link UserBackupManagerService} for user {@code userId}. */
-    @Nullable
-    public UserBackupManagerService getUserService(int userId) {
-        return mUserServices.get(userId);
-    }
-
-    /**
-     * The system user and managed profiles can only be acted on by callers in the system or root
-     * processes. Other users can be acted on by callers who have both android.permission.BACKUP and
-     * android.permission.INTERACT_ACROSS_USERS_FULL permissions.
-     */
-    private void enforcePermissionsOnUser(int userId) throws SecurityException {
-        boolean isRestrictedUser =
-                userId == UserHandle.USER_SYSTEM
-                        || getUserManager().getUserInfo(userId).isManagedProfile();
-
-        if (isRestrictedUser) {
-            int caller = binderGetCallingUid();
-            if (caller != Process.SYSTEM_UID && caller != Process.ROOT_UID) {
-                throw new SecurityException("No permission to configure backup activity");
-            }
-        } else {
-            mContext.enforceCallingOrSelfPermission(
-                    Manifest.permission.BACKUP, "No permission to configure backup activity");
-            mContext.enforceCallingOrSelfPermission(
-                    Manifest.permission.INTERACT_ACROSS_USERS_FULL,
-                    "No permission to configure backup activity");
-        }
-    }
-
-    /**
-     * Only privileged callers should be changing the backup state. Deactivating backup in the
-     * system user also deactivates backup in all users. We are not guaranteed that {@code userId}
-     * is unlocked at this point yet, so handle both cases.
-     */
-    public void setBackupServiceActive(int userId, boolean makeActive) {
-        enforcePermissionsOnUser(userId);
-
-        // In Q, backup is OFF by default for non-system users. In the future, we will change that
-        // to ON unless backup was explicitly deactivated with a (permissioned) call to
-        // setBackupServiceActive.
-        // Therefore, remember this for use in the future. Basically the default in the future will
-        // be: rememberFile.exists() ? rememberFile.value() : ON
-        // Note that this has to be done right after the permission checks and before any other
-        // action since we need to remember that a permissioned call was made irrespective of
-        // whether the call changes the state or not.
-        if (userId != UserHandle.USER_SYSTEM) {
-            try {
-                File rememberFile = getRememberActivatedFileForNonSystemUser(userId);
-                createFile(rememberFile);
-                RandomAccessFileUtils.writeBoolean(rememberFile, makeActive);
-            } catch (IOException e) {
-                Slog.e(TAG, "Unable to persist backup service activity", e);
-            }
-        }
-
-        if (mGlobalDisable) {
-            Slog.i(TAG, "Backup service not supported");
-            return;
-        }
-
-        synchronized (mStateLock) {
-            Slog.i(TAG, "Making backup " + (makeActive ? "" : "in") + "active");
-            if (makeActive) {
-                try {
-                    activateBackupForUserLocked(userId);
-                } catch (IOException e) {
-                    Slog.e(TAG, "Unable to persist backup service activity");
-                }
-
-                // If the user is unlocked, we can start the backup service for it. Otherwise we
-                // will start the service when the user is unlocked as part of its unlock callback.
-                if (getUserManager().isUserUnlocked(userId)) {
-                    // Clear calling identity as initialization enforces the system identity but we
-                    // can be coming from shell.
-                    long oldId = Binder.clearCallingIdentity();
-                    try {
-                        startServiceForUser(userId);
-                    } finally {
-                        Binder.restoreCallingIdentity(oldId);
-                    }
-                }
-            } else {
-                try {
-                    //TODO(b/121198006): what if this throws an exception?
-                    deactivateBackupForUserLocked(userId);
-                } catch (IOException e) {
-                    Slog.e(TAG, "Unable to persist backup service inactivity");
-                }
-                //TODO(b/121198006): loop through active users that have work profile and
-                // stop them as well.
-                onStopUser(userId);
-            }
-        }
-    }
-
-    // IBackupManager binder API
-
-    /**
-     * Querying activity state of backup service.
-     *
-     * @param userId The user in which the activity state of backup service is queried.
-     * @return true if the service is active.
-     */
-    @Override
-    public boolean isBackupServiceActive(int userId) {
-        synchronized (mStateLock) {
-            return !mGlobalDisable && isBackupActivatedForUser(userId);
-        }
-    }
-
-    @Override
-    public void dataChangedForUser(int userId, String packageName) throws RemoteException {
-        if (isUserReadyForBackup(userId)) {
-            dataChanged(userId, packageName);
-        }
-    }
-
-    @Override
-    public void dataChanged(String packageName) throws RemoteException {
-        dataChangedForUser(binderGetCallingUserId(), packageName);
-    }
-
-    /**
-     * An app's backup agent calls this method to let the service know that there's new data to
-     * backup for their app {@code packageName}. Only used for apps participating in key-value
-     * backup.
-     */
-    public void dataChanged(@UserIdInt int userId, String packageName) {
-        UserBackupManagerService userBackupManagerService =
-                getServiceForUserIfCallerHasPermission(userId, "dataChanged()");
-
-        if (userBackupManagerService != null) {
-            userBackupManagerService.dataChanged(packageName);
-        }
-    }
-
-    // ---------------------------------------------
-    // TRANSPORT OPERATIONS
-    // ---------------------------------------------
-
-    @Override
-    public void initializeTransportsForUser(
-            int userId, String[] transportNames, IBackupObserver observer) throws RemoteException {
-        if (isUserReadyForBackup(userId)) {
-            initializeTransports(userId, transportNames, observer);
-        }
-    }
-
-    /** Run an initialize operation for the given transports {@code transportNames}. */
-    public void initializeTransports(
-            @UserIdInt int userId, String[] transportNames, IBackupObserver observer) {
-        UserBackupManagerService userBackupManagerService =
-                getServiceForUserIfCallerHasPermission(userId, "initializeTransports()");
-
-        if (userBackupManagerService != null) {
-            userBackupManagerService.initializeTransports(transportNames, observer);
-        }
-    }
-
-    @Override
-    public void clearBackupDataForUser(int userId, String transportName, String packageName)
-            throws RemoteException {
-        if (isUserReadyForBackup(userId)) {
-            clearBackupData(userId, transportName, packageName);
-        }
-    }
-
-    /**
-     * Clear the given package {@code packageName}'s backup data from the transport {@code
-     * transportName}.
-     */
-    public void clearBackupData(@UserIdInt int userId, String transportName, String packageName) {
-        UserBackupManagerService userBackupManagerService =
-                getServiceForUserIfCallerHasPermission(userId, "clearBackupData()");
-
-        if (userBackupManagerService != null) {
-            userBackupManagerService.clearBackupData(transportName, packageName);
-        }
-    }
-
-    @Override
-    public void clearBackupData(String transportName, String packageName)
-            throws RemoteException {
-        clearBackupDataForUser(binderGetCallingUserId(), transportName, packageName);
-    }
-
-    @Override
-    public void agentConnectedForUser(int userId, String packageName, IBinder agent)
-            throws RemoteException {
-        if (isUserReadyForBackup(userId)) {
-            agentConnected(userId, packageName, agent);
-        }
-    }
-
-    @Override
-    public void agentConnected(String packageName, IBinder agent) throws RemoteException {
-        agentConnectedForUser(binderGetCallingUserId(), packageName, agent);
-    }
-
-    /**
-     * Callback: a requested backup agent has been instantiated. This should only be called from the
-     * {@link ActivityManager}.
-     */
-    public void agentConnected(@UserIdInt int userId, String packageName, IBinder agentBinder) {
-        UserBackupManagerService userBackupManagerService =
-                getServiceForUserIfCallerHasPermission(userId, "agentConnected()");
-
-        if (userBackupManagerService != null) {
-            userBackupManagerService.agentConnected(packageName, agentBinder);
-        }
-    }
-
-    @Override
-    public void agentDisconnectedForUser(int userId, String packageName) throws RemoteException {
-        if (isUserReadyForBackup(userId)) {
-            agentDisconnected(userId, packageName);
-        }
-    }
-
-    @Override
-    public void agentDisconnected(String packageName) throws RemoteException {
-        agentDisconnectedForUser(binderGetCallingUserId(), packageName);
-    }
-
-    /**
-     * Callback: a backup agent has failed to come up, or has unexpectedly quit. This should only be
-     * called from the {@link ActivityManager}.
-     */
-    public void agentDisconnected(@UserIdInt int userId, String packageName) {
-        UserBackupManagerService userBackupManagerService =
-                getServiceForUserIfCallerHasPermission(userId, "agentDisconnected()");
-
-        if (userBackupManagerService != null) {
-            userBackupManagerService.agentDisconnected(packageName);
-        }
-    }
-
-    @Override
-    public void restoreAtInstallForUser(int userId, String packageName, int token)
-            throws RemoteException {
-        if (isUserReadyForBackup(userId)) {
-            restoreAtInstall(userId, packageName, token);
-        }
-    }
-
-    @Override
-    public void restoreAtInstall(String packageName, int token) throws RemoteException {
-        restoreAtInstallForUser(binderGetCallingUserId(), packageName, token);
-    }
-
-    /**
-     * Used to run a restore pass for an application that is being installed. This should only be
-     * called from the {@link PackageManager}.
-     */
-    public void restoreAtInstall(@UserIdInt int userId, String packageName, int token) {
-        UserBackupManagerService userBackupManagerService =
-                getServiceForUserIfCallerHasPermission(userId, "restoreAtInstall()");
-
-        if (userBackupManagerService != null) {
-            userBackupManagerService.restoreAtInstall(packageName, token);
-        }
-    }
-
-    @Override
-    public void setBackupEnabledForUser(@UserIdInt int userId, boolean isEnabled)
-            throws RemoteException {
-        if (isUserReadyForBackup(userId)) {
-            setBackupEnabled(userId, isEnabled);
-        }
-    }
-
-    @Override
-    public void setBackupEnabled(boolean isEnabled) throws RemoteException {
-        setBackupEnabledForUser(binderGetCallingUserId(), isEnabled);
-    }
-
-    /** Enable/disable the backup service. This is user-configurable via backup settings. */
-    public void setBackupEnabled(@UserIdInt int userId, boolean enable) {
-        UserBackupManagerService userBackupManagerService =
-                getServiceForUserIfCallerHasPermission(userId, "setBackupEnabled()");
-
-        if (userBackupManagerService != null) {
-            userBackupManagerService.setBackupEnabled(enable);
-        }
-    }
-
-    @Override
-    public void setAutoRestoreForUser(int userId, boolean doAutoRestore) throws RemoteException {
-        if (isUserReadyForBackup(userId)) {
-            setAutoRestore(userId, doAutoRestore);
-        }
-    }
-
-    @Override
-    public void setAutoRestore(boolean doAutoRestore) throws RemoteException {
-        setAutoRestoreForUser(binderGetCallingUserId(), doAutoRestore);
-    }
-
-    /** Enable/disable automatic restore of app data at install time. */
-    public void setAutoRestore(@UserIdInt int userId, boolean autoRestore) {
-        UserBackupManagerService userBackupManagerService =
-                getServiceForUserIfCallerHasPermission(userId, "setAutoRestore()");
-
-        if (userBackupManagerService != null) {
-            userBackupManagerService.setAutoRestore(autoRestore);
-        }
-    }
-
-    @Override
-    public boolean isBackupEnabledForUser(@UserIdInt int userId) throws RemoteException {
-        return isUserReadyForBackup(userId) && isBackupEnabled(userId);
-    }
-
-    @Override
-    public boolean isBackupEnabled() throws RemoteException {
-        return isBackupEnabledForUser(binderGetCallingUserId());
-    }
-
-    /**
-     * Return {@code true} if the backup mechanism is currently enabled, else returns {@code false}.
-     */
-    public boolean isBackupEnabled(@UserIdInt int userId) {
-        UserBackupManagerService userBackupManagerService =
-                getServiceForUserIfCallerHasPermission(userId, "isBackupEnabled()");
-
-        return userBackupManagerService != null && userBackupManagerService.isBackupEnabled();
-    }
-
-    /** Sets the backup password used when running adb backup. */
-    @Override
-    public boolean setBackupPassword(String currentPassword, String newPassword) {
-        int userId = binderGetCallingUserId();
-        if (!isUserReadyForBackup(userId)) {
-            return false;
-        }
-        UserBackupManagerService userBackupManagerService =
-                getServiceForUserIfCallerHasPermission(
-                        UserHandle.USER_SYSTEM, "setBackupPassword()");
-
-        return userBackupManagerService != null
-                && userBackupManagerService.setBackupPassword(currentPassword, newPassword);
-    }
-
-    /** Returns {@code true} if adb backup was run with a password, else returns {@code false}. */
-    @Override
-    public boolean hasBackupPassword() throws RemoteException {
-        int userId = binderGetCallingUserId();
-        if (!isUserReadyForBackup(userId)) {
-            return false;
-        }
-        UserBackupManagerService userBackupManagerService =
-                getServiceForUserIfCallerHasPermission(
-                        UserHandle.USER_SYSTEM, "hasBackupPassword()");
-
-        return userBackupManagerService != null && userBackupManagerService.hasBackupPassword();
-    }
-
-    @Override
-    public void backupNowForUser(@UserIdInt int userId) throws RemoteException {
-        if (isUserReadyForBackup(userId)) {
-            backupNow(userId);
-        }
-    }
-
-    @Override
-    public void backupNow() throws RemoteException {
-        backupNowForUser(binderGetCallingUserId());
-    }
-
-    /**
-     * Run a backup pass immediately for any key-value backup applications that have declared that
-     * they have pending updates.
-     */
-    public void backupNow(@UserIdInt int userId) {
-        UserBackupManagerService userBackupManagerService =
-                getServiceForUserIfCallerHasPermission(userId, "backupNow()");
-
-        if (userBackupManagerService != null) {
-            userBackupManagerService.backupNow();
-        }
-    }
-
-    /**
-     * Used by 'adb backup' to run a backup pass for packages {@code packageNames} supplied via the
-     * command line, writing the resulting data stream to the supplied {@code fd}. This method is
-     * synchronous and does not return to the caller until the backup has been completed. It
-     * requires on-screen confirmation by the user.
-     */
-    @Override
-    public void adbBackup(
-            @UserIdInt int userId,
-            ParcelFileDescriptor fd,
-            boolean includeApks,
-            boolean includeObbs,
-            boolean includeShared,
-            boolean doWidgets,
-            boolean doAllApps,
-            boolean includeSystem,
-            boolean doCompress,
-            boolean doKeyValue,
-            String[] packageNames) {
-        if (!isUserReadyForBackup(userId)) {
-            return;
-        }
-        UserBackupManagerService userBackupManagerService =
-                getServiceForUserIfCallerHasPermission(userId, "adbBackup()");
-
-        if (userBackupManagerService != null) {
-            userBackupManagerService.adbBackup(
-                    fd,
-                    includeApks,
-                    includeObbs,
-                    includeShared,
-                    doWidgets,
-                    doAllApps,
-                    includeSystem,
-                    doCompress,
-                    doKeyValue,
-                    packageNames);
-        }
-    }
-
-    @Override
-    public void fullTransportBackupForUser(int userId, String[] packageNames)
-            throws RemoteException {
-        if (isUserReadyForBackup(userId)) {
-            fullTransportBackup(userId, packageNames);
-        }
-    }
-
-    /**
-     * Run a full backup pass for the given packages {@code packageNames}. Used by 'adb shell bmgr'.
-     */
-    public void fullTransportBackup(@UserIdInt int userId, String[] packageNames) {
-        UserBackupManagerService userBackupManagerService =
-                getServiceForUserIfCallerHasPermission(userId, "fullTransportBackup()");
-
-        if (userBackupManagerService != null) {
-            userBackupManagerService.fullTransportBackup(packageNames);
-        }
-    }
-
-    /**
-     * Used by 'adb restore' to run a restore pass reading from the supplied {@code fd}. This method
-     * is synchronous and does not return to the caller until the restore has been completed. It
-     * requires on-screen confirmation by the user.
-     */
-    @Override
-    public void adbRestore(@UserIdInt int userId, ParcelFileDescriptor fd) {
-        if (!isUserReadyForBackup(userId)) {
-            return;
-        }
-        UserBackupManagerService userBackupManagerService =
-                getServiceForUserIfCallerHasPermission(userId, "adbRestore()");
-
-        if (userBackupManagerService != null) {
-            userBackupManagerService.adbRestore(fd);
-        }
-    }
-
-    @Override
-    public void acknowledgeFullBackupOrRestoreForUser(
-            int userId,
-            int token,
-            boolean allow,
-            String curPassword,
-            String encryptionPassword,
-            IFullBackupRestoreObserver observer)
-            throws RemoteException {
-        if (isUserReadyForBackup(userId)) {
-            acknowledgeAdbBackupOrRestore(userId, token, allow,
-                    curPassword, encryptionPassword, observer);
-        }
-    }
-
-    /**
-     * Confirm that the previously requested adb backup/restore operation can proceed. This is used
-     * to require a user-facing disclosure about the operation.
-     */
-    public void acknowledgeAdbBackupOrRestore(
-            @UserIdInt int userId,
-            int token,
-            boolean allow,
-            String currentPassword,
-            String encryptionPassword,
-            IFullBackupRestoreObserver observer) {
-        UserBackupManagerService userBackupManagerService =
-                getServiceForUserIfCallerHasPermission(userId, "acknowledgeAdbBackupOrRestore()");
-
-        if (userBackupManagerService != null) {
-            userBackupManagerService.acknowledgeAdbBackupOrRestore(
-                    token, allow, currentPassword, encryptionPassword, observer);
-        }
-    }
-
-    @Override
-    public void acknowledgeFullBackupOrRestore(int token, boolean allow, String curPassword,
-            String encryptionPassword, IFullBackupRestoreObserver observer)
-            throws RemoteException {
-        acknowledgeFullBackupOrRestoreForUser(
-                binderGetCallingUserId(), token, allow, curPassword, encryptionPassword, observer);
-    }
-
-
-    @Override
-    public String getCurrentTransportForUser(int userId) throws RemoteException {
-        return (isUserReadyForBackup(userId)) ? getCurrentTransport(userId) : null;
-    }
-
-    @Override
-    public String getCurrentTransport() throws RemoteException {
-        return getCurrentTransportForUser(binderGetCallingUserId());
-    }
-
-    /** Return the name of the currently active transport. */
-    @Nullable
-    public String getCurrentTransport(@UserIdInt int userId) {
-        UserBackupManagerService userBackupManagerService =
-                getServiceForUserIfCallerHasPermission(userId, "getCurrentTransport()");
-
-        return userBackupManagerService == null
-                ? null
-                : userBackupManagerService.getCurrentTransport();
-    }
-
-    /**
-     * Returns the {@link ComponentName} of the host service of the selected transport or
-     * {@code null} if no transport selected or if the transport selected is not registered.
-     */
-    @Override
-    @Nullable
-    public ComponentName getCurrentTransportComponentForUser(int userId) {
-        return (isUserReadyForBackup(userId)) ? getCurrentTransportComponent(userId) : null;
-    }
-
-    /**
-     * Returns the {@link ComponentName} of the host service of the selected transport or {@code
-     * null} if no transport selected or if the transport selected is not registered.
-     */
-    @Nullable
-    public ComponentName getCurrentTransportComponent(@UserIdInt int userId) {
-        UserBackupManagerService userBackupManagerService =
-                getServiceForUserIfCallerHasPermission(userId, "getCurrentTransportComponent()");
-
-        return userBackupManagerService == null
-                ? null
-                : userBackupManagerService.getCurrentTransportComponent();
-    }
-
-    @Override
-    public String[] listAllTransportsForUser(int userId) throws RemoteException {
-        return (isUserReadyForBackup(userId)) ? listAllTransports(userId) : null;
-    }
-
-    /** Report all known, available backup transports by name. */
-    @Nullable
-    public String[] listAllTransports(@UserIdInt int userId) {
-        UserBackupManagerService userBackupManagerService =
-                getServiceForUserIfCallerHasPermission(userId, "listAllTransports()");
-
-        return userBackupManagerService == null
-                ? null
-                : userBackupManagerService.listAllTransports();
-    }
-
-    @Override
-    public String[] listAllTransports() throws RemoteException {
-        return listAllTransportsForUser(binderGetCallingUserId());
-    }
-
-    @Override
-    public ComponentName[] listAllTransportComponentsForUser(int userId) throws RemoteException {
-        return (isUserReadyForBackup(userId))
-                ? listAllTransportComponents(userId) : null;
-    }
-
-    /** Report all known, available backup transports by {@link ComponentName}. */
-    @Nullable
-    public ComponentName[] listAllTransportComponents(@UserIdInt int userId) {
-        UserBackupManagerService userBackupManagerService =
-                getServiceForUserIfCallerHasPermission(userId, "listAllTransportComponents()");
-
-        return userBackupManagerService == null
-                ? null
-                : userBackupManagerService.listAllTransportComponents();
-    }
-
-    @Override
-    public String[] getTransportWhitelist() {
-        int userId = binderGetCallingUserId();
-        if (!isUserReadyForBackup(userId)) {
-            return null;
-        }
-        // No permission check, intentionally.
-        String[] whitelistedTransports = new String[mTransportWhitelist.size()];
-        int i = 0;
-        for (ComponentName component : mTransportWhitelist) {
-            whitelistedTransports[i] = component.flattenToShortString();
-            i++;
-        }
-        return whitelistedTransports;
-    }
-
-    @Override
-    public void updateTransportAttributesForUser(
-            int userId,
-            ComponentName transportComponent,
-            String name,
-            @Nullable Intent configurationIntent,
-            String currentDestinationString,
-            @Nullable Intent dataManagementIntent,
-            CharSequence dataManagementLabel) {
-        if (isUserReadyForBackup(userId)) {
-            updateTransportAttributes(
-                    userId,
-                    transportComponent,
-                    name,
-                    configurationIntent,
-                    currentDestinationString,
-                    dataManagementIntent,
-                    dataManagementLabel);
-        }
-    }
-
-    /**
-     * Update the attributes of the transport identified by {@code transportComponent}. If the
-     * specified transport has not been bound at least once (for registration), this call will be
-     * ignored. Only the host process of the transport can change its description, otherwise a
-     * {@link SecurityException} will be thrown.
-     *
-     * @param transportComponent The identity of the transport being described.
-     * @param name A {@link String} with the new name for the transport. This is NOT for
-     *     identification. MUST NOT be {@code null}.
-     * @param configurationIntent An {@link Intent} that can be passed to {@link
-     *     Context#startActivity} in order to launch the transport's configuration UI. It may be
-     *     {@code null} if the transport does not offer any user-facing configuration UI.
-     * @param currentDestinationString A {@link String} describing the destination to which the
-     *     transport is currently sending data. MUST NOT be {@code null}.
-     * @param dataManagementIntent An {@link Intent} that can be passed to {@link
-     *     Context#startActivity} in order to launch the transport's data-management UI. It may be
-     *     {@code null} if the transport does not offer any user-facing data management UI.
-     * @param dataManagementLabel A {@link CharSequence} to be used as the label for the transport's
-     *     data management affordance. This MUST be {@code null} when dataManagementIntent is {@code
-     *     null} and MUST NOT be {@code null} when dataManagementIntent is not {@code null}.
-     * @throws SecurityException If the UID of the calling process differs from the package UID of
-     *     {@code transportComponent} or if the caller does NOT have BACKUP permission.
-     */
-    public void updateTransportAttributes(
-            @UserIdInt int userId,
-            ComponentName transportComponent,
-            String name,
-            @Nullable Intent configurationIntent,
-            String currentDestinationString,
-            @Nullable Intent dataManagementIntent,
-            CharSequence dataManagementLabel) {
-        UserBackupManagerService userBackupManagerService =
-                getServiceForUserIfCallerHasPermission(userId, "updateTransportAttributes()");
-
-        if (userBackupManagerService != null) {
-            userBackupManagerService.updateTransportAttributes(
-                    transportComponent,
-                    name,
-                    configurationIntent,
-                    currentDestinationString,
-                    dataManagementIntent,
-                    dataManagementLabel);
-        }
-    }
-
-    @Override
-    public String selectBackupTransportForUser(int userId, String transport)
-            throws RemoteException {
-        return (isUserReadyForBackup(userId))
-                ? selectBackupTransport(userId, transport) : null;
-    }
-
-    @Override
-    public String selectBackupTransport(String transport) throws RemoteException {
-        return selectBackupTransportForUser(binderGetCallingUserId(), transport);
-    }
-
-    /**
-     * Selects transport {@code transportName} and returns the previously selected transport.
-     *
-     * @deprecated Use {@link #selectBackupTransportAsync(ComponentName,
-     *     ISelectBackupTransportCallback)} instead.
-     */
-    @Deprecated
-    @Nullable
-    public String selectBackupTransport(@UserIdInt int userId, String transportName) {
-        UserBackupManagerService userBackupManagerService =
-                getServiceForUserIfCallerHasPermission(userId, "selectBackupTransport()");
-
-        return userBackupManagerService == null
-                ? null
-                : userBackupManagerService.selectBackupTransport(transportName);
-    }
-
-    @Override
-    public void selectBackupTransportAsyncForUser(int userId, ComponentName transport,
-            ISelectBackupTransportCallback listener) throws RemoteException {
-        if (isUserReadyForBackup(userId)) {
-            selectBackupTransportAsync(userId, transport, listener);
-        } else {
-            if (listener != null) {
-                try {
-                    listener.onFailure(BackupManager.ERROR_BACKUP_NOT_ALLOWED);
-                } catch (RemoteException ex) {
-                    // ignore
-                }
-            }
-        }
-    }
-
-    /**
-     * Selects transport {@code transportComponent} asynchronously and notifies {@code listener}
-     * with the result upon completion.
-     */
-    public void selectBackupTransportAsync(
-            @UserIdInt int userId,
-            ComponentName transportComponent,
-            ISelectBackupTransportCallback listener) {
-        UserBackupManagerService userBackupManagerService =
-                getServiceForUserIfCallerHasPermission(userId, "selectBackupTransportAsync()");
-
-        if (userBackupManagerService != null) {
-            userBackupManagerService.selectBackupTransportAsync(transportComponent, listener);
-        }
-    }
-
-    @Override
-    public Intent getConfigurationIntentForUser(int userId, String transport)
-            throws RemoteException {
-        return isUserReadyForBackup(userId) ? getConfigurationIntent(userId, transport)
-                : null;
-    }
-
-    @Override
-    public Intent getConfigurationIntent(String transport)
-            throws RemoteException {
-        return getConfigurationIntentForUser(binderGetCallingUserId(), transport);
-    }
-
-    /**
-     * Supply the configuration intent for the given transport. If the name is not one of the
-     * available transports, or if the transport does not supply any configuration UI, the method
-     * returns {@code null}.
-     */
-    @Nullable
-    public Intent getConfigurationIntent(@UserIdInt int userId, String transportName) {
-        UserBackupManagerService userBackupManagerService =
-                getServiceForUserIfCallerHasPermission(userId, "getConfigurationIntent()");
-
-        return userBackupManagerService == null
-                ? null
-                : userBackupManagerService.getConfigurationIntent(transportName);
-    }
-
-    @Override
-    public String getDestinationStringForUser(int userId, String transport) throws RemoteException {
-        return isUserReadyForBackup(userId) ? getDestinationString(userId, transport)
-                : null;
-    }
-
-    @Override
-    public String getDestinationString(String transport) throws RemoteException {
-        return getDestinationStringForUser(binderGetCallingUserId(), transport);
-    }
-
-    /**
-     * Supply the current destination string for the given transport. If the name is not one of the
-     * registered transports the method will return null.
-     *
-     * <p>This string is used VERBATIM as the summary text of the relevant Settings item.
-     *
-     * @param transportName The name of the registered transport.
-     * @return The current destination string or null if the transport is not registered.
-     */
-    @Nullable
-    public String getDestinationString(@UserIdInt int userId, String transportName) {
-        UserBackupManagerService userBackupManagerService =
-                getServiceForUserIfCallerHasPermission(userId, "getDestinationString()");
-
-        return userBackupManagerService == null
-                ? null
-                : userBackupManagerService.getDestinationString(transportName);
-    }
-
-    @Override
-    public Intent getDataManagementIntentForUser(int userId, String transport)
-            throws RemoteException {
-        return isUserReadyForBackup(userId)
-                ? getDataManagementIntent(userId, transport) : null;
-    }
-
-    @Override
-    public Intent getDataManagementIntent(String transport)
-            throws RemoteException {
-        return getDataManagementIntentForUser(binderGetCallingUserId(), transport);
-    }
-
-    /** Supply the manage-data intent for the given transport. */
-    @Nullable
-    public Intent getDataManagementIntent(@UserIdInt int userId, String transportName) {
-        UserBackupManagerService userBackupManagerService =
-                getServiceForUserIfCallerHasPermission(userId, "getDataManagementIntent()");
-
-        return userBackupManagerService == null
-                ? null
-                : userBackupManagerService.getDataManagementIntent(transportName);
-    }
-
-    @Override
-    public CharSequence getDataManagementLabelForUser(int userId, String transport)
-            throws RemoteException {
-        return isUserReadyForBackup(userId) ? getDataManagementLabel(userId, transport)
-                : null;
-    }
-
-    /**
-     * Supply the menu label for affordances that fire the manage-data intent for the given
-     * transport.
-     */
-    @Nullable
-    public CharSequence getDataManagementLabel(@UserIdInt int userId, String transportName) {
-        UserBackupManagerService userBackupManagerService =
-                getServiceForUserIfCallerHasPermission(userId, "getDataManagementLabel()");
-
-        return userBackupManagerService == null
-                ? null
-                : userBackupManagerService.getDataManagementLabel(transportName);
-    }
-
-    @Override
-    public IRestoreSession beginRestoreSessionForUser(
-            int userId, String packageName, String transportID) throws RemoteException {
-        return isUserReadyForBackup(userId)
-                ? beginRestoreSession(userId, packageName, transportID) : null;
-    }
-
-    /**
-     * Begin a restore for the specified package {@code packageName} using the specified transport
-     * {@code transportName}.
-     */
-    @Nullable
-    public IRestoreSession beginRestoreSession(
-            @UserIdInt int userId, String packageName, String transportName) {
-        UserBackupManagerService userBackupManagerService =
-                getServiceForUserIfCallerHasPermission(userId, "beginRestoreSession()");
-
-        return userBackupManagerService == null
-                ? null
-                : userBackupManagerService.beginRestoreSession(packageName, transportName);
-    }
-
-    @Override
-    public void opCompleteForUser(int userId, int token, long result) throws RemoteException {
-        if (isUserReadyForBackup(userId)) {
-            opComplete(userId, token, result);
-        }
-    }
-
-    @Override
-    public void opComplete(int token, long result) throws RemoteException {
-        opCompleteForUser(binderGetCallingUserId(), token, result);
-    }
-
-    /**
-     * Used by a currently-active backup agent to notify the service that it has completed its given
-     * outstanding asynchronous backup/restore operation.
-     */
-    public void opComplete(@UserIdInt int userId, int token, long result) {
-        UserBackupManagerService userBackupManagerService =
-                getServiceForUserIfCallerHasPermission(userId, "opComplete()");
-
-        if (userBackupManagerService != null) {
-            userBackupManagerService.opComplete(token, result);
-        }
-    }
-
-    @Override
-    public long getAvailableRestoreTokenForUser(int userId, String packageName) {
-        return isUserReadyForBackup(userId) ? getAvailableRestoreToken(userId, packageName) : 0;
-    }
-
-    /**
-     * Get the restore-set token for the best-available restore set for this {@code packageName}:
-     * the active set if possible, else the ancestral one. Returns zero if none available.
-     */
-    public long getAvailableRestoreToken(@UserIdInt int userId, String packageName) {
-        UserBackupManagerService userBackupManagerService =
-                getServiceForUserIfCallerHasPermission(userId, "getAvailableRestoreToken()");
-
-        return userBackupManagerService == null
-                ? 0
-                : userBackupManagerService.getAvailableRestoreToken(packageName);
-    }
-
-    @Override
-    public boolean isAppEligibleForBackupForUser(int userId, String packageName) {
-        return isUserReadyForBackup(userId) && isAppEligibleForBackup(userId,
-                packageName);
-    }
-
-    /** Checks if the given package {@code packageName} is eligible for backup. */
-    public boolean isAppEligibleForBackup(@UserIdInt int userId, String packageName) {
-        UserBackupManagerService userBackupManagerService =
-                getServiceForUserIfCallerHasPermission(userId, "isAppEligibleForBackup()");
-
-        return userBackupManagerService != null
-                && userBackupManagerService.isAppEligibleForBackup(packageName);
-    }
-
-    @Override
-    public String[] filterAppsEligibleForBackupForUser(int userId, String[] packages) {
-        return isUserReadyForBackup(userId) ? filterAppsEligibleForBackup(userId, packages) : null;
-    }
-
-    /**
-     * Returns from the inputted packages {@code packages}, the ones that are eligible for backup.
-     */
-    @Nullable
-    public String[] filterAppsEligibleForBackup(@UserIdInt int userId, String[] packages) {
-        UserBackupManagerService userBackupManagerService =
-                getServiceForUserIfCallerHasPermission(userId, "filterAppsEligibleForBackup()");
-
-        return userBackupManagerService == null
-                ? null
-                : userBackupManagerService.filterAppsEligibleForBackup(packages);
-    }
-
-    @Override
-    public int requestBackupForUser(@UserIdInt int userId, String[] packages, IBackupObserver
-            observer, IBackupManagerMonitor monitor, int flags) throws RemoteException {
-        if (!isUserReadyForBackup(userId)) {
-            return BackupManager.ERROR_BACKUP_NOT_ALLOWED;
-        }
-        return requestBackup(userId, packages, observer, monitor, flags);
-    }
-
-    @Override
-    public int requestBackup(String[] packages, IBackupObserver observer,
-            IBackupManagerMonitor monitor, int flags) throws RemoteException {
-        return requestBackupForUser(binderGetCallingUserId(), packages,
-                observer, monitor, flags);
-    }
-
-    /**
-     * Requests a backup for the inputted {@code packages} with a specified callback {@link
-     * IBackupManagerMonitor} for receiving events during the operation.
-     */
-    public int requestBackup(
-            @UserIdInt int userId,
-            String[] packages,
-            IBackupObserver observer,
-            IBackupManagerMonitor monitor,
-            int flags) {
-        UserBackupManagerService userBackupManagerService =
-                getServiceForUserIfCallerHasPermission(userId, "requestBackup()");
-
-        return userBackupManagerService == null
-                ? BackupManager.ERROR_BACKUP_NOT_ALLOWED
-                : userBackupManagerService.requestBackup(packages, observer, monitor, flags);
-    }
-
-    @Override
-    public void cancelBackupsForUser(@UserIdInt int userId) throws RemoteException {
-        if (isUserReadyForBackup(userId)) {
-            cancelBackups(userId);
-        }
-    }
-
-    @Override
-    public void cancelBackups() throws RemoteException {
-        cancelBackupsForUser(binderGetCallingUserId());
-    }
-
-    /** Cancel all running backup operations. */
-    public void cancelBackups(@UserIdInt int userId) {
-        UserBackupManagerService userBackupManagerService =
-                getServiceForUserIfCallerHasPermission(userId, "cancelBackups()");
-
-        if (userBackupManagerService != null) {
-            userBackupManagerService.cancelBackups();
-        }
-    }
-
-    /**
-     * Returns a {@link UserHandle} for the user that has {@code ancestralSerialNumber} as the
-     * serial number of its ancestral work profile or null if there is no {@link
-     * UserBackupManagerService} associated with that user.
-     *
-     * <p> The ancestral work profile is set by {@link #setAncestralSerialNumber(long)}
-     * and it corresponds to the profile that was used to restore to the callers profile.
-     */
-    @Override
-    @Nullable
-    public UserHandle getUserForAncestralSerialNumber(long ancestralSerialNumber) {
-        if (mGlobalDisable) {
-            return null;
-        }
-        int callingUserId = Binder.getCallingUserHandle().getIdentifier();
-        long oldId = Binder.clearCallingIdentity();
-        final int[] userIds;
-        try {
-            userIds =
-                    mContext
-                            .getSystemService(UserManager.class)
-                            .getProfileIds(callingUserId, false);
-        } finally {
-            Binder.restoreCallingIdentity(oldId);
-        }
-
-        for (int userId : userIds) {
-            UserBackupManagerService userBackupManagerService = mUserServices.get(userId);
-            if (userBackupManagerService != null) {
-                if (userBackupManagerService.getAncestralSerialNumber() == ancestralSerialNumber) {
-                    return UserHandle.of(userId);
-                }
-            }
-        }
-
-        return null;
-    }
-
-    /**
-     * Sets the ancestral work profile for the calling user.
-     *
-     * <p> The ancestral work profile corresponds to the profile that was used to restore to the
-     * callers profile.
-     */
-    @Override
-    public void setAncestralSerialNumber(long ancestralSerialNumber) {
-        if (mGlobalDisable) {
-            return;
-        }
-        UserBackupManagerService userBackupManagerService =
-                getServiceForUserIfCallerHasPermission(
-                        Binder.getCallingUserHandle().getIdentifier(),
-                        "setAncestralSerialNumber()");
-
-        if (userBackupManagerService != null) {
-            userBackupManagerService.setAncestralSerialNumber(ancestralSerialNumber);
-        }
-    }
-
-    @Override
-    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
-        if (!DumpUtils.checkDumpAndUsageStatsPermission(mContext, TAG, pw)) {
-            return;
-        }
-        int userId = binderGetCallingUserId();
-        if (!isUserReadyForBackup(userId)) {
-            pw.println("Inactive");
-            return;
-        }
-
-        if (args != null) {
-            for (String arg : args) {
-                if ("users".equals(arg.toLowerCase())) {
-                    pw.print(DUMP_RUNNING_USERS_MESSAGE);
-                    for (int i = 0; i < mUserServices.size(); i++) {
-                        pw.print(" " + mUserServices.keyAt(i));
-                    }
-                    pw.println();
-                    return;
-                }
-            }
-        }
-
-        UserBackupManagerService userBackupManagerService =
-                getServiceForUserIfCallerHasPermission(UserHandle.USER_SYSTEM, "dump()");
-
-        if (userBackupManagerService != null) {
-            userBackupManagerService.dump(fd, pw, args);
-        }
-    }
-
-    /**
-     * Used by the {@link JobScheduler} to run a full backup when conditions are right. The model we
-     * use is to perform one app backup per scheduled job execution, and to reschedule the job with
-     * zero latency as long as conditions remain right and we still have work to do.
-     *
-     * @return Whether ongoing work will continue. The return value here will be passed along as the
-     *     return value to the callback {@link JobService#onStartJob(JobParameters)}.
-     */
-    public boolean beginFullBackup(@UserIdInt int userId, FullBackupJob scheduledJob) {
-        if (!isUserReadyForBackup(userId)) {
-            return false;
-        }
-        UserBackupManagerService userBackupManagerService =
-                getServiceForUserIfCallerHasPermission(userId, "beginFullBackup()");
-
-        return userBackupManagerService != null
-                && userBackupManagerService.beginFullBackup(scheduledJob);
-    }
-
-    /**
-     * Used by the {@link JobScheduler} to end the current full backup task when conditions are no
-     * longer met for running the full backup job.
-     */
-    public void endFullBackup(@UserIdInt int userId) {
-        if (!isUserReadyForBackup(userId)) {
-            return;
-        }
-        UserBackupManagerService userBackupManagerService =
-                getServiceForUserIfCallerHasPermission(userId, "endFullBackup()");
-
-        if (userBackupManagerService != null) {
-            userBackupManagerService.endFullBackup();
-        }
-    }
-
-    /**
-     * Returns the {@link UserBackupManagerService} instance for the specified user {@code userId}.
-     * If the user is not registered with the service (either the user is locked or not eligible for
-     * the backup service) then return {@code null}.
-     *
-     * @param userId The id of the user to retrieve its instance of {@link
-     *     UserBackupManagerService}.
-     * @param caller A {@link String} identifying the caller for logging purposes.
-     * @throws SecurityException if {@code userId} is different from the calling user id and the
-     *     caller does NOT have the android.permission.INTERACT_ACROSS_USERS_FULL permission.
-     */
-    @Nullable
-    @VisibleForTesting
-    UserBackupManagerService getServiceForUserIfCallerHasPermission(
-            @UserIdInt int userId, String caller) {
-        enforceCallingPermissionOnUserId(userId, caller);
-        UserBackupManagerService userBackupManagerService = mUserServices.get(userId);
-        if (userBackupManagerService == null) {
-            Slog.w(TAG, "Called " + caller + " for unknown user: " + userId);
-        }
-        return userBackupManagerService;
-    }
-
-    /**
-     * If {@code userId} is different from the calling user id, then the caller must hold the
-     * android.permission.INTERACT_ACROSS_USERS_FULL permission.
-     *
-     * @param userId User id on which the backup operation is being requested.
-     * @param message A message to include in the exception if it is thrown.
-     */
-    void enforceCallingPermissionOnUserId(@UserIdInt int userId, String message) {
-        if (Binder.getCallingUserHandle().getIdentifier() != userId) {
-            mContext.enforceCallingOrSelfPermission(
-                    Manifest.permission.INTERACT_ACROSS_USERS_FULL, message);
-        }
-    }
-}
diff --git a/services/backup/java/com/android/server/backup/UserBackupManagerService.java b/services/backup/java/com/android/server/backup/UserBackupManagerService.java
index 0e81e07..77888db 100644
--- a/services/backup/java/com/android/server/backup/UserBackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/UserBackupManagerService.java
@@ -421,13 +421,13 @@
      * Creates an instance of {@link UserBackupManagerService} and initializes state for it. This
      * includes setting up the directories where we keep our bookkeeping and transport management.
      *
-     * @see #createAndInitializeService(int, Context, Trampoline, HandlerThread, File, File,
-     *     TransportManager)
+     * @see #createAndInitializeService(int, Context, BackupManagerService, HandlerThread, File,
+     * File, TransportManager)
      */
     static UserBackupManagerService createAndInitializeService(
             @UserIdInt int userId,
             Context context,
-            Trampoline trampoline,
+            BackupManagerService backupManagerService,
             Set<ComponentName> transportWhitelist) {
         String currentTransport =
                 Settings.Secure.getStringForUser(
@@ -455,7 +455,7 @@
         return createAndInitializeService(
                 userId,
                 context,
-                trampoline,
+                backupManagerService,
                 userBackupThread,
                 baseStateDir,
                 dataDir,
@@ -467,7 +467,7 @@
      *
      * @param userId The user which this service is for.
      * @param context The system server context.
-     * @param trampoline A reference to the proxy to {@link BackupManagerService}.
+     * @param backupManagerService A reference to the proxy to {@link BackupManagerService}.
      * @param userBackupThread The thread running backup/restore operations for the user.
      * @param baseStateDir The directory we store the user's persistent bookkeeping data.
      * @param dataDir The directory we store the user's temporary staging data.
@@ -478,7 +478,7 @@
     public static UserBackupManagerService createAndInitializeService(
             @UserIdInt int userId,
             Context context,
-            Trampoline trampoline,
+            BackupManagerService backupManagerService,
             HandlerThread userBackupThread,
             File baseStateDir,
             File dataDir,
@@ -486,7 +486,7 @@
         return new UserBackupManagerService(
                 userId,
                 context,
-                trampoline,
+                backupManagerService,
                 userBackupThread,
                 baseStateDir,
                 dataDir,
@@ -509,7 +509,7 @@
     private UserBackupManagerService(
             @UserIdInt int userId,
             Context context,
-            Trampoline parent,
+            BackupManagerService parent,
             HandlerThread userBackupThread,
             File baseStateDir,
             File dataDir,
@@ -525,8 +525,8 @@
         mPowerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
         mStorageManager = IStorageManager.Stub.asInterface(ServiceManager.getService("mount"));
 
-        checkNotNull(parent, "trampoline cannot be null");
-        mBackupManagerBinder = Trampoline.asInterface(parent.asBinder());
+        checkNotNull(parent, "parent cannot be null");
+        mBackupManagerBinder = BackupManagerService.asInterface(parent.asBinder());
 
         mAgentTimeoutParameters = new
                 BackupAgentTimeoutParameters(Handler.getMain(), mContext.getContentResolver());
diff --git a/services/core/java/com/android/server/AnimationThread.java b/services/core/java/com/android/server/AnimationThread.java
index c86042b..c607b1e 100644
--- a/services/core/java/com/android/server/AnimationThread.java
+++ b/services/core/java/com/android/server/AnimationThread.java
@@ -21,6 +21,8 @@
 import android.os.Handler;
 import android.os.Trace;
 
+import com.android.internal.annotations.VisibleForTesting;
+
 /**
  * Thread for handling all legacy window animations, or anything that's directly impacting
  * animations like starting windows or traversals.
@@ -55,4 +57,20 @@
             return sHandler;
         }
     }
+
+    /**
+     * Disposes current animation thread if it's initialized. Should only be used in tests to set up
+     * a new environment.
+     */
+    @VisibleForTesting
+    public static void dispose() {
+        synchronized (DisplayThread.class) {
+            if (sInstance == null) {
+                return;
+            }
+
+            getHandler().runWithScissors(() -> sInstance.quit(), 0 /* timeout */);
+            sInstance = null;
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/BatteryService.java b/services/core/java/com/android/server/BatteryService.java
index 6a9f5b6..d18b4f6 100644
--- a/services/core/java/com/android/server/BatteryService.java
+++ b/services/core/java/com/android/server/BatteryService.java
@@ -1235,14 +1235,21 @@
         }
         @Override
         public void scheduleUpdate() throws RemoteException {
-            traceBegin("HealthScheduleUpdate");
-            try {
-                IHealth service = mHealthServiceWrapper.getLastService();
-                if (service == null) throw new RemoteException("no health service");
-                service.update();
-            } finally {
-                traceEnd();
-            }
+            mHealthServiceWrapper.getHandlerThread().getThreadHandler().post(() -> {
+                traceBegin("HealthScheduleUpdate");
+                try {
+                    IHealth service = mHealthServiceWrapper.getLastService();
+                    if (service == null) {
+                        Slog.e(TAG, "no health service");
+                        return;
+                    }
+                    service.update();
+                } catch (RemoteException ex) {
+                    Slog.e(TAG, "Cannot call update on health HAL", ex);
+                } finally {
+                    traceEnd();
+                }
+            });
         }
     }
 
@@ -1319,7 +1326,7 @@
                 Arrays.asList(INSTANCE_VENDOR, INSTANCE_HEALTHD);
 
         private final IServiceNotification mNotification = new Notification();
-        private final HandlerThread mHandlerThread = new HandlerThread("HealthServiceRefresh");
+        private final HandlerThread mHandlerThread = new HandlerThread("HealthServiceHwbinder");
         // These variables are fixed after init.
         private Callback mCallback;
         private IHealthSupplier mHealthSupplier;
diff --git a/services/core/java/com/android/server/DisplayThread.java b/services/core/java/com/android/server/DisplayThread.java
index 85c799c..a07ade0 100644
--- a/services/core/java/com/android/server/DisplayThread.java
+++ b/services/core/java/com/android/server/DisplayThread.java
@@ -20,6 +20,8 @@
 import android.os.Process;
 import android.os.Trace;
 
+import com.android.internal.annotations.VisibleForTesting;
+
 /**
  * Shared singleton foreground thread for the system.  This is a thread for
  * operations that affect what's on the display, which needs to have a minimum
@@ -58,4 +60,20 @@
             return sHandler;
         }
     }
+
+    /**
+     * Disposes current display thread if it's initialized. Should only be used in tests to set up a
+     * new environment.
+     */
+    @VisibleForTesting
+    public static void dispose() {
+        synchronized (DisplayThread.class) {
+            if (sInstance == null) {
+                return;
+            }
+
+            getHandler().runWithScissors(() -> sInstance.quit(), 0 /* timeout */);
+            sInstance = null;
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/DynamicSystemService.java b/services/core/java/com/android/server/DynamicSystemService.java
index e531412..18009e1 100644
--- a/services/core/java/com/android/server/DynamicSystemService.java
+++ b/services/core/java/com/android/server/DynamicSystemService.java
@@ -25,6 +25,7 @@
 import android.os.Environment;
 import android.os.IBinder;
 import android.os.IBinder.DeathRecipient;
+import android.os.ParcelFileDescriptor;
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.os.SystemProperties;
@@ -195,7 +196,20 @@
     }
 
     @Override
-    public boolean write(byte[] buf) throws RemoteException {
-        return getGsiService().commitGsiChunkFromMemory(buf);
+    public boolean setAshmem(ParcelFileDescriptor ashmem, long size) {
+        try {
+            return getGsiService().setGsiAshmem(ashmem, size);
+        } catch (RemoteException e) {
+            throw new RuntimeException(e.toString());
+        }
+    }
+
+    @Override
+    public boolean submitFromAshmem(long size) {
+        try {
+            return getGsiService().commitGsiChunkFromAshmem(size);
+        } catch (RemoteException e) {
+            throw new RuntimeException(e.toString());
+        }
     }
 }
diff --git a/services/core/java/com/android/server/SystemServiceManager.java b/services/core/java/com/android/server/SystemServiceManager.java
index ebe23f6..b085946 100644
--- a/services/core/java/com/android/server/SystemServiceManager.java
+++ b/services/core/java/com/android/server/SystemServiceManager.java
@@ -212,129 +212,67 @@
      * Starts the given user.
      */
     public void startUser(final @NonNull TimingsTraceAndSlog t, final @UserIdInt int userHandle) {
-        t.traceBegin("ssm.startUser-" + userHandle);
-        Slog.i(TAG, "Calling onStartUser u" + userHandle);
-        final UserInfo userInfo = getUserInfo(userHandle);
-        final int serviceLen = mServices.size();
-        for (int i = 0; i < serviceLen; i++) {
-            final SystemService service = mServices.get(i);
-            final String serviceName = service.getClass().getName();
-            t.traceBegin("onStartUser-" + userHandle + " " + serviceName);
-            long time = SystemClock.elapsedRealtime();
-            try {
-                service.onStartUser(userInfo);
-            } catch (Exception ex) {
-                Slog.wtf(TAG, "Failure reporting start of user " + userHandle
-                        + " to service " + service.getClass().getName(), ex);
-            }
-            warnIfTooLong(SystemClock.elapsedRealtime() - time, service, "onStartUser ");
-            t.traceEnd();
-        }
-        t.traceEnd();
+        onUser(t, "Start", userHandle, (s, u) -> s.onStartUser(u));
     }
 
     /**
      * Unlocks the given user.
      */
     public void unlockUser(final @UserIdInt int userHandle) {
-        final TimingsTraceAndSlog t = TimingsTraceAndSlog.newAsyncLog();
-        t.traceBegin("ssm.unlockUser-" + userHandle);
-        Slog.i(TAG, "Calling onUnlockUser u" + userHandle);
-        final UserInfo userInfo = getUserInfo(userHandle);
-        final int serviceLen = mServices.size();
-        for (int i = 0; i < serviceLen; i++) {
-            final SystemService service = mServices.get(i);
-            final String serviceName = service.getClass().getName();
-            t.traceBegin("onUnlockUser-" + userHandle + " " + serviceName);
-            long time = SystemClock.elapsedRealtime();
-            try {
-                service.onUnlockUser(userInfo);
-            } catch (Exception ex) {
-                Slog.wtf(TAG, "Failure reporting unlock of user " + userHandle
-                        + " to service " + serviceName, ex);
-            }
-            warnIfTooLong(SystemClock.elapsedRealtime() - time, service, "onUnlockUser ");
-            t.traceEnd();
-        }
-        t.traceEnd();
+        onUser("Unlock", userHandle, (s, u) -> s.onUnlockUser(u));
     }
 
     /**
      * Switches to the given user.
      */
     public void switchUser(final @UserIdInt int userHandle) {
-        final TimingsTraceAndSlog t = TimingsTraceAndSlog.newAsyncLog();
-        t.traceBegin("ssm.switchUser-" + userHandle);
-        Slog.i(TAG, "Calling switchUser u" + userHandle);
-        final UserInfo userInfo = getUserInfo(userHandle);
-        final int serviceLen = mServices.size();
-        for (int i = 0; i < serviceLen; i++) {
-            final SystemService service = mServices.get(i);
-            final String serviceName = service.getClass().getName();
-            t.traceBegin("onSwitchUser-" + userHandle + " " + serviceName);
-            long time = SystemClock.elapsedRealtime();
-            try {
-                service.onSwitchUser(userInfo);
-            } catch (Exception ex) {
-                Slog.wtf(TAG, "Failure reporting switch of user " + userHandle
-                        + " to service " + serviceName, ex);
-            }
-            warnIfTooLong(SystemClock.elapsedRealtime() - time, service, "onSwitchUser");
-            t.traceEnd();
-        }
-        t.traceEnd();
+        onUser("Switch", userHandle, (s, u) -> s.onSwitchUser(u));
     }
 
     /**
      * Stops the given user.
      */
     public void stopUser(final @UserIdInt int userHandle) {
-        final TimingsTraceAndSlog t = TimingsTraceAndSlog.newAsyncLog();
-        t.traceBegin("ssm.stopUser-" + userHandle);
-        Slog.i(TAG, "Calling onStopUser u" + userHandle);
-        final UserInfo userInfo = getUserInfo(userHandle);
-        final int serviceLen = mServices.size();
-        for (int i = 0; i < serviceLen; i++) {
-            final SystemService service = mServices.get(i);
-            final String serviceName = service.getClass().getName();
-            t.traceBegin("onStopUser-" + userHandle + " " + serviceName);
-            long time = SystemClock.elapsedRealtime();
-            try {
-                service.onStopUser(userInfo);
-            } catch (Exception ex) {
-                Slog.wtf(TAG, "Failure reporting stop of user " + userHandle
-                        + " to service " + serviceName, ex);
-            }
-            warnIfTooLong(SystemClock.elapsedRealtime() - time, service, "onStopUser");
-            t.traceEnd();
-        }
-        t.traceEnd();
+        onUser("Stop", userHandle, (s, u) -> s.onStopUser(u));
     }
 
     /**
      * Cleans up the given user.
      */
     public void cleanupUser(final @UserIdInt int userHandle) {
-        final TimingsTraceAndSlog t = TimingsTraceAndSlog.newAsyncLog();
-        t.traceBegin("ssm.cleanupUser-" + userHandle);
-        Slog.i(TAG, "Calling onCleanupUser u" + userHandle);
+        onUser("Cleanup", userHandle, (s, u) -> s.onCleanupUser(u));
+    }
+
+    private interface ServiceVisitor {
+        void visit(@NonNull SystemService service, @NonNull UserInfo userInfo);
+    }
+
+    private void onUser(@NonNull String onWhat, @UserIdInt int userHandle,
+            @NonNull ServiceVisitor visitor) {
+        onUser(TimingsTraceAndSlog.newAsyncLog(), onWhat, userHandle, visitor);
+    }
+
+    private void onUser(@NonNull TimingsTraceAndSlog t, @NonNull String onWhat,
+            @UserIdInt int userHandle, @NonNull ServiceVisitor visitor) {
+        t.traceBegin("ssm." + onWhat + "User-" + userHandle);
+        Slog.i(TAG, "Calling on" + onWhat + "User u" + userHandle);
         final UserInfo userInfo = getUserInfo(userHandle);
         final int serviceLen = mServices.size();
         for (int i = 0; i < serviceLen; i++) {
             final SystemService service = mServices.get(i);
             final String serviceName = service.getClass().getName();
-            t.traceBegin("onCleanupUser-" + userHandle + " " + serviceName);
+            t.traceBegin("ssm.on" + onWhat + "User-" + userHandle + " " + serviceName);
             long time = SystemClock.elapsedRealtime();
             try {
-                service.onCleanupUser(userInfo);
+                visitor.visit(service, userInfo);
             } catch (Exception ex) {
-                Slog.wtf(TAG, "Failure reporting cleanup of user " + userHandle
+                Slog.wtf(TAG, "Failure reporting " + onWhat + " of user " + userHandle
                         + " to service " + serviceName, ex);
             }
-            warnIfTooLong(SystemClock.elapsedRealtime() - time, service, "onCleanupUser");
-            t.traceEnd();
+            warnIfTooLong(SystemClock.elapsedRealtime() - time, service, "on" + onWhat + "User ");
+            t.traceEnd(); // what on service
         }
-        t.traceEnd();
+        t.traceEnd(); // main entry
     }
 
     /** Sets the safe mode flag for services to query. */
diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java
index e66e596..f7e825e 100644
--- a/services/core/java/com/android/server/TelephonyRegistry.java
+++ b/services/core/java/com/android/server/TelephonyRegistry.java
@@ -1027,7 +1027,12 @@
                 log(str);
             }
             mLocalLog.log(str);
-            if (validatePhoneId(phoneId)) {
+            // for service state updates, don't notify clients when subId is invalid. This prevents
+            // us from sending incorrect notifications like b/133140128
+            // In the future, we can remove this logic for every notification here and add a
+            // callback so listeners know when their PhoneStateListener's subId becomes invalid, but
+            // for now we use the simplest fix.
+            if (validatePhoneId(phoneId) && SubscriptionManager.isValidSubscriptionId(subId)) {
                 mServiceState[phoneId] = state;
 
                 for (Record r : mRecords) {
@@ -1059,7 +1064,8 @@
                     }
                 }
             } else {
-                log("notifyServiceStateForSubscriber: INVALID phoneId=" + phoneId);
+                log("notifyServiceStateForSubscriber: INVALID phoneId=" + phoneId
+                        + " or subId=" + subId);
             }
             handleRemoveListLocked();
         }
diff --git a/services/core/java/com/android/server/VibratorService.java b/services/core/java/com/android/server/VibratorService.java
index 0748279..9936d73 100644
--- a/services/core/java/com/android/server/VibratorService.java
+++ b/services/core/java/com/android/server/VibratorService.java
@@ -1188,6 +1188,7 @@
     private static boolean isNotification(int usageHint) {
         switch (usageHint) {
             case AudioAttributes.USAGE_NOTIFICATION:
+            case AudioAttributes.USAGE_NOTIFICATION_EVENT:
             case AudioAttributes.USAGE_NOTIFICATION_COMMUNICATION_REQUEST:
             case AudioAttributes.USAGE_NOTIFICATION_COMMUNICATION_INSTANT:
             case AudioAttributes.USAGE_NOTIFICATION_COMMUNICATION_DELAYED:
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 19c818f..5c0fe4e 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -282,6 +282,7 @@
 import android.util.ArraySet;
 import android.util.DebugUtils;
 import android.util.EventLog;
+import android.util.FeatureFlagUtils;
 import android.util.Log;
 import android.util.Pair;
 import android.util.PrintWriterPrinter;
@@ -378,7 +379,7 @@
 import java.io.InputStreamReader;
 import java.io.PrintWriter;
 import java.io.StringWriter;
-import java.io.UnsupportedEncodingException;
+import java.nio.charset.StandardCharsets;
 import java.text.SimpleDateFormat;
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -437,6 +438,10 @@
     // need not be the case.
     public static final String ACTION_TRIGGER_IDLE = "com.android.server.ACTION_TRIGGER_IDLE";
 
+    private static final String INTENT_BUGREPORT_REQUESTED =
+            "com.android.internal.intent.action.BUGREPORT_REQUESTED";
+    private static final String SHELL_APP_PACKAGE = "com.android.shell";
+
     /** Control over CPU and battery monitoring */
     // write battery stats every 30 minutes.
     static final long BATTERY_STATS_TIME = 30 * 60 * 1000;
@@ -555,6 +560,10 @@
     OomAdjuster mOomAdjuster;
     final LowMemDetector mLowMemDetector;
 
+    static final String EXTRA_TITLE = "android.intent.extra.TITLE";
+    static final String EXTRA_DESCRIPTION = "android.intent.extra.DESCRIPTION";
+    static final String EXTRA_BUGREPORT_TYPE = "android.intent.extra.BUGREPORT_TYPE";
+
     /** All system services */
     SystemServiceManager mSystemServiceManager;
 
@@ -1480,8 +1489,9 @@
     public ActivityTaskManagerService mActivityTaskManager;
     @VisibleForTesting
     public ActivityTaskManagerInternal mAtmInternal;
+    UriGrantsManagerInternal mUgmInternal;
     @VisibleForTesting
-    public UriGrantsManagerInternal mUgmInternal;
+    public final ActivityManagerInternal mInternal;
     final ActivityThread mSystemThread;
 
     private final class AppDeathRecipient implements IBinder.DeathRecipient {
@@ -2413,6 +2423,8 @@
         mProcStartHandler = null;
         mHiddenApiBlacklist = null;
         mFactoryTest = FACTORY_TEST_OFF;
+        mUgmInternal = LocalServices.getService(UriGrantsManagerInternal.class);
+        mInternal = new LocalService();
     }
 
     // Note: This method is invoked on the main thread but may need to attach various
@@ -2566,6 +2578,7 @@
             Slog.w(TAG, "Setting background thread cpuset failed");
         }
 
+        mInternal = new LocalService();
     }
 
     public void setSystemServiceManager(SystemServiceManager mgr) {
@@ -2583,7 +2596,7 @@
         mBatteryStatsService.publish();
         mAppOpsService.publish(mContext);
         Slog.d("AppOps", "AppOpsService published");
-        LocalServices.addService(ActivityManagerInternal.class, new LocalService());
+        LocalServices.addService(ActivityManagerInternal.class, mInternal);
         mActivityTaskManager.onActivityManagerInternalAdded();
         mUgmInternal.onActivityManagerInternalAdded();
         mPendingIntentController.onActivityManagerInternalAdded();
@@ -8197,39 +8210,7 @@
     @Deprecated
     @Override
     public void requestBugReport(int bugreportType) {
-        String extraOptions = null;
-        switch (bugreportType) {
-            case ActivityManager.BUGREPORT_OPTION_FULL:
-                extraOptions = "bugreportfull";
-                break;
-            case ActivityManager.BUGREPORT_OPTION_INTERACTIVE:
-                extraOptions = "bugreportplus";
-                break;
-            case ActivityManager.BUGREPORT_OPTION_REMOTE:
-                extraOptions = "bugreportremote";
-                break;
-            case ActivityManager.BUGREPORT_OPTION_WEAR:
-                extraOptions = "bugreportwear";
-                break;
-            case ActivityManager.BUGREPORT_OPTION_TELEPHONY:
-                extraOptions = "bugreporttelephony";
-                break;
-            case ActivityManager.BUGREPORT_OPTION_WIFI:
-                extraOptions = "bugreportwifi";
-                break;
-            default:
-                throw new IllegalArgumentException("Provided bugreport type is not correct, value: "
-                        + bugreportType);
-        }
-        // Always log caller, even if it does not have permission to dump.
-        String type = extraOptions == null ? "bugreport" : extraOptions;
-        Slog.i(TAG, type + " requested by UID " + Binder.getCallingUid());
-
-        enforceCallingPermission(android.Manifest.permission.DUMP, "requestBugReport");
-        if (extraOptions != null) {
-            SystemProperties.set("dumpstate.options", extraOptions);
-        }
-        SystemProperties.set("ctl.start", "bugreport");
+        requestBugReportWithDescription(null, null, bugreportType);
     }
 
     /**
@@ -8238,37 +8219,78 @@
      * No new code should be calling it.
      */
     @Deprecated
-    private void requestBugReportWithDescription(String shareTitle, String shareDescription,
-                                                 int bugreportType) {
+    public void requestBugReportWithDescription(@Nullable String shareTitle,
+            @Nullable String shareDescription, int bugreportType) {
+        String type = null;
+        switch (bugreportType) {
+            case ActivityManager.BUGREPORT_OPTION_FULL:
+                type = "bugreportfull";
+                break;
+            case ActivityManager.BUGREPORT_OPTION_INTERACTIVE:
+                type = "bugreportplus";
+                break;
+            case ActivityManager.BUGREPORT_OPTION_REMOTE:
+                type = "bugreportremote";
+                break;
+            case ActivityManager.BUGREPORT_OPTION_WEAR:
+                type = "bugreportwear";
+                break;
+            case ActivityManager.BUGREPORT_OPTION_TELEPHONY:
+                type = "bugreporttelephony";
+                break;
+            case ActivityManager.BUGREPORT_OPTION_WIFI:
+                type = "bugreportwifi";
+                break;
+            default:
+                throw new IllegalArgumentException(
+                    "Provided bugreport type is not correct, value: "
+                        + bugreportType);
+        }
+        // Always log caller, even if it does not have permission to dump.
+        Slog.i(TAG, type + " requested by UID " + Binder.getCallingUid());
+        enforceCallingPermission(android.Manifest.permission.DUMP, "requestBugReport");
+
         if (!TextUtils.isEmpty(shareTitle)) {
             if (shareTitle.length() > MAX_BUGREPORT_TITLE_SIZE) {
                 String errorStr = "shareTitle should be less than " +
                         MAX_BUGREPORT_TITLE_SIZE + " characters";
                 throw new IllegalArgumentException(errorStr);
-            } else {
-                if (!TextUtils.isEmpty(shareDescription)) {
-                    int length;
-                    try {
-                        length = shareDescription.getBytes("UTF-8").length;
-                    } catch (UnsupportedEncodingException e) {
-                        String errorStr = "shareDescription: UnsupportedEncodingException";
-                        throw new IllegalArgumentException(errorStr);
-                    }
-                    if (length > SystemProperties.PROP_VALUE_MAX) {
-                        String errorStr = "shareTitle should be less than " +
-                                SystemProperties.PROP_VALUE_MAX + " bytes";
-                        throw new IllegalArgumentException(errorStr);
-                    } else {
-                        SystemProperties.set("dumpstate.options.description", shareDescription);
-                    }
-                }
-                SystemProperties.set("dumpstate.options.title", shareTitle);
             }
+            if (!TextUtils.isEmpty(shareDescription)) {
+                int length = shareDescription.getBytes(StandardCharsets.UTF_8).length;
+                if (length > SystemProperties.PROP_VALUE_MAX) {
+                    String errorStr = "shareTitle should be less than " +
+                            SystemProperties.PROP_VALUE_MAX + " bytes";
+                    throw new IllegalArgumentException(errorStr);
+                } else {
+                    SystemProperties.set("dumpstate.options.description", shareDescription);
+                }
+            }
+            SystemProperties.set("dumpstate.options.title", shareTitle);
+            Slog.d(TAG, "Bugreport notification title " + shareTitle
+                    + " description " + shareDescription);
         }
+        final boolean useApi = FeatureFlagUtils.isEnabled(mContext,
+                FeatureFlagUtils.USE_BUGREPORT_API);
 
-        Slog.d(TAG, "Bugreport notification title " + shareTitle
-                + " description " + shareDescription);
-        requestBugReport(bugreportType);
+        if (useApi && bugreportType == ActivityManager.BUGREPORT_OPTION_INTERACTIVE) {
+            // Create intent to trigger Bugreport API via Shell
+            Intent triggerShellBugreport = new Intent();
+            triggerShellBugreport.setAction(INTENT_BUGREPORT_REQUESTED);
+            triggerShellBugreport.setPackage(SHELL_APP_PACKAGE);
+            triggerShellBugreport.putExtra(EXTRA_BUGREPORT_TYPE, bugreportType);
+            if (shareTitle != null) {
+                triggerShellBugreport.putExtra(EXTRA_TITLE, shareTitle);
+            }
+            if (shareDescription != null) {
+                triggerShellBugreport.putExtra(EXTRA_DESCRIPTION, shareDescription);
+            }
+            // Send broadcast to shell to trigger bugreport using Bugreport API
+            mContext.sendBroadcast(triggerShellBugreport);
+        } else {
+            SystemProperties.set("dumpstate.options", type);
+            SystemProperties.set("ctl.start", "bugreport");
+        }
     }
 
     /**
@@ -12209,11 +12231,8 @@
             proto.write(MemInfoDumpProto.MemItem.IS_PROC, mi.isProc);
             proto.write(MemInfoDumpProto.MemItem.ID, mi.id);
             proto.write(MemInfoDumpProto.MemItem.HAS_ACTIVITIES, mi.hasActivities);
-            if (dumpPss) {
-                proto.write(MemInfoDumpProto.MemItem.PSS_KB, mi.pss);
-            } else {
-                proto.write(MemInfoDumpProto.MemItem.RSS_KB, mi.mRss);
-            }
+            proto.write(MemInfoDumpProto.MemItem.PSS_KB, mi.pss);
+            proto.write(MemInfoDumpProto.MemItem.RSS_KB, mi.mRss);
             if (dumpSwapPss) {
                 proto.write(MemInfoDumpProto.MemItem.SWAP_PSS_KB, mi.swapPss);
             }
@@ -12511,6 +12530,13 @@
         if (!brief && !opts.oomOnly && (procs.size() == 1 || opts.isCheckinRequest || opts.packages)) {
             opts.dumpDetails = true;
         }
+        final int numProcs = procs.size();
+        final boolean collectNative = !opts.isCheckinRequest && numProcs > 1 && !opts.packages;
+        if (collectNative) {
+            // If we are showing aggregations, also look for native processes to
+            // include so that our aggregations are more accurate.
+            updateCpuStatsNow();
+        }
 
         dumpApplicationMemoryUsageHeader(pw, uptime, realtime, opts.isCheckinRequest, opts.isCompact);
 
@@ -12549,7 +12575,7 @@
         boolean hasSwapPss = false;
 
         Debug.MemoryInfo mi = null;
-        for (int i = procs.size() - 1 ; i >= 0 ; i--) {
+        for (int i = numProcs - 1; i >= 0; i--) {
             final ProcessRecord r = procs.get(i);
             final IApplicationThread thread;
             final int pid;
@@ -12703,10 +12729,7 @@
 
         long nativeProcTotalPss = 0;
 
-        if (!opts.isCheckinRequest && procs.size() > 1 && !opts.packages) {
-            // If we are showing aggregations, also look for native processes to
-            // include so that our aggregations are more accurate.
-            updateCpuStatsNow();
+        if (collectNative) {
             mi = null;
             synchronized (mProcessCpuTracker) {
                 final int N = mProcessCpuTracker.countStats();
@@ -13074,6 +13097,13 @@
         if (!brief && !opts.oomOnly && (procs.size() == 1 || opts.isCheckinRequest || opts.packages)) {
             opts.dumpDetails = true;
         }
+        final int numProcs = procs.size();
+        final boolean collectNative = numProcs > 1 && !opts.packages;
+        if (collectNative) {
+            // If we are showing aggregations, also look for native processes to
+            // include so that our aggregations are more accurate.
+            updateCpuStatsNow();
+        }
 
         ProtoOutputStream proto = new ProtoOutputStream(fd);
 
@@ -13115,7 +13145,7 @@
         boolean hasSwapPss = false;
 
         Debug.MemoryInfo mi = null;
-        for (int i = procs.size() - 1 ; i >= 0 ; i--) {
+        for (int i = numProcs - 1; i >= 0; i--) {
             final ProcessRecord r = procs.get(i);
             final IApplicationThread thread;
             final int pid;
@@ -13262,10 +13292,7 @@
 
         long nativeProcTotalPss = 0;
 
-        if (procs.size() > 1 && !opts.packages) {
-            // If we are showing aggregations, also look for native processes to
-            // include so that our aggregations are more accurate.
-            updateCpuStatsNow();
+        if (collectNative) {
             mi = null;
             synchronized (mProcessCpuTracker) {
                 final int N = mProcessCpuTracker.countStats();
diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java
index 5465309..c0af814 100644
--- a/services/core/java/com/android/server/am/ProcessList.java
+++ b/services/core/java/com/android/server/am/ProcessList.java
@@ -65,7 +65,6 @@
 import android.os.IBinder;
 import android.os.Looper;
 import android.os.Message;
-import android.os.MessageQueue;
 import android.os.Process;
 import android.os.RemoteException;
 import android.os.StrictMode;
@@ -101,11 +100,8 @@
 
 import dalvik.system.VMRuntime;
 
-import libcore.io.IoUtils;
-
 import java.io.File;
 import java.io.IOException;
-import java.io.InputStream;
 import java.io.OutputStream;
 import java.io.PrintWriter;
 import java.nio.ByteBuffer;
@@ -2087,10 +2083,10 @@
                     }
                 }
             }
-            if (lrui <= mLruProcessActivityStart) {
+            if (lrui < mLruProcessActivityStart) {
                 mLruProcessActivityStart--;
             }
-            if (lrui <= mLruProcessServiceStart) {
+            if (lrui < mLruProcessServiceStart) {
                 mLruProcessServiceStart--;
             }
             mLruProcesses.remove(lrui);
@@ -2622,7 +2618,7 @@
                         if (!moved) {
                             // Goes to the end of the group.
                             mLruProcesses.remove(i);
-                            mLruProcesses.add(endIndex - 1, subProc);
+                            mLruProcesses.add(endIndex, subProc);
                             if (DEBUG_LRU) Slog.d(TAG_LRU,
                                     "Moving " + subProc
                                             + " from position " + i + " to end of group @ "
@@ -2867,15 +2863,6 @@
                     pos--;
                 }
                 mLruProcesses.add(pos, app);
-                if (pos == mLruProcessActivityStart) {
-                    mLruProcessActivityStart++;
-                }
-                if (pos == mLruProcessServiceStart) {
-                    // Unless {@code #hasService} is implemented, currently the starting position
-                    // for activity and service are the same, so the incoming position may equal to
-                    // the starting position of service.
-                    mLruProcessServiceStart++;
-                }
                 // If this process is part of a group, need to pull up any other processes
                 // in that group to be with it.
                 int endIndex = pos - 1;
diff --git a/services/core/java/com/android/server/audio/FocusRequester.java b/services/core/java/com/android/server/audio/FocusRequester.java
index 65472c9..bd129f7 100644
--- a/services/core/java/com/android/server/audio/FocusRequester.java
+++ b/services/core/java/com/android/server/audio/FocusRequester.java
@@ -405,7 +405,7 @@
     @GuardedBy("MediaFocusControl.mAudioFocusLock")
     private boolean frameworkHandleFocusLoss(int focusLoss, @NonNull final FocusRequester frWinner,
                                              boolean forceDuck) {
-        if (frWinner.mCallingUid != this.mCallingUid) {
+        if (frWinner.mCallingUid == this.mCallingUid) {
             // the focus change is within the same app, so let the dispatching
             // happen as if the framework was not involved.
             return false;
diff --git a/services/core/java/com/android/server/biometrics/AuthenticationClient.java b/services/core/java/com/android/server/biometrics/AuthenticationClient.java
index 4a9ccde..766e5c4 100644
--- a/services/core/java/com/android/server/biometrics/AuthenticationClient.java
+++ b/services/core/java/com/android/server/biometrics/AuthenticationClient.java
@@ -209,8 +209,7 @@
                     // will show briefly and be replaced by "device locked out" message.
                     if (listener != null) {
                         if (isBiometricPrompt()) {
-                            listener.onAuthenticationFailedInternal(getCookie(),
-                                    getRequireConfirmation());
+                            listener.onAuthenticationFailedInternal();
                         } else {
                             listener.onAuthenticationFailed(getHalDeviceId());
                         }
diff --git a/services/core/java/com/android/server/biometrics/BiometricService.java b/services/core/java/com/android/server/biometrics/BiometricService.java
index af2f24f..24e6a75 100644
--- a/services/core/java/com/android/server/biometrics/BiometricService.java
+++ b/services/core/java/com/android/server/biometrics/BiometricService.java
@@ -25,11 +25,9 @@
 import static android.hardware.biometrics.BiometricAuthenticator.TYPE_NONE;
 
 import android.app.ActivityManager;
-import android.app.ActivityTaskManager;
 import android.app.AppOpsManager;
-import android.app.IActivityTaskManager;
+import android.app.IActivityManager;
 import android.app.KeyguardManager;
-import android.app.TaskStackListener;
 import android.app.UserSwitchObserver;
 import android.content.ContentResolver;
 import android.content.Context;
@@ -69,6 +67,7 @@
 import android.util.StatsLog;
 
 import com.android.internal.R;
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.os.SomeArgs;
 import com.android.internal.statusbar.IStatusBarService;
 import com.android.server.SystemService;
@@ -88,9 +87,8 @@
     private static final String TAG = "BiometricService";
     private static final boolean DEBUG = true;
 
-    private static final int MSG_ON_TASK_STACK_CHANGED = 1;
     private static final int MSG_ON_AUTHENTICATION_SUCCEEDED = 2;
-    private static final int MSG_ON_AUTHENTICATION_FAILED = 3;
+    private static final int MSG_ON_AUTHENTICATION_REJECTED = 3;
     private static final int MSG_ON_ERROR = 4;
     private static final int MSG_ON_ACQUIRED = 5;
     private static final int MSG_ON_DISMISSED = 6;
@@ -101,6 +99,7 @@
     private static final int MSG_ON_CONFIRM_DEVICE_CREDENTIAL_SUCCESS = 11;
     private static final int MSG_ON_CONFIRM_DEVICE_CREDENTIAL_ERROR = 12;
     private static final int MSG_REGISTER_CANCELLATION_CALLBACK = 13;
+    private static final int MSG_ON_AUTHENTICATION_TIMED_OUT = 14;
 
     private static final int[] FEATURE_ID = {
         TYPE_FINGERPRINT,
@@ -112,33 +111,41 @@
      * Authentication either just called and we have not transitioned to the CALLED state, or
      * authentication terminated (success or error).
      */
-    private static final int STATE_AUTH_IDLE = 0;
+    static final int STATE_AUTH_IDLE = 0;
     /**
      * Authentication was called and we are waiting for the <Biometric>Services to return their
      * cookies before starting the hardware and showing the BiometricPrompt.
      */
-    private static final int STATE_AUTH_CALLED = 1;
+    static final int STATE_AUTH_CALLED = 1;
     /**
      * Authentication started, BiometricPrompt is showing and the hardware is authenticating.
      */
-    private static final int STATE_AUTH_STARTED = 2;
+    static final int STATE_AUTH_STARTED = 2;
     /**
      * Authentication is paused, waiting for the user to press "try again" button. Only
      * passive modalities such as Face or Iris should have this state. Note that for passive
      * modalities, the HAL enters the idle state after onAuthenticated(false) which differs from
      * fingerprint.
      */
-    private static final int STATE_AUTH_PAUSED = 3;
+    static final int STATE_AUTH_PAUSED = 3;
     /**
      * Authentication is successful, but we're waiting for the user to press "confirm" button.
      */
-    private static final int STATE_AUTH_PENDING_CONFIRM = 5;
+    static final int STATE_AUTH_PENDING_CONFIRM = 5;
     /**
      * Biometric authentication was canceled, but the device is now showing ConfirmDeviceCredential
      */
-    private static final int STATE_BIOMETRIC_AUTH_CANCELED_SHOWING_CDC = 6;
+    static final int STATE_BIOMETRIC_AUTH_CANCELED_SHOWING_CDC = 6;
+    /**
+     * Biometric authenticated, waiting for SysUI to finish animation
+     */
+    static final int STATE_AUTHENTICATED_PENDING_SYSUI = 7;
+    /**
+     * Biometric error, waiting for SysUI to finish animation
+     */
+    static final int STATE_ERROR_PENDING_SYSUI = 8;
 
-    private final class AuthSession implements IBinder.DeathRecipient {
+    final class AuthSession implements IBinder.DeathRecipient {
         // Map of Authenticator/Cookie pairs. We expect to receive the cookies back from
         // <Biometric>Services before we can start authenticating. Pairs that have been returned
         // are moved to mModalitiesMatched.
@@ -165,10 +172,13 @@
         final boolean mRequireConfirmation;
 
         // The current state, which can be either idle, called, or started
-        private int mState = STATE_AUTH_IDLE;
+        int mState = STATE_AUTH_IDLE;
         // For explicit confirmation, do not send to keystore until the user has confirmed
         // the authentication.
         byte[] mTokenEscrow;
+        // Waiting for SystemUI to complete animation
+        int mErrorEscrow;
+        String mErrorStringEscrow;
 
         // Timestamp when authentication started
         private long mStartTimeMs;
@@ -244,42 +254,37 @@
         }
     }
 
-    private final class BiometricTaskStackListener extends TaskStackListener {
-        @Override
-        public void onTaskStackChanged() {
-            mHandler.sendEmptyMessage(MSG_ON_TASK_STACK_CHANGED);
-        }
-    }
-
+    private final Injector mInjector;
+    @VisibleForTesting
+    final IBiometricService.Stub mImpl;
     private final AppOpsManager mAppOps;
     private final boolean mHasFeatureFingerprint;
     private final boolean mHasFeatureIris;
     private final boolean mHasFeatureFace;
-    private final SettingObserver mSettingObserver;
+    @VisibleForTesting
+    SettingObserver mSettingObserver;
     private final List<EnabledOnKeyguardCallback> mEnabledOnKeyguardCallbacks;
-    private final BiometricTaskStackListener mTaskStackListener = new BiometricTaskStackListener();
     private final Random mRandom = new Random();
 
-    private IFingerprintService mFingerprintService;
-    private IFaceService mFaceService;
-    private IActivityTaskManager mActivityTaskManager;
-    private IStatusBarService mStatusBarService;
+    @VisibleForTesting
+    IFingerprintService mFingerprintService;
+    @VisibleForTesting
+    IFaceService mFaceService;
+    @VisibleForTesting
+    IStatusBarService mStatusBarService;
+    @VisibleForTesting
+    KeyStore mKeyStore;
 
     // Get and cache the available authenticator (manager) classes. Used since aidl doesn't support
     // polymorphism :/
     final ArrayList<Authenticator> mAuthenticators = new ArrayList<>();
 
-    // Cache the current service that's being used. This is the service which
-    // cancelAuthentication() must be forwarded to. This is just a cache, and the actual
-    // check (is caller the current client) is done in the <Biometric>Service.
-    // Since Settings/System (not application) is responsible for changing preference, this
-    // should be safe.
-    private int mCurrentModality;
-
     // The current authentication session, null if idle/done. We need to track both the current
     // and pending sessions since errors may be sent to either.
-    private AuthSession mCurrentAuthSession;
-    private AuthSession mPendingAuthSession;
+    @VisibleForTesting
+    AuthSession mCurrentAuthSession;
+    @VisibleForTesting
+    AuthSession mPendingAuthSession;
 
     // TODO(b/123378871): Remove when moved.
     // When BiometricPrompt#setAllowDeviceCredentials is set to true, we need to store the
@@ -289,15 +294,11 @@
     // to this receiver.
     private IBiometricServiceReceiver mConfirmDeviceCredentialReceiver;
 
-    private final Handler mHandler = new Handler(Looper.getMainLooper()) {
+    @VisibleForTesting
+    final Handler mHandler = new Handler(Looper.getMainLooper()) {
         @Override
         public void handleMessage(Message msg) {
             switch (msg.what) {
-                case MSG_ON_TASK_STACK_CHANGED: {
-                    handleTaskStackChanged();
-                    break;
-                }
-
                 case MSG_ON_AUTHENTICATION_SUCCEEDED: {
                     SomeArgs args = (SomeArgs) msg.obj;
                     handleAuthenticationSucceeded(
@@ -307,8 +308,8 @@
                     break;
                 }
 
-                case MSG_ON_AUTHENTICATION_FAILED: {
-                    handleAuthenticationFailed((String) msg.obj /* failureReason */);
+                case MSG_ON_AUTHENTICATION_REJECTED: {
+                    handleAuthenticationRejected((String) msg.obj /* failureReason */);
                     break;
                 }
 
@@ -397,6 +398,11 @@
                     break;
                 }
 
+                case MSG_ON_AUTHENTICATION_TIMED_OUT: {
+                    handleAuthenticationTimedOut((String) msg.obj /* errorMessage */);
+                    break;
+                }
+
                 default:
                     Slog.e(TAG, "Unknown message: " + msg);
                     break;
@@ -422,7 +428,8 @@
         }
     }
 
-    private final class SettingObserver extends ContentObserver {
+    @VisibleForTesting
+    public static class SettingObserver extends ContentObserver {
 
         private static final boolean DEFAULT_KEYGUARD_ENABLED = true;
         private static final boolean DEFAULT_APP_ENABLED = true;
@@ -436,6 +443,7 @@
                 Settings.Secure.getUriFor(Settings.Secure.FACE_UNLOCK_ALWAYS_REQUIRE_CONFIRMATION);
 
         private final ContentResolver mContentResolver;
+        private final List<BiometricService.EnabledOnKeyguardCallback> mCallbacks;
 
         private Map<Integer, Boolean> mFaceEnabledOnKeyguard = new HashMap<>();
         private Map<Integer, Boolean> mFaceEnabledForApps = new HashMap<>();
@@ -446,13 +454,15 @@
          *
          * @param handler The handler to run {@link #onChange} on, or null if none.
          */
-        SettingObserver(Handler handler) {
+        public SettingObserver(Context context, Handler handler,
+                List<BiometricService.EnabledOnKeyguardCallback> callbacks) {
             super(handler);
-            mContentResolver = getContext().getContentResolver();
+            mContentResolver = context.getContentResolver();
+            mCallbacks = callbacks;
             updateContentObserver();
         }
 
-        void updateContentObserver() {
+        public void updateContentObserver() {
             mContentResolver.unregisterContentObserver(this);
             mContentResolver.registerContentObserver(FACE_UNLOCK_KEYGUARD_ENABLED,
                     false /* notifyForDescendents */,
@@ -495,7 +505,7 @@
             }
         }
 
-        boolean getFaceEnabledOnKeyguard() {
+        public boolean getFaceEnabledOnKeyguard() {
             final int user = ActivityManager.getCurrentUser();
             if (!mFaceEnabledOnKeyguard.containsKey(user)) {
                 onChange(true /* selfChange */, FACE_UNLOCK_KEYGUARD_ENABLED, user);
@@ -503,22 +513,23 @@
             return mFaceEnabledOnKeyguard.get(user);
         }
 
-        boolean getFaceEnabledForApps(int userId) {
+        public boolean getFaceEnabledForApps(int userId) {
+            Slog.e(TAG, "getFaceEnabledForApps: " + userId, new Exception());
             if (!mFaceEnabledForApps.containsKey(userId)) {
                 onChange(true /* selfChange */, FACE_UNLOCK_APP_ENABLED, userId);
             }
             return mFaceEnabledForApps.getOrDefault(userId, DEFAULT_APP_ENABLED);
         }
 
-        boolean getFaceAlwaysRequireConfirmation(int userId) {
+        public boolean getFaceAlwaysRequireConfirmation(int userId) {
             if (!mFaceAlwaysRequireConfirmation.containsKey(userId)) {
                 onChange(true /* selfChange */, FACE_UNLOCK_ALWAYS_REQUIRE_CONFIRMATION, userId);
             }
             return mFaceAlwaysRequireConfirmation.get(userId);
         }
 
-        void notifyEnabledOnKeyguardCallbacks(int userId) {
-            List<EnabledOnKeyguardCallback> callbacks = mEnabledOnKeyguardCallbacks;
+        public void notifyEnabledOnKeyguardCallbacks(int userId) {
+            List<EnabledOnKeyguardCallback> callbacks = mCallbacks;
             for (int i = 0; i < callbacks.size(); i++) {
                 callbacks.get(i).notify(BiometricSourceType.FACE,
                         mFaceEnabledOnKeyguard.getOrDefault(userId, DEFAULT_KEYGUARD_ENABLED),
@@ -527,7 +538,7 @@
         }
     }
 
-    private final class EnabledOnKeyguardCallback implements IBinder.DeathRecipient {
+    final class EnabledOnKeyguardCallback implements IBinder.DeathRecipient {
 
         private final IBiometricEnabledOnKeyguardCallback mCallback;
 
@@ -559,7 +570,8 @@
     }
 
     // Wrap the client's receiver so we can do things with the BiometricDialog first
-    private final IBiometricServiceReceiverInternal mInternalReceiver =
+    @VisibleForTesting
+    final IBiometricServiceReceiverInternal mInternalReceiver =
             new IBiometricServiceReceiverInternal.Stub() {
         @Override
         public void onAuthenticationSucceeded(boolean requireConfirmation, byte[] token)
@@ -571,10 +583,11 @@
         }
 
         @Override
-        public void onAuthenticationFailed(int cookie, boolean requireConfirmation)
+        public void onAuthenticationFailed()
                 throws RemoteException {
             String failureReason = getContext().getString(R.string.biometric_not_recognized);
-            mHandler.obtainMessage(MSG_ON_AUTHENTICATION_FAILED, failureReason).sendToTarget();
+            Slog.v(TAG, "onAuthenticationFailed: " + failureReason);
+            mHandler.obtainMessage(MSG_ON_AUTHENTICATION_REJECTED, failureReason).sendToTarget();
         }
 
         @Override
@@ -583,7 +596,7 @@
             // soft errors and we should allow the user to try authenticating again instead of
             // dismissing BiometricPrompt.
             if (error == BiometricConstants.BIOMETRIC_ERROR_TIMEOUT) {
-                mHandler.obtainMessage(MSG_ON_AUTHENTICATION_FAILED, message).sendToTarget();
+                mHandler.obtainMessage(MSG_ON_AUTHENTICATION_TIMED_OUT, message).sendToTarget();
             } else {
                 SomeArgs args = SomeArgs.obtain();
                 args.argi1 = cookie;
@@ -873,6 +886,44 @@
         }
     }
 
+    @VisibleForTesting
+    static class Injector {
+        IActivityManager getActivityManagerService() {
+            return ActivityManager.getService();
+        }
+
+        IStatusBarService getStatusBarService() {
+            return IStatusBarService.Stub.asInterface(
+                    ServiceManager.getService(Context.STATUS_BAR_SERVICE));
+        }
+
+        IFingerprintService getFingerprintService() {
+            return IFingerprintService.Stub.asInterface(
+                    ServiceManager.getService(Context.FINGERPRINT_SERVICE));
+        }
+
+        IFaceService getFaceService() {
+            return IFaceService.Stub.asInterface(ServiceManager.getService(Context.FACE_SERVICE));
+        }
+
+        SettingObserver getSettingObserver(Context context, Handler handler,
+                List<EnabledOnKeyguardCallback> callbacks) {
+            return new SettingObserver(context, handler, callbacks);
+        }
+
+        KeyStore getKeyStore() {
+            return KeyStore.getInstance();
+        }
+
+        boolean isDebugEnabled(Context context, int userId) {
+            return Utils.isDebugEnabled(context, userId);
+        }
+
+        void publishBinderService(BiometricService service, IBiometricService.Stub impl) {
+            service.publishBinderService(Context.BIOMETRIC_SERVICE, impl);
+        }
+    }
+
     /**
      * Initializes the system service.
      * <p>
@@ -883,11 +934,19 @@
      * @param context The system server context.
      */
     public BiometricService(Context context) {
+        this(context, new Injector());
+    }
+
+    @VisibleForTesting
+    BiometricService(Context context, Injector injector) {
         super(context);
 
+        mInjector = injector;
+        mImpl = new BiometricServiceWrapper();
         mAppOps = context.getSystemService(AppOpsManager.class);
         mEnabledOnKeyguardCallbacks = new ArrayList<>();
-        mSettingObserver = new SettingObserver(mHandler);
+        mSettingObserver = mInjector.getSettingObserver(context, mHandler,
+                mEnabledOnKeyguardCallbacks);
 
         final PackageManager pm = context.getPackageManager();
         mHasFeatureFingerprint = pm.hasSystemFeature(PackageManager.FEATURE_FINGERPRINT);
@@ -895,7 +954,7 @@
         mHasFeatureFace = pm.hasSystemFeature(PackageManager.FEATURE_FACE);
 
         try {
-            ActivityManager.getService().registerUserSwitchObserver(
+            injector.getActivityManagerService().registerUserSwitchObserver(
                     new UserSwitchObserver() {
                         @Override
                         public void onUserSwitchComplete(int newUserId) {
@@ -913,17 +972,14 @@
     public void onStart() {
         // TODO: maybe get these on-demand
         if (mHasFeatureFingerprint) {
-            mFingerprintService = IFingerprintService.Stub.asInterface(
-                    ServiceManager.getService(Context.FINGERPRINT_SERVICE));
+            mFingerprintService = mInjector.getFingerprintService();
         }
         if (mHasFeatureFace) {
-            mFaceService = IFaceService.Stub.asInterface(
-                    ServiceManager.getService(Context.FACE_SERVICE));
+            mFaceService = mInjector.getFaceService();
         }
 
-        mActivityTaskManager = ActivityTaskManager.getService();
-        mStatusBarService = IStatusBarService.Stub.asInterface(
-                ServiceManager.getService(Context.STATUS_BAR_SERVICE));
+        mKeyStore = mInjector.getKeyStore();
+        mStatusBarService = mInjector.getStatusBarService();
 
         // Cache the authenticators
         for (int i = 0; i < FEATURE_ID.length; i++) {
@@ -934,7 +990,7 @@
             }
         }
 
-        publishBinderService(Context.BIOMETRIC_SERVICE, new BiometricServiceWrapper());
+        mInjector.publishBinderService(this, mImpl);
     }
 
     /**
@@ -1068,7 +1124,7 @@
     }
 
     private void logDialogDismissed(int reason) {
-        if (reason == BiometricPrompt.DISMISSED_REASON_POSITIVE) {
+        if (reason == BiometricPrompt.DISMISSED_REASON_CONFIRMED) {
             // Explicit auth, authentication confirmed.
             // Latency in this case is authenticated -> confirmed. <Biometric>Service
             // should have the first half (first acquired -> authenticated).
@@ -1094,7 +1150,7 @@
                     mCurrentAuthSession.mRequireConfirmation,
                     StatsLog.BIOMETRIC_AUTHENTICATED__STATE__CONFIRMED,
                     latency,
-                    Utils.isDebugEnabled(getContext(), mCurrentAuthSession.mUserId));
+                    mInjector.isDebugEnabled(getContext(), mCurrentAuthSession.mUserId));
         } else {
 
             final long latency = System.currentTimeMillis() - mCurrentAuthSession.mStartTimeMs;
@@ -1122,7 +1178,7 @@
                     BiometricsProtoEnums.CLIENT_BIOMETRIC_PROMPT,
                     error,
                     0 /* vendorCode */,
-                    Utils.isDebugEnabled(getContext(), mCurrentAuthSession.mUserId),
+                    mInjector.isDebugEnabled(getContext(), mCurrentAuthSession.mUserId),
                     latency);
         }
     }
@@ -1145,51 +1201,22 @@
         return modality;
     }
 
-    private void handleTaskStackChanged() {
-        try {
-            final List<ActivityManager.RunningTaskInfo> runningTasks =
-                    mActivityTaskManager.getTasks(1);
-            if (!runningTasks.isEmpty()) {
-                final String topPackage = runningTasks.get(0).topActivity.getPackageName();
-                if (mCurrentAuthSession != null
-                        && !topPackage.contentEquals(mCurrentAuthSession.mOpPackageName)) {
-                    mStatusBarService.hideBiometricDialog();
-                    mActivityTaskManager.unregisterTaskStackListener(mTaskStackListener);
-                    mCurrentAuthSession.mClientReceiver.onError(
-                            BiometricConstants.BIOMETRIC_ERROR_CANCELED,
-                            getContext().getString(
-                                    com.android.internal.R.string.biometric_error_canceled)
-                    );
-                    mCurrentAuthSession.mState = STATE_AUTH_IDLE;
-                    mCurrentAuthSession = null;
-                }
-            }
-        } catch (RemoteException e) {
-            Slog.e(TAG, "Unable to get running tasks", e);
-        }
-    }
-
     private void handleAuthenticationSucceeded(boolean requireConfirmation, byte[] token) {
-
         try {
             // Should never happen, log this to catch bad HAL behavior (e.g. auth succeeded
             // after user dismissed/canceled dialog).
             if (mCurrentAuthSession == null) {
-                Slog.e(TAG, "onAuthenticationSucceeded(): Auth session is null");
+                Slog.e(TAG, "handleAuthenticationSucceeded: Auth session is null");
                 return;
             }
 
+            // Store the auth token and submit it to keystore after the dialog is confirmed /
+            // animating away.
+            mCurrentAuthSession.mTokenEscrow = token;
             if (!requireConfirmation) {
-                mActivityTaskManager.unregisterTaskStackListener(mTaskStackListener);
-                KeyStore.getInstance().addAuthToken(token);
-                mCurrentAuthSession.mClientReceiver.onAuthenticationSucceeded();
-                mCurrentAuthSession.mState = STATE_AUTH_IDLE;
-                mCurrentAuthSession = null;
+                mCurrentAuthSession.mState = STATE_AUTHENTICATED_PENDING_SYSUI;
             } else {
                 mCurrentAuthSession.mAuthenticatedTimeMs = System.currentTimeMillis();
-                // Store the auth token and submit it to keystore after the confirmation
-                // button has been pressed.
-                mCurrentAuthSession.mTokenEscrow = token;
                 mCurrentAuthSession.mState = STATE_AUTH_PENDING_CONFIRM;
             }
 
@@ -1201,12 +1228,13 @@
         }
     }
 
-    private void handleAuthenticationFailed(String failureReason) {
+    private void handleAuthenticationRejected(String failureReason) {
+        Slog.v(TAG, "handleAuthenticationRejected: " + failureReason);
         try {
             // Should never happen, log this to catch bad HAL behavior (e.g. auth succeeded
             // after user dismissed/canceled dialog).
             if (mCurrentAuthSession == null) {
-                Slog.e(TAG, "onAuthenticationFailed(): Auth session is null");
+                Slog.e(TAG, "handleAuthenticationRejected: Auth session is null");
                 return;
             }
 
@@ -1225,16 +1253,31 @@
         }
     }
 
+    private void handleAuthenticationTimedOut(String message) {
+        Slog.v(TAG, "handleAuthenticationTimedOut: " + message);
+        try {
+            // Should never happen, log this to catch bad HAL behavior (e.g. auth succeeded
+            // after user dismissed/canceled dialog).
+            if (mCurrentAuthSession == null) {
+                Slog.e(TAG, "handleAuthenticationTimedOut: Auth session is null");
+                return;
+            }
+
+            mStatusBarService.onBiometricAuthenticated(false, message);
+            mCurrentAuthSession.mState = STATE_AUTH_PAUSED;
+        } catch (RemoteException e) {
+            Slog.e(TAG, "Remote exception", e);
+        }
+    }
+
     private void handleOnConfirmDeviceCredentialSuccess() {
         if (mConfirmDeviceCredentialReceiver == null) {
-            Slog.w(TAG, "onCDCASuccess null!");
+            Slog.w(TAG, "handleOnConfirmDeviceCredentialSuccess null!");
             return;
         }
         try {
-            mActivityTaskManager.unregisterTaskStackListener(mTaskStackListener);
             mConfirmDeviceCredentialReceiver.onAuthenticationSucceeded();
             if (mCurrentAuthSession != null) {
-                mCurrentAuthSession.mState = STATE_AUTH_IDLE;
                 mCurrentAuthSession = null;
             }
         } catch (RemoteException e) {
@@ -1245,14 +1288,13 @@
 
     private void handleOnConfirmDeviceCredentialError(int error, String message) {
         if (mConfirmDeviceCredentialReceiver == null) {
-            Slog.w(TAG, "onCDCAError null! Error: " + error + " " + message);
+            Slog.w(TAG, "handleOnConfirmDeviceCredentialError null! Error: "
+                    + error + " " + message);
             return;
         }
         try {
-            mActivityTaskManager.unregisterTaskStackListener(mTaskStackListener);
             mConfirmDeviceCredentialReceiver.onError(error, message);
             if (mCurrentAuthSession != null) {
-                mCurrentAuthSession.mState = STATE_AUTH_IDLE;
                 mCurrentAuthSession = null;
             }
         } catch (RemoteException e) {
@@ -1272,7 +1314,7 @@
     }
 
     private void handleOnError(int cookie, int error, String message) {
-        Slog.d(TAG, "Error: " + error + " cookie: " + cookie);
+        Slog.d(TAG, "handleOnError: " + error + " cookie: " + cookie);
         // Errors can either be from the current auth session or the pending auth session.
         // The pending auth session may receive errors such as ERROR_LOCKOUT before
         // it becomes the current auth session. Similarly, the current auth session may
@@ -1282,6 +1324,9 @@
         try {
             if (mCurrentAuthSession != null && mCurrentAuthSession.containsCookie(cookie)) {
 
+                mCurrentAuthSession.mErrorEscrow = error;
+                mCurrentAuthSession.mErrorStringEscrow = message;
+
                 if (mCurrentAuthSession.isFromConfirmDeviceCredential()) {
                     // If we were invoked by ConfirmDeviceCredential, do not delete the current
                     // auth session since we still need to respond to cancel signal while
@@ -1293,39 +1338,18 @@
                     mCurrentAuthSession.mState = STATE_BIOMETRIC_AUTH_CANCELED_SHOWING_CDC;
                     mStatusBarService.hideBiometricDialog();
                 } else if (mCurrentAuthSession.mState == STATE_AUTH_STARTED) {
-                    mStatusBarService.onBiometricError(message);
+                    mCurrentAuthSession.mState = STATE_ERROR_PENDING_SYSUI;
                     if (error == BiometricConstants.BIOMETRIC_ERROR_CANCELED) {
-                        mActivityTaskManager.unregisterTaskStackListener(
-                                mTaskStackListener);
-                        mCurrentAuthSession.mClientReceiver.onError(error, message);
-                        mCurrentAuthSession.mState = STATE_AUTH_IDLE;
-                        mCurrentAuthSession = null;
                         mStatusBarService.hideBiometricDialog();
                     } else {
-                        // Send errors after the dialog is dismissed.
-                        mHandler.postDelayed(() -> {
-                            try {
-                                if (mCurrentAuthSession != null) {
-                                    mActivityTaskManager.unregisterTaskStackListener(
-                                            mTaskStackListener);
-                                    mCurrentAuthSession.mClientReceiver.onError(error,
-                                            message);
-                                    mCurrentAuthSession.mState = STATE_AUTH_IDLE;
-                                    mCurrentAuthSession = null;
-                                }
-                            } catch (RemoteException e) {
-                                Slog.e(TAG, "Remote exception", e);
-                            }
-                        }, BiometricPrompt.HIDE_DIALOG_DELAY);
+                        mStatusBarService.onBiometricError(message);
                     }
                 } else if (mCurrentAuthSession.mState == STATE_AUTH_PAUSED) {
                     // In the "try again" state, we should forward canceled errors to
-                    // the client and and clean up.
+                    // the client and and clean up. The only error we should get here is
+                    // ERROR_CANCELED due to another client kicking us out.
                     mCurrentAuthSession.mClientReceiver.onError(error, message);
-                    mStatusBarService.onBiometricError(message);
-                    mActivityTaskManager.unregisterTaskStackListener(
-                            mTaskStackListener);
-                    mCurrentAuthSession.mState = STATE_AUTH_IDLE;
+                    mStatusBarService.hideBiometricDialog();
                     mCurrentAuthSession = null;
                 } else {
                     Slog.e(TAG, "Impossible session error state: "
@@ -1335,7 +1359,6 @@
                     && mPendingAuthSession.containsCookie(cookie)) {
                 if (mPendingAuthSession.mState == STATE_AUTH_CALLED) {
                     mPendingAuthSession.mClientReceiver.onError(error, message);
-                    mPendingAuthSession.mState = STATE_AUTH_IDLE;
                     mPendingAuthSession = null;
                 } else {
                     Slog.e(TAG, "Impossible pending session error state: "
@@ -1370,42 +1393,50 @@
 
     private void handleOnDismissed(int reason) {
         if (mCurrentAuthSession == null) {
-            Slog.e(TAG, "onDialogDismissed: " + reason + ", auth session null");
+            Slog.e(TAG, "onDismissed: " + reason + ", auth session null");
             return;
         }
 
         logDialogDismissed(reason);
 
         try {
-            if (reason != BiometricPrompt.DISMISSED_REASON_POSITIVE) {
-                // Positive button is used by passive modalities as a "confirm" button,
-                // do not send to client
-                mCurrentAuthSession.mClientReceiver.onDialogDismissed(reason);
-                // Cancel authentication. Skip the token/package check since we are cancelling
-                // from system server. The interface is permission protected so this is fine.
-                cancelInternal(null /* token */, null /* package */, false /* fromClient */);
-            }
-            if (reason == BiometricPrompt.DISMISSED_REASON_USER_CANCEL) {
-                mCurrentAuthSession.mClientReceiver.onError(
-                        BiometricConstants.BIOMETRIC_ERROR_USER_CANCELED,
-                        getContext().getString(
-                                com.android.internal.R.string.biometric_error_user_canceled));
-            } else if (reason == BiometricPrompt.DISMISSED_REASON_POSITIVE) {
-                // Have the service send the token to KeyStore, and send onAuthenticated
-                // to the application
-                KeyStore.getInstance().addAuthToken(mCurrentAuthSession.mTokenEscrow);
-                mCurrentAuthSession.mClientReceiver.onAuthenticationSucceeded();
+            switch (reason) {
+                case BiometricPrompt.DISMISSED_REASON_CONFIRMED:
+                case BiometricPrompt.DISMISSED_REASON_CONFIRM_NOT_REQUIRED:
+                    mKeyStore.addAuthToken(mCurrentAuthSession.mTokenEscrow);
+                    mCurrentAuthSession.mClientReceiver.onAuthenticationSucceeded();
+                    break;
+
+                case BiometricPrompt.DISMISSED_REASON_NEGATIVE:
+                    mCurrentAuthSession.mClientReceiver.onDialogDismissed(reason);
+                    // Cancel authentication. Skip the token/package check since we are cancelling
+                    // from system server. The interface is permission protected so this is fine.
+                    cancelInternal(null /* token */, null /* package */, false /* fromClient */);
+                    break;
+
+                case BiometricPrompt.DISMISSED_REASON_USER_CANCEL:
+                    mCurrentAuthSession.mClientReceiver.onError(
+                            BiometricConstants.BIOMETRIC_ERROR_USER_CANCELED,
+                            getContext().getString(R.string.biometric_error_user_canceled));
+                    // Cancel authentication. Skip the token/package check since we are cancelling
+                    // from system server. The interface is permission protected so this is fine.
+                    cancelInternal(null /* token */, null /* package */, false /* fromClient */);
+                    break;
+
+                case BiometricPrompt.DISMISSED_REASON_SERVER_REQUESTED:
+                case BiometricPrompt.DISMISSED_REASON_ERROR:
+                    mCurrentAuthSession.mClientReceiver.onError(mCurrentAuthSession.mErrorEscrow,
+                            mCurrentAuthSession.mErrorStringEscrow);
+                    break;
+
+                default:
+                    Slog.w(TAG, "Unhandled reason: " + reason);
+                    break;
             }
 
-            // Do not clean up yet if we are from ConfirmDeviceCredential. We should be in the
-            // STATE_BIOMETRIC_AUTH_CANCELED_SHOWING_CDC. The session should only be removed when
-            // ConfirmDeviceCredential is confirmed or canceled.
-            // TODO(b/123378871): Remove when moved
-            if (!mCurrentAuthSession.isFromConfirmDeviceCredential()) {
-                mActivityTaskManager.unregisterTaskStackListener(mTaskStackListener);
-                mCurrentAuthSession.mState = STATE_AUTH_IDLE;
-                mCurrentAuthSession = null;
-            }
+            // Dialog is gone, auth session is done.
+            mCurrentAuthSession = null;
+
         } catch (RemoteException e) {
             Slog.e(TAG, "Remote exception", e);
         }
@@ -1472,8 +1503,8 @@
 
                 if (!continuing) {
                     mStatusBarService.showBiometricDialog(mCurrentAuthSession.mBundle,
-                            mInternalReceiver, modality, requireConfirmation, userId);
-                    mActivityTaskManager.registerTaskStackListener(mTaskStackListener);
+                            mInternalReceiver, modality, requireConfirmation, userId,
+                            mCurrentAuthSession.mOpPackageName);
                 }
             } catch (RemoteException e) {
                 Slog.e(TAG, "Remote exception", e);
@@ -1517,8 +1548,6 @@
                 return;
             }
 
-            mCurrentModality = modality;
-
             // Start preparing for authentication. Authentication starts when
             // all modalities requested have invoked onReadyForAuthentication.
             authenticateInternal(token, sessionId, userId, receiver, opPackageName, bundle,
@@ -1610,7 +1639,6 @@
                                 com.android.internal.R.string.biometric_error_user_canceled)
                 );
 
-                mCurrentAuthSession.mState = STATE_AUTH_IDLE;
                 mCurrentAuthSession = null;
                 mStatusBarService.hideBiometricDialog();
             } catch (RemoteException e) {
@@ -1637,25 +1665,31 @@
         final int callingUid = Binder.getCallingUid();
         final int callingPid = Binder.getCallingPid();
         final int callingUserId = UserHandle.getCallingUserId();
-        mHandler.post(() -> {
-            try {
-                // TODO: For multiple modalities, send a single ERROR_CANCELED only when all
-                // drivers have canceled authentication.
-                if ((mCurrentModality & TYPE_FINGERPRINT) != 0) {
-                    mFingerprintService.cancelAuthenticationFromService(token, opPackageName,
-                            callingUid, callingPid, callingUserId, fromClient);
-                }
-                if ((mCurrentModality & TYPE_IRIS) != 0) {
-                    Slog.w(TAG, "Iris unsupported");
-                }
-                if ((mCurrentModality & TYPE_FACE) != 0) {
-                    mFaceService.cancelAuthenticationFromService(token, opPackageName,
-                            callingUid, callingPid, callingUserId, fromClient);
-                }
-            } catch (RemoteException e) {
-                Slog.e(TAG, "Unable to cancel authentication");
-            }
-        });
-    }
 
+        try {
+            if (mCurrentAuthSession == null) {
+                Slog.w(TAG, "Skipping cancelInternal");
+                return;
+            } else if (mCurrentAuthSession.mState != STATE_AUTH_STARTED) {
+                Slog.w(TAG, "Skipping cancelInternal, state: " + mCurrentAuthSession.mState);
+                return;
+            }
+
+            // TODO: For multiple modalities, send a single ERROR_CANCELED only when all
+            // drivers have canceled authentication.
+            if ((mCurrentAuthSession.mModality & TYPE_FINGERPRINT) != 0) {
+                mFingerprintService.cancelAuthenticationFromService(token, opPackageName,
+                        callingUid, callingPid, callingUserId, fromClient);
+            }
+            if ((mCurrentAuthSession.mModality & TYPE_IRIS) != 0) {
+                Slog.w(TAG, "Iris unsupported");
+            }
+            if ((mCurrentAuthSession.mModality & TYPE_FACE) != 0) {
+                mFaceService.cancelAuthenticationFromService(token, opPackageName,
+                        callingUid, callingPid, callingUserId, fromClient);
+            }
+        } catch (RemoteException e) {
+            Slog.e(TAG, "Unable to cancel authentication");
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/biometrics/BiometricServiceBase.java b/services/core/java/com/android/server/biometrics/BiometricServiceBase.java
index f3f9754..2de18c3 100644
--- a/services/core/java/com/android/server/biometrics/BiometricServiceBase.java
+++ b/services/core/java/com/android/server/biometrics/BiometricServiceBase.java
@@ -420,7 +420,7 @@
             throw new UnsupportedOperationException("Stub!");
         }
 
-        default void onAuthenticationFailedInternal(int cookie, boolean requireConfirmation)
+        default void onAuthenticationFailedInternal()
                 throws RemoteException {
             throw new UnsupportedOperationException("Stub!");
         }
@@ -457,10 +457,10 @@
         }
 
         @Override
-        public void onAuthenticationFailedInternal(int cookie, boolean requireConfirmation)
+        public void onAuthenticationFailedInternal()
                 throws RemoteException {
             if (getWrapperReceiver() != null) {
-                getWrapperReceiver().onAuthenticationFailed(cookie, requireConfirmation);
+                getWrapperReceiver().onAuthenticationFailed();
             }
         }
     }
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 a706521..ee49f58 100644
--- a/services/core/java/com/android/server/biometrics/face/FaceService.java
+++ b/services/core/java/com/android/server/biometrics/face/FaceService.java
@@ -223,7 +223,8 @@
 
         @Override
         public boolean wasUserDetected() {
-            return mLastAcquire != FaceManager.FACE_ACQUIRED_NOT_DETECTED;
+            return mLastAcquire != FaceManager.FACE_ACQUIRED_NOT_DETECTED
+                    && mLastAcquire != FaceManager.FACE_ACQUIRED_SENSOR_DIRTY;
         }
 
         @Override
diff --git a/services/core/java/com/android/server/broadcastradio/hal2/RadioModule.java b/services/core/java/com/android/server/broadcastradio/hal2/RadioModule.java
index 53890a4..a0eafb4 100644
--- a/services/core/java/com/android/server/broadcastradio/hal2/RadioModule.java
+++ b/services/core/java/com/android/server/broadcastradio/hal2/RadioModule.java
@@ -35,6 +35,8 @@
 import android.hardware.broadcastradio.V2_0.VendorKeyValue;
 import android.hardware.radio.RadioManager;
 import android.os.DeadObjectException;
+import android.os.Handler;
+import android.os.Looper;
 import android.os.RemoteException;
 import android.util.MutableInt;
 import android.util.Slog;
@@ -45,6 +47,7 @@
 import java.util.ArrayList;
 import java.util.HashSet;
 import java.util.List;
+import java.util.Map;
 import java.util.Objects;
 import java.util.Set;
 import java.util.stream.Collectors;
@@ -56,6 +59,7 @@
     @NonNull public final RadioManager.ModuleProperties mProperties;
 
     private final Object mLock = new Object();
+    @NonNull private final Handler mHandler;
 
     @GuardedBy("mLock")
     private ITunerSession mHalTunerSession;
@@ -77,22 +81,24 @@
     private final ITunerCallback mHalTunerCallback = new ITunerCallback.Stub() {
         @Override
         public void onTuneFailed(int result, ProgramSelector programSelector) {
-            fanoutAidlCallback(cb -> cb.onTuneFailed(result, Convert.programSelectorFromHal(
-                    programSelector)));
+            lockAndFireLater(() -> {
+                android.hardware.radio.ProgramSelector csel =
+                        Convert.programSelectorFromHal(programSelector);
+                fanoutAidlCallbackLocked(cb -> cb.onTuneFailed(result, csel));
+            });
         }
 
         @Override
         public void onCurrentProgramInfoChanged(ProgramInfo halProgramInfo) {
-            RadioManager.ProgramInfo programInfo = Convert.programInfoFromHal(halProgramInfo);
-            synchronized (mLock) {
-                mCurrentProgramInfo = programInfo;
-                fanoutAidlCallbackLocked(cb -> cb.onCurrentProgramInfoChanged(programInfo));
-            }
+            lockAndFireLater(() -> {
+                mCurrentProgramInfo = Convert.programInfoFromHal(halProgramInfo);
+                fanoutAidlCallbackLocked(cb -> cb.onCurrentProgramInfoChanged(mCurrentProgramInfo));
+            });
         }
 
         @Override
         public void onProgramListUpdated(ProgramListChunk programListChunk) {
-            synchronized (mLock) {
+            lockAndFireLater(() -> {
                 android.hardware.radio.ProgramList.Chunk chunk =
                         Convert.programListChunkFromHal(programListChunk);
                 mProgramInfoCache.filterAndApplyChunk(chunk);
@@ -100,20 +106,23 @@
                 for (TunerSession tunerSession : mAidlTunerSessions) {
                     tunerSession.onMergedProgramListUpdateFromHal(chunk);
                 }
-            }
+            });
         }
 
         @Override
         public void onAntennaStateChange(boolean connected) {
-            synchronized (mLock) {
+            lockAndFireLater(() -> {
                 mAntennaConnected = connected;
                 fanoutAidlCallbackLocked(cb -> cb.onAntennaState(connected));
-            }
+            });
         }
 
         @Override
         public void onParametersUpdated(ArrayList<VendorKeyValue> parameters) {
-            fanoutAidlCallback(cb -> cb.onParametersUpdated(Convert.vendorInfoFromHal(parameters)));
+            lockAndFireLater(() -> {
+                Map<String, String> cparam = Convert.vendorInfoFromHal(parameters);
+                fanoutAidlCallbackLocked(cb -> cb.onParametersUpdated(cparam));
+            });
         }
     };
 
@@ -126,6 +135,7 @@
             @NonNull RadioManager.ModuleProperties properties) {
         mProperties = Objects.requireNonNull(properties);
         mService = Objects.requireNonNull(service);
+        mHandler = new Handler(Looper.getMainLooper());
     }
 
     public static @Nullable RadioModule tryLoadingModule(int idx, @NonNull String fqName) {
@@ -310,15 +320,22 @@
         }
     }
 
+    // add to mHandler queue, but ensure the runnable holds mLock when it gets executed
+    private void lockAndFireLater(Runnable r) {
+        mHandler.post(() -> {
+            synchronized (mLock) {
+                r.run();
+            }
+        });
+    }
+
     interface AidlCallbackRunnable {
         void run(android.hardware.radio.ITunerCallback callback) throws RemoteException;
     }
 
     // Invokes runnable with each TunerSession currently open.
     void fanoutAidlCallback(AidlCallbackRunnable runnable) {
-        synchronized (mLock) {
-            fanoutAidlCallbackLocked(runnable);
-        }
+        lockAndFireLater(() -> fanoutAidlCallbackLocked(runnable));
     }
 
     private void fanoutAidlCallbackLocked(AidlCallbackRunnable runnable) {
diff --git a/services/core/java/com/android/server/content/ContentService.java b/services/core/java/com/android/server/content/ContentService.java
index 3e1817b..36e872a 100644
--- a/services/core/java/com/android/server/content/ContentService.java
+++ b/services/core/java/com/android/server/content/ContentService.java
@@ -461,7 +461,7 @@
             }
 
             synchronized (mCache) {
-                final String providerPackageName = getProviderPackageName(uri);
+                final String providerPackageName = getProviderPackageName(uri, userHandle);
                 invalidateCacheLocked(userHandle, providerPackageName, uri);
             }
         } finally {
@@ -1145,9 +1145,9 @@
         }
     }
 
-    private @Nullable String getProviderPackageName(Uri uri) {
-        final ProviderInfo pi = mContext.getPackageManager()
-                .resolveContentProvider(uri.getAuthority(), 0);
+    private @Nullable String getProviderPackageName(Uri uri, int userId) {
+        final ProviderInfo pi = mContext.getPackageManager().resolveContentProviderAsUser(
+                uri.getAuthority(), 0, userId);
         return (pi != null) ? pi.packageName : null;
     }
 
@@ -1200,7 +1200,7 @@
         mContext.getSystemService(AppOpsManager.class).checkPackage(Binder.getCallingUid(),
                 packageName);
 
-        final String providerPackageName = getProviderPackageName(key);
+        final String providerPackageName = getProviderPackageName(key, userId);
         final Pair<String, Uri> fullKey = Pair.create(packageName, key);
 
         synchronized (mCache) {
@@ -1222,7 +1222,7 @@
         mContext.getSystemService(AppOpsManager.class).checkPackage(Binder.getCallingUid(),
                 packageName);
 
-        final String providerPackageName = getProviderPackageName(key);
+        final String providerPackageName = getProviderPackageName(key, userId);
         final Pair<String, Uri> fullKey = Pair.create(packageName, key);
 
         synchronized (mCache) {
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index aa548f2..30b2245 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -315,7 +315,7 @@
 
     static final int VIBRATE_PATTERN_MAXLEN = 8 * 2 + 1; // up to eight bumps
 
-    static final int DEFAULT_STREAM_TYPE = AudioManager.STREAM_NOTIFICATION;
+    static final int INVALID_UID = -1;
 
     static final boolean ENABLE_BLOCKED_TOASTS = true;
 
@@ -621,6 +621,8 @@
                 mConditionProviders.readXml(
                         parser, mAllowedManagedServicePackages, forRestore, userId);
                 migratedManagedServices = true;
+            } else if (mSnoozeHelper.XML_TAG_NAME.equals(parser.getName())) {
+                mSnoozeHelper.readXml(parser);
             }
             if (LOCKSCREEN_ALLOW_SECURE_NOTIFICATIONS_TAG.equals(parser.getName())) {
                 if (forRestore && userId != UserHandle.USER_SYSTEM) {
@@ -709,6 +711,7 @@
         mPreferencesHelper.writeXml(out, forBackup, userId);
         mListeners.writeXml(out, forBackup, userId);
         mAssistants.writeXml(out, forBackup, userId);
+        mSnoozeHelper.writeXml(out);
         mConditionProviders.writeXml(out, forBackup, userId);
         if (!forBackup || userId == UserHandle.USER_SYSTEM) {
             writeSecureNotificationsPolicy(out);
@@ -908,8 +911,22 @@
         @Override
         public void onNotificationError(int callingUid, int callingPid, String pkg, String tag,
                 int id, int uid, int initialPid, String message, int userId) {
+            final boolean fgService;
+            synchronized (mNotificationLock) {
+                NotificationRecord r = findNotificationLocked(pkg, tag, id, userId);
+                fgService = r != null && (r.getNotification().flags & FLAG_FOREGROUND_SERVICE) != 0;
+            }
             cancelNotification(callingUid, callingPid, pkg, tag, id, 0, 0, false, userId,
                     REASON_ERROR, null);
+            if (fgService) {
+                // Still crash for foreground services, preventing the not-crash behaviour abused
+                // by apps to give us a garbage notification and silently start a fg service.
+                Binder.withCleanCallingIdentity(
+                        () -> mAm.crashApplication(uid, initialPid, pkg, -1,
+                            "Bad notification(tag=" + tag + ", id=" + id + ") posted from package "
+                                + pkg + ", crashing app(uid=" + uid + ", pid=" + initialPid + "): "
+                                + message));
+            }
         }
 
         @Override
@@ -1753,6 +1770,7 @@
                 com.android.internal.R.integer.config_notificationWarnRemoteViewSizeBytes);
         mStripRemoteViewsSizeBytes = getContext().getResources().getInteger(
                 com.android.internal.R.integer.config_notificationStripRemoteViewSizeBytes);
+
     }
 
     @Override
@@ -4695,6 +4713,12 @@
         // ensure opPkg is delegate if does not match pkg
         int uid = resolveNotificationUid(opPkg, pkg, callingUid, userId);
 
+        if (uid == INVALID_UID) {
+            Slog.w(TAG, opPkg + ":" + callingUid + " trying to cancel notification "
+                    + "for nonexistent pkg " + pkg + " in user " + userId);
+            return;
+        }
+
         // if opPkg is not the same as pkg, make sure the notification given was posted
         // by opPkg
         if (!Objects.equals(pkg, opPkg)) {
@@ -4739,6 +4763,11 @@
         // as "pkg"
         final int notificationUid = resolveNotificationUid(opPkg, pkg, callingUid, userId);
 
+        if (notificationUid == INVALID_UID) {
+            throw new SecurityException("Caller " + opPkg + ":" + callingUid
+                    + " trying to post for invalid pkg " + pkg + " in user " + incomingUserId);
+        }
+
         checkRestrictedCategories(notification);
 
         // Fix the notification as best we can.
@@ -5059,8 +5088,7 @@
     }
 
     @VisibleForTesting
-    int resolveNotificationUid(String callingPkg, String targetPkg,
-            int callingUid, int userId) {
+    int resolveNotificationUid(String callingPkg, String targetPkg, int callingUid, int userId) {
         if (userId == UserHandle.USER_ALL) {
             userId = USER_SYSTEM;
         }
@@ -5071,16 +5099,16 @@
             return callingUid;
         }
 
-        int targetUid = -1;
+        int targetUid = INVALID_UID;
         try {
             targetUid = mPackageManagerClient.getPackageUidAsUser(targetPkg, userId);
         } catch (NameNotFoundException e) {
-            /* ignore */
+            /* ignore, handled by caller */
         }
         // posted from app A on behalf of app B
-        if (targetUid != -1 && (isCallerAndroid(callingPkg, callingUid)
+        if (isCallerAndroid(callingPkg, callingUid)
                 || mPreferencesHelper.isDelegateAllowed(
-                        targetPkg, targetUid, callingPkg, callingUid))) {
+                        targetPkg, targetUid, callingPkg, callingUid)) {
             return targetUid;
         }
 
@@ -5284,7 +5312,7 @@
             updateLightsLocked();
             if (mSnoozeCriterionId != null) {
                 mAssistants.notifyAssistantSnoozedLocked(r.sbn, mSnoozeCriterionId);
-                mSnoozeHelper.snooze(r);
+                mSnoozeHelper.snooze(r, mSnoozeCriterionId);
             } else {
                 mSnoozeHelper.snooze(r, mDuration);
             }
@@ -5387,6 +5415,27 @@
         @Override
         public void run() {
             synchronized (mNotificationLock) {
+                final Long snoozeAt =
+                        mSnoozeHelper.getSnoozeTimeForUnpostedNotification(
+                                r.getUser().getIdentifier(),
+                                r.sbn.getPackageName(), r.sbn.getKey());
+                final long currentTime = System.currentTimeMillis();
+                if (snoozeAt.longValue() > currentTime) {
+                    (new SnoozeNotificationRunnable(r.sbn.getKey(),
+                            snoozeAt.longValue() - currentTime, null)).snoozeLocked(r);
+                    return;
+                }
+
+                final String contextId =
+                        mSnoozeHelper.getSnoozeContextForUnpostedNotification(
+                                r.getUser().getIdentifier(),
+                                r.sbn.getPackageName(), r.sbn.getKey());
+                if (contextId != null) {
+                    (new SnoozeNotificationRunnable(r.sbn.getKey(),
+                            0, contextId)).snoozeLocked(r);
+                    return;
+                }
+
                 mEnqueuedNotifications.add(r);
                 scheduleTimeoutLocked(r);
 
@@ -6937,6 +6986,7 @@
         if (DBG) {
             Slog.d(TAG, String.format("unsnooze event(%s, %s)", key, listenerName));
         }
+        mSnoozeHelper.cleanupPersistedContext(key);
         mSnoozeHelper.repost(key);
         handleSavePolicyFile();
     }
diff --git a/services/core/java/com/android/server/notification/SnoozeHelper.java b/services/core/java/com/android/server/notification/SnoozeHelper.java
index 91f497c..8125d0d 100644
--- a/services/core/java/com/android/server/notification/SnoozeHelper.java
+++ b/services/core/java/com/android/server/notification/SnoozeHelper.java
@@ -55,6 +55,21 @@
  * NotificationManagerService helper for handling snoozed notifications.
  */
 public class SnoozeHelper {
+    public static final String XML_SNOOZED_NOTIFICATION_VERSION = "1";
+
+    protected static final String XML_TAG_NAME = "snoozed-notifications";
+
+    private static final String XML_SNOOZED_NOTIFICATION = "notification";
+    private static final String XML_SNOOZED_NOTIFICATION_CONTEXT = "context";
+    private static final String XML_SNOOZED_NOTIFICATION_PKG = "pkg";
+    private static final String XML_SNOOZED_NOTIFICATION_USER_ID = "user-id";
+    private static final String XML_SNOOZED_NOTIFICATION_KEY = "key";
+    //the time the snoozed notification should be reposted
+    private static final String XML_SNOOZED_NOTIFICATION_TIME = "time";
+    private static final String XML_SNOOZED_NOTIFICATION_CONTEXT_ID = "id";
+    private static final String XML_SNOOZED_NOTIFICATION_VERSION_LABEL = "version";
+
+
     private static final String TAG = "SnoozeHelper";
     private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
     private static final String INDENT = "    ";
@@ -72,6 +87,17 @@
     // User id : package name : notification key : record.
     private ArrayMap<Integer, ArrayMap<String, ArrayMap<String, NotificationRecord>>>
             mSnoozedNotifications = new ArrayMap<>();
+    // User id : package name : notification key : time-milliseconds .
+    // This member stores persisted snoozed notification trigger times. it persists through reboots
+    // It should have the notifications that haven't expired or re-posted yet
+    private ArrayMap<Integer, ArrayMap<String, ArrayMap<String, Long>>>
+            mPersistedSnoozedNotifications = new ArrayMap<>();
+    // User id : package name : notification key : creation ID .
+    // This member stores persisted snoozed notification trigger context for the assistant
+    // it persists through reboots.
+    // It should have the notifications that haven't expired or re-posted yet
+    private ArrayMap<Integer, ArrayMap<String, ArrayMap<String, String>>>
+            mPersistedSnoozedNotificationsWithContext = new ArrayMap<>();
     // notification key : package.
     private ArrayMap<String, String> mPackages = new ArrayMap<>();
     // key : userId
@@ -89,6 +115,34 @@
         mUserProfiles = userProfiles;
     }
 
+    void cleanupPersistedContext(String key){
+        int userId = mUsers.get(key);
+        String pkg = mPackages.get(key);
+        synchronized (mPersistedSnoozedNotificationsWithContext) {
+            removeRecord(pkg, key, userId, mPersistedSnoozedNotificationsWithContext);
+        }
+    }
+
+    //This function has a side effect of removing the time from the list of persisted notifications.
+    //IT IS NOT IDEMPOTENT!
+    @NonNull
+    protected Long getSnoozeTimeForUnpostedNotification(int userId, String pkg, String key) {
+        Long time;
+        synchronized (mPersistedSnoozedNotifications) {
+            time = removeRecord(pkg, key, userId, mPersistedSnoozedNotifications);
+        }
+        if (time == null) {
+            return 0L;
+        }
+        return time;
+    }
+
+    protected String getSnoozeContextForUnpostedNotification(int userId, String pkg, String key) {
+        synchronized (mPersistedSnoozedNotificationsWithContext) {
+            return removeRecord(pkg, key, userId, mPersistedSnoozedNotificationsWithContext);
+        }
+    }
+
     protected boolean isSnoozed(int userId, String pkg, String key) {
         return mSnoozedNotifications.containsKey(userId)
                 && mSnoozedNotifications.get(userId).containsKey(pkg)
@@ -169,32 +223,82 @@
      * Snoozes a notification and schedules an alarm to repost at that time.
      */
     protected void snooze(NotificationRecord record, long duration) {
+        String pkg = record.sbn.getPackageName();
+        String key = record.getKey();
+        int userId = record.getUser().getIdentifier();
+
         snooze(record);
-        scheduleRepost(record.sbn.getPackageName(), record.getKey(), record.getUserId(), duration);
+        scheduleRepost(pkg, key, userId, duration);
+        Long activateAt = System.currentTimeMillis() + duration;
+        synchronized (mPersistedSnoozedNotifications) {
+            storeRecord(pkg, key, userId, mPersistedSnoozedNotifications, activateAt);
+        }
     }
 
     /**
      * Records a snoozed notification.
      */
-    protected void snooze(NotificationRecord record) {
+    protected void snooze(NotificationRecord record, String contextId) {
+        int userId = record.getUser().getIdentifier();
+        if (contextId != null) {
+            synchronized (mPersistedSnoozedNotificationsWithContext) {
+                storeRecord(record.sbn.getPackageName(), record.getKey(),
+                        userId, mPersistedSnoozedNotificationsWithContext, contextId);
+            }
+        }
+        snooze(record);
+    }
+
+    private void snooze(NotificationRecord record) {
         int userId = record.getUser().getIdentifier();
         if (DEBUG) {
             Slog.d(TAG, "Snoozing " + record.getKey());
         }
-        ArrayMap<String, ArrayMap<String, NotificationRecord>> records =
-                mSnoozedNotifications.get(userId);
+        storeRecord(record.sbn.getPackageName(), record.getKey(),
+                userId, mSnoozedNotifications, record);
+        mPackages.put(record.getKey(), record.sbn.getPackageName());
+        mUsers.put(record.getKey(), userId);
+    }
+
+    private <T> void storeRecord(String pkg, String key, Integer userId,
+            ArrayMap<Integer, ArrayMap<String, ArrayMap<String, T>>> targets, T object) {
+
+        ArrayMap<String, ArrayMap<String, T>> records =
+                targets.get(userId);
         if (records == null) {
             records = new ArrayMap<>();
         }
-        ArrayMap<String, NotificationRecord> pkgRecords = records.get(record.sbn.getPackageName());
+        ArrayMap<String, T> pkgRecords = records.get(pkg);
         if (pkgRecords == null) {
             pkgRecords = new ArrayMap<>();
         }
-        pkgRecords.put(record.getKey(), record);
-        records.put(record.sbn.getPackageName(), pkgRecords);
-        mSnoozedNotifications.put(userId, records);
-        mPackages.put(record.getKey(), record.sbn.getPackageName());
-        mUsers.put(record.getKey(), userId);
+        pkgRecords.put(key, object);
+        records.put(pkg, pkgRecords);
+        targets.put(userId, records);
+
+    }
+
+    private <T> T removeRecord(String pkg, String key, Integer userId,
+            ArrayMap<Integer, ArrayMap<String, ArrayMap<String, T>>> targets) {
+        T object = null;
+
+        ArrayMap<String, ArrayMap<String, T>> records =
+                targets.get(userId);
+        if (records == null) {
+            return null;
+        }
+        ArrayMap<String, T> pkgRecords = records.get(pkg);
+        if (pkgRecords == null) {
+            return null;
+        }
+        object = pkgRecords.remove(key);
+        if (pkgRecords.size() == 0) {
+            records.remove(pkg);
+        }
+        if (records.size() == 0) {
+            targets.remove(userId);
+        }
+        return object;
     }
 
     protected boolean cancel(int userId, String pkg, String tag, int id) {
@@ -414,13 +518,121 @@
         }
     }
 
-    protected void writeXml(XmlSerializer out, boolean forBackup) throws IOException {
-
+    protected void writeXml(XmlSerializer out) throws IOException {
+        final long currentTime = System.currentTimeMillis();
+        out.startTag(null, XML_TAG_NAME);
+        writeXml(out, mPersistedSnoozedNotifications, XML_SNOOZED_NOTIFICATION,
+                value -> {
+                    if (value < currentTime) {
+                        return;
+                    }
+                    out.attribute(null, XML_SNOOZED_NOTIFICATION_TIME,
+                            value.toString());
+                });
+        writeXml(out, mPersistedSnoozedNotificationsWithContext, XML_SNOOZED_NOTIFICATION_CONTEXT,
+                value -> {
+                    out.attribute(null, XML_SNOOZED_NOTIFICATION_CONTEXT_ID,
+                            value);
+                });
+        out.endTag(null, XML_TAG_NAME);
     }
 
-    public void readXml(XmlPullParser parser, boolean forRestore)
-            throws XmlPullParserException, IOException {
+    private interface Inserter<T> {
+        void insert(T t) throws IOException;
+    }
+    private <T> void writeXml(XmlSerializer out,
+            ArrayMap<Integer, ArrayMap<String, ArrayMap<String, T>>> targets, String tag,
+            Inserter<T> attributeInserter)
+            throws IOException {
+        synchronized (targets) {
+            final int M = targets.size();
+            for (int i = 0; i < M; i++) {
+                final ArrayMap<String, ArrayMap<String, T>> packages =
+                        targets.valueAt(i);
+                if (packages == null) {
+                    continue;
+                }
+                final int N = packages.size();
+                for (int j = 0; j < N; j++) {
+                    final ArrayMap<String, T> keyToValue = packages.valueAt(j);
+                    if (keyToValue == null) {
+                        continue;
+                    }
+                    final int O = keyToValue.size();
+                    for (int k = 0; k < O; k++) {
+                        T value = keyToValue.valueAt(k);
 
+                        out.startTag(null, tag);
+
+                        attributeInserter.insert(value);
+
+                        out.attribute(null, XML_SNOOZED_NOTIFICATION_VERSION_LABEL,
+                                XML_SNOOZED_NOTIFICATION_VERSION);
+                        out.attribute(null, XML_SNOOZED_NOTIFICATION_KEY, keyToValue.keyAt(k));
+                        out.attribute(null, XML_SNOOZED_NOTIFICATION_PKG, packages.keyAt(j));
+                        out.attribute(null, XML_SNOOZED_NOTIFICATION_USER_ID,
+                                targets.keyAt(i).toString());
+
+                        out.endTag(null, tag);
+
+                    }
+                }
+            }
+        }
+    }
+
+    protected void readXml(XmlPullParser parser)
+            throws XmlPullParserException, IOException {
+        int type;
+        while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) {
+            String tag = parser.getName();
+            if (type == XmlPullParser.END_TAG
+                    && XML_TAG_NAME.equals(tag)) {
+                break;
+            }
+            if (type == XmlPullParser.START_TAG
+                    && (XML_SNOOZED_NOTIFICATION.equals(tag)
+                        || tag.equals(XML_SNOOZED_NOTIFICATION_CONTEXT))
+                    && parser.getAttributeValue(null, XML_SNOOZED_NOTIFICATION_VERSION_LABEL)
+                        .equals(XML_SNOOZED_NOTIFICATION_VERSION)) {
+                try {
+                    final String key = parser.getAttributeValue(null, XML_SNOOZED_NOTIFICATION_KEY);
+                    final String pkg = parser.getAttributeValue(null, XML_SNOOZED_NOTIFICATION_PKG);
+                    final int userId = Integer.parseInt(
+                            parser.getAttributeValue(null, XML_SNOOZED_NOTIFICATION_USER_ID));
+                    if (tag.equals(XML_SNOOZED_NOTIFICATION)) {
+                        final Long time = Long.parseLong(
+                                parser.getAttributeValue(null, XML_SNOOZED_NOTIFICATION_TIME));
+                        if (time > System.currentTimeMillis()) { //only read new stuff
+                            synchronized (mPersistedSnoozedNotifications) {
+                                storeRecord(pkg, key, userId, mPersistedSnoozedNotifications, time);
+                            }
+                            scheduleRepost(pkg, key, userId, time - System.currentTimeMillis());
+                        }
+                        continue;
+                    }
+                    if (tag.equals(XML_SNOOZED_NOTIFICATION_CONTEXT)) {
+                        final String creationId = parser.getAttributeValue(
+                                null, XML_SNOOZED_NOTIFICATION_CONTEXT_ID);
+                        synchronized (mPersistedSnoozedNotificationsWithContext) {
+                            storeRecord(pkg, key, userId, mPersistedSnoozedNotificationsWithContext,
+                                    creationId);
+                        }
+                        continue;
+                    }
+
+
+                } catch (Exception e) {
+                    //we dont cre if it is a number format exception or a null pointer exception.
+                    //we just want to debug it and continue with our lives
+                    if (DEBUG) {
+                        Slog.d(TAG,
+                                "Exception in reading snooze data from policy xml: "
+                                        + e.getMessage());
+                    }
+                }
+            }
+        }
     }
 
     @VisibleForTesting
diff --git a/services/core/java/com/android/server/om/IdmapDaemon.java b/services/core/java/com/android/server/om/IdmapDaemon.java
index 91824c3..20a4c75 100644
--- a/services/core/java/com/android/server/om/IdmapDaemon.java
+++ b/services/core/java/com/android/server/om/IdmapDaemon.java
@@ -129,11 +129,11 @@
         }
     }
 
-    static void startIdmapService() {
+    private static void startIdmapService() {
         SystemProperties.set("ctl.start", IDMAP_DAEMON);
     }
 
-    static void stopIdmapService() {
+    private static void stopIdmapService() {
         SystemProperties.set("ctl.stop", IDMAP_DAEMON);
     }
 
diff --git a/services/core/java/com/android/server/om/OverlayManagerService.java b/services/core/java/com/android/server/om/OverlayManagerService.java
index 8171a8d..965ddc9 100644
--- a/services/core/java/com/android/server/om/OverlayManagerService.java
+++ b/services/core/java/com/android/server/om/OverlayManagerService.java
@@ -262,7 +262,6 @@
 
             initIfNeeded();
             onSwitchUser(UserHandle.USER_SYSTEM);
-            IdmapDaemon.stopIdmapService();
 
             publishBinderService(Context.OVERLAY_SERVICE, mService);
             publishLocalService(OverlayManagerService.class, this);
diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java
index 0032e9a..3aeb2b1 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerService.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerService.java
@@ -1184,9 +1184,26 @@
         synchronized (mSessions) {
             pw.println("Active install sessions:");
             pw.increaseIndent();
+
+            List<PackageInstallerSession> finalizedSessions = new ArrayList<>();
             int N = mSessions.size();
             for (int i = 0; i < N; i++) {
                 final PackageInstallerSession session = mSessions.valueAt(i);
+                if (session.isStagedAndInTerminalState()) {
+                    finalizedSessions.add(session);
+                    continue;
+                }
+                session.dump(pw);
+                pw.println();
+            }
+            pw.println();
+            pw.decreaseIndent();
+
+            pw.println("Finalized install sessions:");
+            pw.increaseIndent();
+            N = finalizedSessions.size();
+            for (int i = 0; i < N; i++) {
+                final PackageInstallerSession session = finalizedSessions.get(i);
                 session.dump(pw);
                 pw.println();
             }
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index 4eddb930..b720290 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -2337,6 +2337,7 @@
         pw.printPair("mInstallerPackageName", mInstallerPackageName);
         pw.printPair("mInstallerUid", mInstallerUid);
         pw.printPair("createdMillis", createdMillis);
+        pw.printPair("updatedMillis", updatedMillis);
         pw.printPair("stageDir", stageDir);
         pw.printPair("stageCid", stageCid);
         pw.println();
@@ -2356,6 +2357,13 @@
         pw.printPair("mFinalMessage", mFinalMessage);
         pw.printPair("params.isMultiPackage", params.isMultiPackage);
         pw.printPair("params.isStaged", params.isStaged);
+        pw.printPair("mParentSessionId", mParentSessionId);
+        pw.printPair("mChildSessionIds", mChildSessionIds);
+        pw.printPair("mStagedSessionApplied", mStagedSessionApplied);
+        pw.printPair("mStagedSessionFailed", mStagedSessionFailed);
+        pw.printPair("mStagedSessionReady", mStagedSessionReady);
+        pw.printPair("mStagedSessionErrorCode", mStagedSessionErrorCode);
+        pw.printPair("mStagedSessionErrorMessage", mStagedSessionErrorMessage);
         pw.println();
 
         pw.decreaseIndent();
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
index e2644ff..fda798c 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
@@ -43,7 +43,6 @@
 import static com.android.server.pm.PackageManagerService.DEBUG_PERMISSIONS;
 import static com.android.server.pm.PackageManagerService.DEBUG_REMOVE;
 import static com.android.server.pm.PackageManagerService.PLATFORM_PACKAGE_NAME;
-import static com.android.server.pm.permission.PermissionManagerService.killUid;
 import static com.android.server.pm.permission.PermissionsState.PERMISSION_OPERATION_FAILURE;
 
 import static java.util.concurrent.TimeUnit.SECONDS;
@@ -782,6 +781,14 @@
 
     @Override
     public int checkPermission(String permName, String pkgName, int userId) {
+        // Not using Preconditions.checkNotNull() here for compatibility reasons.
+        if (permName == null || pkgName == null) {
+            return PackageManager.PERMISSION_DENIED;
+        }
+        if (!mUserManagerInt.exists(userId)) {
+            return PackageManager.PERMISSION_DENIED;
+        }
+
         final CheckPermissionDelegate checkPermissionDelegate;
         synchronized (mLock) {
             if (mCheckPermissionDelegate == null)  {
@@ -794,43 +801,70 @@
     }
 
     private int checkPermissionImpl(String permName, String pkgName, int userId) {
-        return checkPermissionInternal(permName, pkgName, getCallingUid(), userId);
-    }
-
-    private int checkPermissionInternal(String permName, String packageName, int callingUid,
-            int userId) {
-        if (!mUserManagerInt.exists(userId)) {
+        final PackageParser.Package pkg = mPackageManagerInt.getPackage(pkgName);
+        if (pkg == null) {
             return PackageManager.PERMISSION_DENIED;
         }
-        final PackageParser.Package pkg = mPackageManagerInt.getPackage(packageName);
-        if (pkg != null && pkg.mExtras != null) {
+        return checkPermissionInternal(pkg, true, permName, userId);
+    }
+
+    private int checkPermissionInternal(@NonNull Package pkg, boolean isPackageExplicit,
+            @NonNull String permissionName, @UserIdInt int userId) {
+        final int callingUid = getCallingUid();
+        if (isPackageExplicit || pkg.mSharedUserId == null) {
             if (mPackageManagerInt.filterAppAccess(pkg, callingUid, userId)) {
                 return PackageManager.PERMISSION_DENIED;
             }
-            final PackageSetting ps = (PackageSetting) pkg.mExtras;
-            final boolean instantApp = ps.getInstantApp(userId);
-            final PermissionsState permissionsState = ps.getPermissionsState();
-            if (permissionsState.hasPermission(permName, userId)) {
-                if (instantApp) {
-                    synchronized (mLock) {
-                        BasePermission bp = mSettings.getPermissionLocked(permName);
-                        if (bp != null && bp.isInstant()) {
-                            return PackageManager.PERMISSION_GRANTED;
-                        }
-                    }
-                } else {
-                    return PackageManager.PERMISSION_GRANTED;
-                }
-            }
-            if (isImpliedPermissionGranted(permissionsState, permName, userId)) {
-                return PackageManager.PERMISSION_GRANTED;
+        } else {
+            if (mPackageManagerInt.getInstantAppPackageName(callingUid) != null) {
+                return PackageManager.PERMISSION_DENIED;
             }
         }
+
+        final int uid = UserHandle.getUid(userId, pkg.applicationInfo.uid);
+        final PackageSetting ps = (PackageSetting) pkg.mExtras;
+        if (ps == null) {
+            return PackageManager.PERMISSION_DENIED;
+        }
+        final PermissionsState permissionsState = ps.getPermissionsState();
+
+        if (checkSinglePermissionInternal(uid, permissionsState, permissionName)) {
+            return PackageManager.PERMISSION_GRANTED;
+        }
+
+        final String fullerPermissionName = FULLER_PERMISSION_MAP.get(permissionName);
+        if (fullerPermissionName != null
+                && checkSinglePermissionInternal(uid, permissionsState, fullerPermissionName)) {
+            return PackageManager.PERMISSION_GRANTED;
+        }
+
         return PackageManager.PERMISSION_DENIED;
     }
 
+    private boolean checkSinglePermissionInternal(int uid,
+            @NonNull PermissionsState permissionsState, @NonNull String permissionName) {
+        if (!permissionsState.hasPermission(permissionName, UserHandle.getUserId(uid))) {
+            return false;
+        }
+
+        if (mPackageManagerInt.getInstantAppPackageName(uid) != null) {
+            return mSettings.isPermissionInstant(permissionName);
+        }
+
+        return true;
+    }
+
     @Override
     public int checkUidPermission(String permName, int uid) {
+        // Not using Preconditions.checkNotNull() here for compatibility reasons.
+        if (permName == null) {
+            return PackageManager.PERMISSION_DENIED;
+        }
+        final int userId = UserHandle.getUserId(uid);
+        if (!mUserManagerInt.exists(userId)) {
+            return PackageManager.PERMISSION_DENIED;
+        }
+
         final CheckPermissionDelegate checkPermissionDelegate;
         synchronized (mLock) {
             if (mCheckPermissionDelegate == null)  {
@@ -842,11 +876,9 @@
                 PermissionManagerService.this::checkUidPermissionImpl);
     }
 
-    private int checkUidPermissionImpl(@NonNull String permName, int uid) {
+    private int checkUidPermissionImpl(String permName, int uid) {
         final PackageParser.Package pkg = mPackageManagerInt.getPackage(uid);
-        synchronized (mLock) {
-            return checkUidPermissionInternal(permName, pkg, uid, getCallingUid());
-        }
+        return checkUidPermissionInternal(pkg, uid, permName);
     }
 
     /**
@@ -856,55 +888,33 @@
      *
      * @see SystemConfig#getSystemPermissions()
      */
-    private int checkUidPermissionInternal(@NonNull String permName,
-            @Nullable PackageParser.Package pkg, int uid, int callingUid) {
-        final int callingUserId = UserHandle.getUserId(callingUid);
-        final boolean isCallerInstantApp =
-                mPackageManagerInt.getInstantAppPackageName(callingUid) != null;
-        final boolean isUidInstantApp =
-                mPackageManagerInt.getInstantAppPackageName(uid) != null;
-        final int userId = UserHandle.getUserId(uid);
-        if (!mUserManagerInt.exists(userId)) {
-            return PackageManager.PERMISSION_DENIED;
+    private int checkUidPermissionInternal(@Nullable Package pkg, int uid,
+            @NonNull String permissionName) {
+        if (pkg != null) {
+            final int userId = UserHandle.getUserId(uid);
+            return checkPermissionInternal(pkg, false, permissionName, userId);
         }
 
-        if (pkg != null) {
-            if (pkg.mSharedUserId != null) {
-                if (isCallerInstantApp) {
-                    return PackageManager.PERMISSION_DENIED;
-                }
-            } else if (mPackageManagerInt.filterAppAccess(pkg, callingUid, callingUserId)) {
-                return PackageManager.PERMISSION_DENIED;
-            }
-            final PermissionsState permissionsState =
-                    ((PackageSetting) pkg.mExtras).getPermissionsState();
-            if (permissionsState.hasPermission(permName, userId)) {
-                if (isUidInstantApp) {
-                    if (mSettings.isPermissionInstant(permName)) {
-                        return PackageManager.PERMISSION_GRANTED;
-                    }
-                } else {
-                    return PackageManager.PERMISSION_GRANTED;
-                }
-            }
-            if (isImpliedPermissionGranted(permissionsState, permName, userId)) {
-                return PackageManager.PERMISSION_GRANTED;
-            }
-        } else {
-            ArraySet<String> perms = mSystemPermissions.get(uid);
-            if (perms != null) {
-                if (perms.contains(permName)) {
-                    return PackageManager.PERMISSION_GRANTED;
-                }
-                if (FULLER_PERMISSION_MAP.containsKey(permName)
-                        && perms.contains(FULLER_PERMISSION_MAP.get(permName))) {
-                    return PackageManager.PERMISSION_GRANTED;
-                }
-            }
+        if (checkSingleUidPermissionInternal(uid, permissionName)) {
+            return PackageManager.PERMISSION_GRANTED;
         }
+
+        final String fullerPermissionName = FULLER_PERMISSION_MAP.get(permissionName);
+        if (fullerPermissionName != null
+                && checkSingleUidPermissionInternal(uid, fullerPermissionName)) {
+            return PackageManager.PERMISSION_GRANTED;
+        }
+
         return PackageManager.PERMISSION_DENIED;
     }
 
+    private boolean checkSingleUidPermissionInternal(int uid, @NonNull String permissionName) {
+        synchronized (mLock) {
+            ArraySet<String> permissions = mSystemPermissions.get(uid);
+            return permissions != null && permissions.contains(permissionName);
+        }
+    }
+
     @Override
     public void addOnPermissionsChangeListener(IOnPermissionsChangeListener listener) {
         mContext.enforceCallingOrSelfPermission(
@@ -1962,18 +1972,6 @@
         }
     }
 
-    /**
-     * Returns {@code true} if the permission can be implied from another granted permission.
-     * <p>Some permissions, such as ACCESS_FINE_LOCATION, imply other permissions,
-     * such as ACCESS_COURSE_LOCATION. If the caller holds an umbrella permission, give
-     * it access to any implied permissions.
-     */
-    private static boolean isImpliedPermissionGranted(PermissionsState permissionsState,
-            String permName, int userId) {
-        return FULLER_PERMISSION_MAP.containsKey(permName)
-                && permissionsState.hasPermission(FULLER_PERMISSION_MAP.get(permName), userId);
-    }
-
     private int adjustPermissionProtectionFlagsLocked(
             int protectionLevel, String packageName, int uid) {
         // Signature permission flags area always reported
@@ -2062,8 +2060,8 @@
                         final int numPackages = allPackageNames.size();
                         for (int packageNum = 0; packageNum < numPackages; packageNum++) {
                             final String packageName = allPackageNames.get(packageNum);
-                            final int permissionState = checkPermissionInternal(
-                                    permissionName, packageName, UserHandle.USER_SYSTEM, userId);
+                            final int permissionState = checkPermission(permissionName, packageName,
+                                    userId);
                             if (permissionState == PackageManager.PERMISSION_GRANTED) {
                                 EventLog.writeEvent(0x534e4554, "72710897",
                                         newPackage.applicationInfo.uid,
diff --git a/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java b/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
index 02f98b4..bfd280c 100644
--- a/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
+++ b/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
@@ -271,33 +271,9 @@
         }, filter, null, getHandler());
     }
 
-    /**
-     * This method posts a blocking call to the handler thread, so it should not be called from
-     * that same thread.
-     * @throws {@link IllegalStateException} if called from {@link #mHandlerThread}
-     */
     @Override
     public ParceledListSlice getAvailableRollbacks() {
         enforceManageRollbacks("getAvailableRollbacks");
-        if (Thread.currentThread().equals(mHandlerThread)) {
-            Slog.wtf(TAG, "Calling getAvailableRollbacks from mHandlerThread "
-                    + "causes a deadlock");
-            throw new IllegalStateException("Cannot call RollbackManager#getAvailableRollbacks "
-                    + "from the handler thread!");
-        }
-
-        // Wait for the handler thread to get the list of available rollbacks
-        // to get the most up-to-date results. This is intended to reduce test
-        // flakiness when checking available rollbacks immediately after
-        // installing a package with rollback enabled.
-        CountDownLatch latch = new CountDownLatch(1);
-        getHandler().post(() -> latch.countDown());
-        try {
-            latch.await();
-        } catch (InterruptedException ie) {
-            throw new IllegalStateException("RollbackManagerHandlerThread interrupted");
-        }
-
         synchronized (mLock) {
             List<RollbackInfo> rollbacks = new ArrayList<>();
             for (int i = 0; i < mRollbacks.size(); ++i) {
@@ -306,6 +282,15 @@
                     rollbacks.add(rollback.info);
                 }
             }
+
+            // Also return new rollbacks for which the PackageRollbackInfo is complete.
+            for (NewRollback newRollback : mNewRollbacks) {
+                if (newRollback.rollback.info.getPackages().size()
+                        == newRollback.packageSessionIds.length
+                        && !newRollback.isCancelled) {
+                    rollbacks.add(newRollback.rollback.info);
+                }
+            }
             return new ParceledListSlice<>(rollbacks);
         }
     }
@@ -562,6 +547,14 @@
                     }
                 }
             }
+            for (NewRollback newRollback : mNewRollbacks) {
+                for (PackageRollbackInfo info : newRollback.rollback.info.getPackages()) {
+                    if (info.getPackageName().equals(packageName)) {
+                        newRollback.isCancelled = true;
+                        break;
+                    }
+                }
+            }
         }
     }
 
diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
index d67048f..8897eca 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
@@ -610,11 +610,12 @@
 
     @Override
     public void showBiometricDialog(Bundle bundle, IBiometricServiceReceiverInternal receiver,
-            int type, boolean requireConfirmation, int userId) {
+            int type, boolean requireConfirmation, int userId, String opPackageName) {
         enforceBiometricDialog();
         if (mBar != null) {
             try {
-                mBar.showBiometricDialog(bundle, receiver, type, requireConfirmation, userId);
+                mBar.showBiometricDialog(bundle, receiver, type, requireConfirmation, userId,
+                        opPackageName);
             } catch (RemoteException ex) {
             }
         }
diff --git a/services/core/java/com/android/server/textclassifier/TextClassificationManagerService.java b/services/core/java/com/android/server/textclassifier/TextClassificationManagerService.java
index 89a5305..4c93564 100644
--- a/services/core/java/com/android/server/textclassifier/TextClassificationManagerService.java
+++ b/services/core/java/com/android/server/textclassifier/TextClassificationManagerService.java
@@ -142,11 +142,12 @@
         synchronized (mLock) {
             UserState userState = getUserStateLocked(userId);
             if (!userState.bindLocked()) {
+                Slog.d(LOG_TAG, "Unable to bind TextClassifierService at suggestSelection.");
                 callback.onFailure();
             } else if (userState.isBoundLocked()) {
                 userState.mService.onSuggestSelection(sessionId, request, callback);
             } else {
-                userState.mPendingRequests.add(new PendingRequest(
+                userState.mPendingRequests.add(new PendingRequest("suggestSelection",
                         () -> onSuggestSelection(sessionId, request, callback),
                         callback::onFailure, callback.asBinder(), this, userState));
             }
@@ -166,11 +167,12 @@
         synchronized (mLock) {
             UserState userState = getUserStateLocked(userId);
             if (!userState.bindLocked()) {
+                Slog.d(LOG_TAG, "Unable to bind TextClassifierService at classifyText.");
                 callback.onFailure();
             } else if (userState.isBoundLocked()) {
                 userState.mService.onClassifyText(sessionId, request, callback);
             } else {
-                userState.mPendingRequests.add(new PendingRequest(
+                userState.mPendingRequests.add(new PendingRequest("classifyText",
                         () -> onClassifyText(sessionId, request, callback),
                         callback::onFailure, callback.asBinder(), this, userState));
             }
@@ -190,11 +192,12 @@
         synchronized (mLock) {
             UserState userState = getUserStateLocked(userId);
             if (!userState.bindLocked()) {
+                Slog.d(LOG_TAG, "Unable to bind TextClassifierService at generateLinks.");
                 callback.onFailure();
             } else if (userState.isBoundLocked()) {
                 userState.mService.onGenerateLinks(sessionId, request, callback);
             } else {
-                userState.mPendingRequests.add(new PendingRequest(
+                userState.mPendingRequests.add(new PendingRequest("generateLinks",
                         () -> onGenerateLinks(sessionId, request, callback),
                         callback::onFailure, callback.asBinder(), this, userState));
             }
@@ -214,7 +217,7 @@
             if (userState.isBoundLocked()) {
                 userState.mService.onSelectionEvent(sessionId, event);
             } else {
-                userState.mPendingRequests.add(new PendingRequest(
+                userState.mPendingRequests.add(new PendingRequest("selectionEvent",
                         () -> onSelectionEvent(sessionId, event),
                         null /* onServiceFailure */, null /* binder */, this, userState));
             }
@@ -238,7 +241,7 @@
             if (userState.isBoundLocked()) {
                 userState.mService.onTextClassifierEvent(sessionId, event);
             } else {
-                userState.mPendingRequests.add(new PendingRequest(
+                userState.mPendingRequests.add(new PendingRequest("textClassifierEvent",
                         () -> onTextClassifierEvent(sessionId, event),
                         null /* onServiceFailure */, null /* binder */, this, userState));
             }
@@ -258,11 +261,12 @@
         synchronized (mLock) {
             UserState userState = getUserStateLocked(userId);
             if (!userState.bindLocked()) {
+                Slog.d(LOG_TAG, "Unable to bind TextClassifierService at detectLanguage.");
                 callback.onFailure();
             } else if (userState.isBoundLocked()) {
                 userState.mService.onDetectLanguage(sessionId, request, callback);
             } else {
-                userState.mPendingRequests.add(new PendingRequest(
+                userState.mPendingRequests.add(new PendingRequest("detectLanguage",
                         () -> onDetectLanguage(sessionId, request, callback),
                         callback::onFailure, callback.asBinder(), this, userState));
             }
@@ -282,11 +286,13 @@
         synchronized (mLock) {
             UserState userState = getUserStateLocked(userId);
             if (!userState.bindLocked()) {
+                Slog.d(LOG_TAG,
+                        "Unable to bind TextClassifierService at suggestConversationActions.");
                 callback.onFailure();
             } else if (userState.isBoundLocked()) {
                 userState.mService.onSuggestConversationActions(sessionId, request, callback);
             } else {
-                userState.mPendingRequests.add(new PendingRequest(
+                userState.mPendingRequests.add(new PendingRequest("suggestConversationActions",
                         () -> onSuggestConversationActions(sessionId, request, callback),
                         callback::onFailure, callback.asBinder(), this, userState));
             }
@@ -309,7 +315,7 @@
                         classificationContext, sessionId);
                 mSessionUserIds.put(sessionId, userId);
             } else {
-                userState.mPendingRequests.add(new PendingRequest(
+                userState.mPendingRequests.add(new PendingRequest("createTextClassificationSession",
                         () -> onCreateTextClassificationSession(classificationContext, sessionId),
                         null /* onServiceFailure */, null /* binder */, this, userState));
             }
@@ -332,9 +338,10 @@
                 userState.mService.onDestroyTextClassificationSession(sessionId);
                 mSessionUserIds.remove(sessionId);
             } else {
-                userState.mPendingRequests.add(new PendingRequest(
-                        () -> onDestroyTextClassificationSession(sessionId),
-                        null /* onServiceFailure */, null /* binder */, this, userState));
+                userState.mPendingRequests.add(
+                        new PendingRequest("destroyTextClassificationSession",
+                                () -> onDestroyTextClassificationSession(sessionId),
+                                null /* onServiceFailure */, null /* binder */, this, userState));
             }
         }
     }
@@ -379,6 +386,7 @@
 
     private static final class PendingRequest implements IBinder.DeathRecipient {
 
+        @Nullable private final String mName;
         @Nullable private final IBinder mBinder;
         @NonNull private final Runnable mRequest;
         @Nullable private final Runnable mOnServiceFailure;
@@ -394,11 +402,12 @@
          * @param service
          * @param owningUser
          */
-        PendingRequest(
+        PendingRequest(@Nullable String name,
                 @NonNull ThrowingRunnable request, @Nullable ThrowingRunnable onServiceFailure,
                 @Nullable IBinder binder,
                 TextClassificationManagerService service,
                 UserState owningUser) {
+            mName = name;
             mRequest =
                     logOnFailure(Preconditions.checkNotNull(request), "handling pending request");
             mOnServiceFailure =
@@ -499,6 +508,8 @@
                     request.mRequest.run();
                 } else {
                     if (request.mOnServiceFailure != null) {
+                        Slog.d(LOG_TAG, "Unable to bind TextClassifierService for PendingRequest "
+                                + request.mName);
                         request.mOnServiceFailure.run();
                     }
                 }
diff --git a/services/core/java/com/android/server/tv/TvInputManagerService.java b/services/core/java/com/android/server/tv/TvInputManagerService.java
index a2134be..8e66b14 100644
--- a/services/core/java/com/android/server/tv/TvInputManagerService.java
+++ b/services/core/java/com/android/server/tv/TvInputManagerService.java
@@ -842,6 +842,10 @@
     private void setStateLocked(String inputId, int state, int userId) {
         UserState userState = getOrCreateUserStateLocked(userId);
         TvInputState inputState = userState.inputMap.get(inputId);
+        if (inputState == null) {
+            Slog.e(TAG, "failed to setStateLocked - unknown input id " + inputId);
+            return;
+        }
         ServiceState serviceState = userState.serviceStateMap.get(inputState.info.getComponent());
         int oldState = inputState.state;
         inputState.state = state;
diff --git a/services/core/java/com/android/server/wm/ActivityDisplay.java b/services/core/java/com/android/server/wm/ActivityDisplay.java
index f1cd721..e76078f 100644
--- a/services/core/java/com/android/server/wm/ActivityDisplay.java
+++ b/services/core/java/com/android/server/wm/ActivityDisplay.java
@@ -41,6 +41,7 @@
 import static com.android.server.am.ActivityDisplayProto.SINGLE_TASK_INSTANCE;
 import static com.android.server.am.ActivityDisplayProto.STACKS;
 import static com.android.server.wm.ActivityStack.ActivityState.RESUMED;
+import static com.android.server.wm.ActivityStack.REMOVE_TASK_MODE_DESTROYING;
 import static com.android.server.wm.ActivityStack.STACK_VISIBILITY_VISIBLE;
 import static com.android.server.wm.ActivityStackSupervisor.TAG_TASKS;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_STACK;
@@ -173,18 +174,10 @@
         mService = root.mService;
         mDisplayId = display.getDisplayId();
         mDisplay = display;
-        mDisplayContent = createDisplayContent();
+        mDisplayContent = mService.mWindowManager.mRoot.createDisplayContent(mDisplay, this);
         mDisplayContent.reconfigureDisplayLocked();
-        updateBounds();
-    }
-
-    protected DisplayContent createDisplayContent() {
-        return mService.mWindowManager.mRoot.createDisplayContent(mDisplay, this);
-    }
-
-    private void updateBounds() {
-        mDisplay.getRealSize(mTmpDisplaySize);
-        setBounds(0, 0, mTmpDisplaySize.x, mTmpDisplaySize.y);
+        onRequestedOverrideConfigurationChanged(
+                mDisplayContent.getRequestedOverrideConfiguration());
     }
 
     void onDisplayChanged() {
@@ -200,7 +193,8 @@
             }
         }
 
-        updateBounds();
+        mDisplay.getRealSize(mTmpDisplaySize);
+        setBounds(0, 0, mTmpDisplaySize.x, mTmpDisplaySize.y);
         if (mDisplayContent != null) {
             mDisplayContent.updateDisplayInfo();
             mService.mWindowManager.requestTraversal();
@@ -368,7 +362,7 @@
         return null;
     }
 
-    private boolean alwaysCreateStack(int windowingMode, int activityType) {
+    boolean alwaysCreateStack(int windowingMode, int activityType) {
         // Always create a stack for fullscreen, freeform, and split-screen-secondary windowing
         // modes so that we can manage visual ordering and return types correctly.
         return activityType == ACTIVITY_TYPE_STANDARD
@@ -496,10 +490,6 @@
         return null;
     }
 
-    ActivityStack getNextFocusableStack() {
-        return getNextFocusableStack(null /* currentFocus */, false /* ignoreCurrent */);
-    }
-
     ActivityStack getNextFocusableStack(ActivityStack currentFocus, boolean ignoreCurrent) {
         final int currentWindowingMode = currentFocus != null
                 ? currentFocus.getWindowingMode() : WINDOWING_MODE_UNDEFINED;
@@ -1541,6 +1531,17 @@
         return mSingleTaskInstance;
     }
 
+    @VisibleForTesting
+    void removeAllTasks() {
+        for (int i = getChildCount() - 1; i >= 0; --i) {
+            final ActivityStack stack = getChildAt(i);
+            final ArrayList<TaskRecord> tasks = stack.getAllTasks();
+            for (int j = tasks.size() - 1; j >= 0; --j) {
+                stack.removeTask(tasks.get(j), "removeAllTasks", REMOVE_TASK_MODE_DESTROYING);
+            }
+        }
+    }
+
     public void dump(PrintWriter pw, String prefix) {
         pw.println(prefix + "displayId=" + mDisplayId + " stacks=" + mStacks.size()
                 + (mSingleTaskInstance ? " mSingleTaskInstance" : ""));
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 31e8bbdab..fc36e99 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -106,6 +106,7 @@
 import static com.android.server.am.EventLogTags.AM_RELAUNCH_ACTIVITY;
 import static com.android.server.am.EventLogTags.AM_RELAUNCH_RESUME_ACTIVITY;
 import static com.android.server.wm.ActivityStack.ActivityState.DESTROYED;
+import static com.android.server.wm.ActivityStack.ActivityState.DESTROYING;
 import static com.android.server.wm.ActivityStack.ActivityState.FINISHING;
 import static com.android.server.wm.ActivityStack.ActivityState.INITIALIZING;
 import static com.android.server.wm.ActivityStack.ActivityState.PAUSED;
@@ -115,13 +116,13 @@
 import static com.android.server.wm.ActivityStack.ActivityState.STARTED;
 import static com.android.server.wm.ActivityStack.ActivityState.STOPPED;
 import static com.android.server.wm.ActivityStack.ActivityState.STOPPING;
-import static com.android.server.wm.ActivityStack.LAUNCH_TICK;
-import static com.android.server.wm.ActivityStack.LAUNCH_TICK_MSG;
-import static com.android.server.wm.ActivityStack.PAUSE_TIMEOUT_MSG;
+import static com.android.server.wm.ActivityStack.REMOVE_TASK_MODE_DESTROYING;
 import static com.android.server.wm.ActivityStack.STACK_VISIBILITY_VISIBLE;
-import static com.android.server.wm.ActivityStack.STOP_TIMEOUT_MSG;
 import static com.android.server.wm.ActivityStackSupervisor.PAUSE_IMMEDIATELY;
-import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_ALL;
+import static com.android.server.wm.ActivityStackSupervisor.PRESERVE_WINDOWS;
+import static com.android.server.wm.ActivityStackSupervisor.REMOVE_FROM_RECENTS;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_APP;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_CLEANUP;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_CONFIGURATION;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_CONTAINERS;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_FOCUS;
@@ -133,6 +134,8 @@
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_TRANSITION;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_USER_LEAVING;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_VISIBILITY;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_ADD_REMOVE;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_APP;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_CONFIGURATION;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_CONTAINERS;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_FOCUS;
@@ -177,8 +180,10 @@
 import android.app.servertransaction.ActivityConfigurationChangeItem;
 import android.app.servertransaction.ActivityLifecycleItem;
 import android.app.servertransaction.ActivityRelaunchItem;
+import android.app.servertransaction.ActivityResultItem;
 import android.app.servertransaction.ClientTransaction;
 import android.app.servertransaction.ClientTransactionItem;
+import android.app.servertransaction.DestroyActivityItem;
 import android.app.servertransaction.MoveToDisplayItem;
 import android.app.servertransaction.MultiWindowModeChangeItem;
 import android.app.servertransaction.NewIntentItem;
@@ -198,12 +203,12 @@
 import android.graphics.Bitmap;
 import android.graphics.GraphicBuffer;
 import android.graphics.Rect;
+import android.net.Uri;
 import android.os.Binder;
 import android.os.Build;
 import android.os.Bundle;
 import android.os.Debug;
 import android.os.IBinder;
-import android.os.Message;
 import android.os.PersistableBundle;
 import android.os.Process;
 import android.os.RemoteException;
@@ -260,6 +265,8 @@
  */
 final class ActivityRecord extends ConfigurationContainer {
     private static final String TAG = TAG_WITH_CLASS_NAME ? "ActivityRecord" : TAG_ATM;
+    private static final String TAG_ADD_REMOVE = TAG + POSTFIX_ADD_REMOVE;
+    private static final String TAG_APP = TAG + POSTFIX_APP;
     private static final String TAG_CONFIGURATION = TAG + POSTFIX_CONFIGURATION;
     private static final String TAG_CONTAINERS = TAG + POSTFIX_CONTAINERS;
     private static final String TAG_FOCUS = TAG + POSTFIX_FOCUS;
@@ -282,6 +289,9 @@
     private static final String ATTR_COMPONENTSPECIFIED = "component_specified";
     static final String ACTIVITY_ICON_SUFFIX = "_activity_icon_";
 
+    // How many activities have to be scheduled to stop to force a stop pass.
+    private static final int MAX_STOPPING_TO_FORCE = 3;
+
     final ActivityTaskManagerService mAtmService; // owner
     final IApplicationToken.Stub appToken; // window manager token
     // TODO: Remove after unification
@@ -1372,16 +1382,23 @@
         return stack != null ? stack.getDisplay() : null;
     }
 
-    boolean changeWindowTranslucency(boolean toOpaque) {
-        if (fullscreen == toOpaque) {
-            return false;
+    boolean setOccludesParent(boolean occludesParent) {
+        final boolean changed = mAppWindowToken.setOccludesParent(occludesParent);
+        if (changed) {
+            if (!occludesParent) {
+                getActivityStack().convertActivityToTranslucent(this);
+            }
+            // Keep track of the number of fullscreen activities in this task.
+            task.numFullscreen += occludesParent ? +1 : -1;
+            fullscreen = occludesParent;
         }
-
-        // Keep track of the number of fullscreen activities in this task.
-        task.numFullscreen += toOpaque ? +1 : -1;
-
-        fullscreen = toOpaque;
-        return true;
+        // Always ensure visibility if this activity doesn't occlude parent, so the
+        // {@link #returningOptions} of the activity under this one can be applied in
+        // {@link #handleAlreadyVisible()}.
+        if (changed || !occludesParent) {
+            mRootActivityContainer.ensureActivitiesVisible(null, 0, !PRESERVE_WINDOWS);
+        }
+        return changed;
     }
 
     void takeFromHistory() {
@@ -1590,8 +1607,7 @@
             if (!Objects.equals(cur.taskAffinity, taskAffinity)) {
                 break;
             }
-            cur.finishActivityLocked(Activity.RESULT_CANCELED, null /* resultData */,
-                    "request-affinity", true /* oomAdj */);
+            cur.finishIfPossible("request-affinity", true /* oomAdj */);
         }
     }
 
@@ -1648,14 +1664,17 @@
     @interface FinishRequest {}
 
     /**
-     * See {@link #finishActivityLocked(int, Intent, String, boolean, boolean)}
+     * See {@link #finishIfPossible(int, Intent, String, boolean, boolean)}
      */
-    @FinishRequest int finishActivityLocked(int resultCode, Intent resultData, String reason,
-            boolean oomAdj) {
-        return finishActivityLocked(resultCode, resultData, reason, oomAdj, !PAUSE_IMMEDIATELY);
+    @FinishRequest int finishIfPossible(String reason, boolean oomAdj) {
+        return finishIfPossible(Activity.RESULT_CANCELED, null /* resultData */, reason,
+                oomAdj, !PAUSE_IMMEDIATELY);
     }
 
     /**
+     * Finish activity if possible. If activity was resumed - we must first pause it to make the
+     * activity below resumed. Otherwise we will try to complete the request immediately by calling
+     * {@link #completeFinishing(String)}.
      * @return One of {@link FinishRequest} values:
      * {@link #FINISH_RESULT_REMOVED} if this activity has been removed from the history list.
      * {@link #FINISH_RESULT_REQUESTED} if removal process was started, but it is still in the list
@@ -1663,7 +1682,7 @@
      * {@link #FINISH_RESULT_CANCELLED} if activity is already finishing or in invalid state and the
      * request to finish it was not ignored.
      */
-    @FinishRequest int finishActivityLocked(int resultCode, Intent resultData, String reason,
+    @FinishRequest int finishIfPossible(int resultCode, Intent resultData, String reason,
             boolean oomAdj, boolean pauseImmediately) {
         if (DEBUG_RESULTS || DEBUG_STATES) {
             Slog.v(TAG_STATES, "Finishing activity r=" + this + ", result=" + resultCode
@@ -1702,20 +1721,24 @@
             pauseKeyDispatchingLocked();
 
             final ActivityStack stack = getActivityStack();
-            stack.adjustFocusedActivityStack(this, "finishActivity");
+            stack.adjustFocusedActivityStack(this, "finishIfPossible");
 
             finishActivityResults(resultCode, resultData);
 
             final boolean endTask = index <= 0 && !task.isClearingToReuseTask();
             final int transit = endTask ? TRANSIT_TASK_CLOSE : TRANSIT_ACTIVITY_CLOSE;
-            if (stack.getResumedActivity() == this) {
-                if (DEBUG_VISIBILITY || DEBUG_TRANSITION) {
-                    Slog.v(TAG_TRANSITION, "Prepare close transition: finishing " + this);
-                }
+            if (isState(RESUMED)) {
                 if (endTask) {
                     mAtmService.getTaskChangeNotificationController().notifyTaskRemovalStarted(
                             task.getTaskInfo());
                 }
+                // Prepare app close transition, but don't execute just yet. It is possible that
+                // an activity that will be made resumed in place of this one will immediately
+                // launch another new activity. In this case current closing transition will be
+                // combined with open transition for the new activity.
+                if (DEBUG_VISIBILITY || DEBUG_TRANSITION) {
+                    Slog.v(TAG_TRANSITION, "Prepare close transition: finishing " + this);
+                }
                 getDisplay().mDisplayContent.prepareAppTransition(transit, false);
 
                 // When finishing the activity preemptively take the snapshot before the app window
@@ -1742,17 +1765,17 @@
                     mAtmService.getLockTaskController().clearLockedTask(task);
                 }
             } else if (!isState(PAUSING)) {
-                // If the activity is PAUSING, we will complete the finish once
-                // it is done pausing; else we can just directly finish it here.
-                if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Finish not pausing: " + this);
                 if (visible) {
+                    // Prepare and execute close transition.
                     prepareActivityHideTransitionAnimation(transit);
                 }
 
-                final int finishMode = (visible || nowVisible) ? FINISH_AFTER_VISIBLE
-                        : FINISH_AFTER_PAUSE;
-                final boolean removedActivity = finishCurrentActivityLocked(finishMode, oomAdj,
-                        "finishActivityLocked") == null;
+                final boolean removedActivity = completeFinishing("finishIfPossible") == null;
+                // Performance optimization - only invoke OOM adjustment if the state changed to
+                // 'STOPPING'. Otherwise it will not change the OOM scores.
+                if (oomAdj && isState(STOPPING)) {
+                    mAtmService.updateOomAdj();
+                }
 
                 // The following code is an optimization. When the last non-task overlay activity
                 // is removed from the task, we remove the entire task from the stack. However,
@@ -1769,6 +1792,7 @@
                         taskOverlay.prepareActivityHideTransitionAnimation(transit);
                     }
                 }
+
                 return removedActivity ? FINISH_RESULT_REMOVED : FINISH_RESULT_REQUESTED;
             } else {
                 if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Finish waiting for pause of: " + this);
@@ -1787,101 +1811,298 @@
         dc.executeAppTransition();
     }
 
-    static final int FINISH_IMMEDIATELY = 0;
-    private static final int FINISH_AFTER_PAUSE = 1;
-    static final int FINISH_AFTER_VISIBLE = 2;
+    /**
+     * Complete activity finish request that was initiated earlier. If the activity is still
+     * pausing we will wait for it to complete its transition. If the activity that should appear in
+     * place of this one is not visible yet - we'll wait for it first. Otherwise - activity can be
+     * destroyed right away.
+     * @param reason Reason for finishing the activity.
+     * @return Flag indicating whether the activity was removed from history.
+     */
+    ActivityRecord completeFinishing(String reason) {
+        if (!finishing || isState(RESUMED)) {
+            throw new IllegalArgumentException(
+                    "Activity must be finishing and not resumed to complete, r=" + this
+                            + ", finishing=" + finishing + ", state=" + mState);
+        }
 
-    ActivityRecord finishCurrentActivityLocked(int mode, boolean oomAdj, String reason) {
-        // First things first: if this activity is currently visible,
-        // and the resumed activity is not yet visible, then hold off on
-        // finishing until the resumed one becomes visible.
+        if (isState(PAUSING)) {
+            // Activity is marked as finishing and will be processed once it completes.
+            return this;
+        }
 
+        boolean activityRemoved = false;
+
+        // If this activity is currently visible, and the resumed activity is not yet visible, then
+        // hold off on finishing until the resumed one becomes visible.
         // The activity that we are finishing may be over the lock screen. In this case, we do not
         // want to consider activities that cannot be shown on the lock screen as running and should
         // proceed with finishing the activity if there is no valid next top running activity.
         // Note that if this finishing activity is floating task, we don't need to wait the
         // next activity resume and can destroy it directly.
+        // TODO(b/137329632): find the next activity directly underneath this one, not just anywhere
+        final ActivityRecord next = getDisplay().topRunningActivity(
+                true /* considerKeyguardState */);
+        final boolean isVisible = visible || nowVisible;
         final ActivityStack stack = getActivityStack();
-        final ActivityDisplay display = getDisplay();
-        final ActivityRecord next = display.topRunningActivity(true /* considerKeyguardState */);
-        final boolean isFloating = getConfiguration().windowConfiguration.tasksAreFloating();
-
-        if (mode == FINISH_AFTER_VISIBLE && (visible || nowVisible)
-                && next != null && !next.nowVisible && !isFloating) {
-            if (!mStackSupervisor.mStoppingActivities.contains(this)) {
-                stack.addToStopping(this, false /* scheduleIdle */, false /* idleDelayed */,
-                        "finishCurrentActivityLocked");
-            }
+        final boolean notFocusedStack = stack != mRootActivityContainer.getTopDisplayFocusedStack();
+        if (isVisible && next != null && !next.nowVisible) {
+            addToStopping(false /* scheduleIdle */, false /* idleDelayed */,
+                    "completeFinishing");
             if (DEBUG_STATES) {
                 Slog.v(TAG_STATES, "Moving to STOPPING: " + this + " (finish requested)");
             }
-            setState(STOPPING, "finishCurrentActivityLocked");
-            if (oomAdj) {
-                mAtmService.updateOomAdj();
-            }
-            return this;
-        }
-
-        // make sure the record is cleaned out of other places.
-        mStackSupervisor.mStoppingActivities.remove(this);
-        mStackSupervisor.mGoingToSleepActivities.remove(this);
-        final ActivityState prevState = getState();
-        if (DEBUG_STATES) Slog.v(TAG_STATES, "Moving to FINISHING: " + this);
-
-        setState(FINISHING, "finishCurrentActivityLocked");
-
-        // Don't destroy activity immediately if the display contains home stack, although there is
-        // no next activity at the moment but another home activity should be started later. Keep
-        // this activity alive until next home activity is resumed then user won't see a temporary
-        // black screen.
-        final boolean noRunningStack = next == null && display.topRunningActivity() == null
-                && display.getHomeStack() == null;
-        final boolean noFocusedStack = stack != display.getFocusedStack();
-        final boolean finishingInNonFocusedStackOrNoRunning = mode == FINISH_AFTER_VISIBLE
-                && prevState == PAUSED && (noFocusedStack || noRunningStack);
-
-        if (mode == FINISH_IMMEDIATELY
-                || (prevState == PAUSED && (mode == FINISH_AFTER_PAUSE || inPinnedWindowingMode()))
-                || finishingInNonFocusedStackOrNoRunning
-                || prevState == STARTED
-                || prevState == STOPPING
-                || prevState == STOPPED
-                || prevState == ActivityState.INITIALIZING) {
-            makeFinishingLocked();
-            boolean activityRemoved = stack.destroyActivityLocked(this, true /* removeFromApp */,
-                    "finish-imm:" + reason);
-
-            if (finishingInNonFocusedStackOrNoRunning) {
-                // Finishing activity that was in paused state and it was in not currently focused
-                // stack, need to make something visible in its place. Also if the display does not
-                // have running activity, the configuration may need to be updated for restoring
-                // original orientation of the display.
+            setState(STOPPING, "completeFinishing");
+            if (notFocusedStack) {
                 mRootActivityContainer.ensureVisibilityAndConfig(next, getDisplayId(),
                         false /* markFrozenIfConfigChanged */, true /* deferResume */);
             }
-            if (activityRemoved) {
-                mRootActivityContainer.resumeFocusedStacksTopActivities();
-            }
-            if (DEBUG_CONTAINERS) {
-                Slog.d(TAG_CONTAINERS, "destroyActivityLocked: finishCurrentActivityLocked r="
-                        + this + " destroy returned removed=" + activityRemoved);
-            }
-            return activityRemoved ? null : this;
+        } else if (isVisible && isState(PAUSED) && getActivityStack().isFocusedStackOnDisplay()
+                && !inPinnedWindowingMode()) {
+            // TODO(b/137329632): Currently non-focused stack is handled differently.
+            addToFinishingAndWaitForIdle();
+        } else {
+            // Not waiting for the next one to become visible - finish right away.
+            activityRemoved = destroyIfPossible(reason);
         }
 
-        // Need to go through the full pause cycle to get this
-        // activity into the stopped state and then finish it.
-        if (DEBUG_ALL) Slog.v(TAG, "Enqueueing pending finish: " + this);
+        return activityRemoved ? null : this;
+    }
+
+    /**
+     * Destroy and cleanup the activity both on client and server if possible. If activity is the
+     * last one left on display with home stack and there is no other running activity - delay
+     * destroying it until the next one starts.
+     */
+    boolean destroyIfPossible(String reason) {
+        setState(FINISHING, "destroyIfPossible");
+
+        // Make sure the record is cleaned out of other places.
+        mStackSupervisor.mStoppingActivities.remove(this);
+        mStackSupervisor.mGoingToSleepActivities.remove(this);
+
+        final ActivityStack stack = getActivityStack();
+        final ActivityDisplay display = getDisplay();
+        // TODO(b/137329632): Exclude current activity when looking for the next one with
+        //  ActivityDisplay#topRunningActivity().
+        final ActivityRecord next = display.topRunningActivity();
+        final boolean isLastStackOverEmptyHome =
+                next == null && stack.isFocusedStackOnDisplay() && display.getHomeStack() != null;
+        if (isLastStackOverEmptyHome) {
+            // Don't destroy activity immediately if this is the last activity on the display and
+            // the display contains home stack. Although there is no next activity at the moment,
+            // another home activity should be started later. Keep this activity alive until next
+            // home activity is resumed. This way the user won't see a temporary black screen.
+            addToFinishingAndWaitForIdle();
+            return false;
+        }
+        makeFinishingLocked();
+
+        final boolean activityRemoved = destroyImmediately(true /* removeFromApp */,
+                "finish-imm:" + reason);
+
+        // If the display does not have running activity, the configuration may need to be
+        // updated for restoring original orientation of the display.
+        if (next == null) {
+            mRootActivityContainer.ensureVisibilityAndConfig(next, getDisplayId(),
+                    false /* markFrozenIfConfigChanged */, true /* deferResume */);
+        }
+        if (activityRemoved) {
+            mRootActivityContainer.resumeFocusedStacksTopActivities();
+        }
+
+        if (DEBUG_CONTAINERS) {
+            Slog.d(TAG_CONTAINERS, "destroyIfPossible: r=" + this + " destroy returned removed="
+                    + activityRemoved);
+        }
+
+        return activityRemoved;
+    }
+
+    @VisibleForTesting
+    void addToFinishingAndWaitForIdle() {
+        if (DEBUG_STATES) Slog.v(TAG, "Enqueueing pending finish: " + this);
+        setState(FINISHING, "addToFinishingAndWaitForIdle");
         mStackSupervisor.mFinishingActivities.add(this);
         resumeKeyDispatchingLocked();
         mRootActivityContainer.resumeFocusedStacksTopActivities();
-        // If activity was not paused at this point - explicitly pause it to start finishing
-        // process. Finishing will be completed once it reports pause back.
-        if (isState(RESUMED) && stack.mPausingActivity != null) {
-            stack.startPausingLocked(false /* userLeaving */, false /* uiSleeping */,
-                    next /* resuming */, false /* dontWait */);
+    }
+
+    /**
+     * Destroy the current CLIENT SIDE instance of an activity. This may be called both when
+     * actually finishing an activity, or when performing a configuration switch where we destroy
+     * the current client-side object but then create a new client-side object for this same
+     * HistoryRecord.
+     * Normally the server-side record will be removed when the client reports back after
+     * destruction. If, however, at this point there is no client process attached, the record will
+     * removed immediately.
+     */
+    boolean destroyImmediately(boolean removeFromApp, String reason) {
+        if (DEBUG_SWITCH || DEBUG_CLEANUP) {
+            Slog.v(TAG_SWITCH, "Removing activity from " + reason + ": token=" + this
+                    + ", app=" + (hasProcess() ? app.mName : "(null)"));
         }
-        return this;
+
+        if (isState(DESTROYING, DESTROYED)) {
+            if (DEBUG_STATES) {
+                Slog.v(TAG_STATES, "activity " + this + " already destroying."
+                        + "skipping request with reason:" + reason);
+            }
+            return false;
+        }
+
+        EventLog.writeEvent(EventLogTags.AM_DESTROY_ACTIVITY, mUserId,
+                System.identityHashCode(this), getTaskRecord().taskId, shortComponentName, reason);
+
+        boolean removedFromHistory = false;
+
+        cleanUp(false /* cleanServices */, false /* setState */);
+
+        final ActivityStack stack = getActivityStack();
+        final boolean hadApp = hasProcess();
+
+        if (hadApp) {
+            if (removeFromApp) {
+                app.removeActivity(this);
+                if (!app.hasActivities()) {
+                    mAtmService.clearHeavyWeightProcessIfEquals(app);
+                    // Update any services we are bound to that might care about whether
+                    // their client may have activities.
+                    // No longer have activities, so update LRU list and oom adj.
+                    app.updateProcessInfo(true /* updateServiceConnectionActivities */,
+                            false /* activityChange */, true /* updateOomAdj */);
+                }
+            }
+
+            boolean skipDestroy = false;
+
+            try {
+                if (DEBUG_SWITCH) Slog.i(TAG_SWITCH, "Destroying: " + this);
+                mAtmService.getLifecycleManager().scheduleTransaction(app.getThread(), appToken,
+                        DestroyActivityItem.obtain(finishing, configChangeFlags));
+            } catch (Exception e) {
+                // We can just ignore exceptions here...  if the process has crashed, our death
+                // notification will clean things up.
+                if (finishing) {
+                    removeFromHistory(reason + " exceptionInScheduleDestroy");
+                    removedFromHistory = true;
+                    skipDestroy = true;
+                }
+            }
+
+            nowVisible = false;
+
+            // If the activity is finishing, we need to wait on removing it from the list to give it
+            // a chance to do its cleanup.  During that time it may make calls back with its token
+            // so we need to be able to find it on the list and so we don't want to remove it from
+            // the list yet.  Otherwise, we can just immediately put it in the destroyed state since
+            // we are not removing it from the list.
+            if (finishing && !skipDestroy) {
+                if (DEBUG_STATES) {
+                    Slog.v(TAG_STATES, "Moving to DESTROYING: " + this + " (destroy requested)");
+                }
+                setState(DESTROYING,
+                        "destroyActivityLocked. finishing and not skipping destroy");
+                stack.scheduleDestroyTimeoutForActivity(this);
+            } else {
+                if (DEBUG_STATES) {
+                    Slog.v(TAG_STATES, "Moving to DESTROYED: " + this + " (destroy skipped)");
+                }
+                setState(DESTROYED,
+                        "destroyActivityLocked. not finishing or skipping destroy");
+                if (DEBUG_APP) Slog.v(TAG_APP, "Clearing app during destroy for activity " + this);
+                app = null;
+            }
+        } else {
+            // Remove this record from the history.
+            if (finishing) {
+                removeFromHistory(reason + " hadNoApp");
+                removedFromHistory = true;
+            } else {
+                if (DEBUG_STATES) Slog.v(TAG_STATES, "Moving to DESTROYED: " + this + " (no app)");
+                setState(DESTROYED, "destroyActivityLocked. not finishing and had no app");
+            }
+        }
+
+        configChangeFlags = 0;
+
+        if (!stack.removeActivityFromLRUList(this) && hadApp) {
+            Slog.w(TAG, "Activity " + this + " being finished, but not in LRU list");
+        }
+
+        return removedFromHistory;
+    }
+
+    boolean safelyDestroy(String reason) {
+        if (isDestroyable()) {
+            if (DEBUG_SWITCH) {
+                final ActivityStack stack = getActivityStack();
+                Slog.v(TAG_SWITCH, "Safely destroying " + this + " in state " + getState()
+                        + " resumed=" + stack.mResumedActivity
+                        + " pausing=" + stack.mPausingActivity
+                        + " for reason " + reason);
+            }
+            return destroyImmediately(true /* removeFromApp */, reason);
+        }
+        return false;
+    }
+
+    /** Note: call {@link #cleanUp(boolean, boolean)} before this method. */
+    void removeFromHistory(String reason) {
+        finishActivityResults(Activity.RESULT_CANCELED, null /* resultData */);
+        makeFinishingLocked();
+        if (ActivityTaskManagerDebugConfig.DEBUG_ADD_REMOVE) {
+            Slog.i(TAG_ADD_REMOVE, "Removing activity " + this + " from stack callers="
+                    + Debug.getCallers(5));
+        }
+
+        takeFromHistory();
+        final ActivityStack stack = getActivityStack();
+        stack.removeTimeoutsForActivity(this);
+        if (DEBUG_STATES) {
+            Slog.v(TAG_STATES, "Moving to DESTROYED: " + this + " (removed from history)");
+        }
+        setState(DESTROYED, "removeFromHistory");
+        if (DEBUG_APP) Slog.v(TAG_APP, "Clearing app during remove for activity " + this);
+        app = null;
+        removeWindowContainer();
+        final TaskRecord task = getTaskRecord();
+        final boolean lastActivity = task.removeActivity(this);
+        // If we are removing the last activity in the task, not including task overlay activities,
+        // then fall through into the block below to remove the entire task itself
+        final boolean onlyHasTaskOverlays =
+                task.onlyHasTaskOverlayActivities(false /* excludingFinishing */);
+
+        if (lastActivity || onlyHasTaskOverlays) {
+            if (DEBUG_STATES) {
+                Slog.i(TAG, "removeFromHistory: last activity removed from " + this
+                        + " onlyHasTaskOverlays=" + onlyHasTaskOverlays);
+            }
+
+            // The following block can be executed multiple times if there is more than one overlay.
+            // {@link ActivityStackSupervisor#removeTaskByIdLocked} handles this by reverse lookup
+            // of the task by id and exiting early if not found.
+            if (onlyHasTaskOverlays) {
+                // When destroying a task, tell the supervisor to remove it so that any activity it
+                // has can be cleaned up correctly. This is currently the only place where we remove
+                // a task with the DESTROYING mode, so instead of passing the onlyHasTaskOverlays
+                // state into removeTask(), we just clear the task here before the other residual
+                // work.
+                // TODO: If the callers to removeTask() changes such that we have multiple places
+                //       where we are destroying the task, move this back into removeTask()
+                mStackSupervisor.removeTaskByIdLocked(task.taskId, false /* killProcess */,
+                        !REMOVE_FROM_RECENTS, PAUSE_IMMEDIATELY, reason);
+            }
+
+            // We must keep the task around until all activities are destroyed. The following
+            // statement will only execute once since overlays are also considered activities.
+            if (lastActivity) {
+                stack.removeTask(task, reason, REMOVE_TASK_MODE_DESTROYING);
+            }
+        }
+
+        cleanUpActivityServices();
+        removeUriPermissionsLocked();
     }
 
     void makeFinishingLocked() {
@@ -1898,6 +2119,96 @@
         }
     }
 
+    /**
+     * This method is to only be called from the client via binder when the activity is destroyed
+     * AND finished.
+     */
+    void destroyed(String reason) {
+        getActivityStack().removeDestroyTimeoutForActivity(this);
+
+        if (DEBUG_CONTAINERS) Slog.d(TAG_CONTAINERS, "activityDestroyedLocked: r=" + this);
+
+        if (!isState(DESTROYING, DESTROYED)) {
+            throw new IllegalStateException(
+                    "Reported destroyed for activity that is not destroying: r=" + this);
+        }
+
+        if (isInStackLocked()) {
+            cleanUp(true /* cleanServices */, false /* setState */);
+            removeFromHistory(reason);
+        }
+
+        mRootActivityContainer.resumeFocusedStacksTopActivities();
+    }
+
+    /**
+     * Perform the common clean-up of an activity record.  This is called both as part of
+     * destroyActivityLocked() (when destroying the client-side representation) and cleaning things
+     * up as a result of its hosting processing going away, in which case there is no remaining
+     * client-side state to destroy so only the cleanup here is needed.
+     *
+     * Note: Call before {@link #removeFromHistory(String)}.
+     */
+    void cleanUp(boolean cleanServices, boolean setState) {
+        final ActivityStack stack = getActivityStack();
+        stack.onActivityRemovedFromStack(this);
+
+        deferRelaunchUntilPaused = false;
+        frozenBeforeDestroy = false;
+
+        if (setState) {
+            setState(DESTROYED, "cleanUp");
+            if (DEBUG_APP) Slog.v(TAG_APP, "Clearing app during cleanUp for activity " + this);
+            app = null;
+        }
+
+        // Inform supervisor the activity has been removed.
+        mStackSupervisor.cleanupActivity(this);
+
+        // Remove any pending results.
+        if (finishing && pendingResults != null) {
+            for (WeakReference<PendingIntentRecord> apr : pendingResults) {
+                PendingIntentRecord rec = apr.get();
+                if (rec != null) {
+                    mAtmService.mPendingIntentController.cancelIntentSender(rec,
+                            false /* cleanActivity */);
+                }
+            }
+            pendingResults = null;
+        }
+
+        if (cleanServices) {
+            cleanUpActivityServices();
+        }
+
+        // Get rid of any pending idle timeouts.
+        stack.removeTimeoutsForActivity(this);
+        // Clean-up activities are no longer relaunching (e.g. app process died). Notify window
+        // manager so it can update its bookkeeping.
+        mAtmService.mWindowManager.notifyAppRelaunchesCleared(appToken);
+    }
+
+    /**
+     * Perform clean-up of service connections in an activity record.
+     */
+    private void cleanUpActivityServices() {
+        if (mServiceConnectionsHolder == null) {
+            return;
+        }
+        // Throw away any services that have been bound by this activity.
+        mServiceConnectionsHolder.disconnectActivityFromServices();
+    }
+
+    void logStartActivity(int tag, TaskRecord task) {
+        final Uri data = intent.getData();
+        final String strData = data != null ? data.toSafeString() : null;
+
+        EventLog.writeEvent(tag,
+                mUserId, System.identityHashCode(this), task.taskId,
+                shortComponentName, intent.getAction(),
+                intent.getType(), strData, intent.getFlags());
+    }
+
     UriPermissionOwner getUriPermissionsLocked() {
         if (uriPermissions == null) {
             uriPermissions = new UriPermissionOwner(mAtmService.mUgmInternal, this);
@@ -1934,6 +2245,33 @@
         }
     }
 
+    void sendResult(int callingUid, String resultWho, int requestCode, int resultCode,
+            Intent data) {
+        if (callingUid > 0) {
+            mAtmService.mUgmInternal.grantUriPermissionFromIntent(callingUid, packageName,
+                    data, getUriPermissionsLocked(), mUserId);
+        }
+
+        if (DEBUG_RESULTS) {
+            Slog.v(TAG, "Send activity result to " + this
+                    + " : who=" + resultWho + " req=" + requestCode
+                    + " res=" + resultCode + " data=" + data);
+        }
+        if (isState(RESUMED) && attachedToProcess()) {
+            try {
+                final ArrayList<ResultInfo> list = new ArrayList<ResultInfo>();
+                list.add(new ResultInfo(resultWho, requestCode, resultCode, data));
+                mAtmService.getLifecycleManager().scheduleTransaction(app.getThread(), appToken,
+                        ActivityResultItem.obtain(list));
+                return;
+            } catch (Exception e) {
+                Slog.w(TAG, "Exception thrown sending result to " + this, e);
+            }
+        }
+
+        addResultLocked(null /* from */, resultWho, requestCode, resultCode, data);
+    }
+
     private void addNewIntentLocked(ReferrerIntent intent) {
         if (newIntents == null) {
             newIntents = new ArrayList<>();
@@ -2413,6 +2751,65 @@
         }
     }
 
+    void makeInvisible() {
+        if (!visible) {
+            if (DEBUG_VISIBILITY) Slog.v(TAG_VISIBILITY, "Already invisible: " + this);
+            return;
+        }
+        // Now for any activities that aren't visible to the user, make sure they no longer are
+        // keeping the screen frozen.
+        if (DEBUG_VISIBILITY) {
+            Slog.v(TAG_VISIBILITY, "Making invisible: " + this + ", state=" + getState());
+        }
+        try {
+            final boolean canEnterPictureInPicture = checkEnterPictureInPictureState(
+                    "makeInvisible", true /* beforeStopping */);
+            // Defer telling the client it is hidden if it can enter Pip and isn't current paused,
+            // stopped or stopping. This gives it a chance to enter Pip in onPause().
+            // TODO: There is still a question surrounding activities in multi-window mode that want
+            // to enter Pip after they are paused, but are still visible. I they should be okay to
+            // enter Pip in those cases, but not "auto-Pip" which is what this condition covers and
+            // the current contract for "auto-Pip" is that the app should enter it before onPause
+            // returns. Just need to confirm this reasoning makes sense.
+            final boolean deferHidingClient = canEnterPictureInPicture
+                    && !isState(STOPPING, STOPPED, PAUSED);
+            setDeferHidingClient(deferHidingClient);
+            setVisible(false);
+
+            switch (getState()) {
+                case STOPPING:
+                case STOPPED:
+                    if (attachedToProcess()) {
+                        if (DEBUG_VISIBILITY) {
+                            Slog.v(TAG_VISIBILITY, "Scheduling invisibility: " + this);
+                        }
+                        mAtmService.getLifecycleManager().scheduleTransaction(app.getThread(),
+                                appToken, WindowVisibilityItem.obtain(false /* showWindow */));
+                    }
+
+                    // Reset the flag indicating that an app can enter picture-in-picture once the
+                    // activity is hidden
+                    supportsEnterPipOnTaskSwitch = false;
+                    break;
+
+                case INITIALIZING:
+                case RESUMED:
+                case PAUSING:
+                case PAUSED:
+                case STARTED:
+                    addToStopping(true /* scheduleIdle */,
+                            canEnterPictureInPicture /* idleDelayed */, "makeInvisible");
+                    break;
+
+                default:
+                    break;
+            }
+        } catch (Exception e) {
+            // Just skip on any failure; we'll make it visible when it next restarts.
+            Slog.w(TAG, "Exception thrown making hidden: " + intent.getComponent(), e);
+        }
+    }
+
     /**
      * Make activity resumed or paused if needed.
      * @param activeActivity an activity that is resumed or just completed pause action.
@@ -2603,13 +3000,75 @@
         }
     }
 
+    void stopIfPossible() {
+        if (DEBUG_SWITCH) Slog.d(TAG_SWITCH, "Stopping: " + this);
+        final ActivityStack stack = getActivityStack();
+        if ((intent.getFlags() & Intent.FLAG_ACTIVITY_NO_HISTORY) != 0
+                || (info.flags & ActivityInfo.FLAG_NO_HISTORY) != 0) {
+            if (!finishing) {
+                if (!stack.shouldSleepActivities()) {
+                    if (DEBUG_STATES) Slog.d(TAG_STATES, "no-history finish of " + this);
+                    if (finishIfPossible("stop-no-history", false /* oomAdj */)
+                            != FINISH_RESULT_CANCELLED) {
+                        // {@link adjustFocusedActivityStack} must have been already called.
+                        resumeKeyDispatchingLocked();
+                        return;
+                    }
+                } else {
+                    if (DEBUG_STATES) {
+                        Slog.d(TAG_STATES, "Not finishing noHistory " + this
+                                + " on stop because we're just sleeping");
+                    }
+                }
+            }
+        }
+
+        if (!attachedToProcess()) {
+            return;
+        }
+        stack.adjustFocusedActivityStack(this, "stopActivity");
+        resumeKeyDispatchingLocked();
+        try {
+            stopped = false;
+            if (DEBUG_STATES) {
+                Slog.v(TAG_STATES, "Moving to STOPPING: " + this + " (stop requested)");
+            }
+            setState(STOPPING, "stopIfPossible");
+            if (DEBUG_VISIBILITY) {
+                Slog.v(TAG_VISIBILITY, "Stopping visible=" + visible + " for " + this);
+            }
+            if (!visible) {
+                setVisible(false);
+            }
+            EventLogTags.writeAmStopActivity(
+                    mUserId, System.identityHashCode(this), shortComponentName);
+            mAtmService.getLifecycleManager().scheduleTransaction(app.getThread(), appToken,
+                    StopActivityItem.obtain(visible, configChangeFlags));
+            if (stack.shouldSleepOrShutDownActivities()) {
+                setSleeping(true);
+            }
+            stack.scheduleStopTimeoutForActivity(this);
+        } catch (Exception e) {
+            // Maybe just ignore exceptions here...  if the process has crashed, our death
+            // notification will clean things up.
+            Slog.w(TAG, "Exception thrown during pause", e);
+            // Just in case, assume it to be stopped.
+            stopped = true;
+            if (DEBUG_STATES) Slog.v(TAG_STATES, "Stop failed; moving to STOPPED: " + this);
+            setState(STOPPED, "stopIfPossible");
+            if (deferRelaunchUntilPaused) {
+                destroyImmediately(true /* removeFromApp */, "stop-except");
+            }
+        }
+    }
+
     final void activityStoppedLocked(Bundle newIcicle, PersistableBundle newPersistentState,
             CharSequence description) {
         final ActivityStack stack = getActivityStack();
         final boolean isStopping = mState == STOPPING;
         if (!isStopping && mState != RESTARTING_PROCESS) {
             Slog.i(TAG, "Activity reported stop, but no longer stopping: " + this);
-            stack.mHandler.removeMessages(STOP_TIMEOUT_MSG, this);
+            stack.removeStopTimeoutForActivity(this);
             return;
         }
         if (newPersistentState != null) {
@@ -2627,7 +3086,7 @@
         if (DEBUG_SAVED_STATE) Slog.i(TAG_SAVED_STATE, "Saving icicle of " + this + ": " + mIcicle);
         if (!stopped) {
             if (DEBUG_STATES) Slog.v(TAG_STATES, "Moving to STOPPED: " + this + " (stop complete)");
-            stack.mHandler.removeMessages(STOP_TIMEOUT_MSG, this);
+            stack.removeStopTimeoutForActivity(this);
             stopped = true;
             if (isStopping) {
                 setState(STOPPED, "activityStoppedLocked");
@@ -2641,7 +3100,7 @@
                 clearOptionsLocked();
             } else {
                 if (deferRelaunchUntilPaused) {
-                    stack.destroyActivityLocked(this, true /* removeFromApp */, "stop-config");
+                    destroyImmediately(true /* removeFromApp */, "stop-config");
                     mRootActivityContainer.resumeFocusedStacksTopActivities();
                 } else {
                     mRootActivityContainer.updatePreviousProcess(this);
@@ -2650,6 +3109,34 @@
         }
     }
 
+    void addToStopping(boolean scheduleIdle, boolean idleDelayed, String reason) {
+        if (!mStackSupervisor.mStoppingActivities.contains(this)) {
+            EventLog.writeEvent(EventLogTags.AM_ADD_TO_STOPPING, mUserId,
+                    System.identityHashCode(this), shortComponentName, reason);
+            mStackSupervisor.mStoppingActivities.add(this);
+        }
+
+        final ActivityStack stack = getActivityStack();
+        // If we already have a few activities waiting to stop, then give up on things going idle
+        // and start clearing them out. Or if r is the last of activity of the last task the stack
+        // will be empty and must be cleared immediately.
+        boolean forceIdle = mStackSupervisor.mStoppingActivities.size() > MAX_STOPPING_TO_FORCE
+                || (isRootOfTask() && stack.getChildCount() <= 1);
+        if (scheduleIdle || forceIdle) {
+            if (DEBUG_PAUSE) {
+                Slog.v(TAG_PAUSE, "Scheduling idle now: forceIdle=" + forceIdle
+                        + "immediate=" + !idleDelayed);
+            }
+            if (!idleDelayed) {
+                mStackSupervisor.scheduleIdleLocked();
+            } else {
+                mStackSupervisor.scheduleIdleTimeoutLocked(this);
+            }
+        } else {
+            stack.checkReadyForSleep();
+        }
+    }
+
     void startLaunchTickingLocked() {
         if (Build.IS_USER) {
             return;
@@ -2670,18 +3157,18 @@
             return false;
         }
 
-        Message msg = stack.mHandler.obtainMessage(LAUNCH_TICK_MSG, this);
-        stack.mHandler.removeMessages(LAUNCH_TICK_MSG);
-        stack.mHandler.sendMessageDelayed(msg, LAUNCH_TICK);
+        stack.removeLaunchTickMessages();
+        stack.scheduleLaunchTickForActivity(this);
         return true;
     }
 
     void finishLaunchTickingLocked() {
         launchTickTime = 0;
         final ActivityStack stack = getActivityStack();
-        if (stack != null) {
-            stack.mHandler.removeMessages(LAUNCH_TICK_MSG);
+        if (stack == null) {
+            return;
         }
+        stack.removeLaunchTickMessages();
     }
 
     // IApplicationToken
@@ -3658,7 +4145,7 @@
             if (!attachedToProcess()) {
                 if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION,
                         "Config is destroying non-running " + this);
-                stack.destroyActivityLocked(this, true, "config");
+                destroyImmediately(true /* removeFromApp */, "config");
             } else if (mState == PAUSING) {
                 // A little annoying: we are waiting for this activity to finish pausing. Let's not
                 // do anything now, but just flag that it needs to be restarted when done pausing.
@@ -3842,7 +4329,7 @@
         } else {
             final ActivityStack stack = getActivityStack();
             if (stack != null) {
-                stack.mHandler.removeMessages(PAUSE_TIMEOUT_MSG, this);
+                stack.removePauseTimeoutForActivity(this);
             }
             setState(PAUSED, "relaunchActivityLocked");
         }
@@ -3882,7 +4369,7 @@
         if (!visible || mHaveState) {
             // Kill its process immediately because the activity should be in background.
             // The activity state will be update to {@link #DESTROYED} in
-            // {@link ActivityStack#cleanUpActivityLocked} when handling process died.
+            // {@link ActivityStack#cleanUp} when handling process died.
             mAtmService.mH.post(() -> {
                 final WindowProcessController wpc;
                 synchronized (mAtmService.mGlobalLock) {
diff --git a/services/core/java/com/android/server/wm/ActivityStack.java b/services/core/java/com/android/server/wm/ActivityStack.java
index 12eab50..8bdedff 100644
--- a/services/core/java/com/android/server/wm/ActivityStack.java
+++ b/services/core/java/com/android/server/wm/ActivityStack.java
@@ -56,12 +56,7 @@
 import static com.android.server.am.ActivityStackProto.TASKS;
 import static com.android.server.wm.ActivityDisplay.POSITION_BOTTOM;
 import static com.android.server.wm.ActivityDisplay.POSITION_TOP;
-import static com.android.server.wm.ActivityRecord.FINISH_AFTER_VISIBLE;
-import static com.android.server.wm.ActivityRecord.FINISH_IMMEDIATELY;
-import static com.android.server.wm.ActivityRecord.FINISH_RESULT_CANCELLED;
 import static com.android.server.wm.ActivityRecord.FINISH_RESULT_REMOVED;
-import static com.android.server.wm.ActivityStack.ActivityState.DESTROYED;
-import static com.android.server.wm.ActivityStack.ActivityState.DESTROYING;
 import static com.android.server.wm.ActivityStack.ActivityState.PAUSED;
 import static com.android.server.wm.ActivityStack.ActivityState.PAUSING;
 import static com.android.server.wm.ActivityStack.ActivityState.RESUMED;
@@ -70,14 +65,12 @@
 import static com.android.server.wm.ActivityStack.ActivityState.STOPPING;
 import static com.android.server.wm.ActivityStackSupervisor.PAUSE_IMMEDIATELY;
 import static com.android.server.wm.ActivityStackSupervisor.PRESERVE_WINDOWS;
-import static com.android.server.wm.ActivityStackSupervisor.REMOVE_FROM_RECENTS;
 import static com.android.server.wm.ActivityStackSupervisor.dumpHistoryList;
 import static com.android.server.wm.ActivityStackSupervisor.printThisActivity;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_ADD_REMOVE;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_ALL;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_APP;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_CLEANUP;
-import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_CONTAINERS;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_PAUSE;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_RELEASE;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_RESULTS;
@@ -91,7 +84,6 @@
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_ADD_REMOVE;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_APP;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_CLEANUP;
-import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_CONTAINERS;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_PAUSE;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_RELEASE;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_RESULTS;
@@ -108,6 +100,7 @@
 import static com.android.server.wm.ActivityTaskManagerService.RELAUNCH_REASON_FREE_RESIZE;
 import static com.android.server.wm.ActivityTaskManagerService.RELAUNCH_REASON_WINDOWING_MODE_RESIZE;
 import static com.android.server.wm.RootActivityContainer.FindTaskResult;
+import static com.android.server.wm.TaskRecord.REPARENT_LEAVE_STACK_IN_PLACE;
 
 import static java.lang.Integer.MAX_VALUE;
 
@@ -124,12 +117,9 @@
 import android.app.WindowConfiguration.WindowingMode;
 import android.app.servertransaction.ActivityResultItem;
 import android.app.servertransaction.ClientTransaction;
-import android.app.servertransaction.DestroyActivityItem;
 import android.app.servertransaction.NewIntentItem;
 import android.app.servertransaction.PauseActivityItem;
 import android.app.servertransaction.ResumeActivityItem;
-import android.app.servertransaction.StopActivityItem;
-import android.app.servertransaction.WindowVisibilityItem;
 import android.content.ComponentName;
 import android.content.Intent;
 import android.content.pm.ActivityInfo;
@@ -164,11 +154,9 @@
 import com.android.server.am.ActivityManagerService.ItemMatcher;
 import com.android.server.am.AppTimeTracker;
 import com.android.server.am.EventLogTags;
-import com.android.server.am.PendingIntentRecord;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
-import java.lang.ref.WeakReference;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Objects;
@@ -182,7 +170,6 @@
     private static final String TAG_ADD_REMOVE = TAG + POSTFIX_ADD_REMOVE;
     private static final String TAG_APP = TAG + POSTFIX_APP;
     private static final String TAG_CLEANUP = TAG + POSTFIX_CLEANUP;
-    private static final String TAG_CONTAINERS = TAG + POSTFIX_CONTAINERS;
     private static final String TAG_PAUSE = TAG + POSTFIX_PAUSE;
     private static final String TAG_RELEASE = TAG + POSTFIX_RELEASE;
     private static final String TAG_RESULTS = TAG + POSTFIX_RESULTS;
@@ -195,7 +182,7 @@
     private static final String TAG_VISIBILITY = TAG + POSTFIX_VISIBILITY;
 
     // Ticks during which we check progress while waiting for an app to launch.
-    static final int LAUNCH_TICK = 500;
+    private static final int LAUNCH_TICK = 500;
 
     // How long we wait until giving up on the last activity to pause.  This
     // is short because it directly impacts the responsiveness of starting the
@@ -223,9 +210,6 @@
     // convertToTranslucent().
     private static final long TRANSLUCENT_CONVERSION_TIMEOUT = 2000;
 
-    // How many activities have to be scheduled to stop to force a stop pass.
-    private static final int MAX_STOPPING_TO_FORCE = 3;
-
     @IntDef(prefix = {"STACK_VISIBILITY"}, value = {
             STACK_VISIBILITY_VISIBLE,
             STACK_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT,
@@ -412,12 +396,12 @@
     private boolean mTopActivityOccludesKeyguard;
     private ActivityRecord mTopDismissingKeyguardActivity;
 
-    static final int PAUSE_TIMEOUT_MSG = FIRST_ACTIVITY_STACK_MSG + 1;
-    static final int DESTROY_TIMEOUT_MSG = FIRST_ACTIVITY_STACK_MSG + 2;
-    static final int LAUNCH_TICK_MSG = FIRST_ACTIVITY_STACK_MSG + 3;
-    static final int STOP_TIMEOUT_MSG = FIRST_ACTIVITY_STACK_MSG + 4;
-    static final int DESTROY_ACTIVITIES_MSG = FIRST_ACTIVITY_STACK_MSG + 5;
-    static final int TRANSLUCENT_TIMEOUT_MSG = FIRST_ACTIVITY_STACK_MSG + 6;
+    private static final int PAUSE_TIMEOUT_MSG = FIRST_ACTIVITY_STACK_MSG + 1;
+    private static final int DESTROY_TIMEOUT_MSG = FIRST_ACTIVITY_STACK_MSG + 2;
+    private static final int LAUNCH_TICK_MSG = FIRST_ACTIVITY_STACK_MSG + 3;
+    private static final int STOP_TIMEOUT_MSG = FIRST_ACTIVITY_STACK_MSG + 4;
+    private static final int DESTROY_ACTIVITIES_MSG = FIRST_ACTIVITY_STACK_MSG + 5;
+    private static final int TRANSLUCENT_TIMEOUT_MSG = FIRST_ACTIVITY_STACK_MSG + 6;
 
     // TODO: remove after unification.
     TaskStack mTaskStack;
@@ -431,7 +415,7 @@
         }
     }
 
-    final Handler mHandler;
+    private final Handler mHandler;
 
     private class ActivityStackHandler extends Handler {
 
@@ -468,7 +452,9 @@
                     // so we need to be conservative and assume it isn't.
                     Slog.w(TAG, "Activity destroy timeout for " + r);
                     synchronized (mService.mGlobalLock) {
-                        activityDestroyedLocked(r != null ? r.appToken : null, "destroyTimeout");
+                        if (r != null) {
+                            r.destroyed("destroyTimeout");
+                        }
                     }
                 } break;
                 case STOP_TIMEOUT_MSG: {
@@ -1215,12 +1201,17 @@
         return display != null && display.isSingleTaskInstance();
     }
 
-    final void removeActivitiesFromLRUListLocked(TaskRecord task) {
+    private void removeActivitiesFromLRUList(TaskRecord task) {
         for (ActivityRecord r : task.mActivities) {
             mLRUActivities.remove(r);
         }
     }
 
+    /** @return {@code true} if LRU list contained the specified activity. */
+    final boolean removeActivityFromLRUList(ActivityRecord activity) {
+        return mLRUActivities.remove(activity);
+    }
+
     final boolean updateLRUListLocked(ActivityRecord r) {
         final boolean hadit = mLRUActivities.remove(r);
         mLRUActivities.add(r);
@@ -1617,18 +1608,6 @@
     }
 
     /**
-     * Schedule a pause timeout in case the app doesn't respond. We don't give it much time because
-     * this directly impacts the responsiveness seen by the user.
-     */
-    private void schedulePauseTimeout(ActivityRecord r) {
-        final Message msg = mHandler.obtainMessage(PAUSE_TIMEOUT_MSG);
-        msg.obj = r;
-        r.pauseTime = SystemClock.uptimeMillis();
-        mHandler.sendMessageDelayed(msg, PAUSE_TIMEOUT);
-        if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Waiting for pause to complete...");
-    }
-
-    /**
      * Start pausing the currently resumed activity.  It is an error to call this if there
      * is already an activity being paused or there is no resumed activity.
      *
@@ -1728,7 +1707,7 @@
                 return false;
 
             } else {
-                schedulePauseTimeout(prev);
+                schedulePauseTimeoutForActivity(prev);
                 return true;
             }
 
@@ -1771,8 +1750,7 @@
                     if (r.finishing) {
                         if (DEBUG_PAUSE) Slog.v(TAG,
                                 "Executing finish of failed to pause activity: " + r);
-                        r.finishCurrentActivityLocked(FINISH_AFTER_VISIBLE, false,
-                                "activityPausedLocked");
+                        r.completeFinishing("activityPausedLocked");
                     }
                 }
             }
@@ -1790,8 +1768,7 @@
             prev.setState(PAUSED, "completePausedLocked");
             if (prev.finishing) {
                 if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Executing finish of activity: " + prev);
-                prev = prev.finishCurrentActivityLocked(FINISH_AFTER_VISIBLE, false /* oomAdj */,
-                        "completePausedLocked");
+                prev = prev.completeFinishing("completePausedLocked");
             } else if (prev.hasProcess()) {
                 if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Enqueue pending stop if needed: " + prev
                         + " wasStopping=" + wasStopping + " visible=" + prev.visible);
@@ -1810,7 +1787,7 @@
                     prev.setDeferHidingClient(false);
                     // If we were visible then resumeTopActivities will release resources before
                     // stopping.
-                    addToStopping(prev, true /* scheduleIdle */, false /* idleDelayed */,
+                    prev.addToStopping(true /* scheduleIdle */, false /* idleDelayed */,
                             "completePauseLocked");
                 }
             } else {
@@ -1872,32 +1849,6 @@
         mRootActivityContainer.ensureActivitiesVisible(resuming, 0, !PRESERVE_WINDOWS);
     }
 
-    void addToStopping(ActivityRecord r, boolean scheduleIdle, boolean idleDelayed, String reason) {
-        if (!mStackSupervisor.mStoppingActivities.contains(r)) {
-            EventLog.writeEvent(EventLogTags.AM_ADD_TO_STOPPING, r.mUserId,
-                    System.identityHashCode(r), r.shortComponentName, reason);
-            mStackSupervisor.mStoppingActivities.add(r);
-        }
-
-        // If we already have a few activities waiting to stop, then give up
-        // on things going idle and start clearing them out. Or if r is the
-        // last of activity of the last task the stack will be empty and must
-        // be cleared immediately.
-        boolean forceIdle = mStackSupervisor.mStoppingActivities.size() > MAX_STOPPING_TO_FORCE
-                || (r.isRootOfTask() && mTaskHistory.size() <= 1);
-        if (scheduleIdle || forceIdle) {
-            if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Scheduling idle now: forceIdle="
-                    + forceIdle + "immediate=" + !idleDelayed);
-            if (!idleDelayed) {
-                mStackSupervisor.scheduleIdleLocked();
-            } else {
-                mStackSupervisor.scheduleIdleTimeoutLocked(r);
-            }
-        } else {
-            checkReadyForSleep();
-        }
-    }
-
     /**
      * Returns true if the stack is translucent and can have other contents visible behind it if
      * needed. A stack is considered translucent if it don't contain a visible or
@@ -2207,7 +2158,7 @@
                                 + " stackShouldBeVisible=" + stackShouldBeVisible
                                 + " behindFullscreenActivity=" + behindFullscreenActivity
                                 + " mLaunchTaskBehind=" + r.mLaunchTaskBehind);
-                        makeInvisible(r);
+                        r.makeInvisible();
                     }
                 }
                 final int windowingMode = getWindowingMode();
@@ -2384,63 +2335,6 @@
         return false;
     }
 
-    // TODO: Should probably be moved into ActivityRecord.
-    private void makeInvisible(ActivityRecord r) {
-        if (!r.visible) {
-            if (DEBUG_VISIBILITY) Slog.v(TAG_VISIBILITY, "Already invisible: " + r);
-            return;
-        }
-        // Now for any activities that aren't visible to the user, make sure they no longer are
-        // keeping the screen frozen.
-        if (DEBUG_VISIBILITY) Slog.v(TAG_VISIBILITY, "Making invisible: " + r + " " + r.getState());
-        try {
-            final boolean canEnterPictureInPicture = r.checkEnterPictureInPictureState(
-                    "makeInvisible", true /* beforeStopping */);
-            // Defer telling the client it is hidden if it can enter Pip and isn't current paused,
-            // stopped or stopping. This gives it a chance to enter Pip in onPause().
-            // TODO: There is still a question surrounding activities in multi-window mode that want
-            // to enter Pip after they are paused, but are still visible. I they should be okay to
-            // enter Pip in those cases, but not "auto-Pip" which is what this condition covers and
-            // the current contract for "auto-Pip" is that the app should enter it before onPause
-            // returns. Just need to confirm this reasoning makes sense.
-            final boolean deferHidingClient = canEnterPictureInPicture
-                    && !r.isState(STOPPING, STOPPED, PAUSED);
-            r.setDeferHidingClient(deferHidingClient);
-            r.setVisible(false);
-
-            switch (r.getState()) {
-                case STOPPING:
-                case STOPPED:
-                    if (r.attachedToProcess()) {
-                        if (DEBUG_VISIBILITY) Slog.v(TAG_VISIBILITY,
-                                "Scheduling invisibility: " + r);
-                        mService.getLifecycleManager().scheduleTransaction(r.app.getThread(),
-                                r.appToken, WindowVisibilityItem.obtain(false /* showWindow */));
-                    }
-
-                    // Reset the flag indicating that an app can enter picture-in-picture once the
-                    // activity is hidden
-                    r.supportsEnterPipOnTaskSwitch = false;
-                    break;
-
-                case INITIALIZING:
-                case RESUMED:
-                case PAUSING:
-                case PAUSED:
-                case STARTED:
-                    addToStopping(r, true /* scheduleIdle */,
-                            canEnterPictureInPicture /* idleDelayed */, "makeInvisible");
-                    break;
-
-                default:
-                    break;
-            }
-        } catch (Exception e) {
-            // Just skip on any failure; we'll make it visible when it next restarts.
-            Slog.w(TAG, "Exception thrown making hidden: " + r.intent.getComponent(), e);
-        }
-    }
-
     private boolean updateBehindFullscreen(boolean stackInvisible, boolean behindFullscreenActivity,
             ActivityRecord r) {
         if (r.fullscreen) {
@@ -2492,7 +2386,7 @@
             mHandler.removeMessages(TRANSLUCENT_TIMEOUT_MSG);
 
             if (waitingActivity != null) {
-                mWindowManager.setWindowOpaque(waitingActivity.appToken, false);
+                mWindowManager.setWindowOpaqueLocked(waitingActivity.appToken, false);
                 if (waitingActivity.attachedToProcess()) {
                     try {
                         waitingActivity.app.getThread().scheduleTranslucentConversionComplete(
@@ -2782,8 +2676,7 @@
                 !mLastNoHistoryActivity.finishing) {
             if (DEBUG_STATES) Slog.d(TAG_STATES,
                     "no-history finish of " + mLastNoHistoryActivity + " on new resume");
-            mLastNoHistoryActivity.finishActivityLocked(Activity.RESULT_CANCELED,
-                    null /* resultData */, "resume-no-history", false /* oomAdj */);
+            mLastNoHistoryActivity.finishIfPossible("resume-no-history", false /* oomAdj */);
             mLastNoHistoryActivity = null;
         }
 
@@ -2813,7 +2706,7 @@
         // Launching this app's activity, make sure the app is no longer
         // considered stopped.
         try {
-            AppGlobals.getPackageManager().setPackageStoppedState(
+            mService.getPackageManager().setPackageStoppedState(
                     next.packageName, false, next.mUserId); /* TODO: Verify if correct userid */
         } catch (RemoteException e1) {
         } catch (IllegalArgumentException e) {
@@ -3018,8 +2911,7 @@
                 // If any exception gets thrown, toss away this
                 // activity and try the next one.
                 Slog.w(TAG, "Exception thrown during resume of " + next, e);
-                next.finishActivityLocked(Activity.RESULT_CANCELED, null /* resultData */,
-                        "resume-exception", true /* oomAdj */);
+                next.finishIfPossible("resume-exception", true /* oomAdj */);
                 return true;
             }
         } else {
@@ -3322,6 +3214,7 @@
         // to activities in the same chain will depend on what the end activity of the chain needs.
         int replyChainEnd = -1;
         boolean canMoveOptions = true;
+        int numTasksCreated = 0;
 
         // We only do this for activities that are not the root of the task (since if we finish
         // the root, we may no longer have the task!).
@@ -3386,6 +3279,7 @@
                             target.info, null /* intent */, null /* voiceSession */,
                             null /* voiceInteractor */, false /* toTop */);
                     targetTask.affinityIntent = target.intent;
+                    numTasksCreated++;
                     if (DEBUG_TASKS) Slog.v(TAG_TASKS, "Start pushing activity " + target
                             + " out to new task " + targetTask);
                 }
@@ -3414,6 +3308,7 @@
                 }
 
                 positionChildWindowContainerAtBottom(targetTask);
+                mStackSupervisor.mRecentTasks.add(targetTask);
                 replyChainEnd = -1;
             } else if (forceReset || finishOnTaskLaunch || clearWhenTaskReset) {
                 // If the activity should just be removed -- either
@@ -3446,8 +3341,8 @@
                     }
                     if (DEBUG_TASKS) Slog.w(TAG_TASKS,
                             "resetTaskIntendedTask: calling finishActivity on " + p);
-                    if (p.finishActivityLocked(Activity.RESULT_CANCELED, null /* resultData */,
-                            "reset-task", false /* oomAdj */) == FINISH_RESULT_REMOVED) {
+                    if (p.finishIfPossible("reset-task", false /* oomAdj */)
+                            == FINISH_RESULT_REMOVED) {
                         end--;
                         srcPos--;
                     }
@@ -3461,6 +3356,27 @@
             }
         }
 
+        // Create target stack for the newly created tasks if necessary
+        if (numTasksCreated > 0) {
+            ActivityDisplay display = getDisplay();
+            final boolean singleTaskInstanceDisplay = display.isSingleTaskInstance();
+            if (singleTaskInstanceDisplay) {
+                display = mRootActivityContainer.getDefaultDisplay();
+            }
+
+            if (singleTaskInstanceDisplay || display.alwaysCreateStack(getWindowingMode(),
+                    getActivityType())) {
+                for (int index = numTasksCreated - 1; index >= 0; index--) {
+                    final TaskRecord targetTask = mTaskHistory.get(index);
+                    final ActivityStack targetStack = display.getOrCreateStack(getWindowingMode(),
+                            getActivityType(), false /* onTop */);
+                    targetTask.reparent(targetStack, false /* toTop */,
+                            REPARENT_LEAVE_STACK_IN_PLACE, false /* animate */,
+                            true /* deferResume */, "resetTargetTask");
+                }
+            }
+        }
+
         return topOptions;
     }
 
@@ -3531,8 +3447,7 @@
                         if (p.finishing) {
                             continue;
                         }
-                        p.finishActivityLocked(Activity.RESULT_CANCELED, null /* resultData */,
-                                "move-affinity", false /* oomAdj */);
+                        p.finishIfPossible("move-affinity", false /* oomAdj */);
                     }
                 } else {
                     if (taskInsertionPoint < 0) {
@@ -3566,8 +3481,7 @@
                         if (targetNdx > 0) {
                             final ActivityRecord p = taskActivities.get(targetNdx - 1);
                             if (p.intent.getComponent().equals(target.intent.getComponent())) {
-                                p.finishActivityLocked(Activity.RESULT_CANCELED,
-                                        null /* resultData */, "replace", false /* oomAdj */);
+                                p.finishIfPossible("replace", false /* oomAdj */);
                             }
                         }
                     }
@@ -3579,6 +3493,16 @@
         return taskInsertionPoint;
     }
 
+    /**
+     * Reset the task by reparenting the activities that have same affinity to the task or
+     * reparenting the activities that have different affinityies out of the task, while these
+     * activities allow task reparenting.
+     *
+     * @param taskTop     Top activity of the task might be reset.
+     * @param newActivity The activity that going to be started.
+     * @return The non-finishing top activity of the task after reset or the original task top
+     *         activity if all activities within the task are finishing.
+     */
     final ActivityRecord resetTaskIfNeededLocked(ActivityRecord taskTop,
             ActivityRecord newActivity) {
         final boolean forceReset =
@@ -3609,9 +3533,10 @@
 
         int taskNdx = mTaskHistory.indexOf(task);
         if (taskNdx >= 0) {
-            do {
-                taskTop = mTaskHistory.get(taskNdx--).getTopActivity();
-            } while (taskTop == null && taskNdx >= 0);
+            ActivityRecord newTop = mTaskHistory.get(taskNdx).getTopActivity();
+            if (newTop != null) {
+                taskTop = newTop;
+            }
         }
 
         if (topOptions != null) {
@@ -3627,49 +3552,6 @@
         return taskTop;
     }
 
-    void sendActivityResultLocked(int callingUid, ActivityRecord r,
-            String resultWho, int requestCode, int resultCode, Intent data) {
-
-        if (callingUid > 0) {
-            mService.mUgmInternal.grantUriPermissionFromIntent(callingUid, r.packageName,
-                    data, r.getUriPermissionsLocked(), r.mUserId);
-        }
-
-        if (DEBUG_RESULTS) Slog.v(TAG, "Send activity result to " + r
-                + " : who=" + resultWho + " req=" + requestCode
-                + " res=" + resultCode + " data=" + data);
-        if (mResumedActivity == r && r.attachedToProcess()) {
-            try {
-                ArrayList<ResultInfo> list = new ArrayList<ResultInfo>();
-                list.add(new ResultInfo(resultWho, requestCode,
-                        resultCode, data));
-                mService.getLifecycleManager().scheduleTransaction(r.app.getThread(), r.appToken,
-                        ActivityResultItem.obtain(list));
-                return;
-            } catch (Exception e) {
-                Slog.w(TAG, "Exception thrown sending result to " + r, e);
-            }
-        }
-
-        r.addResultLocked(null, resultWho, requestCode, resultCode, data);
-    }
-
-    /** Returns true if the task is one of the task finishing on-top of the top running task. */
-    private boolean isATopFinishingTask(TaskRecord task) {
-        for (int i = mTaskHistory.size() - 1; i >= 0; --i) {
-            final TaskRecord current = mTaskHistory.get(i);
-            final ActivityRecord r = current.topRunningActivityLocked();
-            if (r != null) {
-                // We got a top running activity, so there isn't a top finishing task...
-                return false;
-            }
-            if (current == task) {
-                return true;
-            }
-        }
-        return false;
-    }
-
     void adjustFocusedActivityStack(ActivityRecord r, String reason) {
         if (!mRootActivityContainer.isTopDisplayFocusedStack(this) ||
                 ((mResumedActivity != r) && (mResumedActivity != null))) {
@@ -3748,64 +3630,6 @@
         return stack;
     }
 
-    final void stopActivityLocked(ActivityRecord r) {
-        if (DEBUG_SWITCH) Slog.d(TAG_SWITCH, "Stopping: " + r);
-        if ((r.intent.getFlags()&Intent.FLAG_ACTIVITY_NO_HISTORY) != 0
-                || (r.info.flags&ActivityInfo.FLAG_NO_HISTORY) != 0) {
-            if (!r.finishing) {
-                if (!shouldSleepActivities()) {
-                    if (DEBUG_STATES) Slog.d(TAG_STATES, "no-history finish of " + r);
-                    if (r.finishActivityLocked(Activity.RESULT_CANCELED, null /* resultData */,
-                            "stop-no-history", false /* oomAdj */) != FINISH_RESULT_CANCELLED) {
-                        // {@link adjustFocusedActivityStack} must have been already called.
-                        r.resumeKeyDispatchingLocked();
-                        return;
-                    }
-                } else {
-                    if (DEBUG_STATES) Slog.d(TAG_STATES, "Not finishing noHistory " + r
-                            + " on stop because we're just sleeping");
-                }
-            }
-        }
-
-        if (r.attachedToProcess()) {
-            adjustFocusedActivityStack(r, "stopActivity");
-            r.resumeKeyDispatchingLocked();
-            try {
-                r.stopped = false;
-                if (DEBUG_STATES) Slog.v(TAG_STATES,
-                        "Moving to STOPPING: " + r + " (stop requested)");
-                r.setState(STOPPING, "stopActivityLocked");
-                if (DEBUG_VISIBILITY) Slog.v(TAG_VISIBILITY,
-                        "Stopping visible=" + r.visible + " for " + r);
-                if (!r.visible) {
-                    r.setVisible(false);
-                }
-                EventLogTags.writeAmStopActivity(
-                        r.mUserId, System.identityHashCode(r), r.shortComponentName);
-                mService.getLifecycleManager().scheduleTransaction(r.app.getThread(), r.appToken,
-                        StopActivityItem.obtain(r.visible, r.configChangeFlags));
-                if (shouldSleepOrShutDownActivities()) {
-                    r.setSleeping(true);
-                }
-                Message msg = mHandler.obtainMessage(STOP_TIMEOUT_MSG, r);
-                mHandler.sendMessageDelayed(msg, STOP_TIMEOUT);
-            } catch (Exception e) {
-                // Maybe just ignore exceptions here...  if the process
-                // has crashed, our death notification will clean things
-                // up.
-                Slog.w(TAG, "Exception thrown during pause", e);
-                // Just in case, assume it to be stopped.
-                r.stopped = true;
-                if (DEBUG_STATES) Slog.v(TAG_STATES, "Stop failed; moving to STOPPED: " + r);
-                r.setState(STOPPED, "stopActivityLocked");
-                if (r.deferRelaunchUntilPaused) {
-                    destroyActivityLocked(r, true, "stop-except");
-                }
-            }
-        }
-    }
-
     /** Finish all activities that were started for result from the specified activity. */
     final void finishSubActivityLocked(ActivityRecord self, String resultWho, int requestCode) {
         for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
@@ -3815,8 +3639,7 @@
                 if (r.resultTo == self && r.requestCode == requestCode) {
                     if ((r.resultWho == null && resultWho == null) ||
                         (r.resultWho != null && r.resultWho.equals(resultWho))) {
-                        r.finishActivityLocked(Activity.RESULT_CANCELED, null /* resultData */,
-                                "request-sub", false /* oomAdj */);
+                        r.finishIfPossible("request-sub", false /* oomAdj */);
                     }
                 }
             }
@@ -3846,8 +3669,7 @@
         int activityNdx = task.mActivities.indexOf(r);
         getDisplay().mDisplayContent.prepareAppTransition(
                 TRANSIT_CRASHING_ACTIVITY_CLOSE, false /* alwaysKeepCurrent */);
-        r.finishActivityLocked(Activity.RESULT_CANCELED, null /* resultData */, reason,
-                false /* oomAdj */);
+        r.finishIfPossible(reason, false /* oomAdj */);
         finishedTask = task;
         // Also terminate any activities below it that aren't yet
         // stopped, to avoid a situation where one will get
@@ -3868,8 +3690,7 @@
                 if (!r.isActivityTypeHome() || mService.mHomeProcess != r.app) {
                     Slog.w(TAG, "  Force finishing activity "
                             + r.intent.getComponent().flattenToShortString());
-                    r.finishActivityLocked(Activity.RESULT_CANCELED, null /* resultData */, reason,
-                            false /* oomAdj */);
+                    r.finishIfPossible(reason, false /* oomAdj */);
                 }
             }
         }
@@ -3885,8 +3706,7 @@
                 for (int activityNdx = tr.mActivities.size() - 1; activityNdx >= 0; --activityNdx) {
                     ActivityRecord r = tr.mActivities.get(activityNdx);
                     if (!r.finishing) {
-                        r.finishActivityLocked(Activity.RESULT_CANCELED, null /* resultData */,
-                                "finish-voice", false /* oomAdj */);
+                        r.finishIfPossible("finish-voice", false /* oomAdj */);
                         didOne = true;
                     }
                 }
@@ -3924,8 +3744,7 @@
                 final ActivityRecord r = activities.get(activityNdx);
                 noActivitiesInStack = false;
                 Slog.d(TAG, "finishAllActivitiesImmediatelyLocked: finishing " + r);
-                r.finishCurrentActivityLocked(FINISH_IMMEDIATELY, false /* oomAdj */,
-                        "finishAllActivitiesImmediatelyLocked");
+                r.destroyIfPossible("finishAllActivitiesImmediatelyLocked");
             }
         }
         if (noActivitiesInStack) {
@@ -4029,7 +3848,8 @@
         final long origId = Binder.clearCallingIdentity();
         for (int i = start; i > finishTo; i--) {
             final ActivityRecord r = activities.get(i);
-            r.finishActivityLocked(resultCode, resultData, "navigate-up", true /* oomAdj */);
+            r.finishIfPossible(resultCode, resultData, "navigate-up", true /* oomAdj */,
+                    !PAUSE_IMMEDIATELY);
             // Only return the supplied result for the first activity finished
             resultCode = Activity.RESULT_CANCELED;
             resultData = null;
@@ -4066,8 +3886,8 @@
                 } catch (RemoteException e) {
                     foundParentInTask = false;
                 }
-                parent.finishActivityLocked(resultCode, resultData, "navigate-top",
-                        true /* oomAdj */);
+                parent.finishIfPossible(resultCode, resultData, "navigate-top",
+                        true /* oomAdj */, !PAUSE_IMMEDIATELY);
             }
         }
         Binder.restoreCallingIdentity(origId);
@@ -4079,7 +3899,7 @@
      * an activity moves away from the stack.
      */
     void onActivityRemovedFromStack(ActivityRecord r) {
-        removeTimeoutsForActivityLocked(r);
+        removeTimeoutsForActivity(r);
 
         if (mResumedActivity != null && mResumedActivity == r) {
             setResumedActivity(null, "onActivityRemovedFromStack");
@@ -4095,132 +3915,64 @@
         }
     }
 
-    /**
-     * Perform the common clean-up of an activity record.  This is called both
-     * as part of destroyActivityLocked() (when destroying the client-side
-     * representation) and cleaning things up as a result of its hosting
-     * processing going away, in which case there is no remaining client-side
-     * state to destroy so only the cleanup here is needed.
-     *
-     * Note: Call before #removeActivityFromHistoryLocked.
-     */
-    private void cleanUpActivityLocked(ActivityRecord r, boolean cleanServices, boolean setState) {
-        onActivityRemovedFromStack(r);
-
-        r.deferRelaunchUntilPaused = false;
-        r.frozenBeforeDestroy = false;
-
-        if (setState) {
-            if (DEBUG_STATES) Slog.v(TAG_STATES, "Moving to DESTROYED: " + r + " (cleaning up)");
-            r.setState(DESTROYED, "cleanupActivityLocked");
-            if (DEBUG_APP) Slog.v(TAG_APP, "Clearing app during cleanUp for activity " + r);
-            r.app = null;
-        }
-
-        // Inform supervisor the activity has been removed.
-        mStackSupervisor.cleanupActivity(r);
-
-
-        // Remove any pending results.
-        if (r.finishing && r.pendingResults != null) {
-            for (WeakReference<PendingIntentRecord> apr : r.pendingResults) {
-                PendingIntentRecord rec = apr.get();
-                if (rec != null) {
-                    mService.mPendingIntentController.cancelIntentSender(rec, false);
-                }
-            }
-            r.pendingResults = null;
-        }
-
-        if (cleanServices) {
-            cleanUpActivityServicesLocked(r);
-        }
-
-        // Get rid of any pending idle timeouts.
-        removeTimeoutsForActivityLocked(r);
-        // Clean-up activities are no longer relaunching (e.g. app process died). Notify window
-        // manager so it can update its bookkeeping.
-        mWindowManager.notifyAppRelaunchesCleared(r.appToken);
-    }
-
-    private void removeTimeoutsForActivityLocked(ActivityRecord r) {
+    /// HANDLER INTERFACE BEGIN
+    void removeTimeoutsForActivity(ActivityRecord r) {
         mStackSupervisor.removeTimeoutsForActivityLocked(r);
-        mHandler.removeMessages(PAUSE_TIMEOUT_MSG, r);
-        mHandler.removeMessages(STOP_TIMEOUT_MSG, r);
-        mHandler.removeMessages(DESTROY_TIMEOUT_MSG, r);
+        removePauseTimeoutForActivity(r);
+        removeStopTimeoutForActivity(r);
+        removeDestroyTimeoutForActivity(r);
         r.finishLaunchTickingLocked();
     }
 
-    private void removeActivityFromHistoryLocked(ActivityRecord r, String reason) {
-        r.finishActivityResults(Activity.RESULT_CANCELED, null /* resultData */);
-        r.makeFinishingLocked();
-        if (DEBUG_ADD_REMOVE) Slog.i(TAG_ADD_REMOVE,
-                "Removing activity " + r + " from stack callers=" + Debug.getCallers(5));
-
-        r.takeFromHistory();
-        removeTimeoutsForActivityLocked(r);
-        if (DEBUG_STATES) Slog.v(TAG_STATES,
-                "Moving to DESTROYED: " + r + " (removed from history)");
-        r.setState(DESTROYED, "removeActivityFromHistoryLocked");
-        if (DEBUG_APP) Slog.v(TAG_APP, "Clearing app during remove for activity " + r);
-        r.app = null;
-        r.removeWindowContainer();
-        final TaskRecord task = r.getTaskRecord();
-        final boolean lastActivity = task != null ? task.removeActivity(r) : false;
-        // If we are removing the last activity in the task, not including task overlay activities,
-        // then fall through into the block below to remove the entire task itself
-        final boolean onlyHasTaskOverlays = task != null
-                ? task.onlyHasTaskOverlayActivities(false /* excludingFinishing */) : false;
-
-        if (lastActivity || onlyHasTaskOverlays) {
-            if (DEBUG_STACK) {
-                Slog.i(TAG_STACK,
-                        "removeActivityFromHistoryLocked: last activity removed from " + this
-                                + " onlyHasTaskOverlays=" + onlyHasTaskOverlays);
-            }
-
-            // The following block can be executed multiple times if there is more than one overlay.
-            // {@link ActivityStackSupervisor#removeTaskByIdLocked} handles this by reverse lookup
-            // of the task by id and exiting early if not found.
-            if (onlyHasTaskOverlays) {
-                // When destroying a task, tell the supervisor to remove it so that any activity it
-                // has can be cleaned up correctly. This is currently the only place where we remove
-                // a task with the DESTROYING mode, so instead of passing the onlyHasTaskOverlays
-                // state into removeTask(), we just clear the task here before the other residual
-                // work.
-                // TODO: If the callers to removeTask() changes such that we have multiple places
-                //       where we are destroying the task, move this back into removeTask()
-                mStackSupervisor.removeTaskByIdLocked(task.taskId, false /* killProcess */,
-                        !REMOVE_FROM_RECENTS, PAUSE_IMMEDIATELY, reason);
-            }
-
-            // We must keep the task around until all activities are destroyed. The following
-            // statement will only execute once since overlays are also considered activities.
-            if (lastActivity) {
-                removeTask(task, reason, REMOVE_TASK_MODE_DESTROYING);
-            }
-        }
-        cleanUpActivityServicesLocked(r);
-        r.removeUriPermissionsLocked();
-    }
-
-    /**
-     * Perform clean-up of service connections in an activity record.
-     */
-    private void cleanUpActivityServicesLocked(ActivityRecord r) {
-        if (r.mServiceConnectionsHolder == null) {
-            return;
-        }
-        // Throw away any services that have been bound by this activity.
-        r.mServiceConnectionsHolder.disconnectActivityFromServices();
-    }
-
-    final void scheduleDestroyActivities(WindowProcessController owner, String reason) {
-        Message msg = mHandler.obtainMessage(DESTROY_ACTIVITIES_MSG);
+    void scheduleDestroyActivities(WindowProcessController owner, String reason) {
+        final Message msg = mHandler.obtainMessage(DESTROY_ACTIVITIES_MSG);
         msg.obj = new ScheduleDestroyArgs(owner, reason);
         mHandler.sendMessage(msg);
     }
 
+    void scheduleDestroyTimeoutForActivity(ActivityRecord r) {
+        final Message msg = mHandler.obtainMessage(DESTROY_TIMEOUT_MSG, r);
+        mHandler.sendMessageDelayed(msg, DESTROY_TIMEOUT);
+    }
+
+    void removeDestroyTimeoutForActivity(ActivityRecord r) {
+        mHandler.removeMessages(DESTROY_TIMEOUT_MSG, r);
+    }
+
+    void scheduleStopTimeoutForActivity(ActivityRecord r) {
+        final Message msg = mHandler.obtainMessage(STOP_TIMEOUT_MSG, r);
+        mHandler.sendMessageDelayed(msg, STOP_TIMEOUT);
+    }
+
+    void removeStopTimeoutForActivity(ActivityRecord r) {
+        mHandler.removeMessages(STOP_TIMEOUT_MSG, r);
+    }
+
+    /**
+     * Schedule a pause timeout in case the app doesn't respond. We don't give it much time because
+     * this directly impacts the responsiveness seen by the user.
+     */
+    private void schedulePauseTimeoutForActivity(ActivityRecord r) {
+        final Message msg = mHandler.obtainMessage(PAUSE_TIMEOUT_MSG, r);
+        r.pauseTime = SystemClock.uptimeMillis();
+        mHandler.sendMessageDelayed(msg, PAUSE_TIMEOUT);
+        if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Waiting for pause to complete...");
+    }
+
+    void removePauseTimeoutForActivity(ActivityRecord r) {
+        mHandler.removeMessages(PAUSE_TIMEOUT_MSG, r);
+    }
+
+    void scheduleLaunchTickForActivity(ActivityRecord r) {
+        final Message msg = mHandler.obtainMessage(LAUNCH_TICK_MSG, r);
+        mHandler.sendMessageDelayed(msg, LAUNCH_TICK);
+    }
+
+    void removeLaunchTickMessages() {
+        mHandler.removeMessages(LAUNCH_TICK_MSG);
+    }
+    /// HANDLER INTERFACE END
+
     private void destroyActivitiesLocked(WindowProcessController owner, String reason) {
         boolean lastIsOpaque = false;
         boolean activityRemoved = false;
@@ -4245,7 +3997,7 @@
                             + " in state " + r.getState()
                             + " resumed=" + mResumedActivity
                             + " pausing=" + mPausingActivity + " for reason " + reason);
-                    if (destroyActivityLocked(r, true, reason)) {
+                    if (r.destroyImmediately(true /* removeFromTask */, reason)) {
                         activityRemoved = true;
                     }
                 }
@@ -4256,16 +4008,6 @@
         }
     }
 
-    final boolean safelyDestroyActivityLocked(ActivityRecord r, String reason) {
-        if (r.isDestroyable()) {
-            if (DEBUG_SWITCH) Slog.v(TAG_SWITCH,
-                    "Destroying " + r + " in state " + r.getState() + " resumed=" + mResumedActivity
-                    + " pausing=" + mPausingActivity + " for reason " + reason);
-            return destroyActivityLocked(r, true, reason);
-        }
-        return false;
-    }
-
     final int releaseSomeActivitiesLocked(WindowProcessController app, ArraySet<TaskRecord> tasks,
             String reason) {
         // Iterate over tasks starting at the back (oldest) first.
@@ -4289,7 +4031,7 @@
                     if (DEBUG_RELEASE) Slog.v(TAG_RELEASE, "Destroying " + activity
                             + " in state " + activity.getState() + " resumed=" + mResumedActivity
                             + " pausing=" + mPausingActivity + " for reason " + reason);
-                    destroyActivityLocked(activity, true, reason);
+                    activity.destroyImmediately(true /* removeFromApp */, reason);
                     if (activities.get(actNdx) != activity) {
                         // Was removed from list, back up so we don't miss the next one.
                         actNdx--;
@@ -4311,142 +4053,6 @@
         return numReleased;
     }
 
-    /**
-     * Destroy the current CLIENT SIDE instance of an activity.  This may be
-     * called both when actually finishing an activity, or when performing
-     * a configuration switch where we destroy the current client-side object
-     * but then create a new client-side object for this same HistoryRecord.
-     */
-    final boolean destroyActivityLocked(ActivityRecord r, boolean removeFromApp, String reason) {
-        if (DEBUG_SWITCH || DEBUG_CLEANUP) Slog.v(TAG_SWITCH,
-                "Removing activity from " + reason + ": token=" + r
-                        + ", app=" + (r.hasProcess() ? r.app.mName : "(null)"));
-
-        if (r.isState(DESTROYING, DESTROYED)) {
-            if (DEBUG_STATES) Slog.v(TAG_STATES, "activity " + r + " already destroying."
-                    + "skipping request with reason:" + reason);
-            return false;
-        }
-
-        EventLog.writeEvent(EventLogTags.AM_DESTROY_ACTIVITY,
-                r.mUserId, System.identityHashCode(r),
-                r.getTaskRecord().taskId, r.shortComponentName, reason);
-
-        boolean removedFromHistory = false;
-
-        cleanUpActivityLocked(r, false, false);
-
-        final boolean hadApp = r.hasProcess();
-
-        if (hadApp) {
-            if (removeFromApp) {
-                r.app.removeActivity(r);
-                if (!r.app.hasActivities()) {
-                    mService.clearHeavyWeightProcessIfEquals(r.app);
-                }
-                if (!r.app.hasActivities()) {
-                    // Update any services we are bound to that might care about whether
-                    // their client may have activities.
-                    // No longer have activities, so update LRU list and oom adj.
-                    r.app.updateProcessInfo(true /* updateServiceConnectionActivities */,
-                            false /* activityChange */, true /* updateOomAdj */);
-                }
-            }
-
-            boolean skipDestroy = false;
-
-            try {
-                if (DEBUG_SWITCH) Slog.i(TAG_SWITCH, "Destroying: " + r);
-                mService.getLifecycleManager().scheduleTransaction(r.app.getThread(), r.appToken,
-                        DestroyActivityItem.obtain(r.finishing, r.configChangeFlags));
-            } catch (Exception e) {
-                // We can just ignore exceptions here...  if the process
-                // has crashed, our death notification will clean things
-                // up.
-                //Slog.w(TAG, "Exception thrown during finish", e);
-                if (r.finishing) {
-                    removeActivityFromHistoryLocked(r, reason + " exceptionInScheduleDestroy");
-                    removedFromHistory = true;
-                    skipDestroy = true;
-                }
-            }
-
-            r.nowVisible = false;
-
-            // If the activity is finishing, we need to wait on removing it
-            // from the list to give it a chance to do its cleanup.  During
-            // that time it may make calls back with its token so we need to
-            // be able to find it on the list and so we don't want to remove
-            // it from the list yet.  Otherwise, we can just immediately put
-            // it in the destroyed state since we are not removing it from the
-            // list.
-            if (r.finishing && !skipDestroy) {
-                if (DEBUG_STATES) Slog.v(TAG_STATES, "Moving to DESTROYING: " + r
-                        + " (destroy requested)");
-                r.setState(DESTROYING,
-                        "destroyActivityLocked. finishing and not skipping destroy");
-                Message msg = mHandler.obtainMessage(DESTROY_TIMEOUT_MSG, r);
-                mHandler.sendMessageDelayed(msg, DESTROY_TIMEOUT);
-            } else {
-                if (DEBUG_STATES) Slog.v(TAG_STATES,
-                        "Moving to DESTROYED: " + r + " (destroy skipped)");
-                r.setState(DESTROYED,
-                        "destroyActivityLocked. not finishing or skipping destroy");
-                if (DEBUG_APP) Slog.v(TAG_APP, "Clearing app during destroy for activity " + r);
-                r.app = null;
-            }
-        } else {
-            // remove this record from the history.
-            if (r.finishing) {
-                removeActivityFromHistoryLocked(r, reason + " hadNoApp");
-                removedFromHistory = true;
-            } else {
-                if (DEBUG_STATES) Slog.v(TAG_STATES, "Moving to DESTROYED: " + r + " (no app)");
-                r.setState(DESTROYED, "destroyActivityLocked. not finishing and had no app");
-                if (DEBUG_APP) Slog.v(TAG_APP, "Clearing app during destroy for activity " + r);
-                r.app = null;
-            }
-        }
-
-        r.configChangeFlags = 0;
-
-        if (!mLRUActivities.remove(r) && hadApp) {
-            Slog.w(TAG, "Activity " + r + " being finished, but not in LRU list");
-        }
-
-        return removedFromHistory;
-    }
-
-    final void activityDestroyedLocked(IBinder token, String reason) {
-        final long origId = Binder.clearCallingIdentity();
-        try {
-            activityDestroyedLocked(ActivityRecord.forTokenLocked(token), reason);
-        } finally {
-            Binder.restoreCallingIdentity(origId);
-        }
-    }
-
-    /**
-     * This method is to only be called from the client via binder when the activity is destroyed
-     * AND finished.
-     */
-    final void activityDestroyedLocked(ActivityRecord record, String reason) {
-        if (record != null) {
-            mHandler.removeMessages(DESTROY_TIMEOUT_MSG, record);
-        }
-
-        if (DEBUG_CONTAINERS) Slog.d(TAG_CONTAINERS, "activityDestroyedLocked: r=" + record);
-
-        if (isInStackLocked(record) != null) {
-            if (record.isState(DESTROYING, DESTROYED)) {
-                cleanUpActivityLocked(record, true, false);
-                removeActivityFromHistoryLocked(record, reason);
-            }
-        }
-
-        mRootActivityContainer.resumeFocusedStacksTopActivities();
-    }
-
     private void removeHistoryRecordsForAppLocked(ArrayList<ActivityRecord> list,
             WindowProcessController app, String listName) {
         int i = list.size();
@@ -4459,7 +4065,7 @@
             if (r.app == app) {
                 if (DEBUG_CLEANUP) Slog.v(TAG_CLEANUP, "---> REMOVING this entry!");
                 list.remove(i);
-                removeTimeoutsForActivityLocked(r);
+                removeTimeoutsForActivity(r);
             }
         }
     }
@@ -4554,9 +4160,9 @@
                         // other apps when user transfers focus to the restarted activity.
                         r.nowVisible = r.visible;
                     }
-                    cleanUpActivityLocked(r, true, true);
+                    r.cleanUp(true /* cleanServices */, true /* setState */);
                     if (remove) {
-                        removeActivityFromHistoryLocked(r, "appDied");
+                        r.removeFromHistory("appDied");
                     }
                 }
             }
@@ -4747,16 +4353,6 @@
         return true;
     }
 
-    static void logStartActivity(int tag, ActivityRecord r, TaskRecord task) {
-        final Uri data = r.intent.getData();
-        final String strData = data != null ? data.toSafeString() : null;
-
-        EventLog.writeEvent(tag,
-                r.mUserId, System.identityHashCode(r), task.taskId,
-                r.shortComponentName, r.intent.getAction(),
-                r.intent.getType(), strData, r.intent.getFlags());
-    }
-
     /**
      * Ensures all visible activities at or below the input activity have the right configuration.
      */
@@ -4890,7 +4486,7 @@
             for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) {
                 final ActivityRecord r = activities.get(activityNdx);
                 if ((r.info.flags&ActivityInfo.FLAG_FINISH_ON_CLOSE_SYSTEM_DIALOGS) != 0) {
-                    r.finishActivityLocked(Activity.RESULT_CANCELED, null, "close-sys", true);
+                    r.finishIfPossible("close-sys", true /* oomAdj */);
                 }
             }
         }
@@ -4934,7 +4530,7 @@
                     didSomething = true;
                     Slog.i(TAG, "  Force finishing activity " + r);
                     lastTask = r.getTaskRecord();
-                    r.finishActivityLocked(Activity.RESULT_CANCELED, null, "force-stop", true);
+                    r.finishIfPossible("force-stop", true);
                 }
             }
         }
@@ -4988,8 +4584,7 @@
             final ArrayList<ActivityRecord> activities = mTaskHistory.get(top).mActivities;
             int activityTop = activities.size() - 1;
             if (activityTop >= 0) {
-                activities.get(activityTop).finishActivityLocked(Activity.RESULT_CANCELED, null,
-                        "unhandled-back", true);
+                activities.get(activityTop).finishIfPossible("unhandled-back", true /* oomAdj */);
             }
         }
     }
@@ -5025,8 +4620,7 @@
                     r.app = null;
                     getDisplay().mDisplayContent.prepareAppTransition(
                             TRANSIT_CRASHING_ACTIVITY_CLOSE, false /* alwaysKeepCurrent */);
-                    r.finishCurrentActivityLocked(FINISH_IMMEDIATELY, false /* oomAdj */,
-                            "handleAppCrashedLocked");
+                    r.destroyIfPossible("handleAppCrashedLocked");
                 }
             }
         }
@@ -5167,7 +4761,7 @@
             EventLog.writeEvent(EventLogTags.AM_REMOVE_TASK, task.taskId, getStackId());
         }
 
-        removeActivitiesFromLRUListLocked(task);
+        removeActivitiesFromLRUList(task);
         updateTaskMovement(task, true);
 
         if (mode == REMOVE_TASK_MODE_DESTROYING) {
@@ -5350,7 +4944,7 @@
         // If the activity was previously pausing, then ensure we transfer that as well
         if (setPause) {
             mPausingActivity = r;
-            schedulePauseTimeout(r);
+            schedulePauseTimeoutForActivity(r);
         }
         // Move the stack in which we are placing the activity to the front.
         moveToFront(reason);
diff --git a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
index 1c56a10..a06b9ce 100644
--- a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
@@ -42,6 +42,7 @@
 import static android.content.pm.PackageManager.PERMISSION_GRANTED;
 import static android.graphics.Rect.copyOrNull;
 import static android.os.PowerManager.PARTIAL_WAKE_LOCK;
+import static android.os.Process.INVALID_UID;
 import static android.os.Process.SYSTEM_UID;
 import static android.os.Trace.TRACE_TAG_ACTIVITY_MANAGER;
 import static android.view.Display.DEFAULT_DISPLAY;
@@ -250,7 +251,7 @@
     RecentTasks mRecentTasks;
 
     /** Helper class to abstract out logic for fetching the set of currently running tasks */
-    RunningTasks mRunningTasks;
+    private RunningTasks mRunningTasks;
 
     final ActivityStackSupervisorHandler mHandler;
     final Looper mLooper;
@@ -444,7 +445,7 @@
         }
 
         mInitialized = true;
-        mRunningTasks = createRunningTasks();
+        setRunningTasks(new RunningTasks());
 
         mActivityMetricsLogger = new ActivityMetricsLogger(this, mService.mContext,
                 mHandler.getLooper());
@@ -485,13 +486,20 @@
     }
 
     void setRecentTasks(RecentTasks recentTasks) {
+        if (mRecentTasks != null) {
+            mRecentTasks.unregisterCallback(this);
+        }
         mRecentTasks = recentTasks;
         mRecentTasks.registerCallback(this);
     }
 
     @VisibleForTesting
-    RunningTasks createRunningTasks() {
-        return new RunningTasks();
+    void setRunningTasks(RunningTasks runningTasks) {
+        mRunningTasks = runningTasks;
+    }
+
+    RunningTasks getRunningTasks() {
+        return mRunningTasks;
     }
 
     /**
@@ -890,8 +898,7 @@
                     Slog.e(TAG, "Second failure launching "
                             + r.intent.getComponent().flattenToShortString() + ", giving up", e);
                     proc.appDied();
-                    r.finishActivityLocked(Activity.RESULT_CANCELED, null /* resultData */,
-                            "2nd-crash", false /* oomAdj */);
+                    r.finishIfPossible("2nd-crash", false /* oomAdj */);
                     return false;
                 }
 
@@ -1022,9 +1029,8 @@
         if (componentRestriction == ACTIVITY_RESTRICTION_PERMISSION
                 || actionRestriction == ACTIVITY_RESTRICTION_PERMISSION) {
             if (resultRecord != null) {
-                resultStack.sendActivityResultLocked(-1,
-                        resultRecord, resultWho, requestCode,
-                        Activity.RESULT_CANCELED, null);
+                resultRecord.sendResult(INVALID_UID, resultWho, requestCode,
+                        Activity.RESULT_CANCELED, null /* data */);
             }
             final String msg;
             if (actionRestriction == ACTIVITY_RESTRICTION_PERMISSION) {
@@ -1340,10 +1346,10 @@
             final ActivityStack stack = r.getActivityStack();
             if (stack != null) {
                 if (r.finishing) {
-                    r.finishCurrentActivityLocked(ActivityRecord.FINISH_IMMEDIATELY,
-                            false /* oomAdj */, "activityIdleInternalLocked");
+                    // TODO(b/137329632): Wait for idle of the right activity, not just any.
+                    r.destroyIfPossible("activityIdleInternalLocked");
                 } else {
-                    stack.stopActivityLocked(r);
+                    r.stopIfPossible();
                 }
             }
         }
@@ -1354,7 +1360,7 @@
             r = finishes.get(i);
             final ActivityStack stack = r.getActivityStack();
             if (stack != null) {
-                activityRemoved |= stack.destroyActivityLocked(r, true, "finish-idle");
+                activityRemoved |= r.destroyImmediately(true /* removeFromApp */, "finish-idle");
             }
         }
 
@@ -2735,7 +2741,7 @@
         mWindowManager.deferSurfaceLayout();
         try {
             if (windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) {
-                mWindowManager.setDockedStackCreateState(
+                mWindowManager.setDockedStackCreateStateLocked(
                         activityOptions.getSplitScreenCreateMode(), null /* initialBounds */);
 
                 // Defer updating the stack in which recents is until the app transition is done, to
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index 9ed93c4..48bc963 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -55,6 +55,7 @@
 import static android.content.pm.ActivityInfo.LAUNCH_SINGLE_TASK;
 import static android.content.pm.ActivityInfo.LAUNCH_SINGLE_TOP;
 import static android.content.pm.PackageManager.PERMISSION_GRANTED;
+import static android.os.Process.INVALID_UID;
 import static android.view.Display.DEFAULT_DISPLAY;
 import static android.view.Display.INVALID_DISPLAY;
 
@@ -752,8 +753,8 @@
 
         if (err != START_SUCCESS) {
             if (resultRecord != null) {
-                resultStack.sendActivityResultLocked(
-                        -1, resultRecord, resultWho, requestCode, RESULT_CANCELED, null);
+                resultRecord.sendResult(INVALID_UID, resultWho, requestCode, RESULT_CANCELED,
+                        null /* data */);
             }
             SafeActivityOptions.abort(options);
             return err;
@@ -817,8 +818,8 @@
 
         if (abort) {
             if (resultRecord != null) {
-                resultStack.sendActivityResultLocked(-1, resultRecord, resultWho, requestCode,
-                        RESULT_CANCELED, null);
+                resultRecord.sendResult(INVALID_UID, resultWho, requestCode, RESULT_CANCELED,
+                        null /* data */);
             }
             // We pretend to the caller that it was really started, but
             // they will just get a cancel result.
@@ -1426,8 +1427,7 @@
                 // performing operations without a window container.
                 final ActivityStack stack = mStartActivity.getActivityStack();
                 if (stack != null) {
-                    mStartActivity.finishActivityLocked(RESULT_CANCELED,
-                            null /* intentResultData */, "startActivity", true /* oomAdj */);
+                    mStartActivity.finishIfPossible("startActivity", true /* oomAdj */);
                 }
 
                 // Stack should also be detached from display and be removed if it's empty.
@@ -1451,18 +1451,17 @@
      * TODO(b/131748165): Refactor the logic so we don't need to call this method everywhere.
      */
     private boolean handleBackgroundActivityAbort(ActivityRecord r) {
-        // TODO(b/131747138): Remove toast and refactor related code in Q release.
-        boolean abort = !mService.isBackgroundActivityStartsEnabled();
+        // TODO(b/131747138): Remove toast and refactor related code in R release.
+        final boolean abort = !mService.isBackgroundActivityStartsEnabled();
         if (!abort) {
             return false;
         }
-        ActivityRecord resultRecord = r.resultTo;
-        String resultWho = r.resultWho;
+        final ActivityRecord resultRecord = r.resultTo;
+        final String resultWho = r.resultWho;
         int requestCode = r.requestCode;
         if (resultRecord != null) {
-            ActivityStack resultStack = resultRecord.getActivityStack();
-            resultStack.sendActivityResultLocked(-1, resultRecord, resultWho, requestCode,
-                    RESULT_CANCELED, null);
+            resultRecord.sendResult(INVALID_UID, resultWho, requestCode, RESULT_CANCELED,
+                    null /* data */);
         }
         // We pretend to the caller that it was really started to make it backward compatible, but
         // they will just get a cancel result.
@@ -1627,12 +1626,9 @@
         }
 
         if (mStartActivity.packageName == null) {
-            final ActivityStack sourceStack = mStartActivity.resultTo != null
-                    ? mStartActivity.resultTo.getActivityStack() : null;
-            if (sourceStack != null) {
-                sourceStack.sendActivityResultLocked(-1 /* callingUid */, mStartActivity.resultTo,
-                        mStartActivity.resultWho, mStartActivity.requestCode, RESULT_CANCELED,
-                        null /* data */);
+            if (mStartActivity.resultTo != null) {
+                mStartActivity.resultTo.sendResult(INVALID_UID, mStartActivity.resultWho,
+                        mStartActivity.requestCode, RESULT_CANCELED, null /* data */);
             }
             ActivityOptions.abort(mOptions);
             return START_CLASS_NOT_FOUND;
@@ -1709,8 +1705,8 @@
             EventLog.writeEvent(EventLogTags.AM_CREATE_TASK, mStartActivity.mUserId,
                     mStartActivity.getTaskRecord().taskId);
         }
-        ActivityStack.logStartActivity(
-                EventLogTags.AM_CREATE_ACTIVITY, mStartActivity, mStartActivity.getTaskRecord());
+        mStartActivity.logStartActivity(
+                EventLogTags.AM_CREATE_ACTIVITY, mStartActivity.getTaskRecord());
         mTargetStack.mLastPausedActivity = null;
 
         mRootActivityContainer.sendPowerHintForLaunchStartIfNeeded(
@@ -1928,17 +1924,14 @@
     }
 
     private void sendNewTaskResultRequestIfNeeded() {
-        final ActivityStack sourceStack = mStartActivity.resultTo != null
-                ? mStartActivity.resultTo.getActivityStack() : null;
-        if (sourceStack != null && (mLaunchFlags & FLAG_ACTIVITY_NEW_TASK) != 0) {
+        if (mStartActivity.resultTo != null && (mLaunchFlags & FLAG_ACTIVITY_NEW_TASK) != 0) {
             // For whatever reason this activity is being launched into a new task...
             // yet the caller has requested a result back.  Well, that is pretty messed up,
             // so instead immediately send back a cancel and let the new task continue launched
             // as normal without a dependency on its originator.
             Slog.w(TAG, "Activity is launching as a new task, so cancelling activity result.");
-            sourceStack.sendActivityResultLocked(-1 /* callingUid */, mStartActivity.resultTo,
-                    mStartActivity.resultWho, mStartActivity.requestCode, RESULT_CANCELED,
-                    null /* data */);
+            mStartActivity.resultTo.sendResult(INVALID_UID, mStartActivity.resultWho,
+                    mStartActivity.requestCode, RESULT_CANCELED, null /* data */);
             mStartActivity.resultTo = null;
         }
     }
@@ -2363,7 +2356,7 @@
             return;
         }
 
-        ActivityStack.logStartActivity(AM_NEW_INTENT, activity, activity.getTaskRecord());
+        activity.logStartActivity(AM_NEW_INTENT, activity.getTaskRecord());
         activity.deliverNewIntentLocked(mCallingUid, mStartActivity.intent,
                 mStartActivity.launchedFromPackage);
         mIntentDelivered = true;
@@ -2430,7 +2423,7 @@
             ActivityRecord top = sourceTask.performClearTaskLocked(mStartActivity, mLaunchFlags);
             mKeepCurTransition = true;
             if (top != null) {
-                ActivityStack.logStartActivity(AM_NEW_INTENT, mStartActivity, top.getTaskRecord());
+                mStartActivity.logStartActivity(AM_NEW_INTENT, top.getTaskRecord());
                 deliverNewIntent(top);
                 // For paranoia, make sure we have correctly resumed the top activity.
                 mTargetStack.mLastPausedActivity = null;
@@ -2449,7 +2442,7 @@
                 final TaskRecord task = top.getTaskRecord();
                 task.moveActivityToFrontLocked(top);
                 top.updateOptionsLocked(mOptions);
-                ActivityStack.logStartActivity(AM_NEW_INTENT, mStartActivity, task);
+                mStartActivity.logStartActivity(AM_NEW_INTENT, task);
                 deliverNewIntent(top);
                 mTargetStack.mLastPausedActivity = null;
                 if (mDoResume) {
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index a3ab27e..3c5947a 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -43,11 +43,9 @@
 import static android.content.pm.ConfigurationInfo.GL_ES_VERSION_UNDEFINED;
 import static android.content.pm.PackageManager.FEATURE_ACTIVITIES_ON_SECONDARY_DISPLAYS;
 import static android.content.pm.PackageManager.FEATURE_FREEFORM_WINDOW_MANAGEMENT;
-import static android.content.pm.PackageManager.FEATURE_PC;
 import static android.content.pm.PackageManager.FEATURE_PICTURE_IN_PICTURE;
 import static android.content.pm.PackageManager.PERMISSION_GRANTED;
 import static android.content.res.Configuration.UI_MODE_TYPE_TELEVISION;
-import static android.os.Build.VERSION_CODES.N;
 import static android.os.FactoryTest.FACTORY_TEST_HIGH_LEVEL;
 import static android.os.FactoryTest.FACTORY_TEST_LOW_LEVEL;
 import static android.os.FactoryTest.FACTORY_TEST_OFF;
@@ -87,6 +85,7 @@
 import static com.android.server.wm.ActivityStack.REMOVE_TASK_MODE_DESTROYING;
 import static com.android.server.wm.ActivityStackSupervisor.DEFER_RESUME;
 import static com.android.server.wm.ActivityStackSupervisor.ON_TOP;
+import static com.android.server.wm.ActivityStackSupervisor.PAUSE_IMMEDIATELY;
 import static com.android.server.wm.ActivityStackSupervisor.PRESERVE_WINDOWS;
 import static com.android.server.wm.ActivityStackSupervisor.REMOVE_FROM_RECENTS;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_ALL;
@@ -211,7 +210,7 @@
 import android.sysprop.DisplayProperties;
 import android.telecom.TelecomManager;
 import android.text.TextUtils;
-import android.text.format.Time;
+import android.text.format.TimeMigrationUtils;
 import android.util.ArrayMap;
 import android.util.EventLog;
 import android.util.Log;
@@ -361,7 +360,7 @@
     /* Global service lock used by the package the owns this service. */
     final WindowManagerGlobalLock mGlobalLock = new WindowManagerGlobalLock();
     /**
-     * It is the same instance as {@link mGlobalLock}, just declared as a type that the
+     * It is the same instance as {@link #mGlobalLock}, just declared as a type that the
      * locked-region-code-injection does't recognize it. It is used to skip wrapping priority
      * booster for places that are already in the scope of another booster (e.g. computing oom-adj).
      *
@@ -730,7 +729,6 @@
         final boolean forceRtl = Settings.Global.getInt(resolver, DEVELOPMENT_FORCE_RTL, 0) != 0;
         final boolean forceResizable = Settings.Global.getInt(
                 resolver, DEVELOPMENT_FORCE_RESIZABLE_ACTIVITIES, 0) != 0;
-        final boolean isPc = mContext.getPackageManager().hasSystemFeature(FEATURE_PC);
 
         // Transfer any global setting for forcing RTL layout, into a System Property
         DisplayProperties.debug_force_rtl(forceRtl);
@@ -761,10 +759,6 @@
                 mSupportsPictureInPicture = false;
                 mSupportsMultiDisplay = false;
             }
-            mWindowManager.setForceResizableTasks(mForceResizableActivities);
-            mWindowManager.setSupportsPictureInPicture(mSupportsPictureInPicture);
-            mWindowManager.setSupportsFreeformWindowManagement(mSupportsFreeformWindowManagement);
-            mWindowManager.setIsPc(isPc);
             mWindowManager.mRoot.onSettingsRetrieved();
             // This happens before any activities are started, so we can change global configuration
             // in-place.
@@ -821,8 +815,7 @@
                 new TaskChangeNotificationController(mGlobalLock, mStackSupervisor, mH);
         mLockTaskController = new LockTaskController(mContext, mStackSupervisor, mH);
         mActivityStartController = new ActivityStartController(this);
-        mRecentTasks = createRecentTasks();
-        mStackSupervisor.setRecentTasks(mRecentTasks);
+        setRecentTasks(new RecentTasks(this, mStackSupervisor));
         mVrController = new VrController(mGlobalLock);
         mKeyguardController = mStackSupervisor.getKeyguardController();
     }
@@ -890,8 +883,10 @@
         return mode == AppOpsManager.MODE_ALLOWED;
     }
 
-    protected RecentTasks createRecentTasks() {
-        return new RecentTasks(this, mStackSupervisor);
+    @VisibleForTesting
+    protected void setRecentTasks(RecentTasks recentTasks) {
+        mRecentTasks = recentTasks;
+        mStackSupervisor.setRecentTasks(recentTasks);
     }
 
     RecentTasks getRecentTasks() {
@@ -1619,8 +1614,8 @@
                     // Explicitly dismissing the activity so reset its relaunch flag.
                     r.mRelaunchReason = RELAUNCH_REASON_NONE;
                 } else {
-                    r.finishActivityLocked(resultCode, resultData, "app-request",
-                            true /* oomAdj */);
+                    r.finishIfPossible(resultCode, resultData, "app-request",
+                            true /* oomAdj */, !PAUSE_IMMEDIATELY);
                     res = r.finishing;
                     if (!res) {
                         Slog.i(TAG, "Failed to finish by app-request");
@@ -1758,9 +1753,14 @@
     public final void activityDestroyed(IBinder token) {
         if (DEBUG_SWITCH) Slog.v(TAG_SWITCH, "ACTIVITY DESTROYED: " + token);
         synchronized (mGlobalLock) {
-            ActivityStack stack = ActivityRecord.getStackLocked(token);
-            if (stack != null) {
-                stack.activityDestroyedLocked(token, "activityDestroyed");
+            final long origId = Binder.clearCallingIdentity();
+            try {
+                final ActivityRecord activity = ActivityRecord.forTokenLocked(token);
+                if (activity != null) {
+                    activity.destroyed("activityDestroyed");
+                }
+            } finally {
+                Binder.restoreCallingIdentity(origId);
             }
         }
     }
@@ -1954,12 +1954,7 @@
                 if (r == null) {
                     return false;
                 }
-                final boolean translucentChanged = r.changeWindowTranslucency(true);
-                if (translucentChanged) {
-                    mRootActivityContainer.ensureActivitiesVisible(null, 0, !PRESERVE_WINDOWS);
-                }
-                mWindowManager.setAppFullscreen(token, true);
-                return translucentChanged;
+                return r.setOccludesParent(true);
             }
         } finally {
             Binder.restoreCallingIdentity(origId);
@@ -1982,13 +1977,7 @@
                     ActivityRecord under = task.mActivities.get(index - 1);
                     under.returningOptions = safeOptions != null ? safeOptions.getOptions(r) : null;
                 }
-                final boolean translucentChanged = r.changeWindowTranslucency(false);
-                if (translucentChanged) {
-                    r.getActivityStack().convertActivityToTranslucent(r);
-                }
-                mRootActivityContainer.ensureActivitiesVisible(null, 0, !PRESERVE_WINDOWS);
-                mWindowManager.setAppFullscreen(token, false);
-                return translucentChanged;
+                return r.setOccludesParent(false);
             }
         } finally {
             Binder.restoreCallingIdentity(origId);
@@ -2581,7 +2570,7 @@
                             + taskId + " to stack " + stackId);
                 }
                 if (stack.inSplitScreenPrimaryWindowingMode()) {
-                    mWindowManager.setDockedStackCreateState(
+                    mWindowManager.setDockedStackCreateStateLocked(
                             SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT, null /* initialBounds */);
                 }
                 task.reparent(stack, toTop, REPARENT_KEEP_STACK_AT_FRONT, ANIMATE, !DEFER_RESUME,
@@ -2700,7 +2689,7 @@
                             + " non-standard task " + taskId + " to split-screen windowing mode");
                 }
 
-                mWindowManager.setDockedStackCreateState(createMode, initialBounds);
+                mWindowManager.setDockedStackCreateStateLocked(createMode, initialBounds);
                 final int windowingMode = task.getWindowingMode();
                 final ActivityStack stack = task.getStack();
                 if (toTop) {
@@ -2802,7 +2791,7 @@
         try {
             synchronized (mGlobalLock) {
                 // Cancel the recents animation synchronously (do not hold the WM lock)
-                mWindowManager.cancelRecentsAnimationSynchronously(restoreHomeStackPosition
+                mWindowManager.cancelRecentsAnimation(restoreHomeStackPosition
                         ? REORDER_MOVE_TO_ORIGINAL_POSITION
                         : REORDER_KEEP_IN_PLACE, "cancelRecentsAnimation/uid=" + callingUid);
             }
@@ -3252,7 +3241,7 @@
                 if (r == null) {
                     return false;
                 }
-                return r.getActivityStack().safelyDestroyActivityLocked(r, "app-req");
+                return r.safelyDestroy("app-req");
             } finally {
                 Binder.restoreCallingIdentity(origId);
             }
@@ -5878,9 +5867,9 @@
                 tracesFile = File.createTempFile("app_slow", null, tracesDir);
 
                 StringBuilder sb = new StringBuilder();
-                Time tobj = new Time();
-                tobj.set(System.currentTimeMillis());
-                sb.append(tobj.format("%Y-%m-%d %H:%M:%S"));
+                String timeString =
+                        TimeMigrationUtils.formatMillisWithFixedFormat(System.currentTimeMillis());
+                sb.append(timeString);
                 sb.append(": ");
                 TimeUtils.formatDuration(SystemClock.uptimeMillis()-startTime, sb);
                 sb.append(" since ");
@@ -6500,8 +6489,7 @@
             synchronized (mGlobalLock) {
                 final ActivityRecord r = ActivityRecord.isInStackLocked(activityToken);
                 if (r != null && r.getActivityStack() != null) {
-                    r.getActivityStack().sendActivityResultLocked(callingUid, r, resultWho,
-                            requestCode, resultCode, data);
+                    r.sendResult(callingUid, resultWho, requestCode, resultCode, data);
                 }
             }
         }
diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java
index 7fde6de..7e0d9a0 100644
--- a/services/core/java/com/android/server/wm/AppWindowToken.java
+++ b/services/core/java/com/android/server/wm/AppWindowToken.java
@@ -92,6 +92,7 @@
 import android.content.res.CompatibilityInfo;
 import android.content.res.Configuration;
 import android.graphics.GraphicBuffer;
+import android.graphics.PixelFormat;
 import android.graphics.Point;
 import android.graphics.Rect;
 import android.os.Binder;
@@ -153,8 +154,11 @@
     final ComponentName mActivityComponent;
     final boolean mVoiceInteraction;
 
-    /** @see WindowContainer#fillsParent() */
-    private boolean mFillsParent;
+    /**
+     * The activity is opaque and fills the entire space of this task.
+     * @see WindowContainer#fillsParent()
+     */
+    private boolean mOccludesParent;
     boolean mShowForAllUsers;
     int mTargetSdk;
 
@@ -373,7 +377,7 @@
         appToken = token;
         mActivityComponent = activityComponent;
         mVoiceInteraction = voiceInteraction;
-        mFillsParent = fillsParent;
+        mOccludesParent = fillsParent;
         mInputApplicationHandle = new InputApplicationHandle(appToken.asBinder());
     }
 
@@ -2354,11 +2358,29 @@
 
     @Override
     boolean fillsParent() {
-        return mFillsParent;
+        return occludesParent();
     }
 
-    void setFillsParent(boolean fillsParent) {
-        mFillsParent = fillsParent;
+    /** Returns true if this activity is opaque and fills the entire space of this task. */
+    boolean occludesParent() {
+        return mOccludesParent;
+    }
+
+    boolean setOccludesParent(boolean occludesParent) {
+        final boolean changed = occludesParent != mOccludesParent;
+        mOccludesParent = occludesParent;
+        setMainWindowOpaque(occludesParent);
+        mWmService.mWindowPlacerLocked.requestTraversal();
+        return changed;
+    }
+
+    void setMainWindowOpaque(boolean isOpaque) {
+        final WindowState win = findMainWindow();
+        if (win == null) {
+            return;
+        }
+        isOpaque = isOpaque & !PixelFormat.formatHasAlpha(win.getAttrs().format);
+        win.mWinAnimator.setOpaqueLocked(isOpaque);
     }
 
     boolean containsDismissKeyguardWindow() {
@@ -3035,7 +3057,7 @@
         }
         pw.println(prefix + "component=" + mActivityComponent.flattenToShortString());
         pw.print(prefix); pw.print("task="); pw.println(getTask());
-        pw.print(prefix); pw.print(" mFillsParent="); pw.print(mFillsParent);
+        pw.print(prefix); pw.print(" mOccludesParent="); pw.print(mOccludesParent);
                 pw.print(" mOrientation="); pw.println(mOrientation);
         pw.println(prefix + "hiddenRequested=" + hiddenRequested + " mClientHidden=" + mClientHidden
             + ((mDeferHidingClient) ? " mDeferHidingClient=" + mDeferHidingClient : "")
@@ -3152,7 +3174,7 @@
         if (mThumbnail != null){
             mThumbnail.writeToProto(proto, THUMBNAIL);
         }
-        proto.write(FILLS_PARENT, mFillsParent);
+        proto.write(FILLS_PARENT, mOccludesParent);
         proto.write(APP_STOPPED, mAppStopped);
         proto.write(HIDDEN_REQUESTED, hiddenRequested);
         proto.write(CLIENT_HIDDEN, mClientHidden);
diff --git a/services/core/java/com/android/server/wm/DisplayRotation.java b/services/core/java/com/android/server/wm/DisplayRotation.java
index 282ed42..410cc94 100644
--- a/services/core/java/com/android/server/wm/DisplayRotation.java
+++ b/services/core/java/com/android/server/wm/DisplayRotation.java
@@ -295,7 +295,8 @@
                 false /* forceRelayout */);
     }
 
-    private void setUserRotation(int userRotationMode, int userRotation) {
+    @VisibleForTesting
+    void setUserRotation(int userRotationMode, int userRotation) {
         if (isDefaultDisplay) {
             // We'll be notified via settings listener, so we don't need to update internal values.
             final ContentResolver res = mContext.getContentResolver();
diff --git a/services/core/java/com/android/server/wm/DisplayWindowSettings.java b/services/core/java/com/android/server/wm/DisplayWindowSettings.java
index 207e8ef..8507918 100644
--- a/services/core/java/com/android/server/wm/DisplayWindowSettings.java
+++ b/services/core/java/com/android/server/wm/DisplayWindowSettings.java
@@ -275,14 +275,14 @@
         // This display used to be in freeform, but we don't support freeform anymore, so fall
         // back to fullscreen.
         if (windowingMode == WindowConfiguration.WINDOWING_MODE_FREEFORM
-                && !mService.mSupportsFreeformWindowManagement) {
+                && !mService.mAtmService.mSupportsFreeformWindowManagement) {
             return WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
         }
         // No record is present so use default windowing mode policy.
         if (windowingMode == WindowConfiguration.WINDOWING_MODE_UNDEFINED) {
             final boolean forceDesktopMode = mService.mForceDesktopModeOnExternalDisplays
                     && displayId != Display.DEFAULT_DISPLAY;
-            windowingMode = mService.mSupportsFreeformWindowManagement
+            windowingMode = mService.mAtmService.mSupportsFreeformWindowManagement
                     && (mService.mIsPc || forceDesktopMode)
                     ? WindowConfiguration.WINDOWING_MODE_FREEFORM
                     : WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
diff --git a/services/core/java/com/android/server/wm/DockedStackDividerController.java b/services/core/java/com/android/server/wm/DockedStackDividerController.java
index b1bc2197..120ce3e 100644
--- a/services/core/java/com/android/server/wm/DockedStackDividerController.java
+++ b/services/core/java/com/android/server/wm/DockedStackDividerController.java
@@ -355,7 +355,9 @@
 
     void getTouchRegion(Rect outRegion) {
         outRegion.set(mTouchRegion);
-        outRegion.offset(mWindow.getFrameLw().left, mWindow.getFrameLw().top);
+        if (mWindow != null) {
+            outRegion.offset(mWindow.getFrameLw().left, mWindow.getFrameLw().top);
+        }
     }
 
     private void resetDragResizingChangeReported() {
diff --git a/services/core/java/com/android/server/wm/RecentsAnimation.java b/services/core/java/com/android/server/wm/RecentsAnimation.java
index 4f0332c..1a8944a 100644
--- a/services/core/java/com/android/server/wm/RecentsAnimation.java
+++ b/services/core/java/com/android/server/wm/RecentsAnimation.java
@@ -149,8 +149,8 @@
             // traversal in non-stopped state (ViewRootImpl.mStopped) that would initialize more
             // things (e.g. the measure can be done earlier). The actual stop will be performed when
             // it reports idle.
-            targetStack.addToStopping(targetActivity, true /* scheduleIdle */,
-                    true /* idleDelayed */, "preloadRecents");
+            targetActivity.addToStopping(true /* scheduleIdle */, true /* idleDelayed */,
+                    "preloadRecents");
         }
     }
 
@@ -241,7 +241,7 @@
             // Fetch all the surface controls and pass them to the client to get the animation
             // started. Cancel any existing recents animation running synchronously (do not hold the
             // WM lock)
-            mWindowManager.cancelRecentsAnimationSynchronously(REORDER_MOVE_TO_ORIGINAL_POSITION,
+            mWindowManager.cancelRecentsAnimation(REORDER_MOVE_TO_ORIGINAL_POSITION,
                     "startRecentsActivity");
             mWindowManager.initializeRecentsAnimation(mTargetActivityType, recentsAnimationRunner,
                     this, mDefaultDisplay.mDisplayId,
@@ -396,12 +396,8 @@
 
     @Override
     public void onAnimationFinished(@RecentsAnimationController.ReorderMode int reorderMode,
-            boolean runSychronously, boolean sendUserLeaveHint) {
-        if (runSychronously) {
-            finishAnimation(reorderMode, sendUserLeaveHint);
-        } else {
-            mService.mH.post(() -> finishAnimation(reorderMode, sendUserLeaveHint));
-        }
+            boolean sendUserLeaveHint) {
+        finishAnimation(reorderMode, sendUserLeaveHint);
     }
 
     @Override
@@ -435,8 +431,7 @@
         } else {
             // Just cancel directly to unleash from launcher when the next launching task is the
             // current top task.
-            mWindowManager.cancelRecentsAnimationSynchronously(REORDER_KEEP_IN_PLACE,
-                    "stackOrderChanged");
+            mWindowManager.cancelRecentsAnimation(REORDER_KEEP_IN_PLACE, "stackOrderChanged");
         }
     }
 
diff --git a/services/core/java/com/android/server/wm/RecentsAnimationController.java b/services/core/java/com/android/server/wm/RecentsAnimationController.java
index 724a72e..8752f37 100644
--- a/services/core/java/com/android/server/wm/RecentsAnimationController.java
+++ b/services/core/java/com/android/server/wm/RecentsAnimationController.java
@@ -168,8 +168,7 @@
 
     public interface RecentsAnimationCallbacks {
         /** Callback when recents animation is finished. */
-        void onAnimationFinished(@ReorderMode int reorderMode, boolean runSychronously,
-                boolean sendUserLeaveHint);
+        void onAnimationFinished(@ReorderMode int reorderMode, boolean sendUserLeaveHint);
     }
 
     private final IRecentsAnimationController mController =
@@ -221,8 +220,7 @@
                 // prior to calling the callback
                 mCallbacks.onAnimationFinished(moveHomeToTop
                         ? REORDER_MOVE_TO_TOP
-                        : REORDER_MOVE_TO_ORIGINAL_POSITION,
-                        true /* runSynchronously */, sendUserLeaveHint);
+                        : REORDER_MOVE_TO_ORIGINAL_POSITION, sendUserLeaveHint);
                 mDisplayContent.mBoundsAnimationController.setAnimationType(FADE_IN);
             } finally {
                 Binder.restoreCallingIdentity(token);
@@ -498,21 +496,15 @@
     }
 
     void cancelAnimation(@ReorderMode int reorderMode, String reason) {
-        cancelAnimation(reorderMode, false /* runSynchronously */, false /*screenshot */, reason);
-    }
-
-    void cancelAnimationSynchronously(@ReorderMode int reorderMode, String reason) {
-        cancelAnimation(reorderMode, true /* runSynchronously */, false /* screenshot */, reason);
+        cancelAnimation(reorderMode, false /*screenshot */, reason);
     }
 
     void cancelAnimationWithScreenshot(boolean screenshot) {
-        cancelAnimation(REORDER_KEEP_IN_PLACE, true /* sync */, screenshot, "stackOrderChanged");
+        cancelAnimation(REORDER_KEEP_IN_PLACE, screenshot, "stackOrderChanged");
     }
 
-    private void cancelAnimation(@ReorderMode int reorderMode, boolean runSynchronously,
-            boolean screenshot, String reason) {
-        if (DEBUG_RECENTS_ANIMATIONS) Slog.d(TAG, "cancelAnimation(): reason=" + reason
-                + " runSynchronously=" + runSynchronously);
+    private void cancelAnimation(@ReorderMode int reorderMode, boolean screenshot, String reason) {
+        if (DEBUG_RECENTS_ANIMATIONS) Slog.d(TAG, "cancelAnimation(): reason=" + reason);
         synchronized (mService.getWindowManagerLock()) {
             if (mCanceled) {
                 // We've already canceled the animation
@@ -525,16 +517,14 @@
                 // Screen shot previous task when next task starts transition and notify the runner.
                 // We will actually finish the animation once the runner calls cleanUpScreenshot().
                 final Task task = mPendingAnimations.get(0).mTask;
-                final TaskSnapshot taskSnapshot = screenshotRecentTask(task, reorderMode,
-                        runSynchronously);
+                final TaskSnapshot taskSnapshot = screenshotRecentTask(task, reorderMode);
                 try {
                     mRunner.onAnimationCanceled(taskSnapshot);
                 } catch (RemoteException e) {
                     Slog.e(TAG, "Failed to cancel recents animation", e);
                 }
                 if (taskSnapshot == null) {
-                    mCallbacks.onAnimationFinished(reorderMode, runSynchronously,
-                            false /* sendUserLeaveHint */);
+                    mCallbacks.onAnimationFinished(reorderMode, false /* sendUserLeaveHint */);
                 }
             } else {
                 // Otherwise, notify the runner and clean up the animation immediately
@@ -545,8 +535,7 @@
                 } catch (RemoteException e) {
                     Slog.e(TAG, "Failed to cancel recents animation", e);
                 }
-                mCallbacks.onAnimationFinished(reorderMode, runSynchronously,
-                        false /* sendUserLeaveHint */);
+                mCallbacks.onAnimationFinished(reorderMode, false /* sendUserLeaveHint */);
             }
         }
     }
@@ -592,8 +581,7 @@
         return mRequestDeferCancelUntilNextTransition && mCancelDeferredWithScreenshot;
     }
 
-    TaskSnapshot screenshotRecentTask(Task task, @ReorderMode int reorderMode,
-            boolean runSynchronously) {
+    TaskSnapshot screenshotRecentTask(Task task, @ReorderMode int reorderMode) {
         final TaskSnapshotController snapshotController = mService.mTaskSnapshotController;
         final ArraySet<Task> tasks = Sets.newArraySet(task);
         snapshotController.snapshotTasks(tasks);
@@ -613,8 +601,7 @@
                     if (DEBUG_RECENTS_ANIMATIONS) {
                         Slog.d(TAG, "mRecentScreenshotAnimator finish");
                     }
-                    mCallbacks.onAnimationFinished(reorderMode, runSynchronously,
-                            false /* sendUserLeaveHint */);
+                    mCallbacks.onAnimationFinished(reorderMode, false /* sendUserLeaveHint */);
                 }, mService);
         mRecentScreenshotAnimator.transferAnimation(task.mSurfaceAnimator);
         return taskSnapshot;
diff --git a/services/core/java/com/android/server/wm/RootActivityContainer.java b/services/core/java/com/android/server/wm/RootActivityContainer.java
index 66d42db..3401de6 100644
--- a/services/core/java/com/android/server/wm/RootActivityContainer.java
+++ b/services/core/java/com/android/server/wm/RootActivityContainer.java
@@ -227,15 +227,10 @@
         mStackSupervisor.mRootActivityContainer = this;
     }
 
-    @VisibleForTesting
-    void setWindowContainer(RootWindowContainer container) {
-        mRootWindowContainer = container;
-        mRootWindowContainer.setRootActivityContainer(this);
-    }
-
     void setWindowManager(WindowManagerService wm) {
         mWindowManager = wm;
-        setWindowContainer(mWindowManager.mRoot);
+        mRootWindowContainer = mWindowManager.mRoot;
+        mRootWindowContainer.setRootActivityContainer(this);
         mDisplayManager = mService.mContext.getSystemService(DisplayManager.class);
         mDisplayManager.registerDisplayListener(this, mService.mUiHandler);
         mDisplayManagerInternal = LocalServices.getService(DisplayManagerInternal.class);
@@ -2266,7 +2261,7 @@
             @WindowConfiguration.ActivityType int ignoreActivityType,
             @WindowConfiguration.WindowingMode int ignoreWindowingMode, int callingUid,
             boolean allowed) {
-        mStackSupervisor.mRunningTasks.getTasks(maxNum, list, ignoreActivityType,
+        mStackSupervisor.getRunningTasks().getTasks(maxNum, list, ignoreActivityType,
                 ignoreWindowingMode, mActivityDisplays, callingUid, allowed);
     }
 
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index fd86faa..3a2eb57 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -380,7 +380,7 @@
 
     boolean isResizeable() {
         return ActivityInfo.isResizeableMode(mResizeMode) || mSupportsPictureInPicture
-                || mWmService.mForceResizableTasks;
+                || mWmService.mAtmService.mForceResizableActivities;
     }
 
     /**
diff --git a/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java b/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java
index f51d4c4..1c015d0 100644
--- a/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java
+++ b/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java
@@ -159,11 +159,23 @@
         }
 
         // STEP 2: Resolve launch windowing mode.
-        // STEP 2.1: Determine if any parameter has specified initial bounds. That might be the
-        // launch bounds from activity options, or size/gravity passed in layout. It also treats the
-        // launch windowing mode in options as a suggestion for future resolution.
+        // STEP 2.1: Determine if any parameter can specify initial bounds/windowing mode. That
+        // might be the launch bounds from activity options, or size/gravity passed in layout. It
+        // also treats the launch windowing mode in options and source activity windowing mode in
+        // some cases as a suggestion for future resolution.
         int launchMode = options != null ? options.getLaunchWindowingMode()
                 : WINDOWING_MODE_UNDEFINED;
+        // In some cases we want to use the source's windowing mode as the default value, e.g. when
+        // source is a freeform window in a fullscreen display launching an activity on the same
+        // display.
+        if (launchMode == WINDOWING_MODE_UNDEFINED
+                && canInheritWindowingModeFromSource(display, source)) {
+            launchMode = source.getWindowingMode();
+            if (DEBUG) {
+                appendLog("inherit-from-source="
+                        + WindowConfiguration.windowingModeToString(launchMode));
+            }
+        }
         // hasInitialBounds is set if either activity options or layout has specified bounds. If
         // that's set we'll skip some adjustments later to avoid overriding the initial bounds.
         boolean hasInitialBounds = false;
@@ -342,6 +354,31 @@
                 ? displayId : DEFAULT_DISPLAY;
     }
 
+    private boolean canInheritWindowingModeFromSource(@NonNull ActivityDisplay display,
+            @Nullable ActivityRecord source) {
+        if (source == null) {
+            return false;
+        }
+
+        // There is not really any strong reason to tie the launching windowing mode and the source
+        // on freeform displays. The launching windowing mode is more tied to the content of the new
+        // activities.
+        if (display.inFreeformWindowingMode()) {
+            return false;
+        }
+
+        final int sourceWindowingMode = source.getWindowingMode();
+        if (sourceWindowingMode != WINDOWING_MODE_FULLSCREEN
+                && sourceWindowingMode != WINDOWING_MODE_FREEFORM) {
+            return false;
+        }
+
+        // Only inherit windowing mode if both source and target activities are on the same display.
+        // Otherwise we may have unintended freeform windows showing up if an activity in freeform
+        // window launches an activity on a fullscreen display by specifying display ID.
+        return display.mDisplayId == source.getDisplayId();
+    }
+
     private boolean canApplyFreeformWindowPolicy(@NonNull ActivityDisplay display, int launchMode) {
         return mSupervisor.mService.mSupportsFreeformWindowManagement
                 && (display.inFreeformWindowingMode() || launchMode == WINDOWING_MODE_FREEFORM);
diff --git a/services/core/java/com/android/server/wm/TaskRecord.java b/services/core/java/com/android/server/wm/TaskRecord.java
index 882f411..ede2f56 100644
--- a/services/core/java/com/android/server/wm/TaskRecord.java
+++ b/services/core/java/com/android/server/wm/TaskRecord.java
@@ -1419,8 +1419,9 @@
                 mActivities.remove(activityNdx);
                 --activityNdx;
                 --numActivities;
-            } else if (r.finishActivityLocked(Activity.RESULT_CANCELED, null,
-                    reason, false /* oomAdj */, pauseImmediately) == FINISH_RESULT_REMOVED) {
+            } else if (r.finishIfPossible(Activity.RESULT_CANCELED,
+                    null /* resultData */, reason, false /* oomAdj */, pauseImmediately)
+                    == FINISH_RESULT_REMOVED) {
                 --activityNdx;
                 --numActivities;
             }
@@ -1474,8 +1475,8 @@
                     if (opts != null) {
                         ret.updateOptionsLocked(opts);
                     }
-                    if (r.finishActivityLocked(Activity.RESULT_CANCELED, null /* resultData */,
-                            "clear-task-stack", false /* oomAdj */) == FINISH_RESULT_REMOVED) {
+                    if (r.finishIfPossible("clear-task-stack", false /* oomAdj */)
+                            == FINISH_RESULT_REMOVED) {
                         --activityNdx;
                         --numActivities;
                     }
@@ -1488,8 +1489,7 @@
                         && (launchFlags & Intent.FLAG_ACTIVITY_SINGLE_TOP) == 0
                         && !ActivityStarter.isDocumentLaunchesIntoExisting(launchFlags)) {
                     if (!ret.finishing) {
-                        ret.finishActivityLocked(Activity.RESULT_CANCELED, null /* resultData */,
-                                "clear-task-top", false /* oomAdj */);
+                        ret.finishIfPossible("clear-task-top", false /* oomAdj */);
                         return null;
                     }
                 }
diff --git a/services/core/java/com/android/server/wm/TaskStack.java b/services/core/java/com/android/server/wm/TaskStack.java
index 79367a0..cc2112e 100644
--- a/services/core/java/com/android/server/wm/TaskStack.java
+++ b/services/core/java/com/android/server/wm/TaskStack.java
@@ -1666,7 +1666,7 @@
      *         default bounds.
      */
     Rect getPictureInPictureBounds(float aspectRatio, Rect stackBounds) {
-        if (!mWmService.mSupportsPictureInPicture) {
+        if (!mWmService.mAtmService.mSupportsPictureInPicture) {
             return null;
         }
 
@@ -1762,7 +1762,7 @@
      * Sets the current picture-in-picture aspect ratio.
      */
     void setPictureInPictureAspectRatio(float aspectRatio) {
-        if (!mWmService.mSupportsPictureInPicture) {
+        if (!mWmService.mAtmService.mSupportsPictureInPicture) {
             return;
         }
 
@@ -1792,7 +1792,7 @@
      * Sets the current picture-in-picture actions.
      */
     void setPictureInPictureActions(List<RemoteAction> actions) {
-        if (!mWmService.mSupportsPictureInPicture) {
+        if (!mWmService.mAtmService.mSupportsPictureInPicture) {
             return;
         }
 
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index bbef261..29d232f 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -767,11 +767,11 @@
      */
     void setOrientation(int orientation, @Nullable IBinder freezeDisplayToken,
             @Nullable ConfigurationContainer requestingContainer) {
-        final boolean changed = mOrientation != orientation;
-        mOrientation = orientation;
-        if (!changed) {
+        if (mOrientation == orientation) {
             return;
         }
+
+        mOrientation = orientation;
         final WindowContainer parent = getParent();
         if (parent != null) {
             onDescendantOrientationChanged(freezeDisplayToken, requestingContainer);
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 86faad0..3874fdc 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -28,6 +28,7 @@
 import static android.app.AppOpsManager.OP_SYSTEM_ALERT_WINDOW;
 import static android.app.StatusBarManager.DISABLE_MASK;
 import static android.app.admin.DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED;
+import static android.content.pm.PackageManager.FEATURE_PC;
 import static android.content.pm.PackageManager.PERMISSION_GRANTED;
 import static android.os.Process.SYSTEM_UID;
 import static android.os.Process.myPid;
@@ -591,9 +592,6 @@
     int mDockedStackCreateMode = SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT;
     Rect mDockedStackCreateBounds;
 
-    boolean mForceResizableTasks;
-    boolean mSupportsPictureInPicture;
-    boolean mSupportsFreeformWindowManagement;
     boolean mIsPc;
     /**
      * Flag that indicates that desktop mode is forced for public secondary screens.
@@ -819,7 +817,7 @@
     int mTransactionSequence;
 
     final WindowAnimator mAnimator;
-    final SurfaceAnimationRunner mSurfaceAnimationRunner;
+    SurfaceAnimationRunner mSurfaceAnimationRunner;
 
     /**
      * Keeps track of which animations got transferred to which animators. Entries will get cleaned
@@ -957,6 +955,9 @@
 
     final ArrayList<AppFreezeListener> mAppFreezeListeners = new ArrayList<>();
 
+    @VisibleForTesting
+    final DeviceConfig.OnPropertiesChangedListener mPropertiesChangedListener;
+
     interface AppFreezeListener {
         void onAppFreezeTimeout();
     }
@@ -1010,6 +1011,7 @@
         mGlobalLock = atm.getGlobalLock();
         mAtmService = atm;
         mContext = context;
+        mIsPc = mContext.getPackageManager().hasSystemFeature(FEATURE_PC);
         mAllowBootMessages = showBootMsgs;
         mOnlyCore = onlyCore;
         mLimitedAlphaCompositing = context.getResources().getBoolean(
@@ -1159,26 +1161,28 @@
         mSystemGestureExcludedByPreQStickyImmersive =
                 DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_WINDOW_MANAGER,
                         KEY_SYSTEM_GESTURES_EXCLUDED_BY_PRE_Q_STICKY_IMMERSIVE, false);
-        DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_WINDOW_MANAGER,
-                new HandlerExecutor(mH), properties -> {
-                    synchronized (mGlobalLock) {
-                        final int exclusionLimitDp = Math.max(MIN_GESTURE_EXCLUSION_LIMIT_DP,
-                                properties.getInt(KEY_SYSTEM_GESTURE_EXCLUSION_LIMIT_DP, 0));
-                        final boolean excludedByPreQSticky = DeviceConfig.getBoolean(
-                                DeviceConfig.NAMESPACE_WINDOW_MANAGER,
-                                KEY_SYSTEM_GESTURES_EXCLUDED_BY_PRE_Q_STICKY_IMMERSIVE, false);
-                        if (mSystemGestureExcludedByPreQStickyImmersive != excludedByPreQSticky
-                                || mSystemGestureExclusionLimitDp != exclusionLimitDp) {
-                            mSystemGestureExclusionLimitDp = exclusionLimitDp;
-                            mSystemGestureExcludedByPreQStickyImmersive = excludedByPreQSticky;
-                            mRoot.forAllDisplays(DisplayContent::updateSystemGestureExclusionLimit);
-                        }
 
-                        mSystemGestureExclusionLogDebounceTimeoutMillis =
-                                DeviceConfig.getInt(DeviceConfig.NAMESPACE_WINDOW_MANAGER,
-                                        KEY_SYSTEM_GESTURE_EXCLUSION_LOG_DEBOUNCE_MILLIS, 0);
-                    }
-                });
+        mPropertiesChangedListener = properties -> {
+            synchronized (mGlobalLock) {
+                final int exclusionLimitDp = Math.max(MIN_GESTURE_EXCLUSION_LIMIT_DP,
+                        properties.getInt(KEY_SYSTEM_GESTURE_EXCLUSION_LIMIT_DP, 0));
+                final boolean excludedByPreQSticky = DeviceConfig.getBoolean(
+                        DeviceConfig.NAMESPACE_WINDOW_MANAGER,
+                        KEY_SYSTEM_GESTURES_EXCLUDED_BY_PRE_Q_STICKY_IMMERSIVE, false);
+                if (mSystemGestureExcludedByPreQStickyImmersive != excludedByPreQSticky
+                        || mSystemGestureExclusionLimitDp != exclusionLimitDp) {
+                    mSystemGestureExclusionLimitDp = exclusionLimitDp;
+                    mSystemGestureExcludedByPreQStickyImmersive = excludedByPreQSticky;
+                    mRoot.forAllDisplays(DisplayContent::updateSystemGestureExclusionLimit);
+                }
+
+                mSystemGestureExclusionLogDebounceTimeoutMillis =
+                        DeviceConfig.getInt(DeviceConfig.NAMESPACE_WINDOW_MANAGER,
+                                KEY_SYSTEM_GESTURE_EXCLUSION_LOG_DEBOUNCE_MILLIS, 0);
+            }
+        };
+        DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_WINDOW_MANAGER,
+                new HandlerExecutor(mH), mPropertiesChangedListener);
 
         LocalServices.addService(WindowManagerInternal.class, new LocalService());
     }
@@ -2595,7 +2599,7 @@
         mRecentsAnimationController = controller;
     }
 
-    public RecentsAnimationController getRecentsAnimationController() {
+    RecentsAnimationController getRecentsAnimationController() {
         return mRecentsAnimationController;
     }
 
@@ -2603,74 +2607,37 @@
      * @return Whether the next recents animation can continue to start. Called from
      *         {@link RecentsAnimation#startRecentsActivity}.
      */
-    public boolean canStartRecentsAnimation() {
-        synchronized (mGlobalLock) {
-            // TODO(multi-display): currently only default display support recent activity
-            if (getDefaultDisplayContentLocked().mAppTransition.isTransitionSet()) {
-                return false;
-            }
-            return true;
+    boolean canStartRecentsAnimation() {
+        // TODO(multi-display): currently only default display support recent activity
+        if (getDefaultDisplayContentLocked().mAppTransition.isTransitionSet()) {
+            return false;
         }
+        return true;
     }
 
-    /**
-     * Cancels any running recents animation. The caller should NOT hold the WM lock while calling
-     * this method, as it will call back into AM and may cause a deadlock. Any locking will be done
-     * in the animation controller itself.
-     */
-    public void cancelRecentsAnimationSynchronously(
+    void cancelRecentsAnimation(
             @RecentsAnimationController.ReorderMode int reorderMode, String reason) {
         if (mRecentsAnimationController != null) {
             // This call will call through to cleanupAnimation() below after the animation is
             // canceled
-            mRecentsAnimationController.cancelAnimationSynchronously(reorderMode, reason);
+            mRecentsAnimationController.cancelAnimation(reorderMode, reason);
         }
     }
 
-    public void cleanupRecentsAnimation(@RecentsAnimationController.ReorderMode int reorderMode) {
-        synchronized (mGlobalLock) {
-            if (mRecentsAnimationController != null) {
-                final RecentsAnimationController controller = mRecentsAnimationController;
-                mRecentsAnimationController = null;
-                controller.cleanupAnimation(reorderMode);
-                // TODO(mult-display): currently only default display support recents animation.
-                getDefaultDisplayContentLocked().mAppTransition.updateBooster();
-            }
+    void cleanupRecentsAnimation(@RecentsAnimationController.ReorderMode int reorderMode) {
+        if (mRecentsAnimationController != null) {
+            final RecentsAnimationController controller = mRecentsAnimationController;
+            mRecentsAnimationController = null;
+            controller.cleanupAnimation(reorderMode);
+            // TODO(mult-display): currently only default display support recents animation.
+            getDefaultDisplayContentLocked().mAppTransition.updateBooster();
         }
     }
 
-    public void setAppFullscreen(IBinder token, boolean toOpaque) {
-        synchronized (mGlobalLock) {
-            final AppWindowToken atoken = mRoot.getAppWindowToken(token);
-            if (atoken != null) {
-                atoken.setFillsParent(toOpaque);
-                setWindowOpaqueLocked(token, toOpaque);
-                mWindowPlacerLocked.requestTraversal();
-            }
-        }
-    }
-
-    public void setWindowOpaque(IBinder token, boolean isOpaque) {
-        synchronized (mGlobalLock) {
-            setWindowOpaqueLocked(token, isOpaque);
-        }
-    }
-
-    private void setWindowOpaqueLocked(IBinder token, boolean isOpaque) {
+    void setWindowOpaqueLocked(IBinder token, boolean isOpaque) {
         final AppWindowToken wtoken = mRoot.getAppWindowToken(token);
         if (wtoken != null) {
-            final WindowState win = wtoken.findMainWindow();
-            if (win == null) {
-                return;
-            }
-            isOpaque = isOpaque & !PixelFormat.formatHasAlpha(win.getAttrs().format);
-            win.mWinAnimator.setOpaqueLocked(isOpaque);
-        }
-    }
-
-    public void setDockedStackCreateState(int mode, Rect bounds) {
-        synchronized (mGlobalLock) {
-            setDockedStackCreateStateLocked(mode, bounds);
+            wtoken.setMainWindowOpaque(isOpaque);
         }
     }
 
@@ -2679,14 +2646,12 @@
         mDockedStackCreateBounds = bounds;
     }
 
-    public void checkSplitScreenMinimizedChanged(boolean animate) {
-        synchronized (mGlobalLock) {
-            final DisplayContent displayContent = getDefaultDisplayContentLocked();
-            displayContent.getDockedDividerController().checkMinimizeChanged(animate);
-        }
+    void checkSplitScreenMinimizedChanged(boolean animate) {
+        final DisplayContent displayContent = getDefaultDisplayContentLocked();
+        displayContent.getDockedDividerController().checkMinimizeChanged(animate);
     }
 
-    public boolean isValidPictureInPictureAspectRatio(int displayId, float aspectRatio) {
+    boolean isValidPictureInPictureAspectRatio(int displayId, float aspectRatio) {
         final DisplayContent displayContent = mRoot.getDisplayContent(displayId);
         return displayContent.getPinnedStackController().isValidPictureInPictureAspectRatio(
                 aspectRatio);
@@ -4847,8 +4812,10 @@
                 case UPDATE_DOCKED_STACK_DIVIDER: {
                     synchronized (mGlobalLock) {
                         final DisplayContent displayContent = getDefaultDisplayContentLocked();
-                        displayContent.getDockedDividerController().reevaluateVisibility(false);
-                        displayContent.adjustForImeIfNeeded();
+                        if (displayContent != null) {
+                            displayContent.getDockedDividerController().reevaluateVisibility(false);
+                            displayContent.adjustForImeIfNeeded();
+                        }
                     }
                     break;
                 }
@@ -6495,31 +6462,14 @@
         }
     }
 
-    public void setForceResizableTasks(boolean forceResizableTasks) {
-        synchronized (mGlobalLock) {
-            mForceResizableTasks = forceResizableTasks;
-        }
-    }
-
-    public void setSupportsPictureInPicture(boolean supportsPictureInPicture) {
-        synchronized (mGlobalLock) {
-            mSupportsPictureInPicture = supportsPictureInPicture;
-        }
-    }
-
-    public void setSupportsFreeformWindowManagement(boolean supportsFreeformWindowManagement) {
-        synchronized (mGlobalLock) {
-            mSupportsFreeformWindowManagement = supportsFreeformWindowManagement;
-        }
-    }
-
     void setForceDesktopModeOnExternalDisplays(boolean forceDesktopModeOnExternalDisplays) {
         synchronized (mGlobalLock) {
             mForceDesktopModeOnExternalDisplays = forceDesktopModeOnExternalDisplays;
         }
     }
 
-    public void setIsPc(boolean isPc) {
+    @VisibleForTesting
+    void setIsPc(boolean isPc) {
         synchronized (mGlobalLock) {
             mIsPc = isPc;
         }
@@ -6546,7 +6496,7 @@
                 "registerPinnedStackListener()")) {
             return;
         }
-        if (!mSupportsPictureInPicture) {
+        if (!mAtmService.mSupportsPictureInPicture) {
             return;
         }
         synchronized (mGlobalLock) {
@@ -7767,8 +7717,7 @@
         clientChannel.transferTo(outInputChannel);
         clientChannel.dispose();
 
-        IBinder token = new Binder();
-        mInputManager.registerInputChannel(inputChannel, token);
+        mInputManager.registerInputChannel(inputChannel, null /* generate new token */);
 
         // Prevent the java finalizer from breaking the input channel. But we won't
         // do any further management so we just release the java ref and let the
@@ -7776,7 +7725,7 @@
         inputChannel.release();
 
         InputWindowHandle h = new InputWindowHandle(null, null, displayId);
-        h.token = token;
+        h.token = inputChannel.getToken();
         h.name = name;
         h.layoutParamsFlags = WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
         h.layoutParamsType = 0;
diff --git a/services/core/java/com/android/server/wm/WindowProcessController.java b/services/core/java/com/android/server/wm/WindowProcessController.java
index cf8e1e8..242b116 100644
--- a/services/core/java/com/android/server/wm/WindowProcessController.java
+++ b/services/core/java/com/android/server/wm/WindowProcessController.java
@@ -41,7 +41,6 @@
 import static com.android.server.wm.ActivityTaskManagerService.RELAUNCH_REASON_NONE;
 
 import android.annotation.NonNull;
-import android.app.Activity;
 import android.app.ActivityThread;
 import android.app.IApplicationThread;
 import android.app.ProfilerInfo;
@@ -644,8 +643,7 @@
         for (int i = 0; i < activities.size(); i++) {
             final ActivityRecord r = activities.get(i);
             if (!r.finishing && r.isInStackLocked()) {
-                r.finishActivityLocked(Activity.RESULT_CANCELED, null /* resultData */,
-                        "finish-heavy", true /* oomAdj */);
+                r.finishIfPossible("finish-heavy", true /* oomAdj */);
             }
         }
     }
diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp
index 466ca93..03f4755 100644
--- a/services/core/jni/com_android_server_input_InputManagerService.cpp
+++ b/services/core/jni/com_android_server_input_InputManagerService.cpp
@@ -165,6 +165,7 @@
             outPointerIcon->bitmap.readPixels(bitmapCopy->info(), bitmapCopy->getPixels(),
                     bitmapCopy->rowBytes(), 0, 0);
         }
+        outSpriteIcon->style = outPointerIcon->style;
         outSpriteIcon->hotSpotX = outPointerIcon->hotSpotX;
         outSpriteIcon->hotSpotY = outPointerIcon->hotSpotY;
     }
@@ -1252,7 +1253,8 @@
     status_t status = android_view_PointerIcon_load(env, pointerIconObj.get(),
             displayContext.get(), &pointerIcon);
     if (!status && !pointerIcon.isNullIcon()) {
-        *icon = SpriteIcon(pointerIcon.bitmap, pointerIcon.hotSpotX, pointerIcon.hotSpotY);
+        *icon = SpriteIcon(
+                pointerIcon.bitmap, pointerIcon.style, pointerIcon.hotSpotX, pointerIcon.hotSpotY);
     } else {
         *icon = SpriteIcon();
     }
@@ -1293,10 +1295,12 @@
                     milliseconds_to_nanoseconds(pointerIcon.durationPerFrame);
             animationData.animationFrames.reserve(numFrames);
             animationData.animationFrames.push_back(SpriteIcon(
-                    pointerIcon.bitmap, pointerIcon.hotSpotX, pointerIcon.hotSpotY));
+                    pointerIcon.bitmap, pointerIcon.style,
+                    pointerIcon.hotSpotX, pointerIcon.hotSpotY));
             for (size_t i = 0; i < numFrames - 1; ++i) {
               animationData.animationFrames.push_back(SpriteIcon(
-                      pointerIcon.bitmapFrames[i], pointerIcon.hotSpotX, pointerIcon.hotSpotY));
+                      pointerIcon.bitmapFrames[i], pointerIcon.style,
+                      pointerIcon.hotSpotX, pointerIcon.hotSpotY));
             }
         }
     }
@@ -1711,6 +1715,7 @@
         pointerIcon.bitmap.readPixels(spriteInfo, spriteIcon.bitmap.getPixels(),
                 spriteIcon.bitmap.rowBytes(), 0, 0);
     }
+    spriteIcon.style = pointerIcon.style;
     spriteIcon.hotSpotX = pointerIcon.hotSpotX;
     spriteIcon.hotSpotY = pointerIcon.hotSpotY;
     im->setCustomPointerIcon(spriteIcon);
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index ed900b1..67ae407 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -635,6 +635,13 @@
         SystemServerInitThreadPool.get().submit(SystemConfig::getInstance, TAG_SYSTEM_CONFIG);
         t.traceEnd();
 
+        // Platform compat service is used by ActivityManagerService, PackageManagerService, and
+        // possibly others in the future. b/135010838.
+        t.traceBegin("PlatformCompat");
+        ServiceManager.addService(Context.PLATFORM_COMPAT_SERVICE,
+                new PlatformCompat(mSystemContext));
+        t.traceEnd();
+
         // Wait for installd to finish starting up so that it has a chance to
         // create critical directories such as /data/user with the appropriate
         // permissions.  We need this to complete before we initialize other services.
@@ -1102,10 +1109,6 @@
             SignedConfigService.registerUpdateReceiver(mSystemContext);
             t.traceEnd();
 
-            t.traceBegin("PlatformCompat");
-            ServiceManager.addService("platform_compat", new PlatformCompat(context));
-            t.traceEnd();
-
         } catch (RuntimeException e) {
             Slog.e("System", "******************************************");
             Slog.e("System", "************ Failure starting core service", e);
diff --git a/services/robotests/backup/src/com/android/server/backup/TrampolineRoboTest.java b/services/robotests/backup/src/com/android/server/backup/BackupManagerServiceRoboTest.java
similarity index 86%
rename from services/robotests/backup/src/com/android/server/backup/TrampolineRoboTest.java
rename to services/robotests/backup/src/com/android/server/backup/BackupManagerServiceRoboTest.java
index f0a5d37..a1bfcdf 100644
--- a/services/robotests/backup/src/com/android/server/backup/TrampolineRoboTest.java
+++ b/services/robotests/backup/src/com/android/server/backup/BackupManagerServiceRoboTest.java
@@ -26,8 +26,12 @@
 import static com.google.common.truth.Truth.assertThat;
 
 import static org.junit.Assert.assertEquals;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.doNothing;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.verify;
 import static org.robolectric.Shadows.shadowOf;
 import static org.testng.Assert.expectThrows;
@@ -72,7 +76,7 @@
 import java.io.PrintWriter;
 import java.io.StringWriter;
 
-/** Tests for {@link com.android.server.backup.Trampoline}. */
+/** Tests for {@link BackupManagerService}. */
 @RunWith(RobolectricTestRunner.class)
 @Config(
         shadows = {
@@ -83,7 +87,7 @@
                 ShadowSystemServiceRegistry.class
         })
 @Presubmit
-public class TrampolineRoboTest {
+public class BackupManagerServiceRoboTest {
     private static final String TEST_PACKAGE = "package";
     private static final String TEST_TRANSPORT = "transport";
     private static final String[] ADB_TEST_PACKAGES = {TEST_PACKAGE};
@@ -121,7 +125,7 @@
     /** Test that the service registers users. */
     @Test
     public void testStartServiceForUser_registersUser() throws Exception {
-        Trampoline backupManagerService = createService();
+        BackupManagerService backupManagerService = createService();
         backupManagerService.setBackupServiceActive(mUserOneId, true);
 
         backupManagerService.startServiceForUser(mUserOneId);
@@ -134,7 +138,7 @@
     /** Test that the service registers users. */
     @Test
     public void testStartServiceForUser_withServiceInstance_registersUser() throws Exception {
-        Trampoline backupManagerService = createService();
+        BackupManagerService backupManagerService = createService();
         backupManagerService.setBackupServiceActive(mUserOneId, true);
 
         backupManagerService.startServiceForUser(mUserOneId, mUserOneService);
@@ -147,7 +151,7 @@
     /** Test that the service unregisters users when stopped. */
     @Test
     public void testStopServiceForUser_forRegisteredUser_unregistersCorrectUser() throws Exception {
-        Trampoline backupManagerService =
+        BackupManagerService backupManagerService =
                 createServiceAndRegisterUser(mUserOneId, mUserOneService);
         backupManagerService.startServiceForUser(mUserTwoId, mUserTwoService);
         ShadowBinder.setCallingUid(Process.SYSTEM_UID);
@@ -163,7 +167,7 @@
     /** Test that the service unregisters users when stopped. */
     @Test
     public void testStopServiceForUser_forRegisteredUser_tearsDownCorrectUser() throws Exception {
-        Trampoline backupManagerService =
+        BackupManagerService backupManagerService =
                 createServiceAndRegisterUser(mUserOneId, mUserOneService);
         backupManagerService.setBackupServiceActive(mUserTwoId, true);
         backupManagerService.startServiceForUser(mUserTwoId, mUserTwoService);
@@ -177,7 +181,7 @@
     /** Test that the service unregisters users when stopped. */
     @Test
     public void testStopServiceForUser_forUnknownUser_doesNothing() throws Exception {
-        Trampoline backupManagerService = createService();
+        BackupManagerService backupManagerService = createService();
         backupManagerService.setBackupServiceActive(mUserOneId, true);
         ShadowBinder.setCallingUid(Process.SYSTEM_UID);
 
@@ -194,7 +198,7 @@
     /** Test that the backup service routes methods correctly to the user that requests it. */
     @Test
     public void testDataChanged_onRegisteredUser_callsMethodForUser() throws Exception {
-        Trampoline backupManagerService = createService();
+        BackupManagerService backupManagerService = createService();
         registerUser(backupManagerService, mUserOneId, mUserOneService);
         setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
 
@@ -206,7 +210,7 @@
     /** Test that the backup service does not route methods for non-registered users. */
     @Test
     public void testDataChanged_onUnknownUser_doesNotPropagateCall() throws Exception {
-        Trampoline backupManagerService = createService();
+        BackupManagerService backupManagerService = createService();
         registerUser(backupManagerService, mUserOneId, mUserOneService);
         setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
 
@@ -218,7 +222,7 @@
     /** Test that the backup service routes methods correctly to the user that requests it. */
     @Test
     public void testAgentConnected_onRegisteredUser_callsMethodForUser() throws Exception {
-        Trampoline backupManagerService = createService();
+        BackupManagerService backupManagerService = createService();
         registerUser(backupManagerService, mUserOneId, mUserOneService);
         setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
         IBinder agentBinder = mock(IBinder.class);
@@ -231,7 +235,7 @@
     /** Test that the backup service does not route methods for non-registered users. */
     @Test
     public void testAgentConnected_onUnknownUser_doesNotPropagateCall() throws Exception {
-        Trampoline backupManagerService = createService();
+        BackupManagerService backupManagerService = createService();
         registerUser(backupManagerService, mUserOneId, mUserOneService);
         setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
         IBinder agentBinder = mock(IBinder.class);
@@ -244,7 +248,7 @@
     /** Test that the backup service routes methods correctly to the user that requests it. */
     @Test
     public void testOpComplete_onRegisteredUser_callsMethodForUser() throws Exception {
-        Trampoline backupManagerService = createService();
+        BackupManagerService backupManagerService = createService();
         registerUser(backupManagerService, mUserOneId, mUserOneService);
         setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
 
@@ -256,7 +260,7 @@
     /** Test that the backup service does not route methods for non-registered users. */
     @Test
     public void testOpComplete_onUnknownUser_doesNotPropagateCall() throws Exception {
-        Trampoline backupManagerService = createService();
+        BackupManagerService backupManagerService = createService();
         registerUser(backupManagerService, mUserOneId, mUserOneService);
         setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
 
@@ -272,7 +276,7 @@
     /** Test that the backup service routes methods correctly to the user that requests it. */
     @Test
     public void testInitializeTransports_onRegisteredUser_callsMethodForUser() throws Exception {
-        Trampoline backupManagerService = createService();
+        BackupManagerService backupManagerService = createService();
         registerUser(backupManagerService, mUserOneId, mUserOneService);
         setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
         String[] transports = {TEST_TRANSPORT};
@@ -285,7 +289,7 @@
     /** Test that the backup service does not route methods for non-registered users. */
     @Test
     public void testInitializeTransports_onUnknownUser_doesNotPropagateCall() throws Exception {
-        Trampoline backupManagerService = createService();
+        BackupManagerService backupManagerService = createService();
         registerUser(backupManagerService, mUserOneId, mUserOneService);
         setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
         String[] transports = {TEST_TRANSPORT};
@@ -298,7 +302,7 @@
     /** Test that the backup service routes methods correctly to the user that requests it. */
     @Test
     public void testClearBackupData_onRegisteredUser_callsMethodForUser() throws Exception {
-        Trampoline backupManagerService = createService();
+        BackupManagerService backupManagerService = createService();
         registerUser(backupManagerService, mUserOneId, mUserOneService);
         setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
 
@@ -310,7 +314,7 @@
     /** Test that the backup service does not route methods for non-registered users. */
     @Test
     public void testClearBackupData_onUnknownUser_doesNotPropagateCall() throws Exception {
-        Trampoline backupManagerService = createService();
+        BackupManagerService backupManagerService = createService();
         registerUser(backupManagerService, mUserOneId, mUserOneService);
         setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
 
@@ -322,7 +326,7 @@
     /** Test that the backup service routes methods correctly to the user that requests it. */
     @Test
     public void testGetCurrentTransport_onRegisteredUser_callsMethodForUser() throws Exception {
-        Trampoline backupManagerService = createService();
+        BackupManagerService backupManagerService = createService();
         registerUser(backupManagerService, mUserOneId, mUserOneService);
         setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
 
@@ -334,7 +338,7 @@
     /** Test that the backup service does not route methods for non-registered users. */
     @Test
     public void testGetCurrentTransport_onUnknownUser_doesNotPropagateCall() throws Exception {
-        Trampoline backupManagerService = createService();
+        BackupManagerService backupManagerService = createService();
         registerUser(backupManagerService, mUserOneId, mUserOneService);
         setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
 
@@ -347,7 +351,7 @@
     @Test
     public void testGetCurrentTransportComponent_onRegisteredUser_callsMethodForUser()
             throws Exception {
-        Trampoline backupManagerService = createService();
+        BackupManagerService backupManagerService = createService();
         registerUser(backupManagerService, mUserOneId, mUserOneService);
         setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
 
@@ -360,7 +364,7 @@
     @Test
     public void testGetCurrentTransportComponent_onUnknownUser_doesNotPropagateCall()
             throws Exception {
-        Trampoline backupManagerService = createService();
+        BackupManagerService backupManagerService = createService();
         registerUser(backupManagerService, mUserOneId, mUserOneService);
         setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
 
@@ -372,7 +376,7 @@
     /** Test that the backup service routes methods correctly to the user that requests it. */
     @Test
     public void testListAllTransports_onRegisteredUser_callsMethodForUser() throws Exception {
-        Trampoline backupManagerService = createService();
+        BackupManagerService backupManagerService = createService();
         registerUser(backupManagerService, mUserOneId, mUserOneService);
         setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
 
@@ -384,7 +388,7 @@
     /** Test that the backup service does not route methods for non-registered users. */
     @Test
     public void testListAllTransports_onUnknownUser_doesNotPropagateCall() throws Exception {
-        Trampoline backupManagerService = createService();
+        BackupManagerService backupManagerService = createService();
         registerUser(backupManagerService, mUserOneId, mUserOneService);
         setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
 
@@ -397,7 +401,7 @@
     @Test
     public void testListAllTransportComponents_onRegisteredUser_callsMethodForUser()
             throws Exception {
-        Trampoline backupManagerService = createService();
+        BackupManagerService backupManagerService = createService();
         registerUser(backupManagerService, mUserOneId, mUserOneService);
         setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
 
@@ -410,7 +414,7 @@
     @Test
     public void testListAllTransportComponents_onUnknownUser_doesNotPropagateCall()
             throws Exception {
-        Trampoline backupManagerService = createService();
+        BackupManagerService backupManagerService = createService();
         registerUser(backupManagerService, mUserOneId, mUserOneService);
         setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
 
@@ -422,7 +426,7 @@
     /** Test that the backup service routes methods correctly to the user that requests it. */
     @Test
     public void testSelectBackupTransport_onRegisteredUser_callsMethodForUser() throws Exception {
-        Trampoline backupManagerService = createService();
+        BackupManagerService backupManagerService = createService();
         registerUser(backupManagerService, mUserOneId, mUserOneService);
         setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
 
@@ -434,7 +438,7 @@
     /** Test that the backup service does not route methods for non-registered users. */
     @Test
     public void testSelectBackupTransport_onUnknownUser_doesNotPropagateCall() throws Exception {
-        Trampoline backupManagerService = createService();
+        BackupManagerService backupManagerService = createService();
         registerUser(backupManagerService, mUserOneId, mUserOneService);
         setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
 
@@ -446,7 +450,7 @@
     /** Test that the backup service routes methods correctly to the user that requests it. */
     @Test
     public void testSelectTransportAsync_onRegisteredUser_callsMethodForUser() throws Exception {
-        Trampoline backupManagerService = createService();
+        BackupManagerService backupManagerService = createService();
         registerUser(backupManagerService, mUserOneId, mUserOneService);
         setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
         TransportData transport = backupTransport();
@@ -463,7 +467,7 @@
     @Test
     public void testSelectBackupTransportAsync_onUnknownUser_doesNotPropagateCall()
             throws Exception {
-        Trampoline backupManagerService = createService();
+        BackupManagerService backupManagerService = createService();
         registerUser(backupManagerService, mUserOneId, mUserOneService);
         setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
         TransportData transport = backupTransport();
@@ -479,7 +483,7 @@
     /** Test that the backup service routes methods correctly to the user that requests it. */
     @Test
     public void testGetConfigurationIntent_onRegisteredUser_callsMethodForUser() throws Exception {
-        Trampoline backupManagerService = createService();
+        BackupManagerService backupManagerService = createService();
         registerUser(backupManagerService, mUserOneId, mUserOneService);
         setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
 
@@ -491,7 +495,7 @@
     /** Test that the backup service does not route methods for non-registered users. */
     @Test
     public void testGetConfigurationIntent_onUnknownUser_doesNotPropagateCall() throws Exception {
-        Trampoline backupManagerService = createService();
+        BackupManagerService backupManagerService = createService();
         registerUser(backupManagerService, mUserOneId, mUserOneService);
         setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
 
@@ -503,7 +507,7 @@
     /** Test that the backup service routes methods correctly to the user that requests it. */
     @Test
     public void testGetDestinationString_onRegisteredUser_callsMethodForUser() throws Exception {
-        Trampoline backupManagerService = createService();
+        BackupManagerService backupManagerService = createService();
         registerUser(backupManagerService, mUserOneId, mUserOneService);
         setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
 
@@ -515,7 +519,7 @@
     /** Test that the backup service does not route methods for non-registered users. */
     @Test
     public void testGetDestinationString_onUnknownUser_doesNotPropagateCall() throws Exception {
-        Trampoline backupManagerService = createService();
+        BackupManagerService backupManagerService = createService();
         registerUser(backupManagerService, mUserOneId, mUserOneService);
         setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
 
@@ -527,7 +531,7 @@
     /** Test that the backup service routes methods correctly to the user that requests it. */
     @Test
     public void testGetDataManagementIntent_onRegisteredUser_callsMethodForUser() throws Exception {
-        Trampoline backupManagerService = createService();
+        BackupManagerService backupManagerService = createService();
         registerUser(backupManagerService, mUserOneId, mUserOneService);
         setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
 
@@ -539,7 +543,7 @@
     /** Test that the backup service does not route methods for non-registered users. */
     @Test
     public void testGetDataManagementIntent_onUnknownUser_doesNotPropagateCall() throws Exception {
-        Trampoline backupManagerService = createService();
+        BackupManagerService backupManagerService = createService();
         registerUser(backupManagerService, mUserOneId, mUserOneService);
         setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
 
@@ -551,7 +555,7 @@
     /** Test that the backup service routes methods correctly to the user that requests it. */
     @Test
     public void testGetDataManagementLabel_onRegisteredUser_callsMethodForUser() throws Exception {
-        Trampoline backupManagerService = createService();
+        BackupManagerService backupManagerService = createService();
         registerUser(backupManagerService, mUserOneId, mUserOneService);
         setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
 
@@ -563,7 +567,7 @@
     /** Test that the backup service does not route methods for non-registered users. */
     @Test
     public void testGetDataManagementLabel_onUnknownUser_doesNotPropagateCall() throws Exception {
-        Trampoline backupManagerService = createService();
+        BackupManagerService backupManagerService = createService();
         registerUser(backupManagerService, mUserOneId, mUserOneService);
         setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
 
@@ -576,7 +580,7 @@
     @Test
     public void testUpdateTransportAttributes_onRegisteredUser_callsMethodForUser()
             throws Exception {
-        Trampoline backupManagerService = createService();
+        BackupManagerService backupManagerService = createService();
         registerUser(backupManagerService, mUserOneId, mUserOneService);
         setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
         TransportData transport = backupTransport();
@@ -606,7 +610,7 @@
     @Test
     public void testUpdateTransportAttributes_onUnknownUser_doesNotPropagateCall()
             throws Exception {
-        Trampoline backupManagerService = createService();
+        BackupManagerService backupManagerService = createService();
         registerUser(backupManagerService, mUserOneId, mUserOneService);
         setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
         TransportData transport = backupTransport();
@@ -642,7 +646,7 @@
      */
     @Test
     public void testSetBackupEnabled_withoutPermission_throwsSecurityExceptionForNonCallingUser() {
-        Trampoline backupManagerService = createService();
+        BackupManagerService backupManagerService = createService();
         registerUser(backupManagerService, mUserOneId, mUserOneService);
         setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
 
@@ -657,7 +661,7 @@
      */
     @Test
     public void testSetBackupEnabled_withPermission_propagatesForNonCallingUser() {
-        Trampoline backupManagerService = createService();
+        BackupManagerService backupManagerService = createService();
         registerUser(backupManagerService, mUserOneId, mUserOneService);
         registerUser(backupManagerService, mUserTwoId, mUserTwoService);
 
@@ -671,7 +675,7 @@
     /** Test that the backup service routes methods correctly to the user that requests it. */
     @Test
     public void testSetBackupEnabled_onRegisteredUser_callsMethodForUser() throws Exception {
-        Trampoline backupManagerService = createService();
+        BackupManagerService backupManagerService = createService();
         registerUser(backupManagerService, mUserOneId, mUserOneService);
         setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
 
@@ -683,7 +687,7 @@
     /** Test that the backup service does not route methods for non-registered users. */
     @Test
     public void testSetBackupEnabled_onUnknownUser_doesNotPropagateCall() throws Exception {
-        Trampoline backupManagerService = createService();
+        BackupManagerService backupManagerService = createService();
         registerUser(backupManagerService, mUserOneId, mUserOneService);
         setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
 
@@ -695,7 +699,7 @@
     /** Test that the backup service routes methods correctly to the user that requests it. */
     @Test
     public void testSetAutoRestore_onRegisteredUser_callsMethodForUser() throws Exception {
-        Trampoline backupManagerService = createService();
+        BackupManagerService backupManagerService = createService();
         registerUser(backupManagerService, mUserOneId, mUserOneService);
         setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
 
@@ -707,7 +711,7 @@
     /** Test that the backup service does not route methods for non-registered users. */
     @Test
     public void testSetAutoRestore_onUnknownUser_doesNotPropagateCall() throws Exception {
-        Trampoline backupManagerService = createService();
+        BackupManagerService backupManagerService = createService();
         registerUser(backupManagerService, mUserOneId, mUserOneService);
         setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
 
@@ -719,7 +723,7 @@
     /** Test that the backup service routes methods correctly to the user that requests it. */
     @Test
     public void testIsBackupEnabled_onRegisteredUser_callsMethodForUser() throws Exception {
-        Trampoline backupManagerService = createService();
+        BackupManagerService backupManagerService = createService();
         registerUser(backupManagerService, mUserOneId, mUserOneService);
         setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
 
@@ -731,7 +735,7 @@
     /** Test that the backup service does not route methods for non-registered users. */
     @Test
     public void testIsBackupEnabled_onUnknownUser_doesNotPropagateCall() throws Exception {
-        Trampoline backupManagerService = createService();
+        BackupManagerService backupManagerService = createService();
         registerUser(backupManagerService, mUserOneId, mUserOneService);
         setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
 
@@ -747,7 +751,7 @@
     /** Test that the backup service routes methods correctly to the user that requests it. */
     @Test
     public void testIsAppEligibleForBackup_onRegisteredUser_callsMethodForUser() throws Exception {
-        Trampoline backupManagerService = createService();
+        BackupManagerService backupManagerService = createService();
         registerUser(backupManagerService, mUserOneId, mUserOneService);
         setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
 
@@ -759,7 +763,7 @@
     /** Test that the backup service does not route methods for non-registered users. */
     @Test
     public void testIsAppEligibleForBackup_onUnknownUser_doesNotPropagateCall() throws Exception {
-        Trampoline backupManagerService = createService();
+        BackupManagerService backupManagerService = createService();
         registerUser(backupManagerService, mUserOneId, mUserOneService);
         setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
 
@@ -772,7 +776,7 @@
     @Test
     public void testFilterAppsEligibleForBackup_onRegisteredUser_callsMethodForUser()
             throws Exception {
-        Trampoline backupManagerService = createService();
+        BackupManagerService backupManagerService = createService();
         registerUser(backupManagerService, mUserOneId, mUserOneService);
         setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
         String[] packages = {TEST_PACKAGE};
@@ -786,7 +790,7 @@
     @Test
     public void testFilterAppsEligibleForBackup_onUnknownUser_doesNotPropagateCall()
             throws Exception {
-        Trampoline backupManagerService = createService();
+        BackupManagerService backupManagerService = createService();
         registerUser(backupManagerService, mUserOneId, mUserOneService);
         setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
         String[] packages = {TEST_PACKAGE};
@@ -802,7 +806,7 @@
      */
     @Test
     public void testBackupNow_withoutPermission_throwsSecurityExceptionForNonCallingUser() {
-        Trampoline backupManagerService = createService();
+        BackupManagerService backupManagerService = createService();
         registerUser(backupManagerService, mUserOneId, mUserOneService);
         setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
 
@@ -815,7 +819,7 @@
      */
     @Test
     public void testBackupNow_withPermission_propagatesForNonCallingUser() {
-        Trampoline backupManagerService = createService();
+        BackupManagerService backupManagerService = createService();
         registerUser(backupManagerService, mUserOneId, mUserOneService);
         registerUser(backupManagerService, mUserTwoId, mUserTwoService);
 
@@ -829,7 +833,7 @@
     /** Test that the backup service routes methods correctly to the user that requests it. */
     @Test
     public void testBackupNow_onRegisteredUser_callsMethodForUser() throws Exception {
-        Trampoline backupManagerService = createService();
+        BackupManagerService backupManagerService = createService();
         registerUser(backupManagerService, mUserOneId, mUserOneService);
         setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
 
@@ -841,7 +845,7 @@
     /** Test that the backup service does not route methods for non-registered users. */
     @Test
     public void testBackupNow_onUnknownUser_doesNotPropagateCall() throws Exception {
-        Trampoline backupManagerService = createService();
+        BackupManagerService backupManagerService = createService();
         registerUser(backupManagerService, mUserOneId, mUserOneService);
         setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
 
@@ -856,7 +860,7 @@
      */
     @Test
     public void testRequestBackup_withoutPermission_throwsSecurityExceptionForNonCallingUser() {
-        Trampoline backupManagerService = createService();
+        BackupManagerService backupManagerService = createService();
         registerUser(backupManagerService, mUserOneId, mUserOneService);
         setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
         String[] packages = {TEST_PACKAGE};
@@ -876,7 +880,7 @@
      */
     @Test
     public void testRequestBackup_withPermission_propagatesForNonCallingUser() {
-        Trampoline backupManagerService = createService();
+        BackupManagerService backupManagerService = createService();
         registerUser(backupManagerService, mUserOneId, mUserOneService);
         registerUser(backupManagerService, mUserTwoId, mUserTwoService);
 
@@ -893,7 +897,7 @@
     /** Test that the backup service routes methods correctly to the user that requests it. */
     @Test
     public void testRequestBackup_onRegisteredUser_callsMethodForUser() throws Exception {
-        Trampoline backupManagerService = createService();
+        BackupManagerService backupManagerService = createService();
         registerUser(backupManagerService, mUserOneId, mUserOneService);
         String[] packages = {TEST_PACKAGE};
         IBackupObserver observer = mock(IBackupObserver.class);
@@ -908,7 +912,7 @@
     /** Test that the backup service routes methods correctly to the user that requests it. */
     @Test
     public void testRequestBackup_onUnknownUser_doesNotPropagateCall() throws Exception {
-        Trampoline backupManagerService = createService();
+        BackupManagerService backupManagerService = createService();
         registerUser(backupManagerService, mUserOneId, mUserOneService);
         String[] packages = {TEST_PACKAGE};
         IBackupObserver observer = mock(IBackupObserver.class);
@@ -926,7 +930,7 @@
      */
     @Test
     public void testCancelBackups_withoutPermission_throwsSecurityExceptionForNonCallingUser() {
-        Trampoline backupManagerService = createService();
+        BackupManagerService backupManagerService = createService();
         registerUser(backupManagerService, mUserOneId, mUserOneService);
         setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
 
@@ -939,7 +943,7 @@
      */
     @Test
     public void testCancelBackups_withPermission_propagatesForNonCallingUser() {
-        Trampoline backupManagerService = createService();
+        BackupManagerService backupManagerService = createService();
         registerUser(backupManagerService, mUserOneId, mUserOneService);
         registerUser(backupManagerService, mUserTwoId, mUserTwoService);
         setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ true);
@@ -952,7 +956,7 @@
     /** Test that the backup service routes methods correctly to the user that requests it. */
     @Test
     public void testCancelBackups_onRegisteredUser_callsMethodForUser() throws Exception {
-        Trampoline backupManagerService = createService();
+        BackupManagerService backupManagerService = createService();
         registerUser(backupManagerService, mUserOneId, mUserOneService);
         setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
 
@@ -964,7 +968,7 @@
     /** Test that the backup service does not route methods for non-registered users. */
     @Test
     public void testCancelBackups_onUnknownUser_doesNotPropagateCall() throws Exception {
-        Trampoline backupManagerService = createService();
+        BackupManagerService backupManagerService = createService();
         registerUser(backupManagerService, mUserOneId, mUserOneService);
         setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
 
@@ -976,7 +980,7 @@
     /** Test that the backup service routes methods correctly to the user that requests it. */
     @Test
     public void testBeginFullBackup_onRegisteredUser_callsMethodForUser() throws Exception {
-        Trampoline backupManagerService = createService();
+        BackupManagerService backupManagerService = createService();
         registerUser(backupManagerService, UserHandle.USER_SYSTEM, mUserOneService);
         FullBackupJob job = new FullBackupJob();
 
@@ -988,7 +992,7 @@
     /** Test that the backup service does not route methods for non-registered users. */
     @Test
     public void testBeginFullBackup_onUnknownUser_doesNotPropagateCall() throws Exception {
-        Trampoline backupManagerService = createService();
+        BackupManagerService backupManagerService = createService();
         FullBackupJob job = new FullBackupJob();
 
         backupManagerService.beginFullBackup(UserHandle.USER_SYSTEM, job);
@@ -999,7 +1003,7 @@
     /** Test that the backup service routes methods correctly to the user that requests it. */
     @Test
     public void testEndFullBackup_onRegisteredUser_callsMethodForUser() throws Exception {
-        Trampoline backupManagerService = createService();
+        BackupManagerService backupManagerService = createService();
         registerUser(backupManagerService, UserHandle.USER_SYSTEM, mUserOneService);
 
         backupManagerService.endFullBackup(UserHandle.USER_SYSTEM);
@@ -1010,7 +1014,7 @@
     /** Test that the backup service does not route methods for non-registered users. */
     @Test
     public void testEndFullBackup_onUnknownUser_doesNotPropagateCall() throws Exception {
-        Trampoline backupManagerService = createService();
+        BackupManagerService backupManagerService = createService();
 
         backupManagerService.endFullBackup(UserHandle.USER_SYSTEM);
 
@@ -1020,7 +1024,7 @@
     /** Test that the backup service routes methods correctly to the user that requests it. */
     @Test
     public void testFullTransportBackup_onRegisteredUser_callsMethodForUser() throws Exception {
-        Trampoline backupManagerService = createService();
+        BackupManagerService backupManagerService = createService();
         registerUser(backupManagerService, mUserOneId, mUserOneService);
         setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
         String[] packages = {TEST_PACKAGE};
@@ -1033,7 +1037,7 @@
     /** Test that the backup service does not route methods for non-registered users. */
     @Test
     public void testFullTransportBackup_onUnknownUser_doesNotPropagateCall() throws Exception {
-        Trampoline backupManagerService = createService();
+        BackupManagerService backupManagerService = createService();
         registerUser(backupManagerService, mUserOneId, mUserOneService);
         setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
         String[] packages = {TEST_PACKAGE};
@@ -1050,7 +1054,7 @@
     /** Test that the backup service routes methods correctly to the user that requests it. */
     @Test
     public void testRestoreAtInstall_onRegisteredUser_callsMethodForUser() throws Exception {
-        Trampoline backupManagerService = createService();
+        BackupManagerService backupManagerService = createService();
         registerUser(backupManagerService, mUserOneId, mUserOneService);
         setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
 
@@ -1062,7 +1066,7 @@
     /** Test that the backup service does not route methods for non-registered users. */
     @Test
     public void testRestoreAtInstall_onUnknownUser_doesNotPropagateCall() throws Exception {
-        Trampoline backupManagerService = createService();
+        BackupManagerService backupManagerService = createService();
         registerUser(backupManagerService, mUserOneId, mUserOneService);
         setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
 
@@ -1074,7 +1078,7 @@
     /** Test that the backup service routes methods correctly to the user that requests it. */
     @Test
     public void testBeginRestoreSession_onRegisteredUser_callsMethodForUser() throws Exception {
-        Trampoline backupManagerService = createService();
+        BackupManagerService backupManagerService = createService();
         registerUser(backupManagerService, mUserOneId, mUserOneService);
         setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
 
@@ -1086,7 +1090,7 @@
     /** Test that the backup service does not route methods for non-registered users. */
     @Test
     public void testBeginRestoreSession_onUnknownUser_doesNotPropagateCall() throws Exception {
-        Trampoline backupManagerService = createService();
+        BackupManagerService backupManagerService = createService();
         registerUser(backupManagerService, mUserOneId, mUserOneService);
         setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
 
@@ -1099,7 +1103,7 @@
     @Test
     public void testGetAvailableRestoreToken_onRegisteredUser_callsMethodForUser()
             throws Exception {
-        Trampoline backupManagerService = createService();
+        BackupManagerService backupManagerService = createService();
         registerUser(backupManagerService, mUserOneId, mUserOneService);
         setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
 
@@ -1111,7 +1115,7 @@
     /** Test that the backup service does not route methods for non-registered users. */
     @Test
     public void testGetAvailableRestoreToken_onUnknownUser_doesNotPropagateCall() throws Exception {
-        Trampoline backupManagerService = createService();
+        BackupManagerService backupManagerService = createService();
         registerUser(backupManagerService, mUserOneId, mUserOneService);
         setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
 
@@ -1127,7 +1131,7 @@
     /** Test that the backup service routes methods correctly to the user that requests it. */
     @Test
     public void testSetBackupPassword_onRegisteredUser_callsMethodForUser() throws Exception {
-        Trampoline backupManagerService = createService();
+        BackupManagerService backupManagerService = createService();
         registerUser(backupManagerService, UserHandle.USER_SYSTEM, mUserOneService);
         ShadowBinder.setCallingUserHandle(UserHandle.of(UserHandle.USER_SYSTEM));
 
@@ -1139,7 +1143,7 @@
     /** Test that the backup service does not route methods for non-registered users. */
     @Test
     public void testSetBackupPassword_onUnknownUser_doesNotPropagateCall() throws Exception {
-        Trampoline backupManagerService = createService();
+        BackupManagerService backupManagerService = createService();
 
         backupManagerService.setBackupPassword("currentPassword", "newPassword");
 
@@ -1149,7 +1153,7 @@
     /** Test that the backup service routes methods correctly to the user that requests it. */
     @Test
     public void testHasBackupPassword_onRegisteredUser_callsMethodForUser() throws Exception {
-        Trampoline backupManagerService = createService();
+        BackupManagerService backupManagerService = createService();
         registerUser(backupManagerService, UserHandle.USER_SYSTEM, mUserOneService);
         ShadowBinder.setCallingUserHandle(UserHandle.of(UserHandle.USER_SYSTEM));
 
@@ -1161,7 +1165,7 @@
     /** Test that the backup service does not route methods for non-registered users. */
     @Test
     public void testHasBackupPassword_onUnknownUser_doesNotPropagateCall() throws Exception {
-        Trampoline backupManagerService = createService();
+        BackupManagerService backupManagerService = createService();
 
         backupManagerService.hasBackupPassword();
 
@@ -1174,7 +1178,7 @@
      */
     @Test
     public void testAdbBackup_withoutPermission_throwsSecurityExceptionForNonCallingUser() {
-        Trampoline backupManagerService = createSystemRegisteredService();
+        BackupManagerService backupManagerService = createSystemRegisteredService();
         registerUser(backupManagerService, mUserOneId, mUserOneService);
         registerUser(backupManagerService, mUserTwoId, mUserTwoService);
         setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
@@ -1202,7 +1206,7 @@
      */
     @Test
     public void testAdbBackup_withPermission_propagatesForNonCallingUser() throws Exception {
-        Trampoline backupManagerService = createSystemRegisteredService();
+        BackupManagerService backupManagerService = createSystemRegisteredService();
         registerUser(backupManagerService, mUserOneId, mUserOneService);
         registerUser(backupManagerService, mUserTwoId, mUserTwoService);
 
@@ -1239,7 +1243,7 @@
     /** Test that the backup service routes methods correctly to the user that requests it. */
     @Test
     public void testAdbBackup_onRegisteredUser_callsMethodForUser() throws Exception {
-        Trampoline backupManagerService = createSystemRegisteredService();
+        BackupManagerService backupManagerService = createSystemRegisteredService();
         registerUser(backupManagerService, mUserOneId, mUserOneService);
         ParcelFileDescriptor parcelFileDescriptor = getFileDescriptorForAdbTest();
         setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
@@ -1274,7 +1278,7 @@
     /** Test that the backup service does not route methods for non-registered users. */
     @Test
     public void testAdbBackup_onUnknownUser_doesNotPropagateCall() throws Exception {
-        Trampoline backupManagerService = createSystemRegisteredService();
+        BackupManagerService backupManagerService = createSystemRegisteredService();
         registerUser(backupManagerService, mUserOneId, mUserOneService);
         ParcelFileDescriptor parcelFileDescriptor = getFileDescriptorForAdbTest();
         setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
@@ -1312,7 +1316,7 @@
      */
     @Test
     public void testAdbRestore_withoutPermission_throwsSecurityExceptionForNonCallingUser() {
-        Trampoline backupManagerService = createSystemRegisteredService();
+        BackupManagerService backupManagerService = createSystemRegisteredService();
         registerUser(backupManagerService, mUserOneId, mUserOneService);
         registerUser(backupManagerService, mUserTwoId, mUserTwoService);
         setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
@@ -1327,7 +1331,7 @@
      */
     @Test
     public void testAdbRestore_withPermission_propagatesForNonCallingUser() throws Exception {
-        Trampoline backupManagerService = createSystemRegisteredService();
+        BackupManagerService backupManagerService = createSystemRegisteredService();
         registerUser(backupManagerService, mUserOneId, mUserOneService);
         registerUser(backupManagerService, mUserTwoId, mUserTwoService);
         ParcelFileDescriptor parcelFileDescriptor = getFileDescriptorForAdbTest();
@@ -1341,7 +1345,7 @@
     /** Test that the backup service routes methods correctly to the user that requests it. */
     @Test
     public void testAdbRestore_onRegisteredUser_callsMethodForUser() throws Exception {
-        Trampoline backupManagerService = createSystemRegisteredService();
+        BackupManagerService backupManagerService = createSystemRegisteredService();
         registerUser(backupManagerService, mUserOneId, mUserOneService);
         ParcelFileDescriptor parcelFileDescriptor = getFileDescriptorForAdbTest();
         setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
@@ -1354,7 +1358,7 @@
     /** Test that the backup service does not route methods for non-registered users. */
     @Test
     public void testAdbRestore_onUnknownUser_doesNotPropagateCall() throws Exception {
-        Trampoline backupManagerService = createSystemRegisteredService();
+        BackupManagerService backupManagerService = createSystemRegisteredService();
         registerUser(backupManagerService, mUserOneId, mUserOneService);
         ParcelFileDescriptor parcelFileDescriptor = getFileDescriptorForAdbTest();
         setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
@@ -1374,7 +1378,7 @@
     @Test
     public void testAcknowledgeAdbBackupOrRestore_onRegisteredUser_callsMethodForUser()
             throws Exception {
-        Trampoline backupManagerService = createService();
+        BackupManagerService backupManagerService = createService();
         registerUser(backupManagerService, mUserOneId, mUserOneService);
         setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
         IFullBackupRestoreObserver observer = mock(IFullBackupRestoreObserver.class);
@@ -1400,7 +1404,7 @@
     @Test
     public void testAcknowledgeAdbBackupOrRestore_onUnknownUser_doesNotPropagateCall()
             throws Exception {
-        Trampoline backupManagerService = createService();
+        BackupManagerService backupManagerService = createService();
         registerUser(backupManagerService, mUserOneId, mUserOneService);
         setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
         IFullBackupRestoreObserver observer = mock(IFullBackupRestoreObserver.class);
@@ -1430,7 +1434,7 @@
     @Test
     public void testDump_onRegisteredUser_callsMethodForUser() throws Exception {
         grantDumpPermissions();
-        Trampoline backupManagerService = createSystemRegisteredService();
+        BackupManagerService backupManagerService = createSystemRegisteredService();
         File testFile = createTestFile();
         FileDescriptor fileDescriptor = new FileDescriptor();
         PrintWriter printWriter = new PrintWriter(testFile);
@@ -1446,7 +1450,7 @@
     @Test
     public void testDump_onUnknownUser_doesNotPropagateCall() throws Exception {
         grantDumpPermissions();
-        Trampoline backupManagerService = createService();
+        BackupManagerService backupManagerService = createService();
         File testFile = createTestFile();
         FileDescriptor fileDescriptor = new FileDescriptor();
         PrintWriter printWriter = new PrintWriter(testFile);
@@ -1461,7 +1465,7 @@
     @Test
     public void testDump_users_dumpsListOfRegisteredUsers() {
         grantDumpPermissions();
-        Trampoline backupManagerService = createSystemRegisteredService();
+        BackupManagerService backupManagerService = createSystemRegisteredService();
         registerUser(backupManagerService, mUserOneId, mUserOneService);
         StringWriter out = new StringWriter();
         PrintWriter writer = new PrintWriter(out);
@@ -1471,7 +1475,7 @@
 
         writer.flush();
         assertEquals(
-                String.format("%s %d %d\n", Trampoline.DUMP_RUNNING_USERS_MESSAGE,
+                String.format("%s %d %d\n", BackupManagerService.DUMP_RUNNING_USERS_MESSAGE,
                         UserHandle.USER_SYSTEM, mUserOneId),
                 out.toString());
     }
@@ -1493,7 +1497,7 @@
      */
     @Test
     public void testGetServiceForUser_withoutPermission_throwsSecurityExceptionForNonCallingUser() {
-        Trampoline backupManagerService = createService();
+        BackupManagerService backupManagerService = createService();
         registerUser(backupManagerService, mUserOneId, mUserOneService);
         setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
 
@@ -1510,7 +1514,7 @@
      */
     @Test
     public void testGetServiceForUserIfCallerHasPermission_withPermission_worksForNonCallingUser() {
-        Trampoline backupManagerService = createService();
+        BackupManagerService backupManagerService = createService();
         registerUser(backupManagerService, mUserOneId, mUserOneService);
         setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ true);
 
@@ -1525,7 +1529,7 @@
      */
     @Test
     public void testGetServiceForUserIfCallerHasPermission_withoutPermission_worksForCallingUser() {
-        Trampoline backupManagerService = createService();
+        BackupManagerService backupManagerService = createService();
         registerUser(backupManagerService, mUserOneId, mUserOneService);
         setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
 
@@ -1534,25 +1538,98 @@
                 backupManagerService.getServiceForUserIfCallerHasPermission(mUserOneId, "test"));
     }
 
-    private Trampoline createService() {
-        return new Trampoline(mContext);
+    /**
+     * Test verifying that {@link BackupManagerService#MORE_DEBUG} is set to {@code false}. This is
+     * specifically to prevent overloading the logs in production.
+     */
+    @Test
+    public void testMoreDebug_isFalse() throws Exception {
+        boolean moreDebug = BackupManagerService.MORE_DEBUG;
+
+        assertThat(moreDebug).isFalse();
     }
 
-    private Trampoline createSystemRegisteredService() {
-        Trampoline trampoline = createService();
-        registerUser(trampoline, UserHandle.USER_SYSTEM, mUserSystemService);
-        return trampoline;
+    /** Test that the constructor handles {@code null} parameters. */
+    @Test
+    public void testConstructor_withNullContext_throws() throws Exception {
+        expectThrows(
+                NullPointerException.class,
+                () ->
+                        new BackupManagerService(
+                                /* context */ null,
+                                new SparseArray<>()));
+    }
+
+    /** Test that the constructor does not create {@link UserBackupManagerService} instances. */
+    @Test
+    public void testConstructor_doesNotRegisterUsers() throws Exception {
+        BackupManagerService backupManagerService = createService();
+
+        assertThat(backupManagerService.getUserServices().size()).isEqualTo(0);
+    }
+
+    // ---------------------------------------------
+    //  Lifecycle tests
+    // ---------------------------------------------
+
+    /** testOnStart_publishesService */
+    @Test
+    public void testOnStart_publishesService() {
+        BackupManagerService backupManagerService = mock(BackupManagerService.class);
+        BackupManagerService.Lifecycle lifecycle =
+                spy(new BackupManagerService.Lifecycle(mContext, backupManagerService));
+        doNothing().when(lifecycle).publishService(anyString(), any());
+
+        lifecycle.onStart();
+
+        verify(lifecycle).publishService(Context.BACKUP_SERVICE, backupManagerService);
+    }
+
+    /** testOnUnlockUser_forwards */
+    @Test
+    public void testOnUnlockUser_forwards() {
+        BackupManagerService backupManagerService = mock(BackupManagerService.class);
+        BackupManagerService.Lifecycle lifecycle =
+                new BackupManagerService.Lifecycle(mContext, backupManagerService);
+
+        lifecycle.onUnlockUser(UserHandle.USER_SYSTEM);
+
+        verify(backupManagerService).onUnlockUser(UserHandle.USER_SYSTEM);
+    }
+
+    /** testOnStopUser_forwards */
+    @Test
+    public void testOnStopUser_forwards() {
+        BackupManagerService backupManagerService = mock(BackupManagerService.class);
+        BackupManagerService.Lifecycle lifecycle =
+                new BackupManagerService.Lifecycle(mContext, backupManagerService);
+
+        lifecycle.onStopUser(UserHandle.USER_SYSTEM);
+
+        verify(backupManagerService).onStopUser(UserHandle.USER_SYSTEM);
+    }
+
+    private BackupManagerService createService() {
+        return new BackupManagerService(mContext);
+    }
+
+    private BackupManagerService createSystemRegisteredService() {
+        BackupManagerService backupManagerService = createService();
+        registerUser(backupManagerService, UserHandle.USER_SYSTEM, mUserSystemService);
+        return backupManagerService;
     }
 
     private void registerUser(
-            Trampoline trampoline, int userId, UserBackupManagerService userBackupManagerService) {
-        trampoline.setBackupServiceActive(userId, true);
-        trampoline.startServiceForUser(userId, userBackupManagerService);
+            BackupManagerService backupManagerService,
+            int userId,
+            UserBackupManagerService userBackupManagerService) {
+        backupManagerService.setBackupServiceActive(userId, true);
+        backupManagerService.startServiceForUser(userId, userBackupManagerService);
     }
 
-    private Trampoline createServiceAndRegisterUser(
+    private BackupManagerService createServiceAndRegisterUser(
             int userId, UserBackupManagerService userBackupManagerService) {
-        Trampoline backupManagerService = createService();
+        BackupManagerService backupManagerService = createService();
         backupManagerService.setBackupServiceActive(userBackupManagerService.getUserId(), true);
         backupManagerService.startServiceForUser(userId, userBackupManagerService);
         return backupManagerService;
diff --git a/services/robotests/backup/src/com/android/server/backup/BackupManagerServiceTest.java b/services/robotests/backup/src/com/android/server/backup/BackupManagerServiceTest.java
deleted file mode 100644
index a034474..0000000
--- a/services/robotests/backup/src/com/android/server/backup/BackupManagerServiceTest.java
+++ /dev/null
@@ -1,231 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.backup;
-
-import static android.Manifest.permission.BACKUP;
-import static android.Manifest.permission.INTERACT_ACROSS_USERS_FULL;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyString;
-import static org.mockito.Mockito.doNothing;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.spy;
-import static org.mockito.Mockito.verify;
-import static org.robolectric.Shadows.shadowOf;
-import static org.testng.Assert.expectThrows;
-
-import android.annotation.UserIdInt;
-import android.app.Application;
-import android.content.Context;
-import android.os.ParcelFileDescriptor;
-import android.os.Process;
-import android.os.UserHandle;
-import android.os.UserManager;
-import android.platform.test.annotations.Presubmit;
-import android.util.SparseArray;
-
-import com.android.server.testing.shadows.ShadowApplicationPackageManager;
-import com.android.server.testing.shadows.ShadowBinder;
-import com.android.server.testing.shadows.ShadowEnvironment;
-import com.android.server.testing.shadows.ShadowSystemServiceRegistry;
-import com.android.server.testing.shadows.ShadowUserManager;
-
-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.RuntimeEnvironment;
-import org.robolectric.annotation.Config;
-import org.robolectric.shadow.api.Shadow;
-import org.robolectric.shadows.ShadowContextWrapper;
-
-import java.io.File;
-
-/** Tests for the user-aware backup/restore system service {@link BackupManagerService}. */
-@RunWith(RobolectricTestRunner.class)
-@Config(
-        shadows = {
-            ShadowApplicationPackageManager.class,
-            ShadowBinder.class,
-            ShadowEnvironment.class,
-            ShadowSystemServiceRegistry.class,
-            ShadowUserManager.class,
-        })
-@Presubmit
-public class BackupManagerServiceTest {
-    private static final String TEST_PACKAGE = "package";
-    private static final String TEST_TRANSPORT = "transport";
-    private static final String[] ADB_TEST_PACKAGES = {TEST_PACKAGE};
-
-    private ShadowContextWrapper mShadowContext;
-    private ShadowUserManager mShadowUserManager;
-    private Context mContext;
-    private Trampoline mTrampoline;
-    @UserIdInt private int mUserOneId;
-    @UserIdInt private int mUserTwoId;
-    @Mock private UserBackupManagerService mUserOneService;
-    @Mock private UserBackupManagerService mUserTwoService;
-
-    /** Initialize {@link BackupManagerService}. */
-    @Before
-    public void setUp() throws Exception {
-        MockitoAnnotations.initMocks(this);
-
-        Application application = RuntimeEnvironment.application;
-        mContext = application;
-        mShadowContext = shadowOf(application);
-        mShadowUserManager = Shadow.extract(UserManager.get(application));
-
-        mUserOneId = UserHandle.USER_SYSTEM + 1;
-        mUserTwoId = mUserOneId + 1;
-        mShadowUserManager.addUser(mUserOneId, "mUserOneId", 0);
-        mShadowUserManager.addUser(mUserTwoId, "mUserTwoId", 0);
-
-        mShadowContext.grantPermissions(BACKUP);
-        mShadowContext.grantPermissions(INTERACT_ACROSS_USERS_FULL);
-
-        mTrampoline = new Trampoline(mContext);
-        ShadowBinder.setCallingUid(Process.SYSTEM_UID);
-    }
-
-    /**
-     * Clean up and reset state that was created for testing {@link BackupManagerService}
-     * operations.
-     */
-    @After
-    public void tearDown() throws Exception {
-        ShadowBinder.reset();
-    }
-
-    /**
-     * Test verifying that {@link BackupManagerService#MORE_DEBUG} is set to {@code false}. This is
-     * specifically to prevent overloading the logs in production.
-     */
-    @Test
-    public void testMoreDebug_isFalse() throws Exception {
-        boolean moreDebug = BackupManagerService.MORE_DEBUG;
-
-        assertThat(moreDebug).isFalse();
-    }
-
-    /** Test that the constructor does not create {@link UserBackupManagerService} instances. */
-    @Test
-    public void testConstructor_doesNotRegisterUsers() throws Exception {
-        BackupManagerService backupManagerService = createService();
-
-        assertThat(mTrampoline.getUserServices().size()).isEqualTo(0);
-    }
-
-    /** Test that the constructor handles {@code null} parameters. */
-    @Test
-    public void testConstructor_withNullContext_throws() throws Exception {
-        expectThrows(
-                NullPointerException.class,
-                () ->
-                        new BackupManagerService(
-                                /* context */ null,
-                                new Trampoline(mContext),
-                                new SparseArray<>()));
-    }
-
-    /** Test that the constructor handles {@code null} parameters. */
-    @Test
-    public void testConstructor_withNullTrampoline_throws() throws Exception {
-        expectThrows(
-                NullPointerException.class,
-                () ->
-                        new BackupManagerService(
-                                mContext, /* trampoline */ null, new SparseArray<>()));
-    }
-
-    // ---------------------------------------------
-    //  Lifecycle tests
-    // ---------------------------------------------
-
-    /** testOnStart_publishesService */
-    @Test
-    public void testOnStart_publishesService() {
-        Trampoline trampoline = mock(Trampoline.class);
-        BackupManagerService.Lifecycle lifecycle =
-                spy(new BackupManagerService.Lifecycle(mContext, trampoline));
-        doNothing().when(lifecycle).publishService(anyString(), any());
-
-        lifecycle.onStart();
-
-        verify(lifecycle).publishService(Context.BACKUP_SERVICE, trampoline);
-    }
-
-    /** testOnUnlockUser_forwards */
-    @Test
-    public void testOnUnlockUser_forwards() {
-        Trampoline trampoline = mock(Trampoline.class);
-        BackupManagerService.Lifecycle lifecycle =
-                new BackupManagerService.Lifecycle(mContext, trampoline);
-
-        lifecycle.onUnlockUser(UserHandle.USER_SYSTEM);
-
-        verify(trampoline).onUnlockUser(UserHandle.USER_SYSTEM);
-    }
-
-    /** testOnStopUser_forwards */
-    @Test
-    public void testOnStopUser_forwards() {
-        Trampoline trampoline = mock(Trampoline.class);
-        BackupManagerService.Lifecycle lifecycle =
-                new BackupManagerService.Lifecycle(mContext, trampoline);
-
-        lifecycle.onStopUser(UserHandle.USER_SYSTEM);
-
-        verify(trampoline).onStopUser(UserHandle.USER_SYSTEM);
-    }
-
-    private BackupManagerService createService() {
-        mShadowContext.grantPermissions(BACKUP);
-        return new BackupManagerService(mContext, mTrampoline, mTrampoline.getUserServices());
-    }
-
-    private void registerUser(int userId, UserBackupManagerService userBackupManagerService) {
-        mTrampoline.setBackupServiceActive(userId, true);
-        mTrampoline.startServiceForUser(userId, userBackupManagerService);
-    }
-
-    /**
-     * Sets the calling user to {@code userId} and grants the permission INTERACT_ACROSS_USERS_FULL
-     * to the caller if {@code shouldGrantPermission} is {@code true}, else it denies the
-     * permission.
-     */
-    private void setCallerAndGrantInteractUserPermission(
-            @UserIdInt int userId, boolean shouldGrantPermission) {
-        ShadowBinder.setCallingUserHandle(UserHandle.of(userId));
-        if (shouldGrantPermission) {
-            mShadowContext.grantPermissions(INTERACT_ACROSS_USERS_FULL);
-        } else {
-            mShadowContext.denyPermissions(INTERACT_ACROSS_USERS_FULL);
-        }
-    }
-
-    private ParcelFileDescriptor getFileDescriptorForAdbTest() throws Exception {
-        File testFile = new File(mContext.getFilesDir(), "test");
-        testFile.createNewFile();
-        return ParcelFileDescriptor.open(testFile, ParcelFileDescriptor.MODE_READ_WRITE);
-    }
-}
diff --git a/services/robotests/backup/src/com/android/server/backup/UserBackupManagerServiceTest.java b/services/robotests/backup/src/com/android/server/backup/UserBackupManagerServiceTest.java
index 84e810d..8632ca4 100644
--- a/services/robotests/backup/src/com/android/server/backup/UserBackupManagerServiceTest.java
+++ b/services/robotests/backup/src/com/android/server/backup/UserBackupManagerServiceTest.java
@@ -1005,7 +1005,7 @@
         UserBackupManagerService.createAndInitializeService(
                 USER_ID,
                 mContext,
-                new Trampoline(mContext),
+                new BackupManagerService(mContext),
                 mBackupThread,
                 mBaseStateDir,
                 mDataDir,
@@ -1026,7 +1026,7 @@
         UserBackupManagerService.createAndInitializeService(
                 USER_ID,
                 mContext,
-                new Trampoline(mContext),
+                new BackupManagerService(mContext),
                 mBackupThread,
                 mBaseStateDir,
                 mDataDir,
@@ -1045,7 +1045,7 @@
                         UserBackupManagerService.createAndInitializeService(
                                 USER_ID,
                                 /* context */ null,
-                                new Trampoline(mContext),
+                                new BackupManagerService(mContext),
                                 mBackupThread,
                                 mBaseStateDir,
                                 mDataDir,
@@ -1077,7 +1077,7 @@
                         UserBackupManagerService.createAndInitializeService(
                                 USER_ID,
                                 mContext,
-                                new Trampoline(mContext),
+                                new BackupManagerService(mContext),
                                 /* backupThread */ null,
                                 mBaseStateDir,
                                 mDataDir,
@@ -1093,7 +1093,7 @@
                         UserBackupManagerService.createAndInitializeService(
                                 USER_ID,
                                 mContext,
-                                new Trampoline(mContext),
+                                new BackupManagerService(mContext),
                                 mBackupThread,
                                 /* baseStateDir */ null,
                                 mDataDir,
@@ -1102,8 +1102,8 @@
 
     /**
      * Test checking non-null argument on {@link
-     * UserBackupManagerService#createAndInitializeService(int, Context, Trampoline, HandlerThread,
-     * File, File, TransportManager)}.
+     * UserBackupManagerService#createAndInitializeService(int, Context, BackupManagerService,
+     * HandlerThread, File, File, TransportManager)}.
      */
     @Test
     public void testCreateAndInitializeService_withNullDataDir_throws() {
@@ -1113,7 +1113,7 @@
                         UserBackupManagerService.createAndInitializeService(
                                 USER_ID,
                                 mContext,
-                                new Trampoline(mContext),
+                                new BackupManagerService(mContext),
                                 mBackupThread,
                                 mBaseStateDir,
                                 /* dataDir */ null,
@@ -1122,8 +1122,8 @@
 
     /**
      * Test checking non-null argument on {@link
-     * UserBackupManagerService#createAndInitializeService(int, Context, Trampoline, HandlerThread,
-     * File, File, TransportManager)}.
+     * UserBackupManagerService#createAndInitializeService(int, Context, BackupManagerService,
+     * HandlerThread, File, File, TransportManager)}.
      */
     @Test
     public void testCreateAndInitializeService_withNullTransportManager_throws() {
@@ -1133,7 +1133,7 @@
                         UserBackupManagerService.createAndInitializeService(
                                 USER_ID,
                                 mContext,
-                                new Trampoline(mContext),
+                                new BackupManagerService(mContext),
                                 mBackupThread,
                                 mBaseStateDir,
                                 mDataDir,
@@ -1151,7 +1151,7 @@
         UserBackupManagerService service = UserBackupManagerService.createAndInitializeService(
                 USER_ID,
                 contextSpy,
-                new Trampoline(mContext),
+                new BackupManagerService(mContext),
                 mBackupThread,
                 mBaseStateDir,
                 mDataDir,
diff --git a/services/robotests/backup/src/com/android/server/backup/testing/BackupManagerServiceTestUtils.java b/services/robotests/backup/src/com/android/server/backup/testing/BackupManagerServiceTestUtils.java
index 392d182..84421ef 100644
--- a/services/robotests/backup/src/com/android/server/backup/testing/BackupManagerServiceTestUtils.java
+++ b/services/robotests/backup/src/com/android/server/backup/testing/BackupManagerServiceTestUtils.java
@@ -37,7 +37,7 @@
 import android.util.Log;
 
 import com.android.server.backup.BackupAgentTimeoutParameters;
-import com.android.server.backup.Trampoline;
+import com.android.server.backup.BackupManagerService;
 import com.android.server.backup.TransportManager;
 import com.android.server.backup.UserBackupManagerService;
 
@@ -89,7 +89,7 @@
                 UserBackupManagerService.createAndInitializeService(
                         userId,
                         context,
-                        new Trampoline(context),
+                        new BackupManagerService(context),
                         backupThread,
                         baseStateDir,
                         dataDir,
diff --git a/services/tests/mockingservicestests/src/com/android/server/job/controllers/TimeControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/job/controllers/TimeControllerTest.java
index 3614763..5d041b7 100644
--- a/services/tests/mockingservicestests/src/com/android/server/job/controllers/TimeControllerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/job/controllers/TimeControllerTest.java
@@ -71,6 +71,7 @@
     private static final String SOURCE_PACKAGE = "com.android.frameworks.mockingservicestests";
     private static final int SOURCE_USER_ID = 0;
 
+    private TimeController.TcConstants mConstants;
     private TimeController mTimeController;
 
     private MockitoSession mMockingSession;
@@ -110,6 +111,7 @@
 
         // Initialize real objects.
         mTimeController = new TimeController(mJobSchedulerService);
+        mConstants = mTimeController.getTcConstants();
         spyOn(mTimeController);
     }
 
@@ -528,6 +530,46 @@
     }
 
     @Test
+    public void testJobDelayWakeupAlarmToggling() {
+        final long now = JobSchedulerService.sElapsedRealtimeClock.millis();
+
+        JobStatus job = createJobStatus(
+                "testMaybeStartTrackingJobLocked_DeadlineReverseOrder",
+                createJob().setMinimumLatency(HOUR_IN_MILLIS));
+
+        doReturn(true).when(mTimeController)
+                .wouldBeReadyWithConstraintLocked(eq(job), anyInt());
+
+        // Starting off with using a wakeup alarm.
+        mConstants.USE_NON_WAKEUP_ALARM_FOR_DELAY = false;
+        InOrder inOrder = inOrder(mAlarmManager);
+
+        mTimeController.maybeStartTrackingJobLocked(job, null);
+        inOrder.verify(mAlarmManager, times(1))
+                .set(eq(AlarmManager.ELAPSED_REALTIME_WAKEUP), eq(now + HOUR_IN_MILLIS), anyLong(),
+                        anyLong(),
+                        eq(TAG_DELAY), any(), any(), any());
+
+        // Use a non wakeup alarm.
+        mConstants.USE_NON_WAKEUP_ALARM_FOR_DELAY = true;
+
+        mTimeController.maybeStartTrackingJobLocked(job, null);
+        inOrder.verify(mAlarmManager, times(1))
+                .set(eq(AlarmManager.ELAPSED_REALTIME), eq(now + HOUR_IN_MILLIS), anyLong(),
+                        anyLong(), eq(TAG_DELAY),
+                        any(), any(), any());
+
+        // Back off, use a wakeup alarm.
+        mConstants.USE_NON_WAKEUP_ALARM_FOR_DELAY = false;
+
+        mTimeController.maybeStartTrackingJobLocked(job, null);
+        inOrder.verify(mAlarmManager, times(1))
+                .set(eq(AlarmManager.ELAPSED_REALTIME_WAKEUP), eq(now + HOUR_IN_MILLIS), anyLong(),
+                        anyLong(),
+                        eq(TAG_DELAY), any(), any(), any());
+    }
+
+    @Test
     public void testCheckExpiredDeadlinesAndResetAlarm_AllReady() {
         doReturn(true).when(mTimeController).wouldBeReadyWithConstraintLocked(any(), anyInt());
 
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 1084d62..939aafa 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/AbstractAccessibilityServiceConnectionTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AbstractAccessibilityServiceConnectionTest.java
@@ -752,7 +752,7 @@
         }
 
         @Override
-        protected boolean isCalledForCurrentUserLocked() {
+        protected boolean hasRightsToCurrentUserLocked() {
             return mResolvedUserId == mSystemSupport.getCurrentUserIdLocked();
         }
 
diff --git a/services/tests/servicestests/src/com/android/server/backup/TrampolineTest.java b/services/tests/servicestests/src/com/android/server/backup/BackupManagerServiceTest.java
similarity index 63%
rename from services/tests/servicestests/src/com/android/server/backup/TrampolineTest.java
rename to services/tests/servicestests/src/com/android/server/backup/BackupManagerServiceTest.java
index 848ef45..68b413f 100644
--- a/services/tests/servicestests/src/com/android/server/backup/TrampolineTest.java
+++ b/services/tests/servicestests/src/com/android/server/backup/BackupManagerServiceTest.java
@@ -11,7 +11,7 @@
  * 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
+ * limitations under the License.
  */
 
 package com.android.server.backup;
@@ -76,7 +76,7 @@
 @SmallTest
 @Presubmit
 @RunWith(AndroidJUnit4.class)
-public class TrampolineTest {
+public class BackupManagerServiceTest {
     private static final String PACKAGE_NAME = "some.package.name";
     private static final String TRANSPORT_NAME = "some.transport.name";
     private static final String CURRENT_PASSWORD = "current_password";
@@ -100,8 +100,6 @@
     @UserIdInt
     private int mUserId;
     @Mock
-    private BackupManagerService mBackupManagerServiceMock;
-    @Mock
     private UserBackupManagerService mUserBackupManagerService;
     @Mock
     private Context mContextMock;
@@ -124,7 +122,7 @@
 
     private FileDescriptor mFileDescriptorStub = new FileDescriptor();
 
-    private TrampolineTestable mTrampoline;
+    private BackupManagerServiceTestable mService;
     private File mTestDir;
     private File mSuppressFile;
     private SparseArray<UserBackupManagerService> mUserServices;
@@ -142,38 +140,37 @@
         when(mUserManagerMock.getUserInfo(NON_USER_SYSTEM)).thenReturn(mUserInfoMock);
         when(mUserManagerMock.getUserInfo(UNSTARTED_NON_USER_SYSTEM)).thenReturn(mUserInfoMock);
 
-        TrampolineTestable.sBackupManagerServiceMock = mBackupManagerServiceMock;
-        TrampolineTestable.sCallingUserId = UserHandle.USER_SYSTEM;
-        TrampolineTestable.sCallingUid = Process.SYSTEM_UID;
-        TrampolineTestable.sBackupDisabled = false;
-        TrampolineTestable.sUserManagerMock = mUserManagerMock;
+        BackupManagerServiceTestable.sCallingUserId = UserHandle.USER_SYSTEM;
+        BackupManagerServiceTestable.sCallingUid = Process.SYSTEM_UID;
+        BackupManagerServiceTestable.sBackupDisabled = false;
+        BackupManagerServiceTestable.sUserManagerMock = mUserManagerMock;
 
         mTestDir = InstrumentationRegistry.getContext().getFilesDir();
         mTestDir.mkdirs();
 
         mSuppressFile = new File(mTestDir, "suppress");
-        TrampolineTestable.sSuppressFile = mSuppressFile;
+        BackupManagerServiceTestable.sSuppressFile = mSuppressFile;
 
         setUpStateFilesForNonSystemUser(NON_USER_SYSTEM);
         setUpStateFilesForNonSystemUser(UNSTARTED_NON_USER_SYSTEM);
 
         when(mContextMock.getSystemService(Context.JOB_SCHEDULER_SERVICE))
                 .thenReturn(mock(JobScheduler.class));
-        mTrampoline = new TrampolineTestable(mContextMock, mUserServices);
+        mService = new BackupManagerServiceTestable(mContextMock, mUserServices);
     }
 
     private void setUpStateFilesForNonSystemUser(int userId) {
         File activatedFile = new File(mTestDir, "activate-" + userId);
-        TrampolineTestable.sActivatedFiles.append(userId, activatedFile);
+        BackupManagerServiceTestable.sActivatedFiles.append(userId, activatedFile);
         File rememberActivatedFile = new File(mTestDir, "rem-activate-" + userId);
-        TrampolineTestable.sRememberActivatedFiles.append(userId, rememberActivatedFile);
+        BackupManagerServiceTestable.sRememberActivatedFiles.append(userId, rememberActivatedFile);
     }
 
     @After
     public void tearDown() throws Exception {
         mSuppressFile.delete();
-        deleteFiles(TrampolineTestable.sActivatedFiles);
-        deleteFiles(TrampolineTestable.sRememberActivatedFiles);
+        deleteFiles(BackupManagerServiceTestable.sActivatedFiles);
+        deleteFiles(BackupManagerServiceTestable.sRememberActivatedFiles);
     }
 
     private void deleteFiles(SparseArray<File> files) {
@@ -185,144 +182,149 @@
 
     @Test
     public void testIsBackupServiceActive_whenBackupsNotDisabledAndSuppressFileDoesNotExist() {
-        assertTrue(mTrampoline.isBackupServiceActive(UserHandle.USER_SYSTEM));
+        assertTrue(mService.isBackupServiceActive(UserHandle.USER_SYSTEM));
     }
 
     @Test
     public void testOnUnlockUser_forNonSystemUserWhenBackupsDisabled_doesNotStartUser() {
-        TrampolineTestable.sBackupDisabled = true;
-        TrampolineTestable trampoline = new TrampolineTestable(mContextMock, new SparseArray<>());
+        BackupManagerServiceTestable.sBackupDisabled = true;
+        BackupManagerServiceTestable service =
+                new BackupManagerServiceTestable(mContextMock, new SparseArray<>());
         ConditionVariable unlocked = new ConditionVariable(false);
 
-        trampoline.onUnlockUser(NON_USER_SYSTEM);
+        service.onUnlockUser(NON_USER_SYSTEM);
 
-        trampoline.getBackupHandler().post(unlocked::open);
+        service.getBackupHandler().post(unlocked::open);
         unlocked.block();
-        assertNull(trampoline.getUserService(NON_USER_SYSTEM));
+        assertNull(service.getUserService(NON_USER_SYSTEM));
     }
 
     @Test
     public void testOnUnlockUser_forSystemUserWhenBackupsDisabled_doesNotStartUser() {
-        TrampolineTestable.sBackupDisabled = true;
-        TrampolineTestable trampoline = new TrampolineTestable(mContextMock, new SparseArray<>());
+        BackupManagerServiceTestable.sBackupDisabled = true;
+        BackupManagerServiceTestable service =
+                new BackupManagerServiceTestable(mContextMock, new SparseArray<>());
         ConditionVariable unlocked = new ConditionVariable(false);
 
-        trampoline.onUnlockUser(UserHandle.USER_SYSTEM);
+        service.onUnlockUser(UserHandle.USER_SYSTEM);
 
-        trampoline.getBackupHandler().post(unlocked::open);
+        service.getBackupHandler().post(unlocked::open);
         unlocked.block();
-        assertNull(trampoline.getUserService(UserHandle.USER_SYSTEM));
+        assertNull(service.getUserService(UserHandle.USER_SYSTEM));
     }
 
     @Test
     public void testOnUnlockUser_whenBackupNotActivated_doesNotStartUser() {
-        TrampolineTestable.sBackupDisabled = false;
-        TrampolineTestable trampoline = new TrampolineTestable(mContextMock, new SparseArray<>());
-        trampoline.setBackupServiceActive(NON_USER_SYSTEM, false);
+        BackupManagerServiceTestable.sBackupDisabled = false;
+        BackupManagerServiceTestable service =
+                new BackupManagerServiceTestable(mContextMock, new SparseArray<>());
+        service.setBackupServiceActive(NON_USER_SYSTEM, false);
         ConditionVariable unlocked = new ConditionVariable(false);
 
-        trampoline.onUnlockUser(NON_USER_SYSTEM);
+        service.onUnlockUser(NON_USER_SYSTEM);
 
-        trampoline.getBackupHandler().post(unlocked::open);
+        service.getBackupHandler().post(unlocked::open);
         unlocked.block();
-        assertNull(trampoline.getUserService(NON_USER_SYSTEM));
+        assertNull(service.getUserService(NON_USER_SYSTEM));
     }
 
     @Test
     public void testIsBackupServiceActive_forSystemUserWhenBackupDisabled_returnsTrue()
             throws Exception {
-        TrampolineTestable.sBackupDisabled = true;
-        Trampoline trampoline = new TrampolineTestable(mContextMock, mUserServices);
-        trampoline.setBackupServiceActive(UserHandle.USER_SYSTEM, true);
+        BackupManagerServiceTestable.sBackupDisabled = true;
+        BackupManagerService backupManagerService =
+                new BackupManagerServiceTestable(mContextMock, mUserServices);
+        backupManagerService.setBackupServiceActive(UserHandle.USER_SYSTEM, true);
 
-        assertFalse(trampoline.isBackupServiceActive(UserHandle.USER_SYSTEM));
+        assertFalse(backupManagerService.isBackupServiceActive(UserHandle.USER_SYSTEM));
     }
 
     @Test
     public void testIsBackupServiceActive_forNonSystemUserWhenBackupDisabled_returnsTrue()
             throws Exception {
-        TrampolineTestable.sBackupDisabled = true;
-        Trampoline trampoline = new TrampolineTestable(mContextMock, mUserServices);
-        trampoline.setBackupServiceActive(NON_USER_SYSTEM, true);
+        BackupManagerServiceTestable.sBackupDisabled = true;
+        BackupManagerService backupManagerService =
+                new BackupManagerServiceTestable(mContextMock, mUserServices);
+        backupManagerService.setBackupServiceActive(NON_USER_SYSTEM, true);
 
-        assertFalse(trampoline.isBackupServiceActive(NON_USER_SYSTEM));
+        assertFalse(backupManagerService.isBackupServiceActive(NON_USER_SYSTEM));
     }
 
     @Test
     public void isBackupServiceActive_forSystemUser_returnsTrueWhenActivated() throws Exception {
-        mTrampoline.setBackupServiceActive(UserHandle.USER_SYSTEM, true);
+        mService.setBackupServiceActive(UserHandle.USER_SYSTEM, true);
 
-        assertTrue(mTrampoline.isBackupServiceActive(UserHandle.USER_SYSTEM));
+        assertTrue(mService.isBackupServiceActive(UserHandle.USER_SYSTEM));
     }
 
     @Test
     public void isBackupServiceActive_forSystemUser_returnsFalseWhenDeactivated() throws Exception {
-        mTrampoline.setBackupServiceActive(UserHandle.USER_SYSTEM, false);
+        mService.setBackupServiceActive(UserHandle.USER_SYSTEM, false);
 
-        assertFalse(mTrampoline.isBackupServiceActive(UserHandle.USER_SYSTEM));
+        assertFalse(mService.isBackupServiceActive(UserHandle.USER_SYSTEM));
     }
 
     @Test
     public void isBackupServiceActive_forNonSystemUser_returnsFalseWhenSystemUserDeactivated()
             throws Exception {
-        mTrampoline.setBackupServiceActive(UserHandle.USER_SYSTEM, false);
-        mTrampoline.setBackupServiceActive(NON_USER_SYSTEM, true);
+        mService.setBackupServiceActive(UserHandle.USER_SYSTEM, false);
+        mService.setBackupServiceActive(NON_USER_SYSTEM, true);
 
-        assertFalse(mTrampoline.isBackupServiceActive(NON_USER_SYSTEM));
+        assertFalse(mService.isBackupServiceActive(NON_USER_SYSTEM));
     }
 
     @Test
     public void isBackupServiceActive_forNonSystemUser_returnsFalseWhenNonSystemUserDeactivated()
             throws Exception {
-        mTrampoline.setBackupServiceActive(UserHandle.USER_SYSTEM, true);
+        mService.setBackupServiceActive(UserHandle.USER_SYSTEM, true);
         // Don't activate non-system user.
 
-        assertFalse(mTrampoline.isBackupServiceActive(NON_USER_SYSTEM));
+        assertFalse(mService.isBackupServiceActive(NON_USER_SYSTEM));
     }
 
     @Test
     public void
             isBackupServiceActive_forNonSystemUser_returnsTrueWhenSystemAndNonSystemUserActivated()
-                throws Exception {
-        mTrampoline.setBackupServiceActive(UserHandle.USER_SYSTEM, true);
-        mTrampoline.setBackupServiceActive(NON_USER_SYSTEM, true);
+            throws Exception {
+        mService.setBackupServiceActive(UserHandle.USER_SYSTEM, true);
+        mService.setBackupServiceActive(NON_USER_SYSTEM, true);
 
-        assertTrue(mTrampoline.isBackupServiceActive(NON_USER_SYSTEM));
+        assertTrue(mService.isBackupServiceActive(NON_USER_SYSTEM));
     }
 
     @Test
     public void
             isBackupServiceActive_forUnstartedNonSystemUser_returnsTrueWhenSystemAndUserActivated()
             throws Exception {
-        mTrampoline.setBackupServiceActive(UNSTARTED_NON_USER_SYSTEM, true);
+        mService.setBackupServiceActive(UNSTARTED_NON_USER_SYSTEM, true);
 
-        assertTrue(mTrampoline.isBackupServiceActive(UNSTARTED_NON_USER_SYSTEM));
+        assertTrue(mService.isBackupServiceActive(UNSTARTED_NON_USER_SYSTEM));
     }
 
     @Test
     public void setBackupServiceActive_forSystemUserAndCallerSystemUid_serviceCreated() {
-        TrampolineTestable.sCallingUid = Process.SYSTEM_UID;
+        BackupManagerServiceTestable.sCallingUid = Process.SYSTEM_UID;
 
-        mTrampoline.setBackupServiceActive(UserHandle.USER_SYSTEM, true);
+        mService.setBackupServiceActive(UserHandle.USER_SYSTEM, true);
 
-        assertTrue(mTrampoline.isBackupServiceActive(UserHandle.USER_SYSTEM));
+        assertTrue(mService.isBackupServiceActive(UserHandle.USER_SYSTEM));
     }
 
     @Test
     public void setBackupServiceActive_forSystemUserAndCallerRootUid_serviceCreated() {
-        TrampolineTestable.sCallingUid = Process.ROOT_UID;
+        BackupManagerServiceTestable.sCallingUid = Process.ROOT_UID;
 
-        mTrampoline.setBackupServiceActive(UserHandle.USER_SYSTEM, true);
+        mService.setBackupServiceActive(UserHandle.USER_SYSTEM, true);
 
-        assertTrue(mTrampoline.isBackupServiceActive(UserHandle.USER_SYSTEM));
+        assertTrue(mService.isBackupServiceActive(UserHandle.USER_SYSTEM));
     }
 
     @Test
     public void setBackupServiceActive_forSystemUserAndCallerNonRootNonSystem_throws() {
-        TrampolineTestable.sCallingUid = Process.FIRST_APPLICATION_UID;
+        BackupManagerServiceTestable.sCallingUid = Process.FIRST_APPLICATION_UID;
 
         try {
-            mTrampoline.setBackupServiceActive(UserHandle.USER_SYSTEM, true);
+            mService.setBackupServiceActive(UserHandle.USER_SYSTEM, true);
             fail();
         } catch (SecurityException expected) {
         }
@@ -331,30 +333,30 @@
     @Test
     public void setBackupServiceActive_forManagedProfileAndCallerSystemUid_serviceCreated() {
         when(mUserInfoMock.isManagedProfile()).thenReturn(true);
-        TrampolineTestable.sCallingUid = Process.SYSTEM_UID;
+        BackupManagerServiceTestable.sCallingUid = Process.SYSTEM_UID;
 
-        mTrampoline.setBackupServiceActive(NON_USER_SYSTEM, true);
+        mService.setBackupServiceActive(NON_USER_SYSTEM, true);
 
-        assertTrue(mTrampoline.isBackupServiceActive(NON_USER_SYSTEM));
+        assertTrue(mService.isBackupServiceActive(NON_USER_SYSTEM));
     }
 
     @Test
     public void setBackupServiceActive_forManagedProfileAndCallerRootUid_serviceCreated() {
         when(mUserInfoMock.isManagedProfile()).thenReturn(true);
-        TrampolineTestable.sCallingUid = Process.ROOT_UID;
+        BackupManagerServiceTestable.sCallingUid = Process.ROOT_UID;
 
-        mTrampoline.setBackupServiceActive(NON_USER_SYSTEM, true);
+        mService.setBackupServiceActive(NON_USER_SYSTEM, true);
 
-        assertTrue(mTrampoline.isBackupServiceActive(NON_USER_SYSTEM));
+        assertTrue(mService.isBackupServiceActive(NON_USER_SYSTEM));
     }
 
     @Test
     public void setBackupServiceActive_forManagedProfileAndCallerNonRootNonSystem_throws() {
         when(mUserInfoMock.isManagedProfile()).thenReturn(true);
-        TrampolineTestable.sCallingUid = Process.FIRST_APPLICATION_UID;
+        BackupManagerServiceTestable.sCallingUid = Process.FIRST_APPLICATION_UID;
 
         try {
-            mTrampoline.setBackupServiceActive(NON_USER_SYSTEM, true);
+            mService.setBackupServiceActive(NON_USER_SYSTEM, true);
             fail();
         } catch (SecurityException expected) {
         }
@@ -367,7 +369,7 @@
                 .enforceCallingOrSelfPermission(eq(Manifest.permission.BACKUP), anyString());
 
         try {
-            mTrampoline.setBackupServiceActive(NON_USER_SYSTEM, true);
+            mService.setBackupServiceActive(NON_USER_SYSTEM, true);
             fail();
         } catch (SecurityException expected) {
         }
@@ -381,7 +383,7 @@
                         eq(Manifest.permission.INTERACT_ACROSS_USERS_FULL), anyString());
 
         try {
-            mTrampoline.setBackupServiceActive(NON_USER_SYSTEM, true);
+            mService.setBackupServiceActive(NON_USER_SYSTEM, true);
             fail();
         } catch (SecurityException expected) {
         }
@@ -389,95 +391,96 @@
 
     @Test
     public void setBackupServiceActive_backupDisabled_ignored() {
-        TrampolineTestable.sBackupDisabled = true;
-        TrampolineTestable trampoline = new TrampolineTestable(mContextMock, mUserServices);
+        BackupManagerServiceTestable.sBackupDisabled = true;
+        BackupManagerServiceTestable service =
+                new BackupManagerServiceTestable(mContextMock, mUserServices);
 
-        trampoline.setBackupServiceActive(UserHandle.USER_SYSTEM, true);
+        service.setBackupServiceActive(UserHandle.USER_SYSTEM, true);
 
-        assertFalse(trampoline.isBackupServiceActive(UserHandle.USER_SYSTEM));
+        assertFalse(service.isBackupServiceActive(UserHandle.USER_SYSTEM));
     }
 
     @Test
     public void setBackupServiceActive_alreadyActive_ignored() {
-        mTrampoline.setBackupServiceActive(UserHandle.USER_SYSTEM, true);
-        assertTrue(mTrampoline.isBackupServiceActive(UserHandle.USER_SYSTEM));
+        mService.setBackupServiceActive(UserHandle.USER_SYSTEM, true);
+        assertTrue(mService.isBackupServiceActive(UserHandle.USER_SYSTEM));
 
-        mTrampoline.setBackupServiceActive(UserHandle.USER_SYSTEM, true);
-        assertTrue(mTrampoline.isBackupServiceActive(UserHandle.USER_SYSTEM));
+        mService.setBackupServiceActive(UserHandle.USER_SYSTEM, true);
+        assertTrue(mService.isBackupServiceActive(UserHandle.USER_SYSTEM));
     }
 
     @Test
     public void setBackupServiceActive_makeNonActive_alreadyNonActive_ignored() {
-        mTrampoline.setBackupServiceActive(UserHandle.USER_SYSTEM, false);
-        mTrampoline.setBackupServiceActive(UserHandle.USER_SYSTEM, false);
+        mService.setBackupServiceActive(UserHandle.USER_SYSTEM, false);
+        mService.setBackupServiceActive(UserHandle.USER_SYSTEM, false);
 
-        assertFalse(mTrampoline.isBackupServiceActive(UserHandle.USER_SYSTEM));
+        assertFalse(mService.isBackupServiceActive(UserHandle.USER_SYSTEM));
     }
 
     @Test
     public void setBackupServiceActive_makeActive_serviceCreatedAndSuppressFileDeleted() {
-        mTrampoline.setBackupServiceActive(UserHandle.USER_SYSTEM, true);
+        mService.setBackupServiceActive(UserHandle.USER_SYSTEM, true);
 
-        assertTrue(mTrampoline.isBackupServiceActive(UserHandle.USER_SYSTEM));
+        assertTrue(mService.isBackupServiceActive(UserHandle.USER_SYSTEM));
     }
 
     @Test
     public void setBackupServiceActive_makeNonActive_serviceDeletedAndSuppressFileCreated()
             throws IOException {
-        assertTrue(mTrampoline.isBackupServiceActive(UserHandle.USER_SYSTEM));
+        assertTrue(mService.isBackupServiceActive(UserHandle.USER_SYSTEM));
 
-        mTrampoline.setBackupServiceActive(UserHandle.USER_SYSTEM, false);
+        mService.setBackupServiceActive(UserHandle.USER_SYSTEM, false);
 
-        assertFalse(mTrampoline.isBackupServiceActive(UserHandle.USER_SYSTEM));
+        assertFalse(mService.isBackupServiceActive(UserHandle.USER_SYSTEM));
     }
 
     @Test
     public void setBackupActive_nonSystemUser_disabledForSystemUser_ignored() {
-        mTrampoline.setBackupServiceActive(UserHandle.USER_SYSTEM, false);
-        mTrampoline.setBackupServiceActive(NON_USER_SYSTEM, true);
+        mService.setBackupServiceActive(UserHandle.USER_SYSTEM, false);
+        mService.setBackupServiceActive(NON_USER_SYSTEM, true);
 
-        assertFalse(mTrampoline.isBackupServiceActive(NON_USER_SYSTEM));
+        assertFalse(mService.isBackupServiceActive(NON_USER_SYSTEM));
     }
 
     @Test
     public void setBackupServiceActive_forOneNonSystemUser_doesNotActivateForAllNonSystemUsers() {
         int otherUser = NON_USER_SYSTEM + 1;
         File activateFile = new File(mTestDir, "activate-" + otherUser);
-        TrampolineTestable.sActivatedFiles.append(otherUser, activateFile);
-        mTrampoline.setBackupServiceActive(UserHandle.USER_SYSTEM, true);
+        BackupManagerServiceTestable.sActivatedFiles.append(otherUser, activateFile);
+        mService.setBackupServiceActive(UserHandle.USER_SYSTEM, true);
 
-        mTrampoline.setBackupServiceActive(NON_USER_SYSTEM, true);
+        mService.setBackupServiceActive(NON_USER_SYSTEM, true);
 
-        assertTrue(mTrampoline.isBackupServiceActive(NON_USER_SYSTEM));
-        assertFalse(mTrampoline.isBackupServiceActive(otherUser));
+        assertTrue(mService.isBackupServiceActive(NON_USER_SYSTEM));
+        assertFalse(mService.isBackupServiceActive(otherUser));
         activateFile.delete();
     }
 
     @Test
     public void setBackupServiceActive_forNonSystemUser_remembersActivated() {
 
-        mTrampoline.setBackupServiceActive(NON_USER_SYSTEM, true);
+        mService.setBackupServiceActive(NON_USER_SYSTEM, true);
 
         assertTrue(RandomAccessFileUtils.readBoolean(
-                TrampolineTestable.sRememberActivatedFiles.get(NON_USER_SYSTEM), false));
+                BackupManagerServiceTestable.sRememberActivatedFiles.get(NON_USER_SYSTEM), false));
     }
 
     @Test
     public void setBackupServiceActiveFalse_forNonSystemUser_remembersActivated() {
 
-        mTrampoline.setBackupServiceActive(NON_USER_SYSTEM, false);
+        mService.setBackupServiceActive(NON_USER_SYSTEM, false);
 
         assertFalse(RandomAccessFileUtils.readBoolean(
-                TrampolineTestable.sRememberActivatedFiles.get(NON_USER_SYSTEM), true));
+                BackupManagerServiceTestable.sRememberActivatedFiles.get(NON_USER_SYSTEM), true));
     }
 
     @Test
     public void setBackupServiceActiveTwice_forNonSystemUser_remembersLastActivated() {
-        mTrampoline.setBackupServiceActive(NON_USER_SYSTEM, true);
-        mTrampoline.setBackupServiceActive(NON_USER_SYSTEM, false);
+        mService.setBackupServiceActive(NON_USER_SYSTEM, true);
+        mService.setBackupServiceActive(NON_USER_SYSTEM, false);
 
         assertFalse(RandomAccessFileUtils.readBoolean(
-                TrampolineTestable.sRememberActivatedFiles.get(NON_USER_SYSTEM), true));
+                BackupManagerServiceTestable.sRememberActivatedFiles.get(NON_USER_SYSTEM), true));
     }
 
     @Test
@@ -497,7 +500,7 @@
                     }
                 };
 
-        mTrampoline.selectBackupTransportAsyncForUser(mUserId, TRANSPORT_COMPONENT_NAME, listener);
+        mService.selectBackupTransportAsyncForUser(mUserId, TRANSPORT_COMPONENT_NAME, listener);
 
         assertEquals(BackupManager.ERROR_BACKUP_NOT_ALLOWED, (int) future.get(5, TimeUnit.SECONDS));
     }
@@ -505,14 +508,14 @@
     @Test
     public void selectBackupTransportAsyncForUser_beforeUserUnlockedWithNullListener_doesNotThrow()
             throws Exception {
-        mTrampoline.selectBackupTransportAsyncForUser(mUserId, TRANSPORT_COMPONENT_NAME, null);
+        mService.selectBackupTransportAsyncForUser(mUserId, TRANSPORT_COMPONENT_NAME, null);
 
         // No crash.
     }
 
     @Test
     public void
-            selectBackupTransportAsyncForUser_beforeUserUnlockedWithThrowingListener_doesNotThrow()
+            selectBackupTransportAsyncForUser_beforeUserUnlockedListenerThrowing_doesNotThrow()
             throws Exception {
         ISelectBackupTransportCallback.Stub listener =
                 new ISelectBackupTransportCallback.Stub() {
@@ -524,7 +527,7 @@
                     }
                 };
 
-        mTrampoline.selectBackupTransportAsyncForUser(mUserId, TRANSPORT_COMPONENT_NAME, listener);
+        mService.selectBackupTransportAsyncForUser(mUserId, TRANSPORT_COMPONENT_NAME, listener);
 
         // No crash.
     }
@@ -535,44 +538,45 @@
                 android.Manifest.permission.DUMP)).thenReturn(
                 PackageManager.PERMISSION_DENIED);
 
-        mTrampoline.dump(mFileDescriptorStub, mPrintWriterMock, new String[0]);
+        mService.dump(mFileDescriptorStub, mPrintWriterMock, new String[0]);
 
-        verifyNoMoreInteractions(mBackupManagerServiceMock);
+        verifyNoMoreInteractions(mUserBackupManagerService);
     }
 
     public void testGetUserForAncestralSerialNumber() {
-        TrampolineTestable.sBackupDisabled = false;
-        Trampoline trampoline = new TrampolineTestable(mContextMock, mUserServices);
+        BackupManagerServiceTestable.sBackupDisabled = false;
+        BackupManagerService backupManagerService =
+                new BackupManagerServiceTestable(mContextMock, mUserServices);
         when(mUserBackupManagerService.getAncestralSerialNumber()).thenReturn(11L);
 
-        UserHandle user = trampoline.getUserForAncestralSerialNumber(11L);
+        UserHandle user = backupManagerService.getUserForAncestralSerialNumber(11L);
 
         assertThat(user).isEqualTo(UserHandle.of(1));
     }
 
     public void testGetUserForAncestralSerialNumber_whenDisabled() {
-        TrampolineTestable.sBackupDisabled = true;
-        Trampoline trampoline = new TrampolineTestable(mContextMock, mUserServices);
+        BackupManagerServiceTestable.sBackupDisabled = true;
+        BackupManagerService backupManagerService =
+                new BackupManagerServiceTestable(mContextMock, mUserServices);
         when(mUserBackupManagerService.getAncestralSerialNumber()).thenReturn(11L);
 
-        UserHandle user = trampoline.getUserForAncestralSerialNumber(11L);
+        UserHandle user = backupManagerService.getUserForAncestralSerialNumber(11L);
 
         assertThat(user).isNull();
     }
 
-    private static class TrampolineTestable extends Trampoline {
+    private static class BackupManagerServiceTestable extends BackupManagerService {
         static boolean sBackupDisabled = false;
         static int sCallingUserId = -1;
         static int sCallingUid = -1;
-        static BackupManagerService sBackupManagerServiceMock = null;
         static File sSuppressFile = null;
         static SparseArray<File> sActivatedFiles = new SparseArray<>();
         static SparseArray<File> sRememberActivatedFiles = new SparseArray<>();
         static UserManager sUserManagerMock = null;
 
-        TrampolineTestable(Context context, SparseArray<UserBackupManagerService> userServices) {
+        BackupManagerServiceTestable(
+                Context context, SparseArray<UserBackupManagerService> userServices) {
             super(context, userServices);
-            mService = sBackupManagerServiceMock;
         }
 
         @Override
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/BiometricServiceTest.java b/services/tests/servicestests/src/com/android/server/biometrics/BiometricServiceTest.java
new file mode 100644
index 0000000..ccf3a90
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/biometrics/BiometricServiceTest.java
@@ -0,0 +1,748 @@
+/*
+ * 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.biometrics;
+
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.TestCase.assertNotNull;
+
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.fail;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.app.AppOpsManager;
+import android.app.IActivityManager;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.content.res.Resources;
+import android.hardware.biometrics.BiometricAuthenticator;
+import android.hardware.biometrics.BiometricConstants;
+import android.hardware.biometrics.BiometricPrompt;
+import android.hardware.biometrics.IBiometricService;
+import android.hardware.biometrics.IBiometricServiceReceiver;
+import android.hardware.biometrics.IBiometricServiceReceiverInternal;
+import android.hardware.face.FaceManager;
+import android.hardware.face.IFaceService;
+import android.hardware.fingerprint.FingerprintManager;
+import android.hardware.fingerprint.IFingerprintService;
+import android.os.Binder;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.IBinder;
+import android.security.KeyStore;
+
+import com.android.internal.R;
+import com.android.internal.statusbar.IStatusBarService;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.List;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+
+@SmallTest
+public class BiometricServiceTest {
+
+    private static final String TAG = "BiometricServiceTest";
+
+    private static final String TEST_PACKAGE_NAME = "test_package";
+
+    private static final String ERROR_HW_UNAVAILABLE = "hw_unavailable";
+    private static final String ERROR_NOT_RECOGNIZED = "not_recognized";
+    private static final String ERROR_TIMEOUT = "error_timeout";
+    private static final String ERROR_CANCELED = "error_canceled";
+    private static final String ERROR_UNABLE_TO_PROCESS = "error_unable_to_process";
+    private static final String ERROR_USER_CANCELED = "error_user_canceled";
+
+    private static final String FINGERPRINT_ACQUIRED_SENSOR_DIRTY = "sensor_dirty";
+
+    private BiometricService mBiometricService;
+
+    @Mock
+    private Context mContext;
+    @Mock
+    private ContentResolver mContentResolver;
+    @Mock
+    private Resources mResources;
+    @Mock
+    private PackageManager mPackageManager;
+    @Mock
+    private AppOpsManager mAppOpsManager;
+    @Mock
+    IBiometricServiceReceiver mReceiver1;
+    @Mock
+    IBiometricServiceReceiver mReceiver2;
+    @Mock
+    FingerprintManager mFingerprintManager;
+    @Mock
+    FaceManager mFaceManager;
+
+    private static class MockInjector extends BiometricService.Injector {
+        @Override
+        IActivityManager getActivityManagerService() {
+            return mock(IActivityManager.class);
+        }
+
+        @Override
+        IStatusBarService getStatusBarService() {
+            return mock(IStatusBarService.class);
+        }
+
+        @Override
+        IFingerprintService getFingerprintService() {
+            return mock(IFingerprintService.class);
+        }
+
+        @Override
+        IFaceService getFaceService() {
+            return mock(IFaceService.class);
+        }
+
+        @Override
+        BiometricService.SettingObserver getSettingObserver(Context context, Handler handler,
+                List<BiometricService.EnabledOnKeyguardCallback> callbacks) {
+            return mock(BiometricService.SettingObserver.class);
+        }
+
+        @Override
+        KeyStore getKeyStore() {
+            return mock(KeyStore.class);
+        }
+
+        @Override
+        boolean isDebugEnabled(Context context, int userId) {
+            return false;
+        }
+
+        @Override
+        void publishBinderService(BiometricService service, IBiometricService.Stub impl) {
+            // no-op for test
+        }
+    }
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+
+        when(mContext.getPackageManager()).thenReturn(mPackageManager);
+        when(mContext.getSystemService(Context.APP_OPS_SERVICE)).thenReturn(mAppOpsManager);
+        when(mContext.getSystemService(Context.FINGERPRINT_SERVICE))
+                .thenReturn(mFingerprintManager);
+        when(mContext.getSystemService(Context.FACE_SERVICE)).thenReturn(mFaceManager);
+        when(mContext.getContentResolver()).thenReturn(mContentResolver);
+        when(mContext.getResources()).thenReturn(mResources);
+
+        when(mResources.getString(R.string.biometric_error_hw_unavailable))
+                .thenReturn(ERROR_HW_UNAVAILABLE);
+        when(mResources.getString(R.string.biometric_not_recognized))
+                .thenReturn(ERROR_NOT_RECOGNIZED);
+        when(mResources.getString(R.string.biometric_error_user_canceled))
+                .thenReturn(ERROR_USER_CANCELED);
+    }
+
+    @Test
+    public void testAuthenticate_withoutHardware_returnsErrorHardwareNotPresent() throws Exception {
+        when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_FINGERPRINT))
+                .thenReturn(false);
+        when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_IRIS)).thenReturn(false);
+        when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_FACE)).thenReturn(false);
+
+        mBiometricService = new BiometricService(mContext, new MockInjector());
+        mBiometricService.onStart();
+
+        invokeAuthenticate(mBiometricService.mImpl, mReceiver1, false /* requireConfirmation */);
+        waitForIdle();
+        verify(mReceiver1).onError(
+                eq(BiometricConstants.BIOMETRIC_ERROR_HW_NOT_PRESENT), eq(ERROR_HW_UNAVAILABLE));
+    }
+
+    @Test
+    public void testAuthenticate_withoutEnrolled_returnsErrorNoBiometrics() throws Exception {
+        when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_FINGERPRINT)).thenReturn(true);
+        when(mFingerprintManager.isHardwareDetected()).thenReturn(true);
+
+        mBiometricService = new BiometricService(mContext, new MockInjector());
+        mBiometricService.onStart();
+
+        invokeAuthenticate(mBiometricService.mImpl, mReceiver1, false /* requireConfirmation */);
+        waitForIdle();
+        verify(mReceiver1).onError(
+                eq(BiometricConstants.BIOMETRIC_ERROR_NO_BIOMETRICS), any());
+    }
+
+    @Test
+    public void testAuthenticate_whenHalIsDead_returnsErrorHardwareUnavailable() throws Exception {
+        when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_FINGERPRINT)).thenReturn(true);
+        when(mFingerprintManager.hasEnrolledTemplates(anyInt())).thenReturn(true);
+        when(mFingerprintManager.isHardwareDetected()).thenReturn(false);
+
+        mBiometricService = new BiometricService(mContext, new MockInjector());
+        mBiometricService.onStart();
+
+        invokeAuthenticate(mBiometricService.mImpl, mReceiver1, false /* requireConfirmation */);
+        waitForIdle();
+        verify(mReceiver1).onError(
+                eq(BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE), eq(ERROR_HW_UNAVAILABLE));
+    }
+
+    @Test
+    public void testAuthenticateFace_respectsUserSetting()
+            throws Exception {
+        when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_FACE)).thenReturn(true);
+        when(mFaceManager.hasEnrolledTemplates(anyInt())).thenReturn(true);
+        when(mFaceManager.isHardwareDetected()).thenReturn(true);
+
+        mBiometricService = new BiometricService(mContext, new MockInjector());
+        mBiometricService.onStart();
+
+        // Disabled in user settings receives onError
+        when(mBiometricService.mSettingObserver.getFaceEnabledForApps(anyInt())).thenReturn(false);
+        invokeAuthenticate(mBiometricService.mImpl, mReceiver1, false /* requireConfirmation */);
+        waitForIdle();
+        verify(mReceiver1).onError(
+                eq(BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE), eq(ERROR_HW_UNAVAILABLE));
+
+        // Enrolled, not disabled in settings, user requires confirmation in settings
+        resetReceiver();
+        when(mBiometricService.mSettingObserver.getFaceEnabledForApps(anyInt())).thenReturn(true);
+        when(mBiometricService.mSettingObserver.getFaceAlwaysRequireConfirmation(anyInt()))
+                .thenReturn(true);
+        invokeAuthenticate(mBiometricService.mImpl, mReceiver1, false /* requireConfirmation */);
+        waitForIdle();
+        verify(mReceiver1, never()).onError(anyInt(), any(String.class));
+        verify(mBiometricService.mFaceService).prepareForAuthentication(
+                eq(true) /* requireConfirmation */,
+                any(IBinder.class),
+                anyLong() /* sessionId */,
+                anyInt() /* userId */,
+                any(IBiometricServiceReceiverInternal.class),
+                anyString() /* opPackageName */,
+                anyInt() /* cookie */,
+                anyInt() /* callingUid */,
+                anyInt() /* callingPid */,
+                anyInt() /* callingUserId */);
+
+        // Enrolled, not disabled in settings, user doesn't require confirmation in settings
+        resetReceiver();
+        when(mBiometricService.mSettingObserver.getFaceAlwaysRequireConfirmation(anyInt()))
+                .thenReturn(false);
+        invokeAuthenticate(mBiometricService.mImpl, mReceiver1, false /* requireConfirmation */);
+        waitForIdle();
+        verify(mBiometricService.mFaceService).prepareForAuthentication(
+                eq(false) /* requireConfirmation */,
+                any(IBinder.class),
+                anyLong() /* sessionId */,
+                anyInt() /* userId */,
+                any(IBiometricServiceReceiverInternal.class),
+                anyString() /* opPackageName */,
+                anyInt() /* cookie */,
+                anyInt() /* callingUid */,
+                anyInt() /* callingPid */,
+                anyInt() /* callingUserId */);
+    }
+
+    @Test
+    public void testAuthenticate_happyPathWithoutConfirmation() throws Exception {
+        setupAuthForOnly(BiometricAuthenticator.TYPE_FINGERPRINT);
+        mBiometricService = new BiometricService(mContext, new MockInjector());
+        mBiometricService.onStart();
+
+        // Start testing the happy path
+        invokeAuthenticate(mBiometricService.mImpl, mReceiver1, false /* requireConfirmation */);
+        waitForIdle();
+
+        // Creates a pending auth session with the correct initial states
+        assertEquals(mBiometricService.mPendingAuthSession.mState,
+                BiometricService.STATE_AUTH_CALLED);
+
+        // Invokes <Modality>Service#prepareForAuthentication
+        ArgumentCaptor<Integer> cookieCaptor = ArgumentCaptor.forClass(Integer.class);
+        verify(mReceiver1, never()).onError(anyInt(), any(String.class));
+        verify(mBiometricService.mFingerprintService).prepareForAuthentication(
+                any(IBinder.class),
+                anyLong() /* sessionId */,
+                anyInt() /* userId */,
+                any(IBiometricServiceReceiverInternal.class),
+                anyString() /* opPackageName */,
+                cookieCaptor.capture() /* cookie */,
+                anyInt() /* callingUid */,
+                anyInt() /* callingPid */,
+                anyInt() /* callingUserId */);
+
+        // onReadyForAuthentication, mCurrentAuthSession state OK
+        mBiometricService.mImpl.onReadyForAuthentication(cookieCaptor.getValue(),
+                anyBoolean() /* requireConfirmation */, anyInt() /* userId */);
+        waitForIdle();
+        assertNull(mBiometricService.mPendingAuthSession);
+        assertEquals(mBiometricService.mCurrentAuthSession.mState,
+                BiometricService.STATE_AUTH_STARTED);
+
+        // startPreparedClient invoked
+        verify(mBiometricService.mFingerprintService)
+                .startPreparedClient(cookieCaptor.getValue());
+
+        // StatusBar showBiometricDialog invoked
+        verify(mBiometricService.mStatusBarService).showBiometricDialog(
+                eq(mBiometricService.mCurrentAuthSession.mBundle),
+                any(IBiometricServiceReceiverInternal.class),
+                eq(BiometricAuthenticator.TYPE_FINGERPRINT),
+                anyBoolean() /* requireConfirmation */,
+                anyInt() /* userId */,
+                eq(TEST_PACKAGE_NAME));
+
+        // Hardware authenticated
+        mBiometricService.mInternalReceiver.onAuthenticationSucceeded(
+                false /* requireConfirmation */,
+                new byte[69] /* HAT */);
+        waitForIdle();
+        // Waiting for SystemUI to send dismissed callback
+        assertEquals(mBiometricService.mCurrentAuthSession.mState,
+                BiometricService.STATE_AUTHENTICATED_PENDING_SYSUI);
+        // Notify SystemUI hardware authenticated
+        verify(mBiometricService.mStatusBarService).onBiometricAuthenticated(
+                eq(true) /* authenticated */, eq(null) /* failureReason */);
+
+        // SystemUI sends callback with dismissed reason
+        mBiometricService.mInternalReceiver.onDialogDismissed(
+                BiometricPrompt.DISMISSED_REASON_CONFIRM_NOT_REQUIRED);
+        waitForIdle();
+        // HAT sent to keystore
+        verify(mBiometricService.mKeyStore).addAuthToken(any(byte[].class));
+        // Send onAuthenticated to client
+        verify(mReceiver1).onAuthenticationSucceeded();
+        // Current session becomes null
+        assertNull(mBiometricService.mCurrentAuthSession);
+    }
+
+    @Test
+    public void testAuthenticate_happyPathWithConfirmation() throws Exception {
+        setupAuthForOnly(BiometricAuthenticator.TYPE_FACE);
+        invokeAuthenticateAndStart(mBiometricService.mImpl, mReceiver1,
+                true /* requireConfirmation */);
+
+        // Test authentication succeeded goes to PENDING_CONFIRMATION and that the HAT is not
+        // sent to KeyStore yet
+        mBiometricService.mInternalReceiver.onAuthenticationSucceeded(
+                true /* requireConfirmation */,
+                new byte[69] /* HAT */);
+        waitForIdle();
+        // Waiting for SystemUI to send confirmation callback
+        assertEquals(mBiometricService.mCurrentAuthSession.mState,
+                BiometricService.STATE_AUTH_PENDING_CONFIRM);
+        verify(mBiometricService.mKeyStore, never()).addAuthToken(any(byte[].class));
+
+        // SystemUI sends confirm, HAT is sent to keystore and client is notified.
+        mBiometricService.mInternalReceiver.onDialogDismissed(
+                BiometricPrompt.DISMISSED_REASON_CONFIRMED);
+        waitForIdle();
+        verify(mBiometricService.mKeyStore).addAuthToken(any(byte[].class));
+        verify(mReceiver1).onAuthenticationSucceeded();
+    }
+
+    @Test
+    public void testRejectFace_whenAuthenticating_notifiesSystemUIAndClient_thenPaused()
+            throws Exception {
+        setupAuthForOnly(BiometricAuthenticator.TYPE_FACE);
+        invokeAuthenticateAndStart(mBiometricService.mImpl, mReceiver1,
+                false /* requireConfirmation */);
+
+        mBiometricService.mInternalReceiver.onAuthenticationFailed();
+        waitForIdle();
+
+        verify(mBiometricService.mStatusBarService)
+                .onBiometricAuthenticated(eq(false), eq(ERROR_NOT_RECOGNIZED));
+        verify(mReceiver1).onAuthenticationFailed();
+        assertEquals(mBiometricService.mCurrentAuthSession.mState,
+                BiometricService.STATE_AUTH_PAUSED);
+    }
+
+    @Test
+    public void testRejectFingerprint_whenAuthenticating_notifiesAndKeepsAuthenticating()
+            throws Exception {
+        setupAuthForOnly(BiometricAuthenticator.TYPE_FINGERPRINT);
+        invokeAuthenticateAndStart(mBiometricService.mImpl, mReceiver1,
+                false /* requireConfirmation */);
+
+        mBiometricService.mInternalReceiver.onAuthenticationFailed();
+        waitForIdle();
+
+        verify(mBiometricService.mStatusBarService)
+                .onBiometricAuthenticated(eq(false), eq(ERROR_NOT_RECOGNIZED));
+        verify(mReceiver1).onAuthenticationFailed();
+        assertEquals(mBiometricService.mCurrentAuthSession.mState,
+                BiometricService.STATE_AUTH_STARTED);
+    }
+
+    @Test
+    public void testErrorCanceled_whenAuthenticating_notifiesSystemUIAndClient() throws Exception {
+        setupAuthForOnly(BiometricAuthenticator.TYPE_FINGERPRINT);
+        invokeAuthenticateAndStart(mBiometricService.mImpl, mReceiver1,
+                false /* requireConfirmation */);
+
+        // Create a new pending auth session but don't start it yet. HAL contract is that previous
+        // one must get ERROR_CANCELED. Simulate that here by creating the pending auth session,
+        // sending ERROR_CANCELED to the current auth session, and then having the second one
+        // onReadyForAuthentication.
+        invokeAuthenticate(mBiometricService.mImpl, mReceiver2, false /* requireConfirmation */);
+        waitForIdle();
+
+        assertEquals(mBiometricService.mCurrentAuthSession.mState,
+                BiometricService.STATE_AUTH_STARTED);
+        mBiometricService.mInternalReceiver.onError(
+                getCookieForCurrentSession(mBiometricService.mCurrentAuthSession),
+                BiometricConstants.BIOMETRIC_ERROR_CANCELED, ERROR_CANCELED);
+        waitForIdle();
+
+        // Auth session doesn't become null until SystemUI responds that the animation is completed
+        assertNotNull(mBiometricService.mCurrentAuthSession);
+        // ERROR_CANCELED is not sent until SystemUI responded that animation is completed
+        verify(mReceiver1, never()).onError(
+                anyInt(), anyString());
+        verify(mReceiver2, never()).onError(anyInt(), any(String.class));
+
+        // SystemUI dialog closed
+        verify(mBiometricService.mStatusBarService).hideBiometricDialog();
+
+        // After SystemUI notifies that the animation has completed
+        mBiometricService.mInternalReceiver
+                .onDialogDismissed(BiometricPrompt.DISMISSED_REASON_SERVER_REQUESTED);
+        waitForIdle();
+        verify(mReceiver1).onError(
+                eq(BiometricConstants.BIOMETRIC_ERROR_CANCELED),
+                eq(ERROR_CANCELED));
+        assertNull(mBiometricService.mCurrentAuthSession);
+    }
+
+    @Test
+    public void testErrorHalTimeout_whenAuthenticating_entersPausedState() throws Exception {
+        setupAuthForOnly(BiometricAuthenticator.TYPE_FACE);
+        invokeAuthenticateAndStart(mBiometricService.mImpl, mReceiver1,
+                false /* requireConfirmation */);
+
+        mBiometricService.mInternalReceiver.onError(
+                getCookieForCurrentSession(mBiometricService.mCurrentAuthSession),
+                BiometricConstants.BIOMETRIC_ERROR_TIMEOUT,
+                ERROR_TIMEOUT);
+        waitForIdle();
+
+        assertEquals(mBiometricService.mCurrentAuthSession.mState,
+                BiometricService.STATE_AUTH_PAUSED);
+        verify(mBiometricService.mStatusBarService)
+                .onBiometricAuthenticated(eq(false), eq(ERROR_TIMEOUT));
+        // Timeout does not count as fail as per BiometricPrompt documentation.
+        verify(mReceiver1, never()).onAuthenticationFailed();
+
+        // No pending auth session. Pressing try again will create one.
+        assertNull(mBiometricService.mPendingAuthSession);
+
+        // Pressing "Try again" on SystemUI starts a new auth session.
+        mBiometricService.mInternalReceiver.onTryAgainPressed();
+        waitForIdle();
+
+        // The last one is still paused, and a new one has been created.
+        assertEquals(mBiometricService.mCurrentAuthSession.mState,
+                BiometricService.STATE_AUTH_PAUSED);
+        assertEquals(mBiometricService.mPendingAuthSession.mState,
+                BiometricService.STATE_AUTH_CALLED);
+
+        // Test resuming when hardware becomes ready. SystemUI should not be requested to
+        // show another dialog since it's already showing.
+        resetStatusBar();
+        startPendingAuthSession(mBiometricService);
+        waitForIdle();
+        verify(mBiometricService.mStatusBarService, never()).showBiometricDialog(
+                any(Bundle.class),
+                any(IBiometricServiceReceiverInternal.class),
+                anyInt(),
+                anyBoolean() /* requireConfirmation */,
+                anyInt() /* userId */,
+                anyString());
+    }
+
+    @Test
+    public void testErrorFromHal_whenPaused_notifiesSystemUIAndClient() throws Exception {
+        setupAuthForOnly(BiometricAuthenticator.TYPE_FACE);
+        invokeAuthenticateAndStart(mBiometricService.mImpl, mReceiver1,
+                false /* requireCOnfirmation */);
+
+        mBiometricService.mInternalReceiver.onError(
+                getCookieForCurrentSession(mBiometricService.mCurrentAuthSession),
+                BiometricConstants.BIOMETRIC_ERROR_TIMEOUT,
+                ERROR_TIMEOUT);
+        mBiometricService.mInternalReceiver.onError(
+                getCookieForCurrentSession(mBiometricService.mCurrentAuthSession),
+                BiometricConstants.BIOMETRIC_ERROR_CANCELED,
+                ERROR_CANCELED);
+        waitForIdle();
+
+        // Client receives error immediately
+        verify(mReceiver1).onError(
+                eq(BiometricConstants.BIOMETRIC_ERROR_CANCELED),
+                eq(ERROR_CANCELED));
+        // Dialog is hidden immediately
+        verify(mBiometricService.mStatusBarService).hideBiometricDialog();
+        // Auth session is over
+        assertNull(mBiometricService.mCurrentAuthSession);
+    }
+
+    @Test
+    public void testErrorFromHal_whileAuthenticating_waitsForSysUIBeforeNotifyingClient()
+            throws Exception {
+        // For errors that show in SystemUI, BiometricService stays in STATE_ERROR_PENDING_SYSUI
+        // until SystemUI notifies us that the dialog is dismissed at which point the current
+        // session is done.
+        setupAuthForOnly(BiometricAuthenticator.TYPE_FINGERPRINT);
+        invokeAuthenticateAndStart(mBiometricService.mImpl, mReceiver1,
+                false /* requireConfirmation */);
+
+        mBiometricService.mInternalReceiver.onError(
+                getCookieForCurrentSession(mBiometricService.mCurrentAuthSession),
+                BiometricConstants.BIOMETRIC_ERROR_UNABLE_TO_PROCESS,
+                ERROR_UNABLE_TO_PROCESS);
+        waitForIdle();
+
+        // Sends error to SystemUI and does not notify client yet
+        assertEquals(mBiometricService.mCurrentAuthSession.mState,
+                BiometricService.STATE_ERROR_PENDING_SYSUI);
+        verify(mBiometricService.mStatusBarService)
+                .onBiometricError(eq(ERROR_UNABLE_TO_PROCESS));
+        verify(mBiometricService.mStatusBarService, never()).hideBiometricDialog();
+        verify(mReceiver1, never()).onError(anyInt(), anyString());
+
+        // SystemUI animation completed, client is notified, auth session is over
+        mBiometricService.mInternalReceiver
+                .onDialogDismissed(BiometricPrompt.DISMISSED_REASON_ERROR);
+        waitForIdle();
+        verify(mReceiver1).onError(
+                eq(BiometricConstants.BIOMETRIC_ERROR_UNABLE_TO_PROCESS),
+                eq(ERROR_UNABLE_TO_PROCESS));
+        assertNull(mBiometricService.mCurrentAuthSession);
+    }
+
+    @Test
+    public void testDismissedReasonUserCancel_whileAuthenticating_cancelsHalAuthentication()
+            throws Exception {
+        setupAuthForOnly(BiometricAuthenticator.TYPE_FINGERPRINT);
+        invokeAuthenticateAndStart(mBiometricService.mImpl, mReceiver1,
+                false /* requireConfirmation */);
+
+        mBiometricService.mInternalReceiver
+                .onDialogDismissed(BiometricPrompt.DISMISSED_REASON_USER_CANCEL);
+        waitForIdle();
+        verify(mReceiver1).onError(
+                eq(BiometricConstants.BIOMETRIC_ERROR_USER_CANCELED),
+                eq(ERROR_USER_CANCELED));
+        verify(mBiometricService.mFingerprintService).cancelAuthenticationFromService(
+                any(),
+                any(),
+                anyInt(),
+                anyInt(),
+                anyInt(),
+                eq(false) /* fromClient */);
+        assertNull(mBiometricService.mCurrentAuthSession);
+    }
+
+    @Test
+    public void testDismissedReasonNegative_whilePaused_doesntInvokeHalCancel() throws Exception {
+        setupAuthForOnly(BiometricAuthenticator.TYPE_FACE);
+        invokeAuthenticateAndStart(mBiometricService.mImpl, mReceiver1,
+                false /* requireConfirmation */);
+
+        mBiometricService.mInternalReceiver.onError(
+                getCookieForCurrentSession(mBiometricService.mCurrentAuthSession),
+                BiometricConstants.BIOMETRIC_ERROR_TIMEOUT,
+                ERROR_TIMEOUT);
+        mBiometricService.mInternalReceiver.onDialogDismissed(
+                BiometricPrompt.DISMISSED_REASON_NEGATIVE);
+        waitForIdle();
+
+        verify(mBiometricService.mFaceService, never()).cancelAuthenticationFromService(
+                any(),
+                any(),
+                anyInt(),
+                anyInt(),
+                anyInt(),
+                anyBoolean());
+    }
+
+    @Test
+    public void testDismissedReasonUserCancel_whilePaused_doesntInvokeHalCancel() throws Exception {
+        setupAuthForOnly(BiometricAuthenticator.TYPE_FACE);
+        invokeAuthenticateAndStart(mBiometricService.mImpl, mReceiver1,
+                false /* requireConfirmation */);
+
+        mBiometricService.mInternalReceiver.onError(
+                getCookieForCurrentSession(mBiometricService.mCurrentAuthSession),
+                BiometricConstants.BIOMETRIC_ERROR_TIMEOUT,
+                ERROR_TIMEOUT);
+        mBiometricService.mInternalReceiver.onDialogDismissed(
+                BiometricPrompt.DISMISSED_REASON_USER_CANCEL);
+        waitForIdle();
+
+        verify(mBiometricService.mFaceService, never()).cancelAuthenticationFromService(
+                any(),
+                any(),
+                anyInt(),
+                anyInt(),
+                anyInt(),
+                anyBoolean());
+    }
+
+    @Test
+    public void testDismissedReasonUserCancel_whenPendingConfirmation() throws Exception {
+        setupAuthForOnly(BiometricAuthenticator.TYPE_FACE);
+        invokeAuthenticateAndStart(mBiometricService.mImpl, mReceiver1,
+                true /* requireConfirmation */);
+
+        mBiometricService.mInternalReceiver.onAuthenticationSucceeded(
+                true /* requireConfirmation */,
+                new byte[69] /* HAT */);
+        mBiometricService.mInternalReceiver.onDialogDismissed(
+                BiometricPrompt.DISMISSED_REASON_USER_CANCEL);
+        waitForIdle();
+
+        // doesn't send cancel to HAL
+        verify(mBiometricService.mFaceService, never()).cancelAuthenticationFromService(
+                any(),
+                any(),
+                anyInt(),
+                anyInt(),
+                anyInt(),
+                anyBoolean());
+        verify(mReceiver1).onError(
+                eq(BiometricConstants.BIOMETRIC_ERROR_USER_CANCELED),
+                eq(ERROR_USER_CANCELED));
+        assertNull(mBiometricService.mCurrentAuthSession);
+    }
+
+    @Test
+    public void testAcquire_whenAuthenticating_sentToSystemUI() throws Exception {
+        setupAuthForOnly(BiometricAuthenticator.TYPE_FINGERPRINT);
+        invokeAuthenticateAndStart(mBiometricService.mImpl, mReceiver1,
+                false /* requireConfirmation */);
+
+        mBiometricService.mInternalReceiver.onAcquired(
+                FingerprintManager.FINGERPRINT_ACQUIRED_IMAGER_DIRTY,
+                FINGERPRINT_ACQUIRED_SENSOR_DIRTY);
+        waitForIdle();
+
+        // Sends to SysUI and stays in authenticating state
+        verify(mBiometricService.mStatusBarService)
+                .onBiometricHelp(eq(FINGERPRINT_ACQUIRED_SENSOR_DIRTY));
+        assertEquals(mBiometricService.mCurrentAuthSession.mState,
+                BiometricService.STATE_AUTH_STARTED);
+    }
+
+    // Helper methods
+
+    private void setupAuthForOnly(int modality) {
+        when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_FINGERPRINT))
+                .thenReturn(false);
+        when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_FACE)).thenReturn(false);
+
+        if (modality == BiometricAuthenticator.TYPE_FINGERPRINT) {
+            when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_FINGERPRINT))
+                    .thenReturn(true);
+            when(mFingerprintManager.hasEnrolledTemplates(anyInt())).thenReturn(true);
+            when(mFingerprintManager.isHardwareDetected()).thenReturn(true);
+        } else if (modality == BiometricAuthenticator.TYPE_FACE) {
+            when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_FACE)).thenReturn(true);
+            when(mFaceManager.hasEnrolledTemplates(anyInt())).thenReturn(true);
+            when(mFaceManager.isHardwareDetected()).thenReturn(true);
+        } else {
+            fail("Unknown modality: " + modality);
+        }
+
+        mBiometricService = new BiometricService(mContext, new MockInjector());
+        mBiometricService.onStart();
+
+        when(mBiometricService.mSettingObserver.getFaceEnabledForApps(anyInt())).thenReturn(true);
+    }
+
+    private void resetReceiver() {
+        mReceiver1 = mock(IBiometricServiceReceiver.class);
+        mReceiver2 = mock(IBiometricServiceReceiver.class);
+    }
+
+    private void resetStatusBar() {
+        mBiometricService.mStatusBarService = mock(IStatusBarService.class);
+    }
+
+    private void invokeAuthenticateAndStart(IBiometricService.Stub service,
+            IBiometricServiceReceiver receiver, boolean requireConfirmation) throws Exception {
+        // Request auth, creates a pending session
+        invokeAuthenticate(service, receiver, requireConfirmation);
+        waitForIdle();
+
+        startPendingAuthSession(mBiometricService);
+        waitForIdle();
+    }
+
+    private static void startPendingAuthSession(BiometricService service) throws Exception {
+        // Get the cookie so we can pretend the hardware is ready to authenticate
+        // Currently we only support single modality per auth
+        assertEquals(service.mPendingAuthSession.mModalitiesWaiting.values().size(), 1);
+        final int cookie = service.mPendingAuthSession.mModalitiesWaiting.values()
+                .iterator().next();
+        assertNotEquals(cookie, 0);
+
+        service.mImpl.onReadyForAuthentication(cookie,
+                anyBoolean() /* requireConfirmation */, anyInt() /* userId */);
+    }
+
+    private static void invokeAuthenticate(IBiometricService.Stub service,
+            IBiometricServiceReceiver receiver, boolean requireConfirmation) throws Exception {
+        service.authenticate(
+                new Binder() /* token */,
+                0 /* sessionId */,
+                0 /* userId */,
+                receiver,
+                TEST_PACKAGE_NAME /* packageName */,
+                createTestBiometricPromptBundle(requireConfirmation),
+                null /* IBiometricConfirmDeviceCredentialCallback */);
+    }
+
+    private static Bundle createTestBiometricPromptBundle(boolean requireConfirmation) {
+        final Bundle bundle = new Bundle();
+        bundle.putBoolean(BiometricPrompt.KEY_REQUIRE_CONFIRMATION, requireConfirmation);
+        return bundle;
+    }
+
+    private static int getCookieForCurrentSession(BiometricService.AuthSession session) {
+        assertEquals(session.mModalitiesMatched.values().size(), 1);
+        return session.mModalitiesMatched.values().iterator().next();
+    }
+
+    private static void waitForIdle() {
+        InstrumentationRegistry.getInstrumentation().waitForIdleSync();
+    }
+}
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
index c1c0a30..3ac7a79 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -181,12 +181,6 @@
 @RunWithLooper
 public class NotificationManagerServiceTest extends UiServiceTestCase {
     private static final String TEST_CHANNEL_ID = "NotificationManagerServiceTestChannelId";
-    private static final String CLEAR_DEVICE_CONFIG_KEY_CMD =
-            "device_config delete " + DeviceConfig.NAMESPACE_SYSTEMUI + " "
-                    + SystemUiDeviceConfigFlags.NAS_DEFAULT_SERVICE;
-    private static final String SET_DEFAULT_ASSISTANT_DEVICE_CONFIG_CMD =
-            "device_config put " + DeviceConfig.NAMESPACE_SYSTEMUI + " "
-                    + SystemUiDeviceConfigFlags.NAS_DEFAULT_SERVICE;
 
     private final int mUid = Binder.getCallingUid();
     private TestableNotificationManagerService mService;
@@ -2766,6 +2760,18 @@
     }
 
     @Test
+    public void testReadPolicyXml_readSnoozedNotificationsFromXml() throws Exception {
+        final String upgradeXml = "<notification-policy version=\"1\">"
+                + "<snoozed-notifications>></snoozed-notifications>"
+                + "</notification-policy>";
+        mService.readPolicyXml(
+                new BufferedInputStream(new ByteArrayInputStream(upgradeXml.getBytes())),
+                false,
+                UserHandle.USER_ALL);
+        verify(mSnoozeHelper, times(1)).readXml(any(XmlPullParser.class));
+    }
+
+    @Test
     public void testReadPolicyXml_readApprovedServicesFromSettings() throws Exception {
         final String preupgradeXml = "<notification-policy version=\"1\">"
                 + "<ranking></ranking>"
@@ -4028,6 +4034,41 @@
     }
 
     @Test
+    public void testPostFromAndroidForNonExistentPackage() throws Exception {
+        final String notReal = "NOT REAL";
+        when(mPackageManagerClient.getPackageUidAsUser(anyString(), anyInt())).thenThrow(
+                PackageManager.NameNotFoundException.class);
+        ApplicationInfo ai = new ApplicationInfo();
+        ai.uid = -1;
+        when(mPackageManager.getApplicationInfo(anyString(), anyInt(), anyInt())).thenReturn(ai);
+
+        final StatusBarNotification sbn = generateNotificationRecord(null).sbn;
+        try {
+            mInternalService.enqueueNotification(notReal, "android", 0, 0, "tag",
+                    sbn.getId(), sbn.getNotification(), sbn.getUserId());
+            fail("can't post notifications for nonexistent packages, even if you exist");
+        } catch (SecurityException e) {
+            // yay
+        }
+    }
+
+    @Test
+    public void testCancelFromAndroidForNonExistentPackage() throws Exception {
+        final String notReal = "NOT REAL";
+        when(mPackageManagerClient.getPackageUidAsUser(eq(notReal), anyInt())).thenThrow(
+                PackageManager.NameNotFoundException.class);
+        ApplicationInfo ai = new ApplicationInfo();
+        ai.uid = -1;
+        when(mPackageManager.getApplicationInfo(anyString(), anyInt(), anyInt())).thenReturn(ai);
+
+        // unlike the post case, ignore instead of throwing
+        final StatusBarNotification sbn = generateNotificationRecord(null).sbn;
+
+        mInternalService.cancelNotification(notReal, "android", 0, 0, "tag",
+                sbn.getId(), sbn.getUserId());
+    }
+
+    @Test
     public void testResolveNotificationUid_delegateNotAllowed() throws Exception {
         when(mPackageManagerClient.getPackageUidAsUser("target", 0)).thenReturn(123);
         // no delegate
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/SnoozeHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/SnoozeHelperTest.java
index 2e7277f..36175a9 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/SnoozeHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/SnoozeHelperTest.java
@@ -18,6 +18,7 @@
 import static junit.framework.Assert.assertEquals;
 import static junit.framework.Assert.assertFalse;
 import static junit.framework.Assert.assertTrue;
+import static junit.framework.Assert.assertNull;
 
 import static org.mockito.Matchers.any;
 import static org.mockito.Matchers.anyInt;
@@ -37,9 +38,11 @@
 import android.service.notification.StatusBarNotification;
 import android.test.suitebuilder.annotation.SmallTest;
 import android.util.IntArray;
+import android.util.Xml;
 
 import androidx.test.runner.AndroidJUnit4;
 
+import com.android.internal.util.FastXmlSerializer;
 import com.android.server.UiServiceTestCase;
 
 import org.junit.Before;
@@ -48,6 +51,15 @@
 import org.mockito.ArgumentCaptor;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlSerializer;
+
+import java.io.BufferedInputStream;
+import java.io.BufferedOutputStream;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
 
 @SmallTest
 @RunWith(AndroidJUnit4.class)
@@ -69,6 +81,117 @@
     }
 
     @Test
+    public void testWriteXMLformattedCorrectly_testReadingCorrectTime()
+            throws XmlPullParserException, IOException {
+        final String max_time_str = Long.toString(Long.MAX_VALUE);
+        final String xml_string = "<snoozed-notifications>"
+                + "<notification version=\"1\" user-id=\"0\" notification=\"notification\" "
+                + "pkg=\"pkg\" key=\"key\" time=\"" + max_time_str + "\"/>"
+                + "<notification version=\"1\" user-id=\"0\" notification=\"notification\" "
+                + "pkg=\"pkg\" key=\"key2\" time=\"" + max_time_str + "\"/>"
+                + "</snoozed-notifications>";
+        XmlPullParser parser = Xml.newPullParser();
+        parser.setInput(new BufferedInputStream(
+                new ByteArrayInputStream(xml_string.getBytes())), null);
+        mSnoozeHelper.readXml(parser);
+        assertTrue("Should read the notification time from xml and it should be more than zero",
+                0 < mSnoozeHelper.getSnoozeTimeForUnpostedNotification(
+                        0, "pkg", "key").doubleValue());
+    }
+
+    @Test
+    public void testWriteXMLformattedCorrectly_testCorrectContextURI()
+            throws XmlPullParserException, IOException {
+        final String max_time_str = Long.toString(Long.MAX_VALUE);
+        final String xml_string = "<snoozed-notifications>"
+                + "<context version=\"1\" user-id=\"0\" notification=\"notification\" "
+                + "pkg=\"pkg\" key=\"key\" id=\"uri\"/>"
+                + "<context version=\"1\" user-id=\"0\" notification=\"notification\" "
+                + "pkg=\"pkg\" key=\"key2\" id=\"uri\"/>"
+                + "</snoozed-notifications>";
+        XmlPullParser parser = Xml.newPullParser();
+        parser.setInput(new BufferedInputStream(
+                new ByteArrayInputStream(xml_string.getBytes())), null);
+        mSnoozeHelper.readXml(parser);
+        assertEquals("Should read the notification context from xml and it should be `uri",
+                "uri", mSnoozeHelper.getSnoozeContextForUnpostedNotification(
+                        0, "pkg", "key"));
+    }
+
+    @Test
+    public void testReadValidSnoozedFromCorrectly_timeDeadline()
+            throws XmlPullParserException, IOException {
+        NotificationRecord r = getNotificationRecord("pkg", 1, "one", UserHandle.SYSTEM);
+        mSnoozeHelper.snooze(r, 999999999);
+        XmlSerializer serializer = new FastXmlSerializer();
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        serializer.setOutput(new BufferedOutputStream(baos), "utf-8");
+        serializer.startDocument(null, true);
+        mSnoozeHelper.writeXml(serializer);
+        serializer.endDocument();
+        serializer.flush();
+
+        XmlPullParser parser = Xml.newPullParser();
+        parser.setInput(new BufferedInputStream(
+                new ByteArrayInputStream(baos.toByteArray())), "utf-8");
+        mSnoozeHelper.readXml(parser);
+        assertTrue("Should read the notification time from xml and it should be more than zero",
+                0 < mSnoozeHelper.getSnoozeTimeForUnpostedNotification(
+                        0, "pkg", r.getKey()).doubleValue());
+    }
+
+
+    @Test
+    public void testReadExpiredSnoozedNotification() throws
+            XmlPullParserException, IOException, InterruptedException {
+        NotificationRecord r = getNotificationRecord("pkg", 1, "one", UserHandle.SYSTEM);
+        mSnoozeHelper.snooze(r, 0);
+       // Thread.sleep(100);
+        XmlSerializer serializer = new FastXmlSerializer();
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        serializer.setOutput(new BufferedOutputStream(baos), "utf-8");
+        serializer.startDocument(null, true);
+        mSnoozeHelper.writeXml(serializer);
+        serializer.endDocument();
+        serializer.flush();
+        Thread.sleep(10);
+        XmlPullParser parser = Xml.newPullParser();
+        parser.setInput(new BufferedInputStream(
+                new ByteArrayInputStream(baos.toByteArray())), "utf-8");
+        mSnoozeHelper.readXml(parser);
+        int systemUser = UserHandle.SYSTEM.getIdentifier();
+        assertTrue("Should see a past time returned",
+                System.currentTimeMillis() >  mSnoozeHelper.getSnoozeTimeForUnpostedNotification(
+                        systemUser, "pkg", r.getKey()).longValue());
+    }
+
+    @Test
+    public void testCleanupContextShouldRemovePersistedRecord() {
+        NotificationRecord r = getNotificationRecord("pkg", 1, "one", UserHandle.SYSTEM);
+        mSnoozeHelper.snooze(r, "context");
+        mSnoozeHelper.cleanupPersistedContext(r.sbn.getKey());
+        assertNull(mSnoozeHelper.getSnoozeContextForUnpostedNotification(
+                r.getUser().getIdentifier(),
+                r.sbn.getPackageName(),
+                r.sbn.getKey()
+        ));
+    }
+
+    @Test
+    public void testReadNoneSnoozedNotification() throws XmlPullParserException,
+            IOException, InterruptedException {
+        NotificationRecord r = getNotificationRecord(
+                "pkg", 1, "one", UserHandle.SYSTEM);
+        mSnoozeHelper.snooze(r, 0);
+
+        assertEquals("should see a zero value for unsnoozed notification",
+                0L,
+                mSnoozeHelper.getSnoozeTimeForUnpostedNotification(
+                        UserHandle.SYSTEM.getIdentifier(),
+                        "not_my_package", r.getKey()).longValue());
+    }
+
+    @Test
     public void testSnoozeForTime() throws Exception {
         NotificationRecord r = getNotificationRecord("pkg", 1, "one", UserHandle.SYSTEM);
         mSnoozeHelper.snooze(r, 1000);
@@ -84,7 +207,7 @@
     @Test
     public void testSnooze() throws Exception {
         NotificationRecord r = getNotificationRecord("pkg", 1, "one", UserHandle.SYSTEM);
-        mSnoozeHelper.snooze(r);
+        mSnoozeHelper.snooze(r, (String) null);
         verify(mAm, never()).setExactAndAllowWhileIdle(
                 anyInt(), anyLong(), any(PendingIntent.class));
         assertTrue(mSnoozeHelper.isSnoozed(
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityDisplayTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityDisplayTests.java
index 63b9198..6c78f6f 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityDisplayTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityDisplayTests.java
@@ -28,6 +28,7 @@
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.never;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.reset;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.spy;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
 import static com.android.server.wm.ActivityStackSupervisor.ON_TOP;
 
@@ -140,7 +141,8 @@
     public void testNotResumeHomeStackOnRemovingDisplay() {
         // Create a display which supports system decoration and allows reparenting stacks to
         // another display when the display is removed.
-        final ActivityDisplay display = spy(createNewActivityDisplay());
+        final ActivityDisplay display = createNewActivityDisplay();
+        spyOn(display);
         doReturn(false).when(display).shouldDestroyContentOnRemove();
         doReturn(true).when(display).supportsSystemDecorations();
         mRootActivityContainer.addChild(display, ActivityDisplay.POSITION_TOP);
@@ -304,14 +306,18 @@
                 ACTIVITY_TYPE_STANDARD, ON_TOP);
         final ActivityStack stack4 = display.createStack(WINDOWING_MODE_FULLSCREEN,
                 ACTIVITY_TYPE_STANDARD, ON_TOP);
-        final TaskRecord task1 = new TaskBuilder(mService.mStackSupervisor).setStack(
-                stack1).setTaskId(1).build();
-        final TaskRecord task2 = new TaskBuilder(mService.mStackSupervisor).setStack(
-                stack2).setTaskId(2).build();
-        final TaskRecord task3 = new TaskBuilder(mService.mStackSupervisor).setStack(
-                stack3).setTaskId(3).build();
-        final TaskRecord task4 = new TaskBuilder(mService.mStackSupervisor).setStack(
-                stack4).setTaskId(4).build();
+        final TaskRecord task1 = new TaskBuilder(mService.mStackSupervisor)
+                .setStack(stack1)
+                .build();
+        final TaskRecord task2 = new TaskBuilder(mService.mStackSupervisor)
+                .setStack(stack2)
+                .build();
+        final TaskRecord task3 = new TaskBuilder(mService.mStackSupervisor)
+                .setStack(stack3)
+                .build();
+        final TaskRecord task4 = new TaskBuilder(mService.mStackSupervisor)
+                .setStack(stack4)
+                .build();
 
         // Reordering stacks while removing stacks.
         doAnswer(invocation -> {
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java
index 23bae88..977dd8e 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java
@@ -61,13 +61,13 @@
     private ActivityMetricsLaunchObserver mLaunchObserver;
     private ActivityMetricsLaunchObserverRegistry mLaunchObserverRegistry;
 
-    private TestActivityStack mStack;
+    private ActivityStack mStack;
     private TaskRecord mTask;
     private ActivityRecord mActivityRecord;
     private ActivityRecord mActivityRecordTrampoline;
 
     @Before
-    public void setUpAMLO() throws Exception {
+    public void setUpAMLO() {
         mLaunchObserver = mock(ActivityMetricsLaunchObserver.class);
 
         // ActivityStackSupervisor always creates its own instance of ActivityMetricsLogger.
@@ -78,15 +78,19 @@
 
         // Sometimes we need an ActivityRecord for ActivityMetricsLogger to do anything useful.
         // This seems to be the easiest way to create an ActivityRecord.
-        mStack = mRootActivityContainer.getDefaultDisplay().createStack(
-                WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
-        mTask = new TaskBuilder(mSupervisor).setStack(mStack).build();
-        mActivityRecord = new ActivityBuilder(mService).setTask(mTask).build();
+        mStack = new StackBuilder(mRootActivityContainer)
+                .setActivityType(ACTIVITY_TYPE_STANDARD)
+                .setWindowingMode(WINDOWING_MODE_FULLSCREEN)
+                .setOnTop(true)
+                .setCreateActivity(true)
+                .build();
+        mTask = mStack.topTask();
+        mActivityRecord = mTask.getTopActivity();
         mActivityRecordTrampoline = new ActivityBuilder(mService).setTask(mTask).build();
     }
 
     @After
-    public void tearDownAMLO() throws Exception {
+    public void tearDownAMLO() {
         if (mLaunchObserverRegistry != null) {  // Don't NPE if setUp failed.
             mLaunchObserverRegistry.unregisterLaunchObserver(mLaunchObserver);
         }
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
index 7b252cb..13748cb 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
@@ -16,10 +16,17 @@
 
 package com.android.server.wm;
 
+import static android.content.pm.ActivityInfo.CONFIG_ORIENTATION;
+import static android.content.pm.ActivityInfo.CONFIG_SCREEN_LAYOUT;
+import static android.content.pm.ActivityInfo.RESIZE_MODE_UNRESIZEABLE;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
+import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
+import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
 import static android.os.Process.NOBODY_UID;
 import static android.view.Display.DEFAULT_DISPLAY;
 import static android.view.Surface.ROTATION_0;
 import static android.view.Surface.ROTATION_90;
+import static android.view.WindowManager.TRANSIT_TASK_CLOSE;
 
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.any;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyInt;
@@ -28,18 +35,25 @@
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.eq;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.reset;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.times;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.when;
 import static com.android.server.wm.ActivityRecord.FINISH_RESULT_CANCELLED;
 import static com.android.server.wm.ActivityRecord.FINISH_RESULT_REMOVED;
 import static com.android.server.wm.ActivityRecord.FINISH_RESULT_REQUESTED;
+import static com.android.server.wm.ActivityStack.ActivityState.DESTROYED;
+import static com.android.server.wm.ActivityStack.ActivityState.DESTROYING;
+import static com.android.server.wm.ActivityStack.ActivityState.FINISHING;
 import static com.android.server.wm.ActivityStack.ActivityState.INITIALIZING;
+import static com.android.server.wm.ActivityStack.ActivityState.PAUSED;
 import static com.android.server.wm.ActivityStack.ActivityState.PAUSING;
 import static com.android.server.wm.ActivityStack.ActivityState.RESUMED;
 import static com.android.server.wm.ActivityStack.ActivityState.STARTED;
 import static com.android.server.wm.ActivityStack.ActivityState.STOPPED;
 import static com.android.server.wm.ActivityStack.ActivityState.STOPPING;
+import static com.android.server.wm.ActivityStack.REMOVE_TASK_MODE_DESTROYING;
 import static com.android.server.wm.ActivityStack.REMOVE_TASK_MODE_MOVING;
 import static com.android.server.wm.ActivityStack.STACK_VISIBILITY_INVISIBLE;
 import static com.android.server.wm.ActivityStack.STACK_VISIBILITY_VISIBLE;
@@ -54,6 +68,7 @@
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.never;
 
 import android.app.ActivityManager;
 import android.app.ActivityManagerInternal;
@@ -71,6 +86,7 @@
 import android.platform.test.annotations.Presubmit;
 import android.util.MergedConfiguration;
 import android.util.MutableBoolean;
+import android.view.DisplayInfo;
 import android.view.IRemoteAnimationFinishedCallback;
 import android.view.IRemoteAnimationRunner.Stub;
 import android.view.RemoteAnimationAdapter;
@@ -79,7 +95,7 @@
 import androidx.test.filters.MediumTest;
 
 import com.android.internal.R;
-import com.android.server.wm.utils.WmDisplayCutout;
+import com.android.server.wm.ActivityStack.ActivityState;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -96,13 +112,13 @@
 @MediumTest
 @Presubmit
 public class ActivityRecordTests extends ActivityTestsBase {
-    private TestActivityStack mStack;
+    private ActivityStack mStack;
     private TaskRecord mTask;
     private ActivityRecord mActivity;
 
     @Before
     public void setUp() throws Exception {
-        mStack = (TestActivityStack) new StackBuilder(mRootActivityContainer).build();
+        mStack = new StackBuilder(mRootActivityContainer).build();
         mTask = mStack.getChildAt(0);
         mActivity = mTask.getTopActivity();
 
@@ -113,13 +129,13 @@
     @Test
     public void testStackCleanupOnClearingTask() {
         mActivity.setTask(null);
-        assertEquals(mStack.onActivityRemovedFromStackInvocationCount(), 1);
+        verify(mStack, times(1)).onActivityRemovedFromStack(any());
     }
 
     @Test
     public void testStackCleanupOnActivityRemoval() {
         mTask.removeActivity(mActivity);
-        assertEquals(mStack.onActivityRemovedFromStackInvocationCount(),  1);
+        verify(mStack, times(1)).onActivityRemovedFromStack(any());
     }
 
     @Test
@@ -134,7 +150,7 @@
         final TaskRecord newTask = new TaskBuilder(mService.mStackSupervisor).setStack(mStack)
                 .build();
         mActivity.reparent(newTask, 0, null /*reason*/);
-        assertEquals(mStack.onActivityRemovedFromStackInvocationCount(), 0);
+        verify(mStack, times(0)).onActivityRemovedFromStack(any());
     }
 
     @Test
@@ -181,7 +197,7 @@
         assertTrue(mActivity.isState(STARTED));
 
         mStack.mTranslucentActivityWaiting = null;
-        topActivity.changeWindowTranslucency(false);
+        topActivity.setOccludesParent(false);
         mActivity.setState(STOPPED, "testPausingWhenVisibleFromStopped behind non-opaque");
         mActivity.makeVisibleIfNeeded(null /* starting */, true /* reportToClient */);
         assertTrue(mActivity.isState(STARTED));
@@ -261,8 +277,8 @@
     public void testNewParentConfigurationIncrementsSeq() {
         final Configuration newConfig = new Configuration(
                 mTask.getRequestedOverrideConfiguration());
-        newConfig.orientation = newConfig.orientation == Configuration.ORIENTATION_PORTRAIT
-                ? Configuration.ORIENTATION_LANDSCAPE : Configuration.ORIENTATION_PORTRAIT;
+        newConfig.orientation = newConfig.orientation == ORIENTATION_PORTRAIT
+                ? ORIENTATION_LANDSCAPE : ORIENTATION_PORTRAIT;
 
         final int prevSeq = mActivity.getMergedOverrideConfiguration().seq;
         mTask.onRequestedOverrideConfigurationChanged(newConfig);
@@ -277,7 +293,7 @@
                 .getRequestedOverrideConfiguration();
 
         final Configuration newConfig = new Configuration();
-        newConfig.orientation = Configuration.ORIENTATION_PORTRAIT;
+        newConfig.orientation = ORIENTATION_PORTRAIT;
 
         final int prevSeq = mActivity.getMergedOverrideConfiguration().seq;
         mActivity.onRequestedOverrideConfigurationChanged(newConfig);
@@ -293,10 +309,11 @@
         mActivity.setLastReportedConfiguration(new MergedConfiguration(new Configuration(),
                 mActivity.getConfiguration()));
 
-        mActivity.info.configChanges &= ~ActivityInfo.CONFIG_ORIENTATION;
+        mActivity.info.configChanges &= ~CONFIG_ORIENTATION;
         final Configuration newConfig = new Configuration(mTask.getConfiguration());
-        newConfig.orientation = newConfig.orientation == Configuration.ORIENTATION_PORTRAIT
-                ? Configuration.ORIENTATION_LANDSCAPE : Configuration.ORIENTATION_PORTRAIT;
+        newConfig.orientation = newConfig.orientation == ORIENTATION_PORTRAIT
+                ? ORIENTATION_LANDSCAPE
+                : ORIENTATION_PORTRAIT;
         mTask.onRequestedOverrideConfigurationChanged(newConfig);
 
         mActivity.mRelaunchReason = ActivityTaskManagerService.RELAUNCH_REASON_NONE;
@@ -315,13 +332,14 @@
         mActivity.setLastReportedConfiguration(new MergedConfiguration(new Configuration(),
                 mActivity.getConfiguration()));
 
-        mActivity.info.configChanges &= ~ActivityInfo.CONFIG_ORIENTATION;
+        mActivity.info.configChanges &= ~CONFIG_ORIENTATION;
         final Configuration newConfig = new Configuration(mTask.getConfiguration());
-        newConfig.orientation = newConfig.orientation == Configuration.ORIENTATION_PORTRAIT
-                ? Configuration.ORIENTATION_LANDSCAPE : Configuration.ORIENTATION_PORTRAIT;
+        newConfig.orientation = newConfig.orientation == ORIENTATION_PORTRAIT
+                ? ORIENTATION_LANDSCAPE
+                : ORIENTATION_PORTRAIT;
         mTask.onRequestedOverrideConfigurationChanged(newConfig);
 
-        doReturn(true).when(mTask.getTask()).isDragResizing();
+        doReturn(true).when(mTask.mTask).isDragResizing();
 
         mActivity.mRelaunchReason = ActivityTaskManagerService.RELAUNCH_REASON_NONE;
 
@@ -355,30 +373,39 @@
 
     @Test
     public void testSetRequestedOrientationUpdatesConfiguration() throws Exception {
+        mActivity = new ActivityBuilder(mService)
+                .setTask(mTask)
+                .setConfigChanges(CONFIG_ORIENTATION | CONFIG_SCREEN_LAYOUT)
+                .build();
         mActivity.setState(ActivityStack.ActivityState.RESUMED, "Testing");
 
-        mTask.onRequestedOverrideConfigurationChanged(mTask.getConfiguration());
         mActivity.setLastReportedConfiguration(new MergedConfiguration(new Configuration(),
                 mActivity.getConfiguration()));
 
-        mActivity.info.configChanges |= ActivityInfo.CONFIG_ORIENTATION;
         final Configuration newConfig = new Configuration(mActivity.getConfiguration());
-        newConfig.orientation = newConfig.orientation == Configuration.ORIENTATION_PORTRAIT
-                ? Configuration.ORIENTATION_LANDSCAPE
-                : Configuration.ORIENTATION_PORTRAIT;
+        final int shortSide = Math.min(newConfig.screenWidthDp, newConfig.screenHeightDp);
+        final int longSide = Math.max(newConfig.screenWidthDp, newConfig.screenHeightDp);
+        if (newConfig.orientation == ORIENTATION_PORTRAIT) {
+            newConfig.orientation = ORIENTATION_LANDSCAPE;
+            newConfig.screenWidthDp = longSide;
+            newConfig.screenHeightDp = shortSide;
+        } else {
+            newConfig.orientation = ORIENTATION_PORTRAIT;
+            newConfig.screenWidthDp = shortSide;
+            newConfig.screenHeightDp = longSide;
+        }
 
         // Mimic the behavior that display doesn't handle app's requested orientation.
-        doAnswer(invocation -> {
-            mTask.onConfigurationChanged(newConfig);
-            return null;
-        }).when(mActivity.mAppWindowToken).setOrientation(anyInt(), any(), any());
+        final DisplayContent dc = mTask.mTask.getDisplayContent();
+        doReturn(false).when(dc).onDescendantOrientationChanged(any(), any());
+        doReturn(false).when(dc).handlesOrientationChangeFromDescendant();
 
         final int requestedOrientation;
         switch (newConfig.orientation) {
-            case Configuration.ORIENTATION_LANDSCAPE:
-                requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
+            case ORIENTATION_LANDSCAPE:
+                requestedOrientation = SCREEN_ORIENTATION_LANDSCAPE;
                 break;
-            case Configuration.ORIENTATION_PORTRAIT:
+            case ORIENTATION_PORTRAIT:
                 requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
                 break;
             default:
@@ -421,24 +448,33 @@
 
     @Test
     public void testPushConfigurationWhenLaunchTaskBehind() throws Exception {
+        mActivity = new ActivityBuilder(mService)
+                .setTask(mTask)
+                .setLaunchTaskBehind(true)
+                .setConfigChanges(CONFIG_ORIENTATION)
+                .build();
         mActivity.setState(ActivityStack.ActivityState.STOPPED, "Testing");
 
-        final TestActivityStack stack = (TestActivityStack) new StackBuilder(mRootActivityContainer)
-                .build();
+        final ActivityStack stack = new StackBuilder(mRootActivityContainer).build();
         try {
-            stack.setIsTranslucent(false);
+            doReturn(false).when(stack).isStackTranslucent(any());
             assertFalse(mStack.shouldBeVisible(null /* starting */));
 
-            mTask.onRequestedOverrideConfigurationChanged(mTask.getConfiguration());
             mActivity.setLastReportedConfiguration(new MergedConfiguration(new Configuration(),
                     mActivity.getConfiguration()));
 
-            mActivity.mLaunchTaskBehind = true;
-            mActivity.info.configChanges |= ActivityInfo.CONFIG_ORIENTATION;
             final Configuration newConfig = new Configuration(mActivity.getConfiguration());
-            newConfig.orientation = newConfig.orientation == Configuration.ORIENTATION_PORTRAIT
-                    ? Configuration.ORIENTATION_LANDSCAPE
-                    : Configuration.ORIENTATION_PORTRAIT;
+            final int shortSide = Math.min(newConfig.screenWidthDp, newConfig.screenHeightDp);
+            final int longSide = Math.max(newConfig.screenWidthDp, newConfig.screenHeightDp);
+            if (newConfig.orientation == ORIENTATION_PORTRAIT) {
+                newConfig.orientation = ORIENTATION_LANDSCAPE;
+                newConfig.screenWidthDp = longSide;
+                newConfig.screenHeightDp = shortSide;
+            } else {
+                newConfig.orientation = ORIENTATION_PORTRAIT;
+                newConfig.screenWidthDp = shortSide;
+                newConfig.screenHeightDp = longSide;
+            }
 
             mTask.onConfigurationChanged(newConfig);
 
@@ -457,7 +493,7 @@
     @Test
     public void testShouldPauseWhenMakeClientVisible() {
         ActivityRecord topActivity = new ActivityBuilder(mService).setTask(mTask).build();
-        topActivity.changeWindowTranslucency(false);
+        topActivity.setOccludesParent(false);
         mActivity.setState(ActivityStack.ActivityState.STOPPED, "Testing");
         mActivity.makeClientVisible();
         assertEquals(STARTED, mActivity.getState());
@@ -468,6 +504,7 @@
         setupDisplayContentForCompatDisplayInsets();
         final int decorHeight = 200; // e.g. The device has cutout.
         final DisplayPolicy policy = setupDisplayAndParentSize(600, 800).getDisplayPolicy();
+        spyOn(policy);
         doAnswer(invocationOnMock -> {
             final int rotation = invocationOnMock.<Integer>getArgument(0);
             final Rect insets = invocationOnMock.<Rect>getArgument(4);
@@ -482,7 +519,7 @@
 
         doReturn(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED)
                 .when(mActivity.mAppWindowToken).getOrientationIgnoreVisibility();
-        mActivity.info.resizeMode = ActivityInfo.RESIZE_MODE_UNRESIZEABLE;
+        mActivity.info.resizeMode = RESIZE_MODE_UNRESIZEABLE;
         mActivity.info.minAspectRatio = mActivity.info.maxAspectRatio = 1;
         ensureActivityConfiguration();
         // The parent configuration doesn't change since the first resolved configuration, so the
@@ -506,19 +543,32 @@
     @Test
     public void testSizeCompatMode_FixedScreenConfigurationWhenMovingToDisplay() {
         // Initialize different bounds on a new display.
-        final ActivityDisplay newDisplay = addNewActivityDisplayAt(ActivityDisplay.POSITION_TOP);
-        newDisplay.getWindowConfiguration().setAppBounds(new Rect(0, 0, 1000, 2000));
-        newDisplay.getConfiguration().densityDpi = 300;
+        final Rect newDisplayBounds = new Rect(0, 0, 1000, 2000);
+        DisplayInfo info = new DisplayInfo();
+        mService.mContext.getDisplay().getDisplayInfo(info);
+        info.logicalWidth = newDisplayBounds.width();
+        info.logicalHeight = newDisplayBounds.height();
+        info.logicalDensityDpi = 300;
 
-        mTask.getConfiguration().densityDpi = 200;
-        prepareFixedAspectRatioUnresizableActivity();
+        final ActivityDisplay newDisplay =
+                addNewActivityDisplayAt(info, ActivityDisplay.POSITION_TOP);
+
+        final Configuration c =
+                new Configuration(mStack.getDisplay().getRequestedOverrideConfiguration());
+        c.densityDpi = 200;
+        mStack.getDisplay().onRequestedOverrideConfigurationChanged(c);
+        mActivity = new ActivityBuilder(mService)
+                .setTask(mTask)
+                .setResizeMode(RESIZE_MODE_UNRESIZEABLE)
+                .setMaxAspectRatio(1.5f)
+                .build();
+        mActivity.visible = true;
 
         final Rect originalBounds = new Rect(mActivity.getBounds());
         final int originalDpi = mActivity.getConfiguration().densityDpi;
 
         // Move the non-resizable activity to the new display.
         mStack.reparent(newDisplay, true /* onTop */, false /* displayRemoved */);
-        ensureActivityConfiguration();
 
         assertEquals(originalBounds, mActivity.getBounds());
         assertEquals(originalDpi, mActivity.getConfiguration().densityDpi);
@@ -531,9 +581,9 @@
         when(mActivity.mAppWindowToken.getOrientationIgnoreVisibility()).thenReturn(
                 ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
         mTask.getWindowConfiguration().setAppBounds(mStack.getDisplay().getBounds());
-        mTask.getConfiguration().orientation = Configuration.ORIENTATION_PORTRAIT;
+        mTask.getConfiguration().orientation = ORIENTATION_PORTRAIT;
         mActivity.info.screenOrientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
-        mActivity.info.resizeMode = ActivityInfo.RESIZE_MODE_UNRESIZEABLE;
+        mActivity.info.resizeMode = RESIZE_MODE_UNRESIZEABLE;
         ensureActivityConfiguration();
         final Rect originalBounds = new Rect(mActivity.getBounds());
 
@@ -549,7 +599,7 @@
     public void testSizeCompatMode_FixedScreenLayoutSizeBits() {
         final int fixedScreenLayout = Configuration.SCREENLAYOUT_LONG_NO
                 | Configuration.SCREENLAYOUT_SIZE_NORMAL;
-        mTask.getConfiguration().screenLayout = fixedScreenLayout
+        mTask.getRequestedOverrideConfiguration().screenLayout = fixedScreenLayout
                 | Configuration.SCREENLAYOUT_LAYOUTDIR_LTR;
         prepareFixedAspectRatioUnresizableActivity();
 
@@ -579,7 +629,7 @@
         mTask.getWindowConfiguration().setAppBounds(new Rect(0, 0, 600, 1200));
 
         // Simulate the display changes orientation.
-        doReturn(ActivityInfo.CONFIG_SCREEN_SIZE | ActivityInfo.CONFIG_ORIENTATION
+        doReturn(ActivityInfo.CONFIG_SCREEN_SIZE | CONFIG_ORIENTATION
                 | ActivityInfo.CONFIG_WINDOW_CONFIGURATION)
                         .when(display).getLastOverrideConfigurationChanges();
         mActivity.onConfigurationChanged(mTask.getConfiguration());
@@ -689,12 +739,11 @@
      * incorrect state.
      */
     @Test
-    public void testFinishActivityLocked_cancelled() {
+    public void testFinishActivityIfPossible_cancelled() {
         // Mark activity as finishing
         mActivity.finishing = true;
         assertEquals("Duplicate finish request must be ignored", FINISH_RESULT_CANCELLED,
-                mActivity.finishActivityLocked(0 /* resultCode */, null /* resultData */, "test",
-                        false /* oomAdj */));
+                mActivity.finishIfPossible("test", false /* oomAdj */));
         assertTrue(mActivity.finishing);
         assertTrue(mActivity.isInStackLocked());
 
@@ -702,21 +751,19 @@
         mActivity.finishing = false;
         mActivity.setTask(null);
         assertEquals("Activity outside of task/stack cannot be finished", FINISH_RESULT_CANCELLED,
-                mActivity.finishActivityLocked(0 /* resultCode */, null /* resultData */, "test",
-                        false /* oomAdj */));
+                mActivity.finishIfPossible("test", false /* oomAdj */));
         assertFalse(mActivity.finishing);
     }
 
     /**
-     * Verify that activity finish request is requested, but not executed immediately if activity is
+     * Verify that activity finish request is placed, but not executed immediately if activity is
      * not ready yet.
      */
     @Test
-    public void testFinishActivityLocked_requested() {
+    public void testFinishActivityIfPossible_requested() {
         mActivity.finishing = false;
-        assertEquals("Currently resumed activity be paused removal", FINISH_RESULT_REQUESTED,
-                mActivity.finishActivityLocked(0 /* resultCode */, null /* resultData */, "test",
-                        false /* oomAdj */));
+        assertEquals("Currently resumed activity must be prepared removal", FINISH_RESULT_REQUESTED,
+                mActivity.finishIfPossible("test", false /* oomAdj */));
         assertTrue(mActivity.finishing);
         assertTrue(mActivity.isInStackLocked());
 
@@ -725,8 +772,7 @@
         mActivity.finishing = false;
         mActivity.setState(STOPPED, "test");
         assertEquals("Activity outside of task/stack cannot be finished", FINISH_RESULT_REQUESTED,
-                mActivity.finishActivityLocked(0 /* resultCode */, null /* resultData */, "test",
-                        false /* oomAdj */));
+                mActivity.finishIfPossible("test", false /* oomAdj */));
         assertTrue(mActivity.finishing);
         assertTrue(mActivity.isInStackLocked());
     }
@@ -735,7 +781,7 @@
      * Verify that activity finish request removes activity immediately if it's ready.
      */
     @Test
-    public void testFinishActivityLocked_removed() {
+    public void testFinishActivityIfPossible_removed() {
         // Prepare the activity record to be ready for immediate removal. It should be invisible and
         // have no process. Otherwise, request to finish it will send a message to client first.
         mActivity.setState(STOPPED, "test");
@@ -745,38 +791,445 @@
         // this will cause NPE when updating task's process.
         mActivity.app = null;
         assertEquals("Activity outside of task/stack cannot be finished", FINISH_RESULT_REMOVED,
-                mActivity.finishActivityLocked(0 /* resultCode */, null /* resultData */, "test",
-                        false /* oomAdj */));
+                mActivity.finishIfPossible("test", false /* oomAdj */));
         assertTrue(mActivity.finishing);
         assertFalse(mActivity.isInStackLocked());
     }
 
+    /**
+     * Verify that resumed activity is paused due to finish request.
+     */
+    @Test
+    public void testFinishActivityIfPossible_resumedStartsPausing() {
+        mActivity.finishing = false;
+        mActivity.setState(RESUMED, "test");
+        assertEquals("Currently resumed activity must be paused before removal",
+                FINISH_RESULT_REQUESTED, mActivity.finishIfPossible("test", false /* oomAdj */));
+        assertEquals(PAUSING, mActivity.getState());
+        verify(mActivity).setVisibility(eq(false));
+        verify(mActivity.getDisplay().mDisplayContent)
+                .prepareAppTransition(eq(TRANSIT_TASK_CLOSE), eq(false) /* alwaysKeepCurrent */);
+    }
+
+    /**
+     * Verify that finish request will be completed immediately for non-resumed activity.
+     */
+    @Test
+    public void testFinishActivityIfPossible_nonResumedFinishCompletesImmediately() {
+        final ActivityState[] states = {INITIALIZING, STARTED, PAUSED, STOPPING, STOPPED};
+        for (ActivityState state : states) {
+            mActivity.finishing = false;
+            mActivity.setState(state, "test");
+            reset(mActivity);
+            assertEquals("Finish must be requested", FINISH_RESULT_REQUESTED,
+                    mActivity.finishIfPossible("test", false /* oomAdj */));
+            verify(mActivity).completeFinishing(anyString());
+        }
+    }
+
+    /**
+     * Verify that finishing will not be completed in PAUSING state.
+     */
+    @Test
+    public void testFinishActivityIfPossible_pausing() {
+        mActivity.finishing = false;
+        mActivity.setState(PAUSING, "test");
+        assertEquals("Finish must be requested", FINISH_RESULT_REQUESTED,
+                mActivity.finishIfPossible("test", false /* oomAdj */));
+        verify(mActivity, never()).completeFinishing(anyString());
+    }
+
+    /**
+     * Verify that finish request for resumed activity will prepare an app transition but not
+     * execute it immediately.
+     */
+    @Test
+    public void testFinishActivityIfPossible_visibleResumedPreparesAppTransition() {
+        mActivity.finishing = false;
+        mActivity.visible = true;
+        mActivity.setState(RESUMED, "test");
+        mActivity.finishIfPossible("test", false /* oomAdj */);
+
+        verify(mActivity).setVisibility(eq(false));
+        verify(mActivity.getDisplay().mDisplayContent)
+                .prepareAppTransition(eq(TRANSIT_TASK_CLOSE), eq(false) /* alwaysKeepCurrent */);
+        verify(mActivity.getDisplay().mDisplayContent, never()).executeAppTransition();
+    }
+
+    /**
+     * Verify that finish request for paused activity will prepare and execute an app transition.
+     */
+    @Test
+    public void testFinishActivityIfPossible_visibleNotResumedExecutesAppTransition() {
+        mActivity.finishing = false;
+        mActivity.visible = true;
+        mActivity.setState(PAUSED, "test");
+        mActivity.finishIfPossible("test", false /* oomAdj */);
+
+        verify(mActivity).setVisibility(eq(false));
+        verify(mActivity.getDisplay().mDisplayContent)
+                .prepareAppTransition(eq(TRANSIT_TASK_CLOSE), eq(false) /* alwaysKeepCurrent */);
+        verify(mActivity.getDisplay().mDisplayContent).executeAppTransition();
+    }
+
+    /**
+     * Verify that finish request for non-visible activity will not prepare any transitions.
+     */
+    @Test
+    public void testFinishActivityIfPossible_nonVisibleNoAppTransition() {
+        // Put an activity on top of test activity to make it invisible and prevent us from
+        // accidentally resuming the topmost one again.
+        new ActivityBuilder(mService).build();
+        mActivity.visible = false;
+        mActivity.setState(STOPPED, "test");
+
+        mActivity.finishIfPossible("test", false /* oomAdj */);
+
+        verify(mActivity.getDisplay().mDisplayContent, never())
+                .prepareAppTransition(eq(TRANSIT_TASK_CLOSE), eq(false) /* alwaysKeepCurrent */);
+    }
+
+    /**
+     * Verify that complete finish request for non-finishing activity is invalid.
+     */
+    @Test(expected = IllegalArgumentException.class)
+    public void testCompleteFinishing_failNotFinishing() {
+        mActivity.finishing = false;
+        mActivity.completeFinishing("test");
+    }
+
+    /**
+     * Verify that complete finish request for resumed activity is invalid.
+     */
+    @Test(expected = IllegalArgumentException.class)
+    public void testCompleteFinishing_failResumed() {
+        mActivity.setState(RESUMED, "test");
+        mActivity.completeFinishing("test");
+    }
+
+    /**
+     * Verify that finish request for pausing activity must be a no-op - activity will finish
+     * once it completes pausing.
+     */
+    @Test
+    public void testCompleteFinishing_pausing() {
+        mActivity.setState(PAUSING, "test");
+        mActivity.finishing = true;
+
+        assertEquals("Activity must not be removed immediately - waiting for paused",
+                mActivity, mActivity.completeFinishing("test"));
+        assertEquals(PAUSING, mActivity.getState());
+        verify(mActivity, never()).destroyIfPossible(anyString());
+    }
+
+    /**
+     * Verify that complete finish request for visible activity must be delayed before the next one
+     * becomes visible.
+     */
+    @Test
+    public void testCompleteFinishing_waitForNextVisible() {
+        final ActivityRecord topActivity = new ActivityBuilder(mService).setTask(mTask).build();
+        topActivity.visible = true;
+        topActivity.nowVisible = true;
+        topActivity.finishing = true;
+        topActivity.setState(PAUSED, "true");
+        // Mark the bottom activity as not visible, so that we will wait for it before removing
+        // the top one.
+        mActivity.visible = false;
+        mActivity.nowVisible = false;
+        mActivity.setState(STOPPED, "test");
+
+        assertEquals("Activity must not be removed immediately - waiting for next visible",
+                topActivity, topActivity.completeFinishing("test"));
+        assertEquals("Activity must be stopped to make next one visible", STOPPING,
+                topActivity.getState());
+        assertTrue("Activity must be stopped to make next one visible",
+                topActivity.mStackSupervisor.mStoppingActivities.contains(topActivity));
+        verify(topActivity, never()).destroyIfPossible(anyString());
+    }
+
+    /**
+     * Verify that complete finish request for invisible activity must not be delayed.
+     */
+    @Test
+    public void testCompleteFinishing_noWaitForNextVisible_alreadyInvisible() {
+        final ActivityRecord topActivity = new ActivityBuilder(mService).setTask(mTask).build();
+        topActivity.visible = false;
+        topActivity.nowVisible = false;
+        topActivity.finishing = true;
+        topActivity.setState(PAUSED, "true");
+        // Mark the bottom activity as not visible, so that we would wait for it before removing
+        // the top one.
+        mActivity.visible = false;
+        mActivity.nowVisible = false;
+        mActivity.setState(STOPPED, "test");
+
+        topActivity.completeFinishing("test");
+
+        verify(topActivity).destroyIfPossible(anyString());
+    }
+
+    /**
+     * Verify that paused finishing activity will be added to finishing list and wait for next one
+     * to idle.
+     */
+    @Test
+    public void testCompleteFinishing_waitForIdle() {
+        final ActivityRecord topActivity = new ActivityBuilder(mService).setTask(mTask).build();
+        topActivity.visible = true;
+        topActivity.nowVisible = true;
+        topActivity.finishing = true;
+        topActivity.setState(PAUSED, "true");
+        // Mark the bottom activity as already visible, so that there is no need to wait for it.
+        mActivity.visible = true;
+        mActivity.nowVisible = true;
+        mActivity.setState(RESUMED, "test");
+
+        topActivity.completeFinishing("test");
+
+        verify(topActivity).addToFinishingAndWaitForIdle();
+    }
+
+    /**
+     * Verify that complete finish request for visible activity must not be delayed if the next one
+     * is already visible and it's not the focused stack.
+     */
+    @Test
+    public void testCompleteFinishing_noWaitForNextVisible_stopped() {
+        final ActivityRecord topActivity = new ActivityBuilder(mService).setTask(mTask).build();
+        topActivity.visible = false;
+        topActivity.nowVisible = false;
+        topActivity.finishing = true;
+        topActivity.setState(STOPPED, "true");
+        // Mark the bottom activity as already visible, so that there is no need to wait for it.
+        mActivity.visible = true;
+        mActivity.nowVisible = true;
+        mActivity.setState(RESUMED, "test");
+
+        topActivity.completeFinishing("test");
+
+        verify(topActivity).destroyIfPossible(anyString());
+    }
+
+    /**
+     * Verify that complete finish request for visible activity must not be delayed if the next one
+     * is already visible and it's not the focused stack.
+     */
+    @Test
+    public void testCompleteFinishing_noWaitForNextVisible_nonFocusedStack() {
+        final ActivityRecord topActivity = new ActivityBuilder(mService).setTask(mTask).build();
+        topActivity.visible = true;
+        topActivity.nowVisible = true;
+        topActivity.finishing = true;
+        topActivity.setState(PAUSED, "true");
+        // Mark the bottom activity as already visible, so that there is no need to wait for it.
+        mActivity.visible = true;
+        mActivity.nowVisible = true;
+        mActivity.setState(RESUMED, "test");
+
+        // Add another stack to become focused and make the activity there visible. This way it
+        // simulates finishing in non-focused stack in split-screen.
+        final ActivityStack stack = new StackBuilder(mRootActivityContainer).build();
+        stack.getChildAt(0).getChildAt(0).nowVisible = true;
+
+        topActivity.completeFinishing("test");
+
+        verify(topActivity).destroyIfPossible(anyString());
+    }
+
+    /**
+     * Verify destroy activity request completes successfully.
+     */
+    @Test
+    public void testDestroyIfPossible() {
+        doReturn(false).when(mRootActivityContainer).resumeFocusedStacksTopActivities();
+        spyOn(mStack);
+        mActivity.destroyIfPossible("test");
+
+        assertEquals(DESTROYING, mActivity.getState());
+        assertTrue(mActivity.finishing);
+        verify(mActivity).destroyImmediately(eq(true) /* removeFromApp */, anyString());
+    }
+
+    /**
+     * Verify that complete finish request for visible activity must not destroy it immediately if
+     * it is the last running activity on a display with a home stack. We must wait for home
+     * activity to come up to avoid a black flash in this case.
+     */
+    @Test
+    public void testDestroyIfPossible_lastActivityAboveEmptyHomeStack() {
+        // Empty the home stack.
+        final ActivityStack homeStack = mActivity.getDisplay().getHomeStack();
+        for (TaskRecord t : homeStack.getAllTasks()) {
+            homeStack.removeTask(t, "test", REMOVE_TASK_MODE_DESTROYING);
+        }
+        mActivity.finishing = true;
+        doReturn(false).when(mRootActivityContainer).resumeFocusedStacksTopActivities();
+        spyOn(mStack);
+
+        // Try to destroy the last activity above the home stack.
+        mActivity.destroyIfPossible("test");
+
+        // Verify that the activity was not actually destroyed, but waits for next one to come up
+        // instead.
+        verify(mActivity, never()).destroyImmediately(eq(true) /* removeFromApp */, anyString());
+        assertEquals(FINISHING, mActivity.getState());
+        assertTrue(mActivity.mStackSupervisor.mFinishingActivities.contains(mActivity));
+    }
+
+    /**
+     * Test that the activity will be moved to destroying state and the message to destroy will be
+     * sent to the client.
+     */
+    @Test
+    public void testDestroyImmediately_hadApp_finishing() {
+        mActivity.finishing = true;
+        mActivity.destroyImmediately(false /* removeFromApp */, "test");
+
+        assertEquals(DESTROYING, mActivity.getState());
+    }
+
+    /**
+     * Test that the activity will be moved to destroyed state immediately if it was not marked as
+     * finishing before {@link ActivityRecord#destroyImmediately(boolean, String)}.
+     */
+    @Test
+    public void testDestroyImmediately_hadApp_notFinishing() {
+        mActivity.finishing = false;
+        mActivity.destroyImmediately(false /* removeFromApp */, "test");
+
+        assertEquals(DESTROYED, mActivity.getState());
+    }
+
+    /**
+     * Test that an activity with no process attached and that is marked as finishing will be
+     * removed from task when {@link ActivityRecord#destroyImmediately(boolean, String)} is called.
+     */
+    @Test
+    public void testDestroyImmediately_noApp_finishing() {
+        mActivity.app = null;
+        mActivity.finishing = true;
+        final TaskRecord task = mActivity.getTaskRecord();
+
+        mActivity.destroyImmediately(false /* removeFromApp */, "test");
+
+        assertEquals(DESTROYED, mActivity.getState());
+        assertNull(mActivity.getTaskRecord());
+        assertEquals(0, task.getChildCount());
+    }
+
+    /**
+     * Test that an activity with no process attached and that is not marked as finishing will be
+     * marked as DESTROYED but not removed from task.
+     */
+    @Test
+    public void testDestroyImmediately_noApp_notFinishing() {
+        mActivity.app = null;
+        mActivity.finishing = false;
+        final TaskRecord task = mActivity.getTaskRecord();
+
+        mActivity.destroyImmediately(false /* removeFromApp */, "test");
+
+        assertEquals(DESTROYED, mActivity.getState());
+        assertEquals(task, mActivity.getTaskRecord());
+        assertEquals(1, task.getChildCount());
+    }
+
+    /**
+     * Test that an activity will not be destroyed if it is marked as non-destroyable.
+     */
+    @Test
+    public void testSafelyDestroy_nonDestroyable() {
+        doReturn(false).when(mActivity).isDestroyable();
+
+        mActivity.safelyDestroy("test");
+
+        verify(mActivity, never()).destroyImmediately(eq(true) /* removeFromApp */, anyString());
+    }
+
+    /**
+     * Test that an activity will not be destroyed if it is marked as non-destroyable.
+     */
+    @Test
+    public void testSafelyDestroy_destroyable() {
+        doReturn(true).when(mActivity).isDestroyable();
+
+        mActivity.safelyDestroy("test");
+
+        verify(mActivity).destroyImmediately(eq(true) /* removeFromApp */, anyString());
+    }
+
+    @Test
+    public void testRemoveFromHistory() {
+        final ActivityStack stack = mActivity.getActivityStack();
+        final TaskRecord task = mActivity.getTaskRecord();
+
+        mActivity.removeFromHistory("test");
+
+        assertEquals(DESTROYED, mActivity.getState());
+        assertNull(mActivity.app);
+        assertNull(mActivity.getTaskRecord());
+        assertEquals(0, task.getChildCount());
+        assertNull(task.getStack());
+        assertEquals(0, stack.getChildCount());
+    }
+
+    /**
+     * Test that it's not allowed to call {@link ActivityRecord#destroyed(String)} if activity is
+     * not in destroying or destroyed state.
+     */
+    @Test(expected = IllegalStateException.class)
+    public void testDestroyed_notDestroying() {
+        mActivity.setState(STOPPED, "test");
+        mActivity.destroyed("test");
+    }
+
+    /**
+     * Test that {@link ActivityRecord#destroyed(String)} can be called if an activity is destroying
+     */
+    @Test
+    public void testDestroyed_destroying() {
+        mActivity.setState(DESTROYING, "test");
+        mActivity.destroyed("test");
+
+        verify(mActivity).removeFromHistory(anyString());
+    }
+
+    /**
+     * Test that {@link ActivityRecord#destroyed(String)} can be called if an activity is destroyed.
+     */
+    @Test
+    public void testDestroyed_destroyed() {
+        mActivity.setState(DESTROYED, "test");
+        mActivity.destroyed("test");
+
+        verify(mActivity).removeFromHistory(anyString());
+    }
+
     /** Setup {@link #mActivity} as a size-compat-mode-able activity without fixed orientation. */
     private void prepareFixedAspectRatioUnresizableActivity() {
         setupDisplayContentForCompatDisplayInsets();
-        when(mActivity.mAppWindowToken.getOrientationIgnoreVisibility()).thenReturn(
-                ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED);
-        mActivity.info.resizeMode = ActivityInfo.RESIZE_MODE_UNRESIZEABLE;
+        mActivity.info.resizeMode = RESIZE_MODE_UNRESIZEABLE;
         mActivity.info.maxAspectRatio = 1.5f;
+        mActivity.visible = true;
         ensureActivityConfiguration();
     }
 
     private void setupDisplayContentForCompatDisplayInsets() {
         final Rect displayBounds = mStack.getDisplay().getBounds();
-        final DisplayContent displayContent = setupDisplayAndParentSize(
-                displayBounds.width(), displayBounds.height());
-        doReturn(mock(DisplayPolicy.class)).when(displayContent).getDisplayPolicy();
-        doReturn(mock(WmDisplayCutout.class)).when(displayContent)
-                .calculateDisplayCutoutForRotation(anyInt());
+        setupDisplayAndParentSize(displayBounds.width(), displayBounds.height());
     }
 
     private DisplayContent setupDisplayAndParentSize(int width, int height) {
-        // The DisplayContent is already a mocked object.
         final DisplayContent displayContent = mStack.getDisplay().mDisplayContent;
         displayContent.mBaseDisplayWidth = width;
         displayContent.mBaseDisplayHeight = height;
-        mTask.getWindowConfiguration().setAppBounds(0, 0, width, height);
-        mTask.getWindowConfiguration().setRotation(ROTATION_0);
+        final Configuration c =
+                new Configuration(mStack.getDisplay().getRequestedOverrideConfiguration());
+        c.windowConfiguration.setBounds(new Rect(0, 0, width, height));
+        c.windowConfiguration.setAppBounds(0, 0, width, height);
+        c.windowConfiguration.setRotation(ROTATION_0);
+        mStack.getDisplay().onRequestedOverrideConfigurationChanged(c);
         return displayContent;
     }
 }
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java
index e5278d8..60c5f0b 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java
@@ -16,7 +16,6 @@
 
 package com.android.server.wm;
 
-import static android.app.Activity.RESULT_CANCELED;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
@@ -29,11 +28,10 @@
 
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.spy;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
 import static com.android.server.wm.ActivityStack.ActivityState.DESTROYING;
 import static com.android.server.wm.ActivityStack.ActivityState.FINISHING;
-import static com.android.server.wm.ActivityStack.ActivityState.PAUSED;
 import static com.android.server.wm.ActivityStack.ActivityState.PAUSING;
 import static com.android.server.wm.ActivityStack.ActivityState.RESUMED;
 import static com.android.server.wm.ActivityStack.ActivityState.STOPPED;
@@ -44,6 +42,7 @@
 import static com.android.server.wm.ActivityStack.STACK_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT;
 import static com.android.server.wm.ActivityTaskManagerService.RELAUNCH_REASON_FREE_RESIZE;
 import static com.android.server.wm.ActivityTaskManagerService.RELAUNCH_REASON_WINDOWING_MODE_RESIZE;
+import static com.android.server.wm.TaskRecord.REPARENT_MOVE_STACK_TO_FRONT;
 
 import static com.google.common.truth.Truth.assertThat;
 
@@ -83,8 +82,9 @@
     @Before
     public void setUp() throws Exception {
         mDefaultDisplay = mRootActivityContainer.getDefaultDisplay();
-        mStack = spy(mDefaultDisplay.createStack(WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_STANDARD,
-                true /* onTop */));
+        mStack = mDefaultDisplay.createStack(WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_STANDARD,
+                true /* onTop */);
+        spyOn(mStack);
         mTask = new TaskBuilder(mSupervisor).setStack(mStack).build();
     }
 
@@ -140,10 +140,8 @@
 
         final ActivityStack destStack = mRootActivityContainer.getDefaultDisplay().createStack(
                 WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
-        final TaskRecord destTask = new TaskBuilder(mSupervisor).setStack(destStack).build();
-
-        mTask.removeActivity(r);
-        destTask.addActivityToTop(r);
+        mTask.reparent(destStack, true /*toTop*/, REPARENT_MOVE_STACK_TO_FRONT, false, false,
+                "testResumedActivityFromActivityReparenting");
 
         assertNull(mStack.getResumedActivity());
         assertEquals(r, destStack.getResumedActivity());
@@ -235,7 +233,7 @@
         final ActivityRecord r = new ActivityBuilder(mService).setTask(mTask).build();
         r.info.flags |= ActivityInfo.FLAG_NO_HISTORY;
         mStack.moveToFront("testStopActivityWithDestroy");
-        mStack.stopActivityLocked(r);
+        r.stopIfPossible();
         // Mostly testing to make sure there is a crash in the call part, so if we get here we are
         // good-to-go!
     }
@@ -313,45 +311,50 @@
 
     @Test
     public void testShouldBeVisible_Fullscreen() {
-        final TestActivityStack homeStack = createStackForShouldBeVisibleTest(mDefaultDisplay,
+        final ActivityStack homeStack = createStackForShouldBeVisibleTest(mDefaultDisplay,
                 WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, true /* onTop */);
         final ActivityStack pinnedStack = createStackForShouldBeVisibleTest(mDefaultDisplay,
                 WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD, true /* onTop */);
+        // Add an activity to the pinned stack so it isn't considered empty for visibility check.
+        final ActivityRecord pinnedActivity = new ActivityBuilder(mService)
+                .setCreateTask(true)
+                .setStack(pinnedStack)
+                .build();
 
         assertTrue(homeStack.shouldBeVisible(null /* starting */));
         assertTrue(pinnedStack.shouldBeVisible(null /* starting */));
 
-        final TestActivityStack fullscreenStack = createStackForShouldBeVisibleTest(mDefaultDisplay,
+        final ActivityStack fullscreenStack = createStackForShouldBeVisibleTest(mDefaultDisplay,
                 WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
         // Home stack shouldn't be visible behind an opaque fullscreen stack, but pinned stack
         // should be visible since it is always on-top.
-        fullscreenStack.setIsTranslucent(false);
+        doReturn(false).when(fullscreenStack).isStackTranslucent(any());
         assertFalse(homeStack.shouldBeVisible(null /* starting */));
         assertTrue(pinnedStack.shouldBeVisible(null /* starting */));
         assertTrue(fullscreenStack.shouldBeVisible(null /* starting */));
 
         // Home stack should be visible behind a translucent fullscreen stack.
-        fullscreenStack.setIsTranslucent(true);
+        doReturn(true).when(fullscreenStack).isStackTranslucent(any());
         assertTrue(homeStack.shouldBeVisible(null /* starting */));
         assertTrue(pinnedStack.shouldBeVisible(null /* starting */));
     }
 
     @Test
     public void testShouldBeVisible_SplitScreen() {
-        final TestActivityStack homeStack = createStackForShouldBeVisibleTest(mDefaultDisplay,
+        final ActivityStack homeStack = createStackForShouldBeVisibleTest(mDefaultDisplay,
                 WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, true /* onTop */);
         // Home stack should always be fullscreen for this test.
-        homeStack.setSupportsSplitScreen(false);
-        final TestActivityStack splitScreenPrimary =
+        doReturn(false).when(homeStack).supportsSplitScreenWindowingMode();
+        final ActivityStack splitScreenPrimary =
                 createStackForShouldBeVisibleTest(mDefaultDisplay,
                 WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD, true /* onTop */);
-        final TestActivityStack splitScreenSecondary =
+        final ActivityStack splitScreenSecondary =
                 createStackForShouldBeVisibleTest(mDefaultDisplay,
                 WINDOWING_MODE_SPLIT_SCREEN_SECONDARY, ACTIVITY_TYPE_STANDARD, true /* onTop */);
 
         // Home stack shouldn't be visible if both halves of split-screen are opaque.
-        splitScreenPrimary.setIsTranslucent(false);
-        splitScreenSecondary.setIsTranslucent(false);
+        doReturn(false).when(splitScreenPrimary).isStackTranslucent(any());
+        doReturn(false).when(splitScreenSecondary).isStackTranslucent(any());
         assertFalse(homeStack.shouldBeVisible(null /* starting */));
         assertTrue(splitScreenPrimary.shouldBeVisible(null /* starting */));
         assertTrue(splitScreenSecondary.shouldBeVisible(null /* starting */));
@@ -362,7 +365,7 @@
                 splitScreenSecondary.getVisibility(null /* starting */));
 
         // Home stack should be visible if one of the halves of split-screen is translucent.
-        splitScreenPrimary.setIsTranslucent(true);
+        doReturn(true).when(splitScreenPrimary).isStackTranslucent(any());
         assertTrue(homeStack.shouldBeVisible(null /* starting */));
         assertTrue(splitScreenPrimary.shouldBeVisible(null /* starting */));
         assertTrue(splitScreenSecondary.shouldBeVisible(null /* starting */));
@@ -373,12 +376,12 @@
         assertEquals(STACK_VISIBILITY_VISIBLE,
                 splitScreenSecondary.getVisibility(null /* starting */));
 
-        final TestActivityStack splitScreenSecondary2 =
+        final ActivityStack splitScreenSecondary2 =
                 createStackForShouldBeVisibleTest(mDefaultDisplay,
                 WINDOWING_MODE_SPLIT_SCREEN_SECONDARY, ACTIVITY_TYPE_STANDARD, true /* onTop */);
         // First split-screen secondary shouldn't be visible behind another opaque split-split
         // secondary.
-        splitScreenSecondary2.setIsTranslucent(false);
+        doReturn(false).when(splitScreenSecondary2).isStackTranslucent(any());
         assertFalse(splitScreenSecondary.shouldBeVisible(null /* starting */));
         assertTrue(splitScreenSecondary2.shouldBeVisible(null /* starting */));
         assertEquals(STACK_VISIBILITY_INVISIBLE,
@@ -388,7 +391,7 @@
 
         // First split-screen secondary should be visible behind another translucent split-screen
         // secondary.
-        splitScreenSecondary2.setIsTranslucent(true);
+        doReturn(true).when(splitScreenSecondary2).isStackTranslucent(any());
         assertTrue(splitScreenSecondary.shouldBeVisible(null /* starting */));
         assertTrue(splitScreenSecondary2.shouldBeVisible(null /* starting */));
         assertEquals(STACK_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT,
@@ -396,11 +399,11 @@
         assertEquals(STACK_VISIBILITY_VISIBLE,
                 splitScreenSecondary2.getVisibility(null /* starting */));
 
-        final TestActivityStack assistantStack = createStackForShouldBeVisibleTest(mDefaultDisplay,
+        final ActivityStack assistantStack = createStackForShouldBeVisibleTest(mDefaultDisplay,
                 WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_ASSISTANT, true /* onTop */);
 
         // Split-screen stacks shouldn't be visible behind an opaque fullscreen stack.
-        assistantStack.setIsTranslucent(false);
+        doReturn(false).when(assistantStack).isStackTranslucent(any());
         assertTrue(assistantStack.shouldBeVisible(null /* starting */));
         assertFalse(splitScreenPrimary.shouldBeVisible(null /* starting */));
         assertFalse(splitScreenSecondary.shouldBeVisible(null /* starting */));
@@ -415,7 +418,7 @@
                 splitScreenSecondary2.getVisibility(null /* starting */));
 
         // Split-screen stacks should be visible behind a translucent fullscreen stack.
-        assistantStack.setIsTranslucent(true);
+        doReturn(true).when(assistantStack).isStackTranslucent(any());
         assertTrue(assistantStack.shouldBeVisible(null /* starting */));
         assertTrue(splitScreenPrimary.shouldBeVisible(null /* starting */));
         assertTrue(splitScreenSecondary.shouldBeVisible(null /* starting */));
@@ -430,9 +433,9 @@
                 splitScreenSecondary2.getVisibility(null /* starting */));
 
         // Assistant stack shouldn't be visible behind translucent split-screen stack
-        assistantStack.setIsTranslucent(false);
-        splitScreenPrimary.setIsTranslucent(true);
-        splitScreenSecondary2.setIsTranslucent(true);
+        doReturn(false).when(assistantStack).isStackTranslucent(any());
+        doReturn(true).when(splitScreenPrimary).isStackTranslucent(any());
+        doReturn(true).when(splitScreenSecondary2).isStackTranslucent(any());
         splitScreenSecondary2.moveToFront("testShouldBeVisible_SplitScreen");
         splitScreenPrimary.moveToFront("testShouldBeVisible_SplitScreen");
         assertFalse(assistantStack.shouldBeVisible(null /* starting */));
@@ -450,10 +453,10 @@
 
     @Test
     public void testGetVisibility_FullscreenBehindTranslucent() {
-        final TestActivityStack bottomStack =
+        final ActivityStack bottomStack =
                 createStandardStackForVisibilityTest(WINDOWING_MODE_FULLSCREEN,
                         false /* translucent */);
-        final TestActivityStack translucentStack =
+        final ActivityStack translucentStack =
                 createStandardStackForVisibilityTest(WINDOWING_MODE_FULLSCREEN,
                         true /* translucent */);
 
@@ -465,13 +468,13 @@
 
     @Test
     public void testGetVisibility_FullscreenBehindTranslucentAndOpaque() {
-        final TestActivityStack bottomStack =
+        final ActivityStack bottomStack =
                 createStandardStackForVisibilityTest(WINDOWING_MODE_FULLSCREEN,
                         false /* translucent */);
-        final TestActivityStack translucentStack =
+        final ActivityStack translucentStack =
                 createStandardStackForVisibilityTest(WINDOWING_MODE_FULLSCREEN,
                         true /* translucent */);
-        final TestActivityStack opaqueStack =
+        final ActivityStack opaqueStack =
                 createStandardStackForVisibilityTest(WINDOWING_MODE_FULLSCREEN,
                         false /* translucent */);
 
@@ -483,13 +486,13 @@
 
     @Test
     public void testGetVisibility_FullscreenBehindOpaqueAndTranslucent() {
-        final TestActivityStack bottomStack =
+        final ActivityStack bottomStack =
                 createStandardStackForVisibilityTest(WINDOWING_MODE_FULLSCREEN,
                         false /* translucent */);
-        final TestActivityStack opaqueStack =
+        final ActivityStack opaqueStack =
                 createStandardStackForVisibilityTest(WINDOWING_MODE_FULLSCREEN,
                         false /* translucent */);
-        final TestActivityStack translucentStack =
+        final ActivityStack translucentStack =
                 createStandardStackForVisibilityTest(WINDOWING_MODE_FULLSCREEN,
                         true /* translucent */);
 
@@ -502,10 +505,10 @@
 
     @Test
     public void testGetVisibility_FullscreenTranslucentBehindTranslucent() {
-        final TestActivityStack bottomTranslucentStack =
+        final ActivityStack bottomTranslucentStack =
                 createStandardStackForVisibilityTest(WINDOWING_MODE_FULLSCREEN,
                         true /* translucent */);
-        final TestActivityStack translucentStack =
+        final ActivityStack translucentStack =
                 createStandardStackForVisibilityTest(WINDOWING_MODE_FULLSCREEN,
                         true /* translucent */);
 
@@ -517,10 +520,10 @@
 
     @Test
     public void testGetVisibility_FullscreenTranslucentBehindOpaque() {
-        final TestActivityStack bottomTranslucentStack =
+        final ActivityStack bottomTranslucentStack =
                 createStandardStackForVisibilityTest(WINDOWING_MODE_FULLSCREEN,
                         true /* translucent */);
-        final TestActivityStack opaqueStack =
+        final ActivityStack opaqueStack =
                 createStandardStackForVisibilityTest(WINDOWING_MODE_FULLSCREEN,
                         false /* translucent */);
 
@@ -531,10 +534,10 @@
 
     @Test
     public void testGetVisibility_FullscreenBehindTranslucentAndPip() {
-        final TestActivityStack bottomStack =
+        final ActivityStack bottomStack =
                 createStandardStackForVisibilityTest(WINDOWING_MODE_FULLSCREEN,
                         false /* translucent */);
-        final TestActivityStack translucentStack =
+        final ActivityStack translucentStack =
                 createStandardStackForVisibilityTest(WINDOWING_MODE_FULLSCREEN,
                         true /* translucent */);
         final ActivityStack pinnedStack = createStackForShouldBeVisibleTest(mDefaultDisplay,
@@ -544,22 +547,34 @@
                 bottomStack.getVisibility(null /* starting */));
         assertEquals(STACK_VISIBILITY_VISIBLE,
                 translucentStack.getVisibility(null /* starting */));
+        // Add an activity to the pinned stack so it isn't considered empty for visibility check.
+        final ActivityRecord pinnedActivity = new ActivityBuilder(mService)
+                .setCreateTask(true)
+                .setStack(pinnedStack)
+                .build();
         assertEquals(STACK_VISIBILITY_VISIBLE, pinnedStack.getVisibility(null /* starting */));
     }
 
     @Test
     public void testShouldBeVisible_Finishing() {
-        final TestActivityStack homeStack = createStackForShouldBeVisibleTest(mDefaultDisplay,
+        final ActivityStack homeStack = createStackForShouldBeVisibleTest(mDefaultDisplay,
                 WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, true /* onTop */);
-        final TestActivityStack translucentStack = createStackForShouldBeVisibleTest(
+        ActivityRecord topRunningHomeActivity = homeStack.topRunningActivityLocked();
+        if (topRunningHomeActivity == null) {
+            topRunningHomeActivity = new ActivityBuilder(mService)
+                    .setStack(homeStack)
+                    .setCreateTask(true)
+                    .build();
+        }
+
+        final ActivityStack translucentStack = createStackForShouldBeVisibleTest(
                 mDefaultDisplay, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD,
                 true /* onTop */);
-        translucentStack.setIsTranslucent(true);
+        doReturn(true).when(translucentStack).isStackTranslucent(any());
 
         assertTrue(homeStack.shouldBeVisible(null /* starting */));
         assertTrue(translucentStack.shouldBeVisible(null /* starting */));
 
-        final ActivityRecord topRunningHomeActivity = homeStack.topRunningActivityLocked();
         topRunningHomeActivity.finishing = true;
         final ActivityRecord topRunningTranslucentActivity =
                 translucentStack.topRunningActivityLocked();
@@ -577,13 +592,13 @@
     public void testMoveHomeStackBehindBottomMostVisibleStack_NoMoveHomeBehindFullscreen() {
         mDefaultDisplay.removeChild(mStack);
 
-        final TestActivityStack homeStack = createStackForShouldBeVisibleTest(mDefaultDisplay,
+        final ActivityStack homeStack = createStackForShouldBeVisibleTest(mDefaultDisplay,
                 WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, true /* onTop */);
-        final TestActivityStack fullscreenStack = createStackForShouldBeVisibleTest(mDefaultDisplay,
+        final ActivityStack fullscreenStack = createStackForShouldBeVisibleTest(mDefaultDisplay,
                 WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
 
-        homeStack.setIsTranslucent(false);
-        fullscreenStack.setIsTranslucent(false);
+        doReturn(false).when(homeStack).isStackTranslucent(any());
+        doReturn(false).when(fullscreenStack).isStackTranslucent(any());
 
         // Ensure that we don't move the home stack if it is already behind the top fullscreen stack
         int homeStackIndex = mDefaultDisplay.getIndexOf(homeStack);
@@ -596,13 +611,13 @@
     public void testMoveHomeStackBehindBottomMostVisibleStack_NoMoveHomeBehindTranslucent() {
         mDefaultDisplay.removeChild(mStack);
 
-        final TestActivityStack homeStack = createStackForShouldBeVisibleTest(mDefaultDisplay,
+        final ActivityStack homeStack = createStackForShouldBeVisibleTest(mDefaultDisplay,
                 WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, true /* onTop */);
-        final TestActivityStack fullscreenStack = createStackForShouldBeVisibleTest(mDefaultDisplay,
+        final ActivityStack fullscreenStack = createStackForShouldBeVisibleTest(mDefaultDisplay,
                 WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
 
-        homeStack.setIsTranslucent(false);
-        fullscreenStack.setIsTranslucent(true);
+        doReturn(false).when(homeStack).isStackTranslucent(any());
+        doReturn(true).when(fullscreenStack).isStackTranslucent(any());
 
         // Ensure that we don't move the home stack if it is already behind the top fullscreen stack
         int homeStackIndex = mDefaultDisplay.getIndexOf(homeStack);
@@ -615,13 +630,13 @@
     public void testMoveHomeStackBehindBottomMostVisibleStack_NoMoveHomeOnTop() {
         mDefaultDisplay.removeChild(mStack);
 
-        final TestActivityStack fullscreenStack = createStackForShouldBeVisibleTest(mDefaultDisplay,
+        final ActivityStack fullscreenStack = createStackForShouldBeVisibleTest(mDefaultDisplay,
                 WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
-        final TestActivityStack homeStack = createStackForShouldBeVisibleTest(mDefaultDisplay,
+        final ActivityStack homeStack = createStackForShouldBeVisibleTest(mDefaultDisplay,
                 WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, true /* onTop */);
 
-        homeStack.setIsTranslucent(false);
-        fullscreenStack.setIsTranslucent(false);
+        doReturn(false).when(homeStack).isStackTranslucent(any());
+        doReturn(false).when(fullscreenStack).isStackTranslucent(any());
 
         // Ensure we don't move the home stack if it is already on top
         int homeStackIndex = mDefaultDisplay.getIndexOf(homeStack);
@@ -634,20 +649,20 @@
     public void testMoveHomeStackBehindBottomMostVisibleStack_MoveHomeBehindFullscreen() {
         mDefaultDisplay.removeChild(mStack);
 
-        final TestActivityStack homeStack = createStackForShouldBeVisibleTest(mDefaultDisplay,
+        final ActivityStack homeStack = createStackForShouldBeVisibleTest(mDefaultDisplay,
                 WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, true /* onTop */);
-        final TestActivityStack fullscreenStack1 = createStackForShouldBeVisibleTest(
+        final ActivityStack fullscreenStack1 = createStackForShouldBeVisibleTest(
                 mDefaultDisplay, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD,
                 true /* onTop */);
-        final TestActivityStack fullscreenStack2 = createStackForShouldBeVisibleTest(
+        final ActivityStack fullscreenStack2 = createStackForShouldBeVisibleTest(
                 mDefaultDisplay, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD,
                 true /* onTop */);
         final ActivityStack pinnedStack = createStackForShouldBeVisibleTest(mDefaultDisplay,
                 WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD, true /* onTop */);
 
-        homeStack.setIsTranslucent(false);
-        fullscreenStack1.setIsTranslucent(false);
-        fullscreenStack2.setIsTranslucent(false);
+        doReturn(false).when(homeStack).isStackTranslucent(any());
+        doReturn(false).when(fullscreenStack1).isStackTranslucent(any());
+        doReturn(false).when(fullscreenStack2).isStackTranslucent(any());
 
         // Ensure that we move the home stack behind the bottom most fullscreen stack, ignoring the
         // pinned stack
@@ -661,18 +676,18 @@
             testMoveHomeStackBehindBottomMostVisibleStack_MoveHomeBehindFullscreenAndTranslucent() {
         mDefaultDisplay.removeChild(mStack);
 
-        final TestActivityStack homeStack = createStackForShouldBeVisibleTest(mDefaultDisplay,
+        final ActivityStack homeStack = createStackForShouldBeVisibleTest(mDefaultDisplay,
                 WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, true /* onTop */);
-        final TestActivityStack fullscreenStack1 = createStackForShouldBeVisibleTest(
+        final ActivityStack fullscreenStack1 = createStackForShouldBeVisibleTest(
                 mDefaultDisplay, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD,
                 true /* onTop */);
-        final TestActivityStack fullscreenStack2 = createStackForShouldBeVisibleTest(
+        final ActivityStack fullscreenStack2 = createStackForShouldBeVisibleTest(
                 mDefaultDisplay, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD,
                 true /* onTop */);
 
-        homeStack.setIsTranslucent(false);
-        fullscreenStack1.setIsTranslucent(false);
-        fullscreenStack2.setIsTranslucent(true);
+        doReturn(false).when(homeStack).isStackTranslucent(any());
+        doReturn(false).when(fullscreenStack1).isStackTranslucent(any());
+        doReturn(true).when(fullscreenStack2).isStackTranslucent(any());
 
         // Ensure that we move the home stack behind the bottom most non-translucent fullscreen
         // stack
@@ -685,18 +700,18 @@
     public void testMoveHomeStackBehindStack_BehindHomeStack() {
         mDefaultDisplay.removeChild(mStack);
 
-        final TestActivityStack fullscreenStack1 = createStackForShouldBeVisibleTest(
+        final ActivityStack fullscreenStack1 = createStackForShouldBeVisibleTest(
                 mDefaultDisplay, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD,
                 true /* onTop */);
-        final TestActivityStack fullscreenStack2 = createStackForShouldBeVisibleTest(
+        final ActivityStack fullscreenStack2 = createStackForShouldBeVisibleTest(
                 mDefaultDisplay, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD,
                 true /* onTop */);
-        final TestActivityStack homeStack = createStackForShouldBeVisibleTest(mDefaultDisplay,
+        final ActivityStack homeStack = createStackForShouldBeVisibleTest(mDefaultDisplay,
                 WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, true /* onTop */);
 
-        homeStack.setIsTranslucent(false);
-        fullscreenStack1.setIsTranslucent(false);
-        fullscreenStack2.setIsTranslucent(false);
+        doReturn(false).when(homeStack).isStackTranslucent(any());
+        doReturn(false).when(fullscreenStack1).isStackTranslucent(any());
+        doReturn(false).when(fullscreenStack2).isStackTranslucent(any());
 
         // Ensure we don't move the home stack behind itself
         int homeStackIndex = mDefaultDisplay.getIndexOf(homeStack);
@@ -708,19 +723,19 @@
     public void testMoveHomeStackBehindStack() {
         mDefaultDisplay.removeChild(mStack);
 
-        final TestActivityStack fullscreenStack1 = createStackForShouldBeVisibleTest(
+        final ActivityStack fullscreenStack1 = createStackForShouldBeVisibleTest(
                 mDefaultDisplay, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD,
                 true /* onTop */);
-        final TestActivityStack fullscreenStack2 = createStackForShouldBeVisibleTest(
+        final ActivityStack fullscreenStack2 = createStackForShouldBeVisibleTest(
                 mDefaultDisplay, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD,
                 true /* onTop */);
-        final TestActivityStack fullscreenStack3 = createStackForShouldBeVisibleTest(
+        final ActivityStack fullscreenStack3 = createStackForShouldBeVisibleTest(
                 mDefaultDisplay, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD,
                 true /* onTop */);
-        final TestActivityStack fullscreenStack4 = createStackForShouldBeVisibleTest(
+        final ActivityStack fullscreenStack4 = createStackForShouldBeVisibleTest(
                 mDefaultDisplay, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD,
                 true /* onTop */);
-        final TestActivityStack homeStack = createStackForShouldBeVisibleTest(mDefaultDisplay,
+        final ActivityStack homeStack = createStackForShouldBeVisibleTest(mDefaultDisplay,
                 WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, true /* onTop */);
 
         mDefaultDisplay.moveStackBehindStack(homeStack, fullscreenStack1);
@@ -735,13 +750,13 @@
 
     @Test
     public void testSetAlwaysOnTop() {
-        final TestActivityStack homeStack = createStackForShouldBeVisibleTest(mDefaultDisplay,
+        final ActivityStack homeStack = createStackForShouldBeVisibleTest(mDefaultDisplay,
                 WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, true /* onTop */);
         final ActivityStack pinnedStack = createStackForShouldBeVisibleTest(mDefaultDisplay,
                 WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD, true /* onTop */);
         assertEquals(pinnedStack, mDefaultDisplay.getStackAbove(homeStack));
 
-        final TestActivityStack alwaysOnTopStack = createStackForShouldBeVisibleTest(
+        final ActivityStack alwaysOnTopStack = createStackForShouldBeVisibleTest(
                 mDefaultDisplay, WINDOWING_MODE_FREEFORM, ACTIVITY_TYPE_STANDARD,
                 true /* onTop */);
         alwaysOnTopStack.setAlwaysOnTop(true);
@@ -749,13 +764,13 @@
         // Ensure (non-pinned) always on top stack is put below pinned stack.
         assertEquals(pinnedStack, mDefaultDisplay.getStackAbove(alwaysOnTopStack));
 
-        final TestActivityStack nonAlwaysOnTopStack = createStackForShouldBeVisibleTest(
+        final ActivityStack nonAlwaysOnTopStack = createStackForShouldBeVisibleTest(
                 mDefaultDisplay, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD,
                 true /* onTop */);
         // Ensure non always on top stack is put below always on top stacks.
         assertEquals(alwaysOnTopStack, mDefaultDisplay.getStackAbove(nonAlwaysOnTopStack));
 
-        final TestActivityStack alwaysOnTopStack2 = createStackForShouldBeVisibleTest(
+        final ActivityStack alwaysOnTopStack2 = createStackForShouldBeVisibleTest(
                 mDefaultDisplay, WINDOWING_MODE_FREEFORM, ACTIVITY_TYPE_STANDARD,
                 true /* onTop */);
         alwaysOnTopStack2.setAlwaysOnTop(true);
@@ -780,18 +795,18 @@
 
     @Test
     public void testSplitScreenMoveToFront() {
-        final TestActivityStack splitScreenPrimary = createStackForShouldBeVisibleTest(
+        final ActivityStack splitScreenPrimary = createStackForShouldBeVisibleTest(
                 mDefaultDisplay, WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD,
                 true /* onTop */);
-        final TestActivityStack splitScreenSecondary = createStackForShouldBeVisibleTest(
+        final ActivityStack splitScreenSecondary = createStackForShouldBeVisibleTest(
                 mDefaultDisplay, WINDOWING_MODE_SPLIT_SCREEN_SECONDARY, ACTIVITY_TYPE_STANDARD,
                 true /* onTop */);
-        final TestActivityStack assistantStack = createStackForShouldBeVisibleTest(mDefaultDisplay,
+        final ActivityStack assistantStack = createStackForShouldBeVisibleTest(mDefaultDisplay,
                 WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_ASSISTANT, true /* onTop */);
 
-        splitScreenPrimary.setIsTranslucent(false);
-        splitScreenSecondary.setIsTranslucent(false);
-        assistantStack.setIsTranslucent(false);
+        doReturn(false).when(splitScreenPrimary).isStackTranslucent(any());
+        doReturn(false).when(splitScreenSecondary).isStackTranslucent(any());
+        doReturn(false).when(assistantStack).isStackTranslucent(any());
 
         assertFalse(splitScreenPrimary.shouldBeVisible(null /* starting */));
         assertFalse(splitScreenSecondary.shouldBeVisible(null /* starting */));
@@ -804,18 +819,18 @@
         assertFalse(assistantStack.shouldBeVisible(null /* starting */));
     }
 
-    private TestActivityStack createStandardStackForVisibilityTest(int windowingMode,
+    private ActivityStack createStandardStackForVisibilityTest(int windowingMode,
             boolean translucent) {
-        final TestActivityStack stack = createStackForShouldBeVisibleTest(mDefaultDisplay,
+        final ActivityStack stack = createStackForShouldBeVisibleTest(mDefaultDisplay,
                 windowingMode, ACTIVITY_TYPE_STANDARD, true /* onTop */);
-        stack.setIsTranslucent(translucent);
+        doReturn(translucent).when(stack).isStackTranslucent(any());
         return stack;
     }
 
     @SuppressWarnings("TypeParameterUnusedInFormals")
     private <T extends ActivityStack> T createStackForShouldBeVisibleTest(
             ActivityDisplay display, int windowingMode, int activityType, boolean onTop) {
-        final T stack;
+        final ActivityStack stack;
         if (activityType == ACTIVITY_TYPE_HOME) {
             // Home stack and activity are created in ActivityTestsBase#setupActivityManagerService
             stack = mDefaultDisplay.getStack(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME);
@@ -825,11 +840,15 @@
                 mDefaultDisplay.positionChildAtBottom(stack);
             }
         } else {
-            stack = display.createStack(windowingMode, activityType, onTop);
-            final ActivityRecord r = new ActivityBuilder(mService).setUid(0).setStack(stack)
-                    .setCreateTask(true).build();
+            stack = new StackBuilder(mRootActivityContainer)
+                    .setDisplay(display)
+                    .setWindowingMode(windowingMode)
+                    .setActivityType(activityType)
+                    .setOnTop(onTop)
+                    .setCreateActivity(true)
+                    .build();
         }
-        return stack;
+        return (T) stack;
     }
 
     @Test
@@ -860,7 +879,7 @@
         final ActivityRecord overlayActivity = new ActivityBuilder(mService).setTask(mTask)
                 .setComponent(new ComponentName("package.overlay", ".OverlayActivity")).build();
         // If the task only remains overlay activity, the task should also be removed.
-        // See {@link ActivityStack#removeActivityFromHistoryLocked}.
+        // See {@link ActivityStack#removeFromHistory}.
         overlayActivity.mTaskOverlay = true;
 
         // The activity without an app means it will be removed immediately.
@@ -961,17 +980,21 @@
 
     @Test
     public void testAdjustFocusedStackToHomeWhenNoActivity() {
+        final ActivityStack homeStask = mDefaultDisplay.getHomeStack();
+        TaskRecord homeTask = homeStask.topTask();
+        if (homeTask == null) {
+            // Create home task if there isn't one.
+            homeTask = new TaskBuilder(mSupervisor).setStack(homeStask).build();
+        }
+
         final ActivityRecord topActivity = new ActivityBuilder(mService).setTask(mTask).build();
         mStack.moveToFront("testAdjustFocusedStack");
 
-        final ActivityStack homeStask = mDefaultDisplay.getHomeStack();
-        final TaskRecord homeTask = homeStask.topTask();
         // Simulate that home activity has not been started or is force-stopped.
         homeStask.removeTask(homeTask, "testAdjustFocusedStack", REMOVE_TASK_MODE_DESTROYING);
 
         // Finish the only activity.
-        topActivity.finishActivityLocked(RESULT_CANCELED /* resultCode */, null /* resultData */,
-                "testAdjustFocusedStack", false /* oomAdj */);
+        topActivity.finishIfPossible("testAdjustFocusedStack", false /* oomAdj */);
         // Although home stack is empty, it should still be the focused stack.
         assertEquals(homeStask, mDefaultDisplay.getFocusedStack());
     }
@@ -981,8 +1004,16 @@
         final ActivityStack homeStack = createStackForShouldBeVisibleTest(mDefaultDisplay,
                 WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, true /* onTop */);
 
+        ActivityRecord activity = homeStack.topRunningActivityLocked();
+        if (activity == null) {
+            activity = new ActivityBuilder(mService)
+                    .setStack(homeStack)
+                    .setCreateTask(true)
+                    .build();
+        }
+
         // Home stack should not be destroyed immediately.
-        final ActivityRecord activity1 = finishCurrentActivity(homeStack);
+        final ActivityRecord activity1 = finishTopActivity(homeStack);
         assertEquals(FINISHING, activity1.getState());
     }
 
@@ -998,25 +1029,24 @@
         // There is still an activity1 in stack1 so the activity2 should be added to finishing list
         // that will be destroyed until idle.
         stack2.getTopActivity().visible = true;
-        final ActivityRecord activity2 = finishCurrentActivity(stack2);
+        final ActivityRecord activity2 = finishTopActivity(stack2);
         assertEquals(STOPPING, activity2.getState());
         assertThat(mSupervisor.mStoppingActivities).contains(activity2);
 
         // The display becomes empty. Since there is no next activity to be idle, the activity
         // should be destroyed immediately with updating configuration to restore original state.
-        final ActivityRecord activity1 = finishCurrentActivity(stack1);
+        final ActivityRecord activity1 = finishTopActivity(stack1);
         assertEquals(DESTROYING, activity1.getState());
         verify(mRootActivityContainer).ensureVisibilityAndConfig(eq(null) /* starting */,
                 eq(display.mDisplayId), anyBoolean(), anyBoolean());
     }
 
-    private ActivityRecord finishCurrentActivity(ActivityStack stack) {
+    private ActivityRecord finishTopActivity(ActivityStack stack) {
         final ActivityRecord activity = stack.topRunningActivityLocked();
         assertNotNull(activity);
-        activity.setState(PAUSED, "finishCurrentActivity");
+        activity.setState(STOPPED, "finishTopActivity");
         activity.makeFinishingLocked();
-        activity.finishCurrentActivityLocked(ActivityRecord.FINISH_AFTER_VISIBLE,
-                false /* oomAdj */, "finishCurrentActivity");
+        activity.completeFinishing("finishTopActivity");
         return activity;
     }
 
@@ -1068,7 +1098,7 @@
     public void testStackOrderChangedOnPositionStack() {
         StackOrderChangedListener listener = new StackOrderChangedListener();
         try {
-            final TestActivityStack fullscreenStack1 = createStackForShouldBeVisibleTest(
+            final ActivityStack fullscreenStack1 = createStackForShouldBeVisibleTest(
                     mDefaultDisplay, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD,
                     true /* onTop */);
             mDefaultDisplay.registerStackOrderChangedListener(listener);
@@ -1079,6 +1109,19 @@
         assertTrue(listener.mChanged);
     }
 
+    @Test
+    public void testResetTaskWithFinishingActivities() {
+        final ActivityRecord taskTop =
+                new ActivityBuilder(mService).setStack(mStack).setCreateTask(true).build();
+        // Make all activities in the task are finishing to simulate TaskRecord#getTopActivity
+        // returns null.
+        taskTop.finishing = true;
+
+        final ActivityRecord newR = new ActivityBuilder(mService).build();
+        final ActivityRecord result = mStack.resetTaskIfNeededLocked(taskTop, newR);
+        assertThat(result).isEqualTo(taskTop);
+    }
+
     private void verifyShouldSleepActivities(boolean focusedStack,
             boolean keyguardGoingAway, boolean displaySleeping, boolean expected) {
         final ActivityDisplay display = mock(ActivityDisplay.class);
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
index 3d94467..81fbfe4 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
@@ -79,6 +79,7 @@
 import androidx.test.filters.SmallTest;
 
 import com.android.server.wm.LaunchParamsController.LaunchParamsModifier;
+import com.android.server.wm.utils.MockTracker;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -186,6 +187,19 @@
         verifyStartActivityPreconditions(preconditions, 0 /*launchFlags*/, expectedResult);
     }
 
+    private void verifyStartActivityPreconditions(int preconditions, int launchFlags,
+            int expectedResult) {
+        // We track mocks created here because this is used in a single test
+        // (testStartActivityPreconditions) as a specific case, and mocks created inside it won't be
+        // used for other cases. To avoid extensive memory usage, we clean up all used mocks after
+        // each case. This is necessary because usually we only clean up mocks after a test
+        // finishes, but this test creates too many mocks that the intermediate memory usage can be
+        // ~0.8 GiB and thus very susceptible to OutOfMemoryException.
+        try (MockTracker tracker = new MockTracker()) {
+            verifyStartActivityPreconditionsUntracked(preconditions, launchFlags, expectedResult);
+        }
+    }
+
     /**
      * Excercises how the {@link ActivityStarter} reacts to various preconditions. The caller
      * provides a bitmask of all the set conditions (such as {@link #PRECONDITION_NO_CALLER_APP})
@@ -197,7 +211,7 @@
      * @param launchFlags The launch flags to be provided by the launch {@link Intent}.
      * @param expectedResult The expected result from the launch.
      */
-    private void verifyStartActivityPreconditions(int preconditions, int launchFlags,
+    private void verifyStartActivityPreconditionsUntracked(int preconditions, int launchFlags,
             int expectedResult) {
         final ActivityTaskManagerService service = mService;
         final IPackageManager packageManager = mock(IPackageManager.class);
@@ -329,9 +343,6 @@
                 any(), any(), any(), anyInt(), anyInt(), anyInt(), any(),
                 anyBoolean(), anyBoolean(), any(), any(), any());
 
-        // Use factory that only returns spy task.
-        mockTaskRecordFactory();
-
         if (mockGetLaunchStack) {
             // Instrument the stack and task used.
             final ActivityStack stack = mRootActivityContainer.getDefaultDisplay().createStack(
@@ -482,7 +493,7 @@
     @Test
     public void testTaskModeViolation() {
         final ActivityDisplay display = mService.mRootActivityContainer.getDefaultDisplay();
-        ((TestActivityDisplay) display).removeAllTasks();
+        display.removeAllTasks();
         assertNoTasks(display);
 
         final ActivityStarter starter = prepareStarter(0);
@@ -676,18 +687,27 @@
         doReturn(isCallingUidDeviceOwner).when(mService).isDeviceOwner(callingUid);
 
         final ActivityOptions options = spy(ActivityOptions.makeBasic());
+        ActivityRecord[] outActivity = new ActivityRecord[1];
         ActivityStarter starter = prepareStarter(FLAG_ACTIVITY_NEW_TASK)
                 .setCallingPackage("com.whatever.dude")
                 .setCaller(caller)
                 .setCallingUid(callingUid)
                 .setRealCallingUid(realCallingUid)
-                .setActivityOptions(new SafeActivityOptions(options));
+                .setActivityOptions(new SafeActivityOptions(options))
+                .setOutActivity(outActivity);
 
         final int result = starter.setReason("testBackgroundActivityStarts_" + name).execute();
 
         assertEquals(ActivityStarter.getExternalResult(
                 shouldHaveAborted ? START_ABORTED : START_SUCCESS), result);
         verify(options, times(shouldHaveAborted ? 1 : 0)).abort();
+
+        final ActivityRecord startedActivity = outActivity[0];
+        if (startedActivity != null && startedActivity.getTaskRecord() != null) {
+            // Remove the activity so it doesn't interfere with with subsequent activity launch
+            // tests from this method.
+            startedActivity.getTaskRecord().removeActivity(startedActivity);
+        }
     }
 
     /**
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java
index d8a9bb0..297aa7e 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java
@@ -45,8 +45,7 @@
     /** Verify that activity is finished correctly upon request. */
     @Test
     public void testActivityFinish() {
-        final TestActivityStack stack =
-                (TestActivityStack) new StackBuilder(mRootActivityContainer).build();
+        final ActivityStack stack = new StackBuilder(mRootActivityContainer).build();
         final ActivityRecord activity = stack.getChildAt(0).getTopActivity();
         assertTrue("Activity must be finished", mService.finishActivity(activity.appToken,
                 0 /* resultCode */, null /* resultData */,
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java
index ab2da2b..a5dc241 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java
@@ -16,96 +16,58 @@
 
 package com.android.server.wm;
 
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
 import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
-import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
-import static android.view.Display.DEFAULT_DISPLAY;
-import static android.view.DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS;
+import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
 
 import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
 
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.any;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyBoolean;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyInt;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyString;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.doCallRealMethod;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.reset;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
-import static com.android.server.wm.ActivityStack.REMOVE_TASK_MODE_DESTROYING;
-import static com.android.server.wm.ActivityStackSupervisor.ON_TOP;
 
-import android.app.ActivityManagerInternal;
 import android.app.ActivityOptions;
-import android.app.AppOpsManager;
 import android.app.IApplicationThread;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
 import android.content.pm.ActivityInfo;
 import android.content.pm.ApplicationInfo;
-import android.content.pm.IPackageManager;
-import android.content.pm.PackageManagerInternal;
 import android.content.res.Configuration;
 import android.graphics.Rect;
-import android.hardware.display.DisplayManager;
-import android.hardware.display.DisplayManagerGlobal;
-import android.os.Handler;
-import android.os.Looper;
-import android.os.PowerManager;
-import android.os.Process;
 import android.os.UserHandle;
 import android.service.voice.IVoiceInteractionSession;
 import android.testing.DexmakerShareClassLoaderRule;
-import android.view.Display;
 import android.view.DisplayInfo;
 
-import com.android.internal.app.IVoiceInteractor;
 import com.android.server.AttributeCache;
-import com.android.server.ServiceThread;
-import com.android.server.am.ActivityManagerService;
-import com.android.server.am.PendingIntentController;
-import com.android.server.appop.AppOpsService;
-import com.android.server.firewall.IntentFirewall;
-import com.android.server.policy.PermissionPolicyInternal;
-import com.android.server.uri.UriGrantsManagerInternal;
-import com.android.server.wm.TaskRecord.TaskRecordFactory;
-import com.android.server.wm.utils.MockTracker;
 
-import org.junit.After;
 import org.junit.Before;
 import org.junit.BeforeClass;
 import org.junit.Rule;
-import org.mockito.invocation.InvocationOnMock;
-
-import java.io.File;
-import java.util.List;
-import java.util.function.Consumer;
 
 /**
  * A base class to handle common operations in activity related unit tests.
  */
 class ActivityTestsBase {
-    private static int sNextDisplayId = DEFAULT_DISPLAY + 1;
 
     @Rule
     public final DexmakerShareClassLoaderRule mDexmakerShareClassLoaderRule =
             new DexmakerShareClassLoaderRule();
 
+    @Rule
+    public final SystemServicesTestRule mSystemServicesTestRule = new SystemServicesTestRule();
+
     final Context mContext = getInstrumentation().getTargetContext();
-    final TestInjector mTestInjector = new TestInjector(mContext);
 
     ActivityTaskManagerService mService;
     RootActivityContainer mRootActivityContainer;
     ActivityStackSupervisor mSupervisor;
 
-    private MockTracker mMockTracker;
-
     // Default package name
     static final String DEFAULT_COMPONENT_PACKAGE_NAME = "com.foo";
 
@@ -119,37 +81,18 @@
 
     @Before
     public void setUpBase() {
-        mMockTracker = new MockTracker();
-
-        mTestInjector.setUp();
-
-        mService = new TestActivityTaskManagerService(mContext);
+        mService = mSystemServicesTestRule.getActivityTaskManagerService();
         mSupervisor = mService.mStackSupervisor;
         mRootActivityContainer = mService.mRootActivityContainer;
     }
 
-    @After
-    public void tearDownBase() {
-        mTestInjector.tearDown();
-        if (mService != null) {
-            mService.setWindowManager(null);
-            mService = null;
-        }
-        if (sMockWindowManagerService != null) {
-            reset(sMockWindowManagerService);
-        }
-
-        mMockTracker.close();
-        mMockTracker = null;
-    }
-
     /** Creates a {@link TestActivityDisplay}. */
     TestActivityDisplay createNewActivityDisplay() {
-        return TestActivityDisplay.create(mSupervisor, sNextDisplayId++);
+        return TestActivityDisplay.create(mSupervisor);
     }
 
     TestActivityDisplay createNewActivityDisplay(DisplayInfo info) {
-        return TestActivityDisplay.create(mSupervisor, sNextDisplayId++, info);
+        return TestActivityDisplay.create(mSupervisor, info);
     }
 
     /** Creates and adds a {@link TestActivityDisplay} to supervisor at the given position. */
@@ -166,25 +109,9 @@
         return display;
     }
 
-    /**
-     * Delegates task creation to {@link #TaskBuilder} to avoid the dependency of window hierarchy
-     * when starting activity in unit tests.
-     */
-    void mockTaskRecordFactory(Consumer<TaskBuilder> taskBuilderSetup) {
-        final TaskBuilder taskBuilder = new TaskBuilder(mSupervisor).setCreateStack(false);
-        if (taskBuilderSetup != null) {
-            taskBuilderSetup.accept(taskBuilder);
-        }
-        final TaskRecord task = taskBuilder.build();
-        final TaskRecordFactory factory = mock(TaskRecordFactory.class);
-        TaskRecord.setTaskRecordFactory(factory);
-        doReturn(task).when(factory).create(any() /* service */, anyInt() /* taskId */,
-                any() /* info */, any() /* intent */, any() /* voiceSession */,
-                any() /* voiceInteractor */);
-    }
-
-    void mockTaskRecordFactory() {
-        mockTaskRecordFactory(null /* taskBuilderSetup */);
+    /** Sets the default minimum task size to 1 so that tests can use small task sizes */
+    public void removeGlobalMinSizeRestriction() {
+        mService.mRootActivityContainer.mDefaultMinSizeOfResizeableTaskDp = 1;
     }
 
     /**
@@ -204,6 +131,11 @@
         private ActivityStack mStack;
         private int mActivityFlags;
         private int mLaunchMode;
+        private int mResizeMode = RESIZE_MODE_RESIZEABLE;
+        private float mMaxAspectRatio;
+        private int mScreenOrientation = SCREEN_ORIENTATION_UNSPECIFIED;
+        private boolean mLaunchTaskBehind;
+        private int mConfigChanges;
 
         ActivityBuilder(ActivityTaskManagerService service) {
             mService = service;
@@ -254,6 +186,31 @@
             return this;
         }
 
+        ActivityBuilder setResizeMode(int resizeMode) {
+            mResizeMode = resizeMode;
+            return this;
+        }
+
+        ActivityBuilder setMaxAspectRatio(float maxAspectRatio) {
+            mMaxAspectRatio = maxAspectRatio;
+            return this;
+        }
+
+        ActivityBuilder setScreenOrientation(int screenOrientation) {
+            mScreenOrientation = screenOrientation;
+            return this;
+        }
+
+        ActivityBuilder setLaunchTaskBehind(boolean launchTaskBehind) {
+            mLaunchTaskBehind = launchTaskBehind;
+            return this;
+        }
+
+        ActivityBuilder setConfigChanges(int configChanges) {
+            mConfigChanges = configChanges;
+            return this;
+        }
+
         ActivityRecord build() {
             if (mComponent == null) {
                 final int id = sCurrentActivityId++;
@@ -279,24 +236,31 @@
             }
             aInfo.flags |= mActivityFlags;
             aInfo.launchMode = mLaunchMode;
+            aInfo.resizeMode = mResizeMode;
+            aInfo.maxAspectRatio = mMaxAspectRatio;
+            aInfo.screenOrientation = mScreenOrientation;
+            aInfo.configChanges |= mConfigChanges;
+
+            ActivityOptions options = null;
+            if (mLaunchTaskBehind) {
+                options =  ActivityOptions.makeTaskLaunchBehind();
+            }
 
             final ActivityRecord activity = new ActivityRecord(mService, null /* caller */,
                     0 /* launchedFromPid */, 0, null, intent, null,
                     aInfo /*aInfo*/, new Configuration(), null /* resultTo */, null /* resultWho */,
                     0 /* reqCode */, false /*componentSpecified*/, false /* rootVoiceInteraction */,
-                    mService.mStackSupervisor, null /* options */, null /* sourceRecord */);
+                    mService.mStackSupervisor, options, null /* sourceRecord */);
             spyOn(activity);
-            activity.mAppWindowToken = mock(AppWindowToken.class);
-            doCallRealMethod().when(activity.mAppWindowToken).getOrientationIgnoreVisibility();
-            doCallRealMethod().when(activity.mAppWindowToken)
-                    .setOrientation(anyInt(), any(), any());
-            doCallRealMethod().when(activity.mAppWindowToken).setOrientation(anyInt());
-            doNothing().when(activity).removeWindowContainer();
-            doReturn(mock(Configuration.class)).when(activity.mAppWindowToken)
-                    .getRequestedOverrideConfiguration();
-
             if (mTaskRecord != null) {
-                mTaskRecord.addActivityToTop(activity);
+                // fullscreen value is normally read from resources in ctor, so for testing we need
+                // to set it somewhere else since we can't mock resources.
+                activity.fullscreen = true;
+                activity.setTask(mTaskRecord);
+                activity.createAppWindowToken();
+                spyOn(activity.mAppWindowToken);
+                // Make visible by default...
+                activity.mAppWindowToken.setHidden(false);
             }
 
             final WindowProcessController wpc = new WindowProcessController(mService,
@@ -305,6 +269,9 @@
                     mock(WindowProcessListener.class));
             wpc.setThread(mock(IApplicationThread.class));
             activity.setProcess(wpc);
+
+            // Resume top activities to make sure all other signals in the system are connected.
+            mService.mRootActivityContainer.resumeFocusedStacksTopActivities();
             return activity;
         }
     }
@@ -313,16 +280,13 @@
      * Builder for creating new tasks.
      */
     protected static class TaskBuilder {
-        // Default package name
-        static final String DEFAULT_PACKAGE = "com.bar";
-
         private final ActivityStackSupervisor mSupervisor;
 
         private ComponentName mComponent;
         private String mPackage;
         private int mFlags = 0;
         // Task id 0 is reserved in ARC for the home app.
-        private int mTaskId = 1;
+        private int mTaskId = SystemServicesTestRule.sNextTaskId++;
         private int mUserId = 0;
         private IVoiceInteractionSession mVoiceSession;
         private boolean mCreateStack = true;
@@ -381,6 +345,7 @@
             if (mStack == null && mCreateStack) {
                 mStack = mSupervisor.mRootActivityContainer.getDefaultDisplay().createStack(
                         WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
+                spyOn(mStack);
             }
 
             final ActivityInfo aInfo = new ActivityInfo();
@@ -396,450 +361,22 @@
             intent.setComponent(mComponent);
             intent.setFlags(mFlags);
 
-            final TestTaskRecord task = new TestTaskRecord(mSupervisor.mService, mTaskId, aInfo,
+            final TaskRecord task = new TaskRecord(mSupervisor.mService, mTaskId, aInfo,
                     intent /*intent*/, mVoiceSession, null /*_voiceInteractor*/);
+            spyOn(task);
             task.userId = mUserId;
 
             if (mStack != null) {
                 mStack.moveToFront("test");
                 mStack.addTask(task, true, "creating test task");
-                task.setStack(mStack);
-                task.setTask();
-                mStack.getTaskStack().addChild(task.mTask, 0);
+                task.createTask(true, true);
+                spyOn(task.mTask);
             }
 
             task.touchActiveTime();
 
             return task;
         }
-
-        private static class TestTaskRecord extends TaskRecord {
-            TestTaskRecord(ActivityTaskManagerService service, int taskId, ActivityInfo info,
-                       Intent intent, IVoiceInteractionSession voiceSession,
-                       IVoiceInteractor voiceInteractor) {
-                super(service, taskId, info, intent, voiceSession, voiceInteractor);
-            }
-
-            @Override
-            void createTask(boolean onTop, boolean showForAllUsers) {
-                setTask();
-            }
-
-            void setTask() {
-                Task mockTask = mock(Task.class);
-                mockTask.mTaskRecord = this;
-                doCallRealMethod().when(mockTask).onDescendantOrientationChanged(any(), any());
-                setTask(mock(Task.class));
-            }
-        }
-    }
-
-    protected class TestActivityTaskManagerService extends ActivityTaskManagerService {
-        private PackageManagerInternal mPmInternal;
-        private PermissionPolicyInternal mPermissionPolicyInternal;
-
-        // ActivityStackSupervisor may be created more than once while setting up AMS and ATMS.
-        // We keep the reference in order to prevent creating it twice.
-        ActivityStackSupervisor mTestStackSupervisor;
-
-        ActivityDisplay mDefaultDisplay;
-        AppOpsService mAppOpsService;
-
-        TestActivityTaskManagerService(Context context) {
-            super(context);
-            spyOn(this);
-
-            mUgmInternal = mock(UriGrantsManagerInternal.class);
-            mAppOpsService = mock(AppOpsService.class);
-
-            // Make sure permission checks aren't overridden.
-            doReturn(AppOpsManager.MODE_DEFAULT)
-                    .when(mAppOpsService).noteOperation(anyInt(), anyInt(), anyString());
-
-            mSupportsMultiWindow = true;
-            mSupportsMultiDisplay = true;
-            mSupportsSplitScreenMultiWindow = true;
-            mSupportsFreeformWindowManagement = true;
-            mSupportsPictureInPicture = true;
-
-            final TestActivityManagerService am =
-                    new TestActivityManagerService(mTestInjector, this);
-
-            spyOn(getLifecycleManager());
-            spyOn(getLockTaskController());
-            spyOn(getTaskChangeNotificationController());
-            doReturn(mock(IPackageManager.class)).when(this).getPackageManager();
-            // allow background activity starts by default
-            doReturn(true).when(this).isBackgroundActivityStartsEnabled();
-            doNothing().when(this).updateCpuStats();
-        }
-
-        void setup(IntentFirewall intentFirewall, PendingIntentController intentController,
-                ActivityManagerInternal amInternal, WindowManagerService wm, Looper looper) {
-            mAmInternal = amInternal;
-            initialize(intentFirewall, intentController, looper);
-            initRootActivityContainerMocks(wm);
-            setWindowManager(wm);
-            createDefaultDisplay();
-        }
-
-        void initRootActivityContainerMocks(WindowManagerService wm) {
-            spyOn(mRootActivityContainer);
-            mRootActivityContainer.setWindowContainer(mock(RootWindowContainer.class));
-            mRootActivityContainer.mWindowManager = wm;
-            mRootActivityContainer.mDisplayManager =
-                    (DisplayManager) mContext.getSystemService(Context.DISPLAY_SERVICE);
-            doNothing().when(mRootActivityContainer).setWindowManager(any());
-            // Invoked during {@link ActivityStack} creation.
-            doNothing().when(mRootActivityContainer).updateUIDsPresentOnDisplay();
-            // Always keep things awake.
-            doReturn(true).when(mRootActivityContainer).hasAwakeDisplay();
-            // Called when moving activity to pinned stack.
-            doNothing().when(mRootActivityContainer).ensureActivitiesVisible(any(), anyInt(),
-                    anyBoolean());
-        }
-
-        void createDefaultDisplay() {
-            // Create a default display and put a home stack on it so that we'll always have
-            // something focusable.
-            mDefaultDisplay = TestActivityDisplay.create(mStackSupervisor, DEFAULT_DISPLAY);
-            spyOn(mDefaultDisplay);
-            mRootActivityContainer.addChild(mDefaultDisplay, ActivityDisplay.POSITION_TOP);
-            mDefaultDisplay.createStack(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, ON_TOP);
-            final TaskRecord task = new TaskBuilder(mStackSupervisor)
-                    .setStack(mDefaultDisplay.getHomeStack()).build();
-            new ActivityBuilder(this).setTask(task).build();
-
-            doReturn(mDefaultDisplay).when(mRootActivityContainer).getDefaultDisplay();
-        }
-
-        @Override
-        int handleIncomingUser(int callingPid, int callingUid, int userId, String name) {
-            return userId;
-        }
-
-        @Override
-        AppOpsService getAppOpsService() {
-            return mAppOpsService;
-        }
-
-        @Override
-        void updateCpuStats() {
-        }
-
-        @Override
-        void updateBatteryStats(ActivityRecord component, boolean resumed) {
-        }
-
-        @Override
-        void updateActivityUsageStats(ActivityRecord activity, int event) {
-        }
-
-        @Override
-        protected ActivityStackSupervisor createStackSupervisor() {
-            if (mTestStackSupervisor == null) {
-                mTestStackSupervisor = new TestActivityStackSupervisor(this, mH.getLooper());
-            }
-            return mTestStackSupervisor;
-        }
-
-        @Override
-        PackageManagerInternal getPackageManagerInternalLocked() {
-            if (mPmInternal == null) {
-                mPmInternal = mock(PackageManagerInternal.class);
-                doReturn(false)
-                        .when(mPmInternal)
-                        .isPermissionsReviewRequired(anyString(), anyInt());
-            }
-            return mPmInternal;
-        }
-
-        @Override
-        PermissionPolicyInternal getPermissionPolicyInternal() {
-            if (mPermissionPolicyInternal == null) {
-                mPermissionPolicyInternal = mock(PermissionPolicyInternal.class);
-                doReturn(true).when(mPermissionPolicyInternal).checkStartActivity(any(), anyInt(),
-                        any());
-            }
-            return mPermissionPolicyInternal;
-        }
-    }
-
-    private static class TestInjector extends ActivityManagerService.Injector {
-        private ServiceThread mHandlerThread;
-
-        TestInjector(Context context) {
-            super(context);
-        }
-
-        @Override
-        public AppOpsService getAppOpsService(File file, Handler handler) {
-            return null;
-        }
-
-        @Override
-        public Handler getUiHandler(ActivityManagerService service) {
-            return mHandlerThread.getThreadHandler();
-        }
-
-        @Override
-        public boolean isNetworkRestrictedForUid(int uid) {
-            return false;
-        }
-
-        void setUp() {
-            mHandlerThread = new ServiceThread("ActivityTestsThread",
-                    Process.THREAD_PRIORITY_DEFAULT, true /* allowIo */);
-            mHandlerThread.start();
-        }
-
-        void tearDown() {
-            // Make sure there are no running messages and then quit the thread so the next test
-            // won't be affected.
-            mHandlerThread.getThreadHandler().runWithScissors(mHandlerThread::quit,
-                    0 /* timeout */);
-        }
-    }
-
-    // TODO: Replace this with a mock object since we are no longer in AMS package.
-    /**
-     * An {@link ActivityManagerService} subclass which provides a test
-     * {@link ActivityStackSupervisor}.
-     */
-    class TestActivityManagerService extends ActivityManagerService {
-
-        TestActivityManagerService(TestInjector testInjector, TestActivityTaskManagerService atm) {
-            super(testInjector, testInjector.mHandlerThread);
-            spyOn(this);
-
-            mWindowManager = prepareMockWindowManager();
-            mUgmInternal = mock(UriGrantsManagerInternal.class);
-
-            atm.setup(mIntentFirewall, mPendingIntentController, new LocalService(), mWindowManager,
-                    testInjector.mHandlerThread.getLooper());
-
-            mActivityTaskManager = atm;
-            mAtmInternal = atm.mInternal;
-
-            doReturn(mock(IPackageManager.class)).when(this).getPackageManager();
-            PackageManagerInternal mockPackageManager = mock(PackageManagerInternal.class);
-            doReturn(mockPackageManager).when(this).getPackageManagerInternalLocked();
-            doReturn(null).when(mockPackageManager).getDefaultHomeActivity(anyInt());
-            doNothing().when(this).grantEphemeralAccessLocked(anyInt(), any(), anyInt(), anyInt());
-        }
-    }
-
-    /**
-     * An {@link ActivityStackSupervisor} which stubs out certain methods that depend on
-     * setup not available in the test environment. Also specifies an injector for
-     */
-    protected class TestActivityStackSupervisor extends ActivityStackSupervisor {
-        private KeyguardController mKeyguardController;
-
-        TestActivityStackSupervisor(ActivityTaskManagerService service, Looper looper) {
-            super(service, looper);
-            spyOn(this);
-            mWindowManager = prepareMockWindowManager();
-            mKeyguardController = mock(KeyguardController.class);
-
-            // Do not schedule idle that may touch methods outside the scope of the test.
-            doNothing().when(this).scheduleIdleLocked();
-            doNothing().when(this).scheduleIdleTimeoutLocked(any());
-            // unit test version does not handle launch wake lock
-            doNothing().when(this).acquireLaunchWakelock();
-            doReturn(mKeyguardController).when(this).getKeyguardController();
-
-            mLaunchingActivityWakeLock = mock(PowerManager.WakeLock.class);
-
-            initialize();
-        }
-
-        @Override
-        public KeyguardController getKeyguardController() {
-            return mKeyguardController;
-        }
-
-        @Override
-        void setWindowManager(WindowManagerService wm) {
-            mWindowManager = wm;
-        }
-    }
-
-    protected static class TestActivityDisplay extends ActivityDisplay {
-        private final ActivityStackSupervisor mSupervisor;
-
-        static TestActivityDisplay create(ActivityStackSupervisor supervisor, int displayId) {
-            return create(supervisor, displayId, new DisplayInfo());
-        }
-
-        static TestActivityDisplay create(ActivityStackSupervisor supervisor, int displayId,
-                DisplayInfo info) {
-            if (displayId == DEFAULT_DISPLAY) {
-                return new TestActivityDisplay(supervisor,
-                        supervisor.mRootActivityContainer.mDisplayManager.getDisplay(displayId));
-            }
-            final Display display = new Display(DisplayManagerGlobal.getInstance(), displayId,
-                    info, DEFAULT_DISPLAY_ADJUSTMENTS);
-            return new TestActivityDisplay(supervisor, display);
-        }
-
-        TestActivityDisplay(ActivityStackSupervisor supervisor, Display display) {
-            super(supervisor.mService.mRootActivityContainer, display);
-            // Normally this comes from display-properties as exposed by WM. Without that, just
-            // hard-code to FULLSCREEN for tests.
-            setWindowingMode(WINDOWING_MODE_FULLSCREEN);
-            mSupervisor = supervisor;
-        }
-
-        @SuppressWarnings("TypeParameterUnusedInFormals")
-        @Override
-        ActivityStack createStackUnchecked(int windowingMode, int activityType,
-                int stackId, boolean onTop) {
-            return new StackBuilder(mSupervisor.mRootActivityContainer).setDisplay(this)
-                    .setWindowingMode(windowingMode).setActivityType(activityType)
-                    .setStackId(stackId).setOnTop(onTop).setCreateActivity(false).build();
-        }
-
-        @Override
-        protected DisplayContent createDisplayContent() {
-            final DisplayContent displayContent = mock(DisplayContent.class);
-            DockedStackDividerController divider = mock(DockedStackDividerController.class);
-            doReturn(divider).when(displayContent).getDockedDividerController();
-            return displayContent;
-        }
-
-        void removeAllTasks() {
-            for (int i = 0; i < getChildCount(); i++) {
-                final ActivityStack stack = getChildAt(i);
-                for (TaskRecord task : (List<TaskRecord>) stack.getAllTasks()) {
-                    stack.removeTask(task, "removeAllTasks", REMOVE_TASK_MODE_DESTROYING);
-                }
-            }
-        }
-    }
-
-    private static WindowManagerService sMockWindowManagerService;
-
-    private static WindowManagerService prepareMockWindowManager() {
-        if (sMockWindowManagerService == null) {
-            sMockWindowManagerService = mock(WindowManagerService.class);
-        }
-
-        sMockWindowManagerService.mRoot = mock(RootWindowContainer.class);
-
-        doAnswer((InvocationOnMock invocationOnMock) -> {
-            final Runnable runnable = invocationOnMock.<Runnable>getArgument(0);
-            if (runnable != null) {
-                runnable.run();
-            }
-            return null;
-        }).when(sMockWindowManagerService).inSurfaceTransaction(any());
-
-        return sMockWindowManagerService;
-    }
-
-    /**
-     * Overridden {@link ActivityStack} that tracks test metrics, such as the number of times a
-     * method is called. Note that its functionality depends on the implementations of the
-     * construction arguments.
-     */
-    protected static class TestActivityStack
-            extends ActivityStack {
-        private int mOnActivityRemovedFromStackCount = 0;
-
-        static final int IS_TRANSLUCENT_UNSET = 0;
-        static final int IS_TRANSLUCENT_FALSE = 1;
-        static final int IS_TRANSLUCENT_TRUE = 2;
-        private int mIsTranslucent = IS_TRANSLUCENT_UNSET;
-
-        static final int SUPPORTS_SPLIT_SCREEN_UNSET = 0;
-        static final int SUPPORTS_SPLIT_SCREEN_FALSE = 1;
-        static final int SUPPORTS_SPLIT_SCREEN_TRUE = 2;
-        private int mSupportsSplitScreen = SUPPORTS_SPLIT_SCREEN_UNSET;
-
-        TestActivityStack(ActivityDisplay display, int stackId, ActivityStackSupervisor supervisor,
-                int windowingMode, int activityType, boolean onTop, boolean createActivity) {
-            super(display, stackId, supervisor, windowingMode, activityType, onTop);
-            if (createActivity) {
-                new ActivityBuilder(mService).setCreateTask(true).setStack(this).build();
-                if (onTop) {
-                    // We move the task to front again in order to regain focus after activity
-                    // added to the stack. Or {@link ActivityDisplay#mPreferredTopFocusableStack}
-                    // could be other stacks (e.g. home stack).
-                    moveToFront("createActivityStack");
-                } else {
-                    moveToBack("createActivityStack", null);
-                }
-            }
-        }
-
-        @Override
-        void onActivityRemovedFromStack(ActivityRecord r) {
-            mOnActivityRemovedFromStackCount++;
-            super.onActivityRemovedFromStack(r);
-        }
-
-        // Returns the number of times {@link #onActivityRemovedFromStack} has been called
-        int onActivityRemovedFromStackInvocationCount() {
-            return mOnActivityRemovedFromStackCount;
-        }
-
-        @Override
-        protected void createTaskStack(int displayId, boolean onTop, Rect outBounds) {
-            mTaskStack = mock(TaskStack.class);
-
-            // Primary pinned stacks require a non-empty out bounds to be set or else all tasks
-            // will be moved to the full screen stack.
-            if (getWindowingMode() == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) {
-                outBounds.set(0, 0, 100, 100);
-            }
-        }
-
-        @Override
-        TaskStack getTaskStack() {
-            return mTaskStack;
-        }
-
-        void setIsTranslucent(boolean isTranslucent) {
-            mIsTranslucent = isTranslucent ? IS_TRANSLUCENT_TRUE : IS_TRANSLUCENT_FALSE;
-        }
-
-        @Override
-        boolean isStackTranslucent(ActivityRecord starting) {
-            switch (mIsTranslucent) {
-                case IS_TRANSLUCENT_TRUE:
-                    return true;
-                case IS_TRANSLUCENT_FALSE:
-                    return false;
-                case IS_TRANSLUCENT_UNSET:
-                default:
-                    return super.isStackTranslucent(starting);
-            }
-        }
-
-        void setSupportsSplitScreen(boolean supportsSplitScreen) {
-            mSupportsSplitScreen = supportsSplitScreen
-                    ? SUPPORTS_SPLIT_SCREEN_TRUE : SUPPORTS_SPLIT_SCREEN_FALSE;
-        }
-
-        @Override
-        public boolean supportsSplitScreenWindowingMode() {
-            switch (mSupportsSplitScreen) {
-                case SUPPORTS_SPLIT_SCREEN_TRUE:
-                    return true;
-                case SUPPORTS_SPLIT_SCREEN_FALSE:
-                    return false;
-                case SUPPORTS_SPLIT_SCREEN_UNSET:
-                default:
-                    return super.supportsSplitScreenWindowingMode();
-            }
-        }
-
-        @Override
-        void startActivityLocked(ActivityRecord r, ActivityRecord focusedTopActivity,
-                                 boolean newTask, boolean keepCurTransition,
-                                 ActivityOptions options) {
-        }
     }
 
     static class StackBuilder {
@@ -886,27 +423,45 @@
             return this;
         }
 
-        @SuppressWarnings("TypeParameterUnusedInFormals")
         ActivityStack build() {
             final int stackId = mStackId >= 0 ? mStackId : mDisplay.getNextStackId();
+            final ActivityStack stack;
+            final ActivityStackSupervisor supervisor = mRootActivityContainer.mStackSupervisor;
             if (mWindowingMode == WINDOWING_MODE_PINNED) {
-                return new ActivityStack(mDisplay, stackId, mRootActivityContainer.mStackSupervisor,
+                stack = new ActivityStack(mDisplay, stackId, supervisor,
                         mWindowingMode, ACTIVITY_TYPE_STANDARD, mOnTop) {
                     @Override
                     Rect getDefaultPictureInPictureBounds(float aspectRatio) {
                         return new Rect(50, 50, 100, 100);
                     }
-
-                    @Override
-                    void createTaskStack(int displayId, boolean onTop, Rect outBounds) {
-                        mTaskStack = mock(TaskStack.class);
-                    }
                 };
             } else {
-                return new TestActivityStack(mDisplay, stackId,
-                        mRootActivityContainer.mStackSupervisor, mWindowingMode,
-                        mActivityType, mOnTop, mCreateActivity);
+                stack = new ActivityStack(mDisplay, stackId, supervisor,
+                        mWindowingMode, mActivityType, mOnTop);
+
+                if (mCreateActivity) {
+                    new ActivityBuilder(supervisor.mService)
+                            .setCreateTask(true)
+                            .setStack(stack)
+                            .build();
+                    if (mOnTop) {
+                        // We move the task to front again in order to regain focus after activity
+                        // added to the stack.
+                        // Or {@link ActivityDisplay#mPreferredTopFocusableStack} could be other
+                        // stacks (e.g. home stack).
+                        stack.moveToFront("createActivityStack");
+                    } else {
+                        stack.moveToBack("createActivityStack", null);
+                    }
+                }
             }
+
+            spyOn(stack);
+            spyOn(stack.mTaskStack);
+            doNothing().when(stack).startActivityLocked(
+                    any(), any(), anyBoolean(), anyBoolean(), any());
+
+            return stack;
         }
 
     }
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java
index 20379a2..e71c8f4 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java
@@ -60,7 +60,7 @@
                     WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
             final AppWindowToken translucentOpening = createAppWindowToken(mDisplayContent,
                     WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
-            translucentOpening.setFillsParent(false);
+            translucentOpening.setOccludesParent(false);
             translucentOpening.setHidden(true);
             mDisplayContent.mOpeningApps.add(behind);
             mDisplayContent.mOpeningApps.add(translucentOpening);
@@ -78,7 +78,7 @@
                     WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
             final AppWindowToken translucentClosing = createAppWindowToken(mDisplayContent,
                     WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
-            translucentClosing.setFillsParent(false);
+            translucentClosing.setOccludesParent(false);
             mDisplayContent.mClosingApps.add(translucentClosing);
             assertEquals(WindowManager.TRANSIT_TRANSLUCENT_ACTIVITY_CLOSE,
                     mAppTransitionController.maybeUpdateTransitToTranslucentAnim(
@@ -94,7 +94,7 @@
                     WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
             final AppWindowToken translucentOpening = createAppWindowToken(mDisplayContent,
                     WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
-            translucentOpening.setFillsParent(false);
+            translucentOpening.setOccludesParent(false);
             translucentOpening.setHidden(true);
             mDisplayContent.mOpeningApps.add(behind);
             mDisplayContent.mOpeningApps.add(translucentOpening);
@@ -110,10 +110,10 @@
         synchronized (mWm.mGlobalLock) {
             final AppWindowToken opening = createAppWindowToken(mDisplayContent,
                     WINDOWING_MODE_FREEFORM, ACTIVITY_TYPE_STANDARD);
-            opening.setFillsParent(false);
+            opening.setOccludesParent(false);
             final AppWindowToken closing = createAppWindowToken(mDisplayContent,
                     WINDOWING_MODE_FREEFORM, ACTIVITY_TYPE_STANDARD);
-            closing.setFillsParent(false);
+            closing.setOccludesParent(false);
             Task task = opening.getTask();
             mDisplayContent.mOpeningApps.add(opening);
             mDisplayContent.mClosingApps.add(closing);
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java b/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java
index d1dc382..c162b6a 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java
@@ -30,12 +30,14 @@
 
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyBoolean;
 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.spyOn;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
 
 import android.graphics.Rect;
 import android.os.IBinder;
@@ -195,6 +197,7 @@
     @Test
     public void testCancelRemoteAnimationWhenFreeze() {
         final DisplayContent dc = createNewDisplay(Display.STATE_ON);
+        doReturn(false).when(dc).onDescendantOrientationChanged(any(), any());
         final WindowState exitingAppWindow = createWindow(null /* parent */, TYPE_BASE_APPLICATION,
                 dc, "exiting app");
         final AppWindowToken exitingAppToken = exitingAppWindow.mAppToken;
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java b/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java
index d9566a3..e387e18 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java
@@ -36,7 +36,7 @@
 import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
 
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.spy;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
 import static com.android.server.wm.WindowStateAnimator.STACK_CLIP_AFTER_ANIM;
 import static com.android.server.wm.WindowStateAnimator.STACK_CLIP_BEFORE_ANIM;
 import static com.android.server.wm.WindowStateAnimator.STACK_CLIP_NONE;
@@ -152,10 +152,6 @@
     @Test
     @FlakyTest(bugId = 131005232)
     public void testLandscapeSeascapeRotationByApp() {
-        // Some plumbing to get the service ready for rotation updates.
-        mWm.mDisplayReady = true;
-        mWm.mDisplayEnabled = true;
-
         final WindowManager.LayoutParams attrs = new WindowManager.LayoutParams(
                 TYPE_BASE_APPLICATION);
         attrs.setTitle("AppWindow");
@@ -185,25 +181,21 @@
 
     @Test
     public void testLandscapeSeascapeRotationByPolicy() {
-        // Some plumbing to get the service ready for rotation updates.
-        mWm.mDisplayReady = true;
-        mWm.mDisplayEnabled = true;
-
-        final DisplayRotation spiedRotation = spy(mDisplayContent.getDisplayRotation());
-        mDisplayContent.setDisplayRotation(spiedRotation);
+        final DisplayRotation displayRotation = mDisplayContent.getDisplayRotation();
+        spyOn(displayRotation);
 
         final WindowManager.LayoutParams attrs = new WindowManager.LayoutParams(
                 TYPE_BASE_APPLICATION);
-        attrs.setTitle("AppWindow");
+        attrs.setTitle("RotationByPolicy");
         final WindowTestUtils.TestWindowState appWindow = createWindowState(attrs, mToken);
         mToken.addWindow(appWindow);
 
         // Set initial orientation and update.
-        performRotation(spiedRotation, Surface.ROTATION_90);
+        performRotation(displayRotation, Surface.ROTATION_90);
         appWindow.mResizeReported = false;
 
         // Update the rotation to perform 180 degree rotation and check that resize was reported.
-        performRotation(spiedRotation, Surface.ROTATION_270);
+        performRotation(displayRotation, Surface.ROTATION_270);
         assertTrue(appWindow.mResizeReported);
 
         appWindow.removeImmediately();
@@ -211,14 +203,7 @@
 
     private void performRotation(DisplayRotation spiedRotation, int rotationToReport) {
         doReturn(rotationToReport).when(spiedRotation).rotationForOrientation(anyInt(), anyInt());
-        int oldRotation = mDisplayContent.getRotation();
         mWm.updateRotation(false, false);
-        // Must manually apply here since ATM doesn't know about the display during this test
-        // (meaning it can't perform the normal sendNewConfiguration flow).
-        mDisplayContent.applyRotationLocked(oldRotation, mDisplayContent.getRotation());
-        // Prevent the next rotation from being deferred by animation.
-        mWm.mAnimator.setScreenRotationAnimationLocked(mDisplayContent.getDisplayId(), null);
-        mWm.mRoot.performSurfacePlacement(false /* recoveringMemory */);
     }
 
     @Test
@@ -266,14 +251,14 @@
     public void testGetOrientation() {
         mToken.setOrientation(SCREEN_ORIENTATION_LANDSCAPE);
 
-        mToken.setFillsParent(false);
-        // Can specify orientation if app doesn't fill parent.
+        mToken.setOccludesParent(false);
+        // Can specify orientation if app doesn't occludes parent.
         assertEquals(SCREEN_ORIENTATION_LANDSCAPE, mToken.getOrientation());
 
-        mToken.setFillsParent(true);
+        mToken.setOccludesParent(true);
         mToken.setHidden(true);
         mToken.sendingToBottom = true;
-        // Can not specify orientation if app isn't visible even though it fills parent.
+        // Can not specify orientation if app isn't visible even though it occludes parent.
         assertEquals(SCREEN_ORIENTATION_UNSET, mToken.getOrientation());
         // Can specify orientation if the current orientation candidate is orientation behind.
         assertEquals(SCREEN_ORIENTATION_LANDSCAPE,
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
index 388658d..6289768 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
@@ -83,7 +83,6 @@
 
 import androidx.test.filters.SmallTest;
 
-import com.android.dx.mockito.inline.extended.ExtendedMockito;
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.nano.MetricsProto;
 import com.android.server.wm.utils.WmDisplayCutout;
@@ -653,25 +652,27 @@
     @Test
     public void testOnDescendantOrientationRequestChanged() {
         final DisplayContent dc = createNewDisplay();
-        mWm.mAtmService.mRootActivityContainer = mock(RootActivityContainer.class);
+        dc.getDisplayRotation().setFixedToUserRotation(
+                DisplayRotation.FIXED_TO_USER_ROTATION_DISABLED);
         final int newOrientation = dc.getLastOrientation() == SCREEN_ORIENTATION_LANDSCAPE
                 ? SCREEN_ORIENTATION_PORTRAIT
                 : SCREEN_ORIENTATION_LANDSCAPE;
 
-        final WindowState window = createWindow(null /* parent */, TYPE_BASE_APPLICATION, dc, "w");
-        window.getTask().mTaskRecord = mock(TaskRecord.class, ExtendedMockito.RETURNS_DEEP_STUBS);
-        window.mAppToken.setOrientation(newOrientation);
+        final ActivityStack stack =
+                new ActivityTestsBase.StackBuilder(mWm.mAtmService.mRootActivityContainer)
+                        .setDisplay(dc.mAcitvityDisplay).build();
+        final ActivityRecord activity = stack.topTask().getTopActivity();
 
-        ActivityRecord activityRecord = mock(ActivityRecord.class);
-
-        assertTrue("Display should rotate to handle orientation request by default.",
-                dc.onDescendantOrientationChanged(window.mToken.token, activityRecord));
+        activity.setRequestedOrientation(newOrientation);
 
         final ArgumentCaptor<Configuration> captor = ArgumentCaptor.forClass(Configuration.class);
         verify(dc.mAcitvityDisplay).updateDisplayOverrideConfigurationLocked(captor.capture(),
-                same(activityRecord), anyBoolean(), same(null));
+                same(activity), anyBoolean(), same(null));
         final Configuration newDisplayConfig = captor.getValue();
-        assertEquals(Configuration.ORIENTATION_PORTRAIT, newDisplayConfig.orientation);
+        final int expectedOrientation = newOrientation == SCREEN_ORIENTATION_PORTRAIT
+                ? Configuration.ORIENTATION_PORTRAIT
+                : Configuration.ORIENTATION_LANDSCAPE;
+        assertEquals(expectedOrientation, newDisplayConfig.orientation);
     }
 
     @Test
@@ -679,22 +680,20 @@
         final DisplayContent dc = createNewDisplay();
         dc.getDisplayRotation().setFixedToUserRotation(
                 DisplayRotation.FIXED_TO_USER_ROTATION_ENABLED);
-        mWm.mAtmService.mRootActivityContainer = mock(RootActivityContainer.class);
         final int newOrientation = dc.getLastOrientation() == SCREEN_ORIENTATION_LANDSCAPE
                 ? SCREEN_ORIENTATION_PORTRAIT
                 : SCREEN_ORIENTATION_LANDSCAPE;
 
-        final WindowState window = createWindow(null /* parent */, TYPE_BASE_APPLICATION, dc, "w");
-        window.getTask().mTaskRecord = mock(TaskRecord.class, ExtendedMockito.RETURNS_DEEP_STUBS);
-        window.mAppToken.setOrientation(newOrientation);
+        final ActivityStack stack =
+                new ActivityTestsBase.StackBuilder(mWm.mAtmService.mRootActivityContainer)
+                        .setDisplay(dc.mAcitvityDisplay).build();
+        final ActivityRecord activity = stack.topTask().getTopActivity();
 
-        ActivityRecord activityRecord = mock(ActivityRecord.class);
+        activity.setRequestedOrientation(newOrientation);
 
-        assertFalse("Display shouldn't rotate to handle orientation request if fixed to"
-                        + " user rotation.",
-                dc.onDescendantOrientationChanged(window.mToken.token, activityRecord));
         verify(dc.mAcitvityDisplay, never()).updateDisplayOverrideConfigurationLocked(any(),
-                eq(activityRecord), anyBoolean(), same(null));
+                eq(activity), anyBoolean(), same(null));
+        assertEquals(dc.getDisplayRotation().getUserRotation(), dc.getRotation());
     }
 
     @Test
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayWindowSettingsTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayWindowSettingsTests.java
index bfede51..f6f8811 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayWindowSettingsTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayWindowSettingsTests.java
@@ -95,7 +95,7 @@
     public void setUp() throws Exception {
         deleteRecursively(TEST_FOLDER);
 
-        mWm.setSupportsFreeformWindowManagement(false);
+        mWm.mAtmService.mSupportsFreeformWindowManagement = false;
         mWm.setIsPc(false);
         mWm.setForceDesktopModeOnExternalDisplays(false);
 
@@ -134,7 +134,7 @@
 
     @Test
     public void testPrimaryDisplayDefaultToFullscreen_HasFreeformSupport_NonPc_NoDesktopMode() {
-        mWm.setSupportsFreeformWindowManagement(true);
+        mWm.mAtmService.mSupportsFreeformWindowManagement = true;
 
         mTarget.applySettingsToDisplayLocked(mPrimaryDisplay);
 
@@ -144,7 +144,7 @@
 
     @Test
     public void testPrimaryDisplayDefaultToFullscreen_HasFreeformSupport_NonPc_HasDesktopMode() {
-        mWm.setSupportsFreeformWindowManagement(true);
+        mWm.mAtmService.mSupportsFreeformWindowManagement = true;
         mWm.setForceDesktopModeOnExternalDisplays(true);
 
         mTarget.applySettingsToDisplayLocked(mPrimaryDisplay);
@@ -155,7 +155,7 @@
 
     @Test
     public void testPrimaryDisplayDefaultToFreeform_HasFreeformSupport_IsPc() {
-        mWm.setSupportsFreeformWindowManagement(true);
+        mWm.mAtmService.mSupportsFreeformWindowManagement = true;
         mWm.setIsPc(true);
 
         mTarget.applySettingsToDisplayLocked(mPrimaryDisplay);
@@ -168,7 +168,7 @@
     public void testPrimaryDisplayUpdateToFreeform_HasFreeformSupport_IsPc() {
         mTarget.applySettingsToDisplayLocked(mPrimaryDisplay);
 
-        mWm.setSupportsFreeformWindowManagement(true);
+        mWm.mAtmService.mSupportsFreeformWindowManagement = true;
         mWm.setIsPc(true);
 
         mTarget.updateSettingsForDisplay(mPrimaryDisplay);
@@ -187,7 +187,7 @@
 
     @Test
     public void testSecondaryDisplayDefaultToFreeform_HasFreeformSupport_NonPc_NoDesktopMode() {
-        mWm.setSupportsFreeformWindowManagement(true);
+        mWm.mAtmService.mSupportsFreeformWindowManagement = true;
 
         mTarget.applySettingsToDisplayLocked(mSecondaryDisplay);
 
@@ -197,7 +197,7 @@
 
     @Test
     public void testSecondaryDisplayDefaultToFreeform_HasFreeformSupport_NonPc_HasDesktopMode() {
-        mWm.setSupportsFreeformWindowManagement(true);
+        mWm.mAtmService.mSupportsFreeformWindowManagement = true;
         mWm.setForceDesktopModeOnExternalDisplays(true);
 
         mTarget.applySettingsToDisplayLocked(mSecondaryDisplay);
@@ -208,7 +208,7 @@
 
     @Test
     public void testSecondaryDisplayDefaultToFreeform_HasFreeformSupport_IsPc() {
-        mWm.setSupportsFreeformWindowManagement(true);
+        mWm.mAtmService.mSupportsFreeformWindowManagement = true;
         mWm.setIsPc(true);
 
         mTarget.applySettingsToDisplayLocked(mSecondaryDisplay);
diff --git a/services/tests/wmtests/src/com/android/server/wm/LaunchParamsControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/LaunchParamsControllerTests.java
index b28ae40..be2ee29 100644
--- a/services/tests/wmtests/src/com/android/server/wm/LaunchParamsControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/LaunchParamsControllerTests.java
@@ -331,7 +331,9 @@
 
         mController.layoutTask(task, null /* windowLayout */);
 
-        assertEquals(expected, task.getBounds());
+        // TaskRecord will make adjustments to requested bounds. We only need to guarantee that the
+        // reuqested bounds are expected.
+        assertEquals(expected, task.getRequestedOverrideBounds());
     }
 
     /**
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 e4d3770..49d778f 100644
--- a/services/tests/wmtests/src/com/android/server/wm/LaunchParamsPersisterTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/LaunchParamsPersisterTests.java
@@ -104,6 +104,7 @@
 
         mDisplayUniqueId = "test:" + Integer.toString(sNextUniqueId++);
         final DisplayInfo info = new DisplayInfo();
+        mService.mContext.getDisplay().getDisplayInfo(info);
         info.uniqueId = mDisplayUniqueId;
         mTestDisplay = createNewActivityDisplay(info);
         mRootActivityContainer.addChild(mTestDisplay, ActivityDisplay.POSITION_TOP);
diff --git a/services/tests/wmtests/src/com/android/server/wm/PinnedStackControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/PinnedStackControllerTest.java
index 63d9fb9..efd468f 100644
--- a/services/tests/wmtests/src/com/android/server/wm/PinnedStackControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/PinnedStackControllerTest.java
@@ -61,7 +61,7 @@
 
     @Test
     public void setShelfHeight_shelfVisibilityChangedTriggered() throws RemoteException {
-        mWm.mSupportsPictureInPicture = true;
+        mWm.mAtmService.mSupportsPictureInPicture = true;
         mWm.registerPinnedStackListener(DEFAULT_DISPLAY, mIPinnedStackListener);
 
         verify(mIPinnedStackListener).onImeVisibilityChanged(false, 0);
diff --git a/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java
index b7a85d7..fb4e330 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java
@@ -32,7 +32,7 @@
 
 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.spy;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
 
 import static com.google.common.truth.Truth.assertThat;
 import static com.google.common.truth.Truth.assertWithMessage;
@@ -53,14 +53,12 @@
 import android.app.ActivityTaskManager;
 import android.app.WindowConfiguration;
 import android.content.ComponentName;
-import android.content.Context;
 import android.content.pm.PackageManager;
 import android.content.pm.ParceledListSlice;
 import android.content.pm.UserInfo;
 import android.content.res.Configuration;
 import android.graphics.Rect;
 import android.os.Bundle;
-import android.os.Looper;
 import android.os.RemoteException;
 import android.os.SystemClock;
 import android.platform.test.annotations.Presubmit;
@@ -93,11 +91,8 @@
     private static final int TEST_QUIET_USER_ID = 20;
     private static final UserInfo DEFAULT_USER_INFO = new UserInfo();
     private static final UserInfo QUIET_USER_INFO = new UserInfo();
-    private static int sLastTaskId = 1;
-    private static int sLastStackId = 1;
     private static final int INVALID_STACK_ID = 999;
 
-    private TestActivityTaskManagerService mTestService;
     private ActivityDisplay mDisplay;
     private ActivityDisplay mOtherDisplay;
     private ActivityDisplay mSingleTaskDisplay;
@@ -115,13 +110,29 @@
     @Before
     public void setUp() throws Exception {
         mTaskPersister = new TestTaskPersister(mContext.getFilesDir());
-        mTestService = new MyTestActivityTaskManagerService(mContext);
-        mRecentTasks = (TestRecentTasks) mTestService.getRecentTasks();
+
+        // Set testing displays
+        mDisplay = mRootActivityContainer.getActivityDisplay(DEFAULT_DISPLAY);
+        mOtherDisplay = createNewActivityDisplay();
+        mSingleTaskDisplay = createNewActivityDisplay();
+        mSingleTaskDisplay.setDisplayToSingleTaskInstance();
+        mRootActivityContainer.addChild(mOtherDisplay, ActivityDisplay.POSITION_TOP);
+        mRootActivityContainer.addChild(mDisplay, ActivityDisplay.POSITION_TOP);
+        mRootActivityContainer.addChild(mSingleTaskDisplay, ActivityDisplay.POSITION_TOP);
+
+        // Set the recent tasks we should use for testing in this class.
+        mRecentTasks = new TestRecentTasks(mService, mTaskPersister);
+        spyOn(mRecentTasks);
+        mService.setRecentTasks(mRecentTasks);
         mRecentTasks.loadParametersFromResources(mContext.getResources());
-        mRunningTasks = (TestRunningTasks) mTestService.mStackSupervisor.mRunningTasks;
-        mHomeStack = mTestService.mRootActivityContainer.getDefaultDisplay().getOrCreateStack(
+
+        // Set the running tasks we should use for testing in this class.
+        mRunningTasks = new TestRunningTasks();
+        mService.mStackSupervisor.setRunningTasks(mRunningTasks);
+
+        mHomeStack = mDisplay.getOrCreateStack(
                 WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, true /* onTop */);
-        mStack = mTestService.mRootActivityContainer.getDefaultDisplay().createStack(
+        mStack = mDisplay.createStack(
                 WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
         mCallbacksRecorder = new CallbacksRecorder();
         mRecentTasks.registerCallback(mCallbacksRecorder);
@@ -723,7 +734,7 @@
 
         ActivityStack stack = mTasks.get(2).getStack();
         stack.moveToFront("", mTasks.get(2));
-        doReturn(stack).when(mTestService.mRootActivityContainer).getTopDisplayFocusedStack();
+        doReturn(stack).when(mService.mRootActivityContainer).getTopDisplayFocusedStack();
 
         // Simulate the reset from the timeout
         mRecentTasks.resetFreezeTaskListReorderingOnTimeout();
@@ -742,10 +753,9 @@
     public void testBackStackTasks_expectNoTrim() {
         mRecentTasks.setParameters(-1 /* min */, 1 /* max */, -1 /* ms */);
 
-        final MyTestActivityStackSupervisor supervisor =
-                (MyTestActivityStackSupervisor) mTestService.mStackSupervisor;
         final ActivityStack homeStack = mDisplay.getHomeStack();
-        final ActivityStack aboveHomeStack = new MyTestActivityStack(mDisplay, supervisor);
+        final ActivityStack aboveHomeStack = mDisplay.createStack(
+                WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
 
         // Add a number of tasks (beyond the max) but ensure that nothing is trimmed because all
         // the tasks belong in stacks above the home stack
@@ -761,11 +771,11 @@
     public void testBehindHomeStackTasks_expectTaskTrimmed() {
         mRecentTasks.setParameters(-1 /* min */, 1 /* max */, -1 /* ms */);
 
-        final MyTestActivityStackSupervisor supervisor =
-                (MyTestActivityStackSupervisor) mTestService.mStackSupervisor;
-        final ActivityStack behindHomeStack = new MyTestActivityStack(mDisplay, supervisor);
+        final ActivityStack behindHomeStack = mDisplay.createStack(
+                WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
         final ActivityStack homeStack = mDisplay.getHomeStack();
-        final ActivityStack aboveHomeStack = new MyTestActivityStack(mDisplay, supervisor);
+        final ActivityStack aboveHomeStack = mDisplay.createStack(
+                WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
 
         // Add a number of tasks (beyond the max) but ensure that only the task in the stack behind
         // the home stack is trimmed once a new task is added
@@ -783,10 +793,9 @@
     public void testOtherDisplayTasks_expectNoTrim() {
         mRecentTasks.setParameters(-1 /* min */, 1 /* max */, -1 /* ms */);
 
-        final MyTestActivityStackSupervisor supervisor =
-                (MyTestActivityStackSupervisor) mTestService.mStackSupervisor;
         final ActivityStack homeStack = mDisplay.getHomeStack();
-        final ActivityStack otherDisplayStack = new MyTestActivityStack(mOtherDisplay, supervisor);
+        final ActivityStack otherDisplayStack = mOtherDisplay.createStack(
+                WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
 
         // Add a number of tasks (beyond the max) on each display, ensure that the tasks are not
         // removed
@@ -887,16 +896,16 @@
         mStack.remove();
 
         // The following APIs should not restore task from recents to the active list.
-        assertNotRestoreTask(() -> mTestService.setFocusedTask(taskId));
-        assertNotRestoreTask(() -> mTestService.startSystemLockTaskMode(taskId));
-        assertNotRestoreTask(() -> mTestService.cancelTaskWindowTransition(taskId));
+        assertNotRestoreTask(() -> mService.setFocusedTask(taskId));
+        assertNotRestoreTask(() -> mService.startSystemLockTaskMode(taskId));
+        assertNotRestoreTask(() -> mService.cancelTaskWindowTransition(taskId));
         assertNotRestoreTask(
-                () -> mTestService.resizeTask(taskId, null /* bounds */, 0 /* resizeMode */));
+                () -> mService.resizeTask(taskId, null /* bounds */, 0 /* resizeMode */));
         assertNotRestoreTask(
-                () -> mTestService.setTaskWindowingMode(taskId, WINDOWING_MODE_FULLSCREEN,
+                () -> mService.setTaskWindowingMode(taskId, WINDOWING_MODE_FULLSCREEN,
                         false/* toTop */));
         assertNotRestoreTask(
-                () -> mTestService.setTaskWindowingModeSplitScreenPrimary(taskId,
+                () -> mService.setTaskWindowingModeSplitScreenPrimary(taskId,
                         SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT,
                         false /* toTop */, false /* animate */, null /* initialBounds */,
                         true /* showRecents */));
@@ -910,7 +919,7 @@
         mRecentTasks.remove(task);
 
         TaskChangeNotificationController controller =
-                mTestService.getTaskChangeNotificationController();
+                mService.getTaskChangeNotificationController();
         verify(controller, times(2)).notifyTaskListUpdated();
     }
 
@@ -923,7 +932,7 @@
 
         // 2 calls - Once for add and once for remove
         TaskChangeNotificationController controller =
-                mTestService.getTaskChangeNotificationController();
+                mService.getTaskChangeNotificationController();
         verify(controller, times(2)).notifyTaskListUpdated();
     }
 
@@ -938,7 +947,7 @@
 
         // 4 calls - Twice for add and twice for remove
         TaskChangeNotificationController controller =
-                mTestService.getTaskChangeNotificationController();
+                mService.getTaskChangeNotificationController();
         verify(controller, times(4)).notifyTaskListUpdated();
     }
 
@@ -980,7 +989,7 @@
 
     @Test
     public void testNotRecentsComponent_denyApiAccess() throws Exception {
-        doReturn(PackageManager.PERMISSION_DENIED).when(mTestService)
+        doReturn(PackageManager.PERMISSION_DENIED).when(mService)
                 .checkGetTasksPermission(anyString(), anyInt(), anyInt());
         // Expect the following methods to fail due to recents component not being set
         mRecentTasks.setIsCallerRecentsOverride(TestRecentTasks.DENY_THROW_SECURITY_EXCEPTION);
@@ -992,7 +1001,7 @@
 
     @Test
     public void testRecentsComponent_allowApiAccessWithoutPermissions() {
-        doReturn(PackageManager.PERMISSION_DENIED).when(mTestService)
+        doReturn(PackageManager.PERMISSION_DENIED).when(mService)
                 .checkGetTasksPermission(anyString(), anyInt(), anyInt());
 
         // Set the recents component and ensure that the following calls do not fail
@@ -1002,62 +1011,62 @@
     }
 
     private void doTestRecentTasksApis(boolean expectCallable) {
-        assertSecurityException(expectCallable, () -> mTestService.removeStack(INVALID_STACK_ID));
+        assertSecurityException(expectCallable, () -> mService.removeStack(INVALID_STACK_ID));
         assertSecurityException(expectCallable,
-                () -> mTestService.removeStacksInWindowingModes(
+                () -> mService.removeStacksInWindowingModes(
                         new int[]{WINDOWING_MODE_UNDEFINED}));
         assertSecurityException(expectCallable,
-                () -> mTestService.removeStacksWithActivityTypes(
+                () -> mService.removeStacksWithActivityTypes(
                         new int[]{ACTIVITY_TYPE_UNDEFINED}));
-        assertSecurityException(expectCallable, () -> mTestService.removeTask(0));
+        assertSecurityException(expectCallable, () -> mService.removeTask(0));
         assertSecurityException(expectCallable,
-                () -> mTestService.setTaskWindowingMode(0, WINDOWING_MODE_UNDEFINED, true));
+                () -> mService.setTaskWindowingMode(0, WINDOWING_MODE_UNDEFINED, true));
         assertSecurityException(expectCallable,
-                () -> mTestService.moveTaskToStack(0, INVALID_STACK_ID, true));
+                () -> mService.moveTaskToStack(0, INVALID_STACK_ID, true));
         assertSecurityException(expectCallable,
-                () -> mTestService.setTaskWindowingModeSplitScreenPrimary(0,
+                () -> mService.setTaskWindowingModeSplitScreenPrimary(0,
                         SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT, true, true, new Rect(), true));
-        assertSecurityException(expectCallable, () -> mTestService.dismissSplitScreenMode(true));
-        assertSecurityException(expectCallable, () -> mTestService.dismissPip(true, 0));
+        assertSecurityException(expectCallable, () -> mService.dismissSplitScreenMode(true));
+        assertSecurityException(expectCallable, () -> mService.dismissPip(true, 0));
         assertSecurityException(expectCallable,
-                () -> mTestService.moveTopActivityToPinnedStack(INVALID_STACK_ID, new Rect()));
+                () -> mService.moveTopActivityToPinnedStack(INVALID_STACK_ID, new Rect()));
         assertSecurityException(expectCallable,
-                () -> mTestService.resizeStack(INVALID_STACK_ID, new Rect(), true, true, true, 0));
+                () -> mService.resizeStack(INVALID_STACK_ID, new Rect(), true, true, true, 0));
         assertSecurityException(expectCallable,
-                () -> mTestService.resizeDockedStack(new Rect(), new Rect(), new Rect(), new Rect(),
+                () -> mService.resizeDockedStack(new Rect(), new Rect(), new Rect(), new Rect(),
                         new Rect()));
         assertSecurityException(expectCallable,
-                () -> mTestService.resizePinnedStack(new Rect(), new Rect()));
-        assertSecurityException(expectCallable, () -> mTestService.getAllStackInfos());
+                () -> mService.resizePinnedStack(new Rect(), new Rect()));
+        assertSecurityException(expectCallable, () -> mService.getAllStackInfos());
         assertSecurityException(expectCallable,
-                () -> mTestService.getStackInfo(WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_UNDEFINED));
+                () -> mService.getStackInfo(WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_UNDEFINED));
         assertSecurityException(expectCallable, () -> {
             try {
-                mTestService.getFocusedStackInfo();
+                mService.getFocusedStackInfo();
             } catch (RemoteException e) {
                 // Ignore
             }
         });
         assertSecurityException(expectCallable,
-                () -> mTestService.moveTasksToFullscreenStack(INVALID_STACK_ID, true));
+                () -> mService.moveTasksToFullscreenStack(INVALID_STACK_ID, true));
         assertSecurityException(expectCallable,
-                () -> mTestService.startActivityFromRecents(0, new Bundle()));
-        assertSecurityException(expectCallable, () -> mTestService.getTaskSnapshot(0, true));
-        assertSecurityException(expectCallable, () -> mTestService.registerTaskStackListener(null));
+                () -> mService.startActivityFromRecents(0, new Bundle()));
+        assertSecurityException(expectCallable, () -> mService.getTaskSnapshot(0, true));
+        assertSecurityException(expectCallable, () -> mService.registerTaskStackListener(null));
         assertSecurityException(expectCallable,
-                () -> mTestService.unregisterTaskStackListener(null));
-        assertSecurityException(expectCallable, () -> mTestService.getTaskDescription(0));
-        assertSecurityException(expectCallable, () -> mTestService.cancelTaskWindowTransition(0));
-        assertSecurityException(expectCallable, () -> mTestService.startRecentsActivity(null, null,
+                () -> mService.unregisterTaskStackListener(null));
+        assertSecurityException(expectCallable, () -> mService.getTaskDescription(0));
+        assertSecurityException(expectCallable, () -> mService.cancelTaskWindowTransition(0));
+        assertSecurityException(expectCallable, () -> mService.startRecentsActivity(null, null,
                 null));
-        assertSecurityException(expectCallable, () -> mTestService.cancelRecentsAnimation(true));
-        assertSecurityException(expectCallable, () -> mTestService.stopAppSwitches());
-        assertSecurityException(expectCallable, () -> mTestService.resumeAppSwitches());
+        assertSecurityException(expectCallable, () -> mService.cancelRecentsAnimation(true));
+        assertSecurityException(expectCallable, () -> mService.stopAppSwitches());
+        assertSecurityException(expectCallable, () -> mService.resumeAppSwitches());
     }
 
     private void testGetTasksApis(boolean expectCallable) {
-        mTestService.getRecentTasks(MAX_VALUE, 0, TEST_USER_0_ID);
-        mTestService.getTasks(MAX_VALUE);
+        mService.getRecentTasks(MAX_VALUE, 0, TEST_USER_0_ID);
+        mService.getTasks(MAX_VALUE);
         if (expectCallable) {
             assertTrue(mRecentTasks.mLastAllowed);
             assertTrue(mRunningTasks.mLastAllowed);
@@ -1072,10 +1081,9 @@
     }
 
     private TaskBuilder createTaskBuilder(String packageName, String className) {
-        return new TaskBuilder(mTestService.mStackSupervisor)
+        return new TaskBuilder(mService.mStackSupervisor)
                 .setComponent(new ComponentName(packageName, className))
                 .setStack(mStack)
-                .setTaskId(sLastTaskId++)
                 .setUserId(TEST_USER_0_ID);
     }
 
@@ -1140,68 +1148,6 @@
         }
     }
 
-    private class MyTestActivityTaskManagerService extends TestActivityTaskManagerService {
-        MyTestActivityTaskManagerService(Context context) {
-            super(context);
-        }
-
-        @Override
-        protected RecentTasks createRecentTasks() {
-            return spy(new TestRecentTasks(this, mTaskPersister));
-        }
-
-        @Override
-        protected ActivityStackSupervisor createStackSupervisor() {
-            if (mTestStackSupervisor == null) {
-                mTestStackSupervisor = new MyTestActivityStackSupervisor(this, mH.getLooper());
-            }
-            return mTestStackSupervisor;
-        }
-
-        @Override
-        void createDefaultDisplay() {
-            super.createDefaultDisplay();
-            mDisplay = mRootActivityContainer.getActivityDisplay(DEFAULT_DISPLAY);
-            mOtherDisplay = TestActivityDisplay.create(mTestStackSupervisor, DEFAULT_DISPLAY + 1);
-            mSingleTaskDisplay = TestActivityDisplay.create(mTestStackSupervisor,
-                    DEFAULT_DISPLAY + 2);
-            mSingleTaskDisplay.setDisplayToSingleTaskInstance();
-            mRootActivityContainer.addChild(mOtherDisplay, ActivityDisplay.POSITION_TOP);
-            mRootActivityContainer.addChild(mDisplay, ActivityDisplay.POSITION_TOP);
-            mRootActivityContainer.addChild(mSingleTaskDisplay, ActivityDisplay.POSITION_TOP);
-        }
-    }
-
-    private class MyTestActivityStackSupervisor extends TestActivityStackSupervisor {
-        MyTestActivityStackSupervisor(ActivityTaskManagerService service, Looper looper) {
-            super(service, looper);
-        }
-
-        @Override
-        RunningTasks createRunningTasks() {
-            mRunningTasks = new TestRunningTasks();
-            return mRunningTasks;
-        }
-    }
-
-    private static class MyTestActivityStack extends TestActivityStack {
-        private ActivityDisplay mDisplay = null;
-
-        MyTestActivityStack(ActivityDisplay display, ActivityStackSupervisor supervisor) {
-            super(display, sLastStackId++, supervisor, WINDOWING_MODE_FULLSCREEN,
-                    ACTIVITY_TYPE_STANDARD, true /* onTop */, false /* createActivity */);
-            mDisplay = display;
-        }
-
-        @Override
-        ActivityDisplay getDisplay() {
-            if (mDisplay != null) {
-                return mDisplay;
-            }
-            return super.getDisplay();
-        }
-    }
-
     private static class CallbacksRecorder implements Callbacks {
         public final ArrayList<TaskRecord> mAdded = new ArrayList<>();
         public final ArrayList<TaskRecord> mTrimmed = new ArrayList<>();
diff --git a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java
index f5a1d75..9ca0180 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java
@@ -30,6 +30,7 @@
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.verifyNoMoreInteractions;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.when;
+import static com.android.server.wm.ActivityStackSupervisor.ON_TOP;
 import static com.android.server.wm.RecentsAnimationController.REORDER_KEEP_IN_PLACE;
 import static com.android.server.wm.RecentsAnimationController.REORDER_MOVE_TO_ORIGINAL_POSITION;
 
@@ -132,8 +133,14 @@
     @Test
     public void testIncludedApps_expectTargetAndVisible() {
         mWm.setRecentsAnimationController(mController);
-        final AppWindowToken homeAppWindow = createAppWindowToken(mDisplayContent,
-                WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME);
+        final ActivityStack homStack = mDisplayContent.mAcitvityDisplay.getOrCreateStack(
+                WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, ON_TOP);
+        final AppWindowToken homeAppWindow =
+                new ActivityTestsBase.ActivityBuilder(mWm.mAtmService)
+                        .setStack(homStack)
+                        .setCreateTask(true)
+                        .build()
+                        .mAppWindowToken;
         final AppWindowToken appWindow = createAppWindowToken(mDisplayContent,
                 WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
         final AppWindowToken hiddenAppWindow = createAppWindowToken(mDisplayContent,
@@ -169,7 +176,7 @@
 
         // Simulate the app transition finishing
         mController.mAppTransitionListener.onAppTransitionStartingLocked(0, 0, 0, 0);
-        verify(mAnimationCallbacks).onAnimationFinished(REORDER_KEEP_IN_PLACE, true, false);
+        verify(mAnimationCallbacks).onAnimationFinished(REORDER_KEEP_IN_PLACE, false);
     }
 
     @Test
@@ -201,7 +208,7 @@
         spyOn(mController.mRecentScreenshotAnimator.mAnimatable);
         mController.mRecentScreenshotAnimator.cancelAnimation();
         verify(mController.mRecentScreenshotAnimator.mAnimatable).onAnimationLeashLost(any());
-        verify(mAnimationCallbacks).onAnimationFinished(REORDER_KEEP_IN_PLACE, true, false);
+        verify(mAnimationCallbacks).onAnimationFinished(REORDER_KEEP_IN_PLACE, false);
     }
 
     @Test
diff --git a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java
index 0e119e3..dcc295c 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java
@@ -74,8 +74,9 @@
     @Before
     public void setUp() throws Exception {
         mRecentsAnimationController = mock(RecentsAnimationController.class);
-        doReturn(mRecentsAnimationController).when(
-                mService.mWindowManager).getRecentsAnimationController();
+        mService.mWindowManager.setRecentsAnimationController(mRecentsAnimationController);
+        doNothing().when(mService.mWindowManager).initializeRecentsAnimation(
+                anyInt(), any(), any(), anyInt(), any());
         doReturn(true).when(mService.mWindowManager).canStartRecentsAnimation();
 
         final RecentTasks recentTasks = mService.getRecentTasks();
@@ -107,16 +108,25 @@
         assertTrue(recentActivity.visible);
 
         // Simulate the animation is cancelled without changing the stack order.
-        recentsAnimation.onAnimationFinished(REORDER_KEEP_IN_PLACE, true /* runSychronously */,
-                false /* sendUserLeaveHint */);
+        recentsAnimation.onAnimationFinished(REORDER_KEEP_IN_PLACE, false /* sendUserLeaveHint */);
         // The non-top recents activity should be invisible by the restored launch-behind state.
         assertFalse(recentActivity.visible);
     }
 
     @Test
     public void testPreloadRecentsActivity() {
-        // Ensure that the fake recent component can be resolved by the recents intent.
-        mockTaskRecordFactory(builder -> builder.setComponent(mRecentsComponent));
+        final ActivityDisplay defaultDisplay = mRootActivityContainer.getDefaultDisplay();
+        final ActivityStack homeStack =
+                defaultDisplay.getStack(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME);
+        defaultDisplay.positionChildAtTop(homeStack, false /* includingParents */);
+        ActivityRecord topRunningHomeActivity = homeStack.topRunningActivityLocked();
+        if (topRunningHomeActivity == null) {
+            topRunningHomeActivity = new ActivityBuilder(mService)
+                    .setStack(homeStack)
+                    .setCreateTask(true)
+                    .build();
+        }
+
         ActivityInfo aInfo = new ActivityInfo();
         aInfo.applicationInfo = new ApplicationInfo();
         aInfo.applicationInfo.uid = 10001;
@@ -204,6 +214,13 @@
         ActivityStack homeStack = display.getHomeStack();
         // Assume the home activity support recents.
         ActivityRecord targetActivity = homeStack.getTopActivity();
+        if (targetActivity == null) {
+            targetActivity = new ActivityBuilder(mService)
+                    .setCreateTask(true)
+                    .setStack(homeStack)
+                    .build();
+        }
+
         // Put another home activity in home stack.
         ActivityRecord anotherHomeActivity = new ActivityBuilder(mService)
                 .setComponent(new ComponentName(mContext.getPackageName(), "Home2"))
@@ -226,13 +243,12 @@
 
         anotherHomeActivity.moveFocusableActivityToTop("launchAnotherHome");
         // The current top activity is not the recents so the animation should be canceled.
-        verify(mService.mWindowManager, times(1)).cancelRecentsAnimationSynchronously(
+        verify(mService.mWindowManager, times(1)).cancelRecentsAnimation(
                 eq(REORDER_KEEP_IN_PLACE), any() /* reason */);
 
         // The test uses mocked RecentsAnimationController so we have to invoke the callback
         // manually to simulate the flow.
-        recentsAnimation.onAnimationFinished(REORDER_KEEP_IN_PLACE, true /* runSychronously */,
-                false /* sendUserLeaveHint */);
+        recentsAnimation.onAnimationFinished(REORDER_KEEP_IN_PLACE, false /* sendUserLeaveHint */);
         // We should restore the launch-behind of the original target activity.
         assertFalse(targetActivity.mLaunchTaskBehind);
     }
@@ -269,7 +285,7 @@
         fullscreenStack.moveToFront("Activity start");
 
         // Ensure that the recents animation was canceled by cancelAnimationSynchronously().
-        verify(mService.mWindowManager, times(1)).cancelRecentsAnimationSynchronously(
+        verify(mService.mWindowManager, times(1)).cancelRecentsAnimation(
                 eq(REORDER_KEEP_IN_PLACE), any());
 
         // Assume recents animation already started, set a state that cancel recents animation
@@ -314,7 +330,7 @@
         fullscreenStack.remove();
 
         // Ensure that the recents animation was NOT canceled
-        verify(mService.mWindowManager, times(0)).cancelRecentsAnimationSynchronously(
+        verify(mService.mWindowManager, times(0)).cancelRecentsAnimation(
                 eq(REORDER_KEEP_IN_PLACE), any());
         verify(mRecentsAnimationController, times(0)).setCancelOnNextTransitionStart();
     }
diff --git a/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java
index d4f24f9..539a79c 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java
@@ -97,7 +97,7 @@
      */
     @Test
     public void testRestoringInvalidTask() {
-        ((TestActivityDisplay) mRootActivityContainer.getDefaultDisplay()).removeAllTasks();
+        mRootActivityContainer.getDefaultDisplay().removeAllTasks();
         TaskRecord task = mRootActivityContainer.anyTaskForId(0 /*taskId*/,
                 MATCH_TASK_IN_STACKS_OR_RECENT_TASKS_AND_RESTORE, null, false /* onTop */);
         assertNull(task);
@@ -304,21 +304,23 @@
      */
     @Test
     public void testResizeDockedStackForSplitScreenPrimary() {
-        final Rect taskSize = new Rect(0, 0, 600, 600);
+        final Rect taskSize = new Rect(0, 0, 1000, 1000);
         final Rect stackSize = new Rect(0, 0, 300, 300);
 
         // Create primary split-screen stack with a task.
-        final ActivityStack primaryStack = mRootActivityContainer.getDefaultDisplay()
-                .createStack(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD,
-                        true /* onTop */);
-        final TaskRecord task = new TaskBuilder(mSupervisor).setStack(primaryStack).build();
+        final ActivityStack primaryStack = new StackBuilder(mRootActivityContainer)
+                .setActivityType(ACTIVITY_TYPE_STANDARD)
+                .setWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY)
+                .setOnTop(true)
+                .build();
+        final TaskRecord task = primaryStack.topTask();
 
         // Resize dock stack.
         mService.resizeDockedStack(stackSize, taskSize, null, null, null);
 
         // Verify dock stack & its task bounds if is equal as resized result.
-        assertEquals(primaryStack.getBounds(), stackSize);
-        assertEquals(task.getBounds(), taskSize);
+        assertEquals(stackSize, primaryStack.getBounds());
+        assertEquals(taskSize, task.getBounds());
     }
 
     /**
@@ -328,8 +330,9 @@
     public void testFindTaskToMoveToFrontWhenRecentsOnTop() {
         // Create stack/task on default display.
         final ActivityDisplay display = mRootActivityContainer.getDefaultDisplay();
-        final TestActivityStack targetStack = (TestActivityStack) new StackBuilder(
-                mRootActivityContainer).setOnTop(false).build();
+        final ActivityStack targetStack = new StackBuilder(mRootActivityContainer)
+                .setOnTop(false)
+                .build();
         final TaskRecord targetTask = targetStack.getChildAt(0);
 
         // Create Recents on top of the display.
@@ -505,12 +508,10 @@
         mockResolveSecondaryHomeActivity();
 
         // Create secondary displays.
-        final TestActivityDisplay secondDisplay = spy(createNewActivityDisplay());
+        final TestActivityDisplay secondDisplay = createNewActivityDisplay();
         mRootActivityContainer.addChild(secondDisplay, POSITION_TOP);
         doReturn(true).when(secondDisplay).supportsSystemDecorations();
 
-        // Create mock tasks and other necessary mocks.
-        mockTaskRecordFactory();
         doReturn(true).when(mRootActivityContainer)
                 .ensureVisibilityAndConfig(any(), anyInt(), anyBoolean(), anyBoolean());
         doReturn(true).when(mRootActivityContainer).canStartHomeOnDisplay(
@@ -621,7 +622,6 @@
         info.applicationInfo.packageName = "android";
         info.name = ResolverActivity.class.getName();
         doReturn(info).when(mRootActivityContainer).resolveHomeActivity(anyInt(), any());
-        mockTaskRecordFactory();
 
         mRootActivityContainer.startHomeOnDisplay(0 /* userId */, "test", DEFAULT_DISPLAY);
         final ActivityRecord resolverActivity = mRootActivityContainer.topRunningActivity();
diff --git a/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java
index f51ce13..db105dd 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java
@@ -96,7 +96,7 @@
                     mWm.getDefaultDisplayContentLocked().getWindowingMode());
 
             mWm.mIsPc = true;
-            mWm.mSupportsFreeformWindowManagement = true;
+            mWm.mAtmService.mSupportsFreeformWindowManagement = true;
 
             mWm.mRoot.onSettingsRetrieved();
 
diff --git a/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java b/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java
index df7c9a4..1ad0e00 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java
@@ -16,6 +16,9 @@
 
 package com.android.server.wm;
 
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+import static android.os.Process.THREAD_PRIORITY_DEFAULT;
 import static android.testing.DexmakerShareClassLoaderRule.runWithDexmakerShareClassLoader;
 import static android.view.Display.DEFAULT_DISPLAY;
 
@@ -25,67 +28,91 @@
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyBoolean;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyInt;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyString;
-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.eq;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.nullable;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.spy;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
 
 import android.app.ActivityManagerInternal;
 import android.app.AppOpsManager;
+import android.app.usage.UsageStatsManagerInternal;
 import android.content.BroadcastReceiver;
 import android.content.ContentResolver;
 import android.content.Context;
 import android.content.IntentFilter;
+import android.content.pm.IPackageManager;
+import android.content.pm.PackageManagerInternal;
 import android.database.ContentObserver;
+import android.hardware.display.DisplayManager;
 import android.hardware.display.DisplayManagerInternal;
 import android.net.Uri;
 import android.os.Handler;
+import android.os.Looper;
+import android.os.PowerManager;
 import android.os.PowerManagerInternal;
 import android.os.PowerSaveState;
 import android.os.UserHandle;
-import android.view.Display;
+import android.provider.DeviceConfig;
 import android.view.InputChannel;
+import android.view.Surface;
+import android.view.SurfaceControl;
 
 import com.android.dx.mockito.inline.extended.StaticMockitoSession;
+import com.android.server.AnimationThread;
+import com.android.server.DisplayThread;
 import com.android.server.LocalServices;
 import com.android.server.LockGuard;
+import com.android.server.ServiceThread;
 import com.android.server.Watchdog;
+import com.android.server.am.ActivityManagerService;
+import com.android.server.appop.AppOpsService;
+import com.android.server.display.color.ColorDisplayService;
 import com.android.server.input.InputManagerService;
+import com.android.server.policy.PermissionPolicyInternal;
 import com.android.server.policy.WindowManagerPolicy;
-import com.android.server.wm.utils.MockTracker;
+import com.android.server.statusbar.StatusBarManagerInternal;
+import com.android.server.uri.UriGrantsManagerInternal;
 
 import org.junit.rules.TestRule;
 import org.junit.runner.Description;
 import org.junit.runners.model.Statement;
-import org.mockito.invocation.InvocationOnMock;
+import org.mockito.Mockito;
 import org.mockito.quality.Strictness;
 
+import java.io.File;
 import java.util.concurrent.atomic.AtomicBoolean;
 
 /**
- * JUnit test rule to create a mock {@link WindowManagerService} instance for tests.
+ * JUnit test rule to correctly setting up system services like {@link WindowManagerService}
+ * and {@link ActivityTaskManagerService} for tests.
  */
 public class SystemServicesTestRule implements TestRule {
 
     private static final String TAG = SystemServicesTestRule.class.getSimpleName();
 
+    static int sNextDisplayId = DEFAULT_DISPLAY + 100;
+    static int sNextTaskId = 100;
+
     private final AtomicBoolean mCurrentMessagesProcessed = new AtomicBoolean(false);
 
-    private MockTracker mMockTracker;
+    private Context mContext;
     private StaticMockitoSession mMockitoSession;
-    private WindowManagerService mWindowManagerService;
-    private TestWindowManagerPolicy mWindowManagerPolicy;
-
-    /** {@link MockTracker} to track mocks created by {@link SystemServicesTestRule}. */
-    private static class Tracker extends MockTracker {
-        // This empty extended class is necessary since Mockito distinguishes a listener by it
-        // class.
-    }
+    ServiceThread mHandlerThread;
+    private ActivityManagerService mAmService;
+    private ActivityTaskManagerService mAtmService;
+    private WindowManagerService mWmService;
+    private TestWindowManagerPolicy mWMPolicy;
+    private WindowState.PowerManagerWrapper mPowerManagerWrapper;
+    private InputManagerService mImService;
+    /**
+     * Spied {@link SurfaceControl.Transaction} class than can be used to verify calls.
+     */
+    SurfaceControl.Transaction mTransaction;
 
     @Override
     public Statement apply(Statement base, Description description) {
@@ -103,8 +130,6 @@
     }
 
     private void setUp() {
-        mMockTracker = new Tracker();
-
         mMockitoSession = mockitoSession()
                 .spyStatic(LocalServices.class)
                 .mockStatic(LockGuard.class)
@@ -112,101 +137,220 @@
                 .strictness(Strictness.LENIENT)
                 .startMocking();
 
+        setUpSystemCore();
+        setUpLocalServices();
+        setUpActivityTaskManagerService();
+        setUpWindowManagerService();
+    }
+
+    private void setUpSystemCore() {
+        mHandlerThread = new ServiceThread(
+                "WmTestsThread", THREAD_PRIORITY_DEFAULT, true /* allowIo */);
+        mHandlerThread.start();
+
         doReturn(mock(Watchdog.class)).when(Watchdog::getInstance);
 
-        final Context context = getInstrumentation().getTargetContext();
-        spyOn(context);
+        mContext = getInstrumentation().getTargetContext();
+        spyOn(mContext);
 
-        doReturn(null).when(context)
+        doReturn(null).when(mContext)
                 .registerReceiver(nullable(BroadcastReceiver.class), any(IntentFilter.class));
-        doReturn(null).when(context)
+        doReturn(null).when(mContext)
                 .registerReceiverAsUser(any(BroadcastReceiver.class), any(UserHandle.class),
                         any(IntentFilter.class), nullable(String.class), nullable(Handler.class));
 
-        final ContentResolver contentResolver = context.getContentResolver();
+        final ContentResolver contentResolver = mContext.getContentResolver();
         spyOn(contentResolver);
         doNothing().when(contentResolver)
                 .registerContentObserver(any(Uri.class), anyBoolean(), any(ContentObserver.class),
                         anyInt());
+    }
 
-        final AppOpsManager appOpsManager = mock(AppOpsManager.class);
-        doReturn(appOpsManager).when(context)
-                .getSystemService(eq(Context.APP_OPS_SERVICE));
+    private void setUpLocalServices() {
+        // Tear down any local services just in case.
+        tearDownLocalServices();
 
+        // UriGrantsManagerInternal
+        final UriGrantsManagerInternal ugmi = mock(UriGrantsManagerInternal.class);
+        LocalServices.addService(UriGrantsManagerInternal.class, ugmi);
+
+        // AppOpsManager
+        final AppOpsManager aom = mock(AppOpsManager.class);
+        doReturn(aom).when(mContext).getSystemService(eq(Context.APP_OPS_SERVICE));
+
+        // DisplayManagerInternal
         final DisplayManagerInternal dmi = mock(DisplayManagerInternal.class);
         doReturn(dmi).when(() -> LocalServices.getService(eq(DisplayManagerInternal.class)));
 
+        // ColorDisplayServiceInternal
+        final ColorDisplayService.ColorDisplayServiceInternal cds =
+                mock(ColorDisplayService.ColorDisplayServiceInternal.class);
+        doReturn(cds).when(() -> LocalServices.getService(
+                eq(ColorDisplayService.ColorDisplayServiceInternal.class)));
+
+        final UsageStatsManagerInternal usmi = mock(UsageStatsManagerInternal.class);
+        LocalServices.addService(UsageStatsManagerInternal.class, usmi);
+
+        // PackageManagerInternal
+        final PackageManagerInternal packageManagerInternal = mock(PackageManagerInternal.class);
+        LocalServices.addService(PackageManagerInternal.class, packageManagerInternal);
+        doReturn(false).when(packageManagerInternal).isPermissionsReviewRequired(
+                anyString(), anyInt());
+        doReturn(null).when(packageManagerInternal).getDefaultHomeActivity(anyInt());
+
+        // PowerManagerInternal
         final PowerManagerInternal pmi = mock(PowerManagerInternal.class);
         final PowerSaveState state = new PowerSaveState.Builder().build();
         doReturn(state).when(pmi).getLowPowerState(anyInt());
         doReturn(pmi).when(() -> LocalServices.getService(eq(PowerManagerInternal.class)));
 
-        final ActivityManagerInternal ami = mock(ActivityManagerInternal.class);
-        doReturn(ami).when(() -> LocalServices.getService(eq(ActivityManagerInternal.class)));
+        // PermissionPolicyInternal
+        final PermissionPolicyInternal ppi = mock(PermissionPolicyInternal.class);
+        LocalServices.addService(PermissionPolicyInternal.class, ppi);
+        doReturn(true).when(ppi).checkStartActivity(any(), anyInt(), any());
 
-        final ActivityTaskManagerInternal atmi = mock(ActivityTaskManagerInternal.class);
-        doAnswer((InvocationOnMock invocationOnMock) -> {
-            final Runnable runnable = invocationOnMock.getArgument(0);
-            if (runnable != null) {
-                runnable.run();
-            }
-            return null;
-        }).when(atmi).notifyKeyguardFlagsChanged(nullable(Runnable.class), anyInt());
-        doReturn(atmi).when(() -> LocalServices.getService(eq(ActivityTaskManagerInternal.class)));
-
-        final InputManagerService ims = mock(InputManagerService.class);
+        // InputManagerService
+        mImService = mock(InputManagerService.class);
         // InputChannel is final and can't be mocked.
         final InputChannel[] input = InputChannel.openInputChannelPair(TAG_WM);
         if (input != null && input.length > 1) {
-            doReturn(input[1]).when(ims).monitorInput(anyString(), anyInt());
+            doReturn(input[1]).when(mImService).monitorInput(anyString(), anyInt());
         }
 
-        final ActivityTaskManagerService atms = mock(ActivityTaskManagerService.class);
-        final TaskChangeNotificationController taskChangeNotificationController = mock(
-                TaskChangeNotificationController.class);
-        doReturn(taskChangeNotificationController).when(atms).getTaskChangeNotificationController();
-        final WindowManagerGlobalLock wmLock = new WindowManagerGlobalLock();
-        doReturn(wmLock).when(atms).getGlobalLock();
+        // StatusBarManagerInternal
+        final StatusBarManagerInternal sbmi = mock(StatusBarManagerInternal.class);
+        doReturn(sbmi).when(() -> LocalServices.getService(eq(StatusBarManagerInternal.class)));
+    }
 
-        mWindowManagerPolicy = new TestWindowManagerPolicy(this::getWindowManagerService);
-        mWindowManagerService = WindowManagerService.main(
-                context, ims, false, false, mWindowManagerPolicy, atms, StubTransaction::new);
+    private void setUpActivityTaskManagerService() {
+        // ActivityManagerService
+        mAmService = new ActivityManagerService(
+                new AMTestInjector(mContext, mHandlerThread), mHandlerThread);
+        spyOn(mAmService);
+        doReturn(mock(IPackageManager.class)).when(mAmService).getPackageManager();
+        doNothing().when(mAmService).grantEphemeralAccessLocked(
+                anyInt(), any(), anyInt(), anyInt());
 
-        mWindowManagerService.onInitReady();
+        // ActivityManagerInternal
+        final ActivityManagerInternal amInternal = mAmService.mInternal;
+        spyOn(amInternal);
+        doNothing().when(amInternal).trimApplications();
+        doNothing().when(amInternal).updateCpuStats();
+        doNothing().when(amInternal).updateOomAdj();
+        doNothing().when(amInternal).updateBatteryStats(any(), anyInt(), anyInt(), anyBoolean());
+        doNothing().when(amInternal).updateActivityUsageStats(
+                any(), anyInt(), anyInt(), any(), any());
+        doNothing().when(amInternal).startProcess(
+                any(), any(), anyBoolean(), anyBoolean(), any(), any());
+        doNothing().when(amInternal).updateOomLevelsForDisplay(anyInt());
+        LocalServices.addService(ActivityManagerInternal.class, amInternal);
 
-        final Display display = mWindowManagerService.mDisplayManager.getDisplay(DEFAULT_DISPLAY);
-        // Display creation is driven by the ActivityManagerService via
-        // ActivityStackSupervisor. We emulate those steps here.
-        DisplayContent displayContent = mWindowManagerService.mRoot
-                .createDisplayContent(display, mock(ActivityDisplay.class));
-        displayContent.reconfigureDisplayLocked();
+        mAtmService = new TestActivityTaskManagerService(mContext, mAmService);
+        LocalServices.addService(ActivityTaskManagerInternal.class, mAtmService.getAtmInternal());
+    }
 
-        mMockTracker.stopTracking();
+    private void setUpWindowManagerService() {
+        mPowerManagerWrapper = mock(WindowState.PowerManagerWrapper.class);
+        mWMPolicy = new TestWindowManagerPolicy(this::getWindowManagerService,
+                mPowerManagerWrapper);
+        mWmService = WindowManagerService.main(
+                mContext, mImService, false, false, mWMPolicy, mAtmService, StubTransaction::new);
+        spyOn(mWmService);
+
+        // Setup factory classes to prevent calls to native code.
+        mTransaction = spy(StubTransaction.class);
+        // Return a spied Transaction class than can be used to verify calls.
+        mWmService.mTransactionFactory = () -> mTransaction;
+        // Return a SurfaceControl.Builder class that creates mocked SurfaceControl instances.
+        mWmService.mSurfaceBuilderFactory = (unused) -> new MockSurfaceControlBuilder();
+        // Return mocked Surface instances.
+        mWmService.mSurfaceFactory = () -> mock(Surface.class);
+        mWmService.mSurfaceAnimationRunner = new SurfaceAnimationRunner(
+                null, null, mTransaction, mWmService.mPowerManagerInternal);
+
+        mWmService.onInitReady();
+        mAmService.setWindowManager(mWmService);
+        mWmService.mDisplayEnabled = true;
+        mWmService.mDisplayReady = true;
+        // Set configuration for default display
+        mWmService.getDefaultDisplayContentLocked().reconfigureDisplayLocked();
+
+        // Mock root, some default display, and home stack.
+        spyOn(mWmService.mRoot);
+        final ActivityDisplay display = mAtmService.mRootActivityContainer.getDefaultDisplay();
+        spyOn(display);
+        spyOn(display.mDisplayContent);
+        final ActivityStack homeStack = display.getStack(
+                WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME);
+        spyOn(homeStack);
+        spyOn(homeStack.mTaskStack);
     }
 
     private void tearDown() {
         waitUntilWindowManagerHandlersIdle();
-        removeLocalServices();
-        mWindowManagerService = null;
-        mWindowManagerPolicy = null;
+        // Unregister display listener from root to avoid issues with subsequent tests.
+        mContext.getSystemService(DisplayManager.class)
+                .unregisterDisplayListener(mAtmService.mRootActivityContainer);
+        // ProptertiesChangesListener is registered in the constructor of WindowManagerService to
+        // a static object, so we need to clean it up in tearDown(), even though we didn't set up
+        // in tests.
+        DeviceConfig.removeOnPropertiesChangedListener(mWmService.mPropertiesChangedListener);
+        mWmService = null;
+        mWMPolicy = null;
+        mPowerManagerWrapper = null;
+
+        tearDownLocalServices();
+        tearDownSystemCore();
+
+        // Needs to explicitly dispose current static threads because there could be messages
+        // scheduled at a later time, and all mocks are invalid when it's executed.
+        DisplayThread.dispose();
+        AnimationThread.dispose();
+        // Reset priority booster because animation thread has been changed.
+        WindowManagerService.sThreadPriorityBooster = new WindowManagerThreadPriorityBooster();
+
+        Mockito.framework().clearInlineMocks();
+    }
+
+    private void tearDownSystemCore() {
         if (mMockitoSession != null) {
             mMockitoSession.finishMocking();
             mMockitoSession = null;
         }
 
-        if (mMockTracker != null) {
-            mMockTracker.close();
-            mMockTracker = null;
+        if (mHandlerThread != null) {
+            // Make sure there are no running messages and then quit the thread so the next test
+            // won't be affected.
+            mHandlerThread.getThreadHandler().runWithScissors(mHandlerThread::quit,
+                    0 /* timeout */);
         }
     }
 
-    private static void removeLocalServices() {
+    private static void tearDownLocalServices() {
+        LocalServices.removeServiceForTest(DisplayManagerInternal.class);
+        LocalServices.removeServiceForTest(PowerManagerInternal.class);
+        LocalServices.removeServiceForTest(ActivityManagerInternal.class);
+        LocalServices.removeServiceForTest(ActivityTaskManagerInternal.class);
         LocalServices.removeServiceForTest(WindowManagerInternal.class);
         LocalServices.removeServiceForTest(WindowManagerPolicy.class);
+        LocalServices.removeServiceForTest(PackageManagerInternal.class);
+        LocalServices.removeServiceForTest(UriGrantsManagerInternal.class);
+        LocalServices.removeServiceForTest(PermissionPolicyInternal.class);
+        LocalServices.removeServiceForTest(ColorDisplayService.ColorDisplayServiceInternal.class);
+        LocalServices.removeServiceForTest(UsageStatsManagerInternal.class);
+        LocalServices.removeServiceForTest(StatusBarManagerInternal.class);
     }
 
     WindowManagerService getWindowManagerService() {
-        return mWindowManagerService;
+        return mWmService;
+    }
+
+    ActivityTaskManagerService getActivityTaskManagerService() {
+        return mAtmService;
+    }
+
+    WindowState.PowerManagerWrapper getPowerManagerWrapper() {
+        return mPowerManagerWrapper;
     }
 
     void cleanupWindowManagerHandlers() {
@@ -229,6 +373,7 @@
         waitHandlerIdle(wm.mH);
         waitHandlerIdle(wm.mAnimationHandler);
         waitHandlerIdle(SurfaceAnimationThread.getHandler());
+        waitHandlerIdle(mHandlerThread.getThreadHandler());
     }
 
     private void waitHandlerIdle(Handler handler) {
@@ -251,4 +396,121 @@
             }
         }
     }
+
+    protected class TestActivityTaskManagerService extends ActivityTaskManagerService {
+        // ActivityStackSupervisor may be created more than once while setting up AMS and ATMS.
+        // We keep the reference in order to prevent creating it twice.
+        ActivityStackSupervisor mTestStackSupervisor;
+
+        TestActivityTaskManagerService(Context context, ActivityManagerService ams) {
+            super(context);
+            spyOn(this);
+
+            mSupportsMultiWindow = true;
+            mSupportsMultiDisplay = true;
+            mSupportsSplitScreenMultiWindow = true;
+            mSupportsFreeformWindowManagement = true;
+            mSupportsPictureInPicture = true;
+
+            doReturn(mock(IPackageManager.class)).when(this).getPackageManager();
+            // allow background activity starts by default
+            doReturn(true).when(this).isBackgroundActivityStartsEnabled();
+            doNothing().when(this).updateCpuStats();
+
+            // AppOpsService
+            final AppOpsService aos = mock(AppOpsService.class);
+            doReturn(aos).when(this).getAppOpsService();
+            // Make sure permission checks aren't overridden.
+            doReturn(AppOpsManager.MODE_DEFAULT)
+                    .when(aos).noteOperation(anyInt(), anyInt(), anyString());
+
+            setUsageStatsManager(LocalServices.getService(UsageStatsManagerInternal.class));
+            ams.mActivityTaskManager = this;
+            ams.mAtmInternal = mInternal;
+            onActivityManagerInternalAdded();
+            initialize(
+                    ams.mIntentFirewall, ams.mPendingIntentController, mHandlerThread.getLooper());
+            spyOn(getLifecycleManager());
+            spyOn(getLockTaskController());
+            spyOn(getTaskChangeNotificationController());
+            initRootActivityContainerMocks();
+        }
+
+        void initRootActivityContainerMocks() {
+            spyOn(mRootActivityContainer);
+            // Invoked during {@link ActivityStack} creation.
+            doNothing().when(mRootActivityContainer).updateUIDsPresentOnDisplay();
+            // Always keep things awake.
+            doReturn(true).when(mRootActivityContainer).hasAwakeDisplay();
+            // Called when moving activity to pinned stack.
+            doNothing().when(mRootActivityContainer).ensureActivitiesVisible(any(), anyInt(),
+                    anyBoolean());
+        }
+
+        @Override
+        int handleIncomingUser(int callingPid, int callingUid, int userId, String name) {
+            return userId;
+        }
+
+        @Override
+        protected ActivityStackSupervisor createStackSupervisor() {
+            if (mTestStackSupervisor == null) {
+                mTestStackSupervisor = new TestActivityStackSupervisor(this, mH.getLooper());
+            }
+            return mTestStackSupervisor;
+        }
+    }
+
+    /**
+     * An {@link ActivityStackSupervisor} which stubs out certain methods that depend on
+     * setup not available in the test environment. Also specifies an injector for
+     */
+    protected class TestActivityStackSupervisor extends ActivityStackSupervisor {
+
+        TestActivityStackSupervisor(ActivityTaskManagerService service, Looper looper) {
+            super(service, looper);
+            spyOn(this);
+
+            // Do not schedule idle that may touch methods outside the scope of the test.
+            doNothing().when(this).scheduleIdleLocked();
+            doNothing().when(this).scheduleIdleTimeoutLocked(any());
+            // unit test version does not handle launch wake lock
+            doNothing().when(this).acquireLaunchWakelock();
+            doReturn(mock(KeyguardController.class)).when(this).getKeyguardController();
+
+            mLaunchingActivityWakeLock = mock(PowerManager.WakeLock.class);
+
+            initialize();
+        }
+    }
+
+    // TODO: Can we just mock this?
+    private static class AMTestInjector extends ActivityManagerService.Injector {
+        private ServiceThread mHandlerThread;
+
+        AMTestInjector(Context context, ServiceThread handlerThread) {
+            super(context);
+            mHandlerThread = handlerThread;
+        }
+
+        @Override
+        public Context getContext() {
+            return getInstrumentation().getTargetContext();
+        }
+
+        @Override
+        public AppOpsService getAppOpsService(File file, Handler handler) {
+            return null;
+        }
+
+        @Override
+        public Handler getUiHandler(ActivityManagerService service) {
+            return mHandlerThread.getThreadHandler();
+        }
+
+        @Override
+        public boolean isNetworkRestrictedForUid(int uid) {
+            return false;
+        }
+    }
 }
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java
index 2cebebf..e6c9b9f 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java
@@ -249,6 +249,20 @@
     }
 
     @Test
+    public void testInheritsFreeformModeFromSourceOnFullscreenDisplay() {
+        final TestActivityDisplay fullscreenDisplay = createNewActivityDisplay(
+                WINDOWING_MODE_FULLSCREEN);
+        final ActivityRecord source = createSourceActivity(fullscreenDisplay);
+        source.setWindowingMode(WINDOWING_MODE_FREEFORM);
+
+        assertEquals(RESULT_CONTINUE, mTarget.onCalculate(/* task */ null, /* layout */ null,
+                mActivity, source, /* options */ null, mCurrent, mResult));
+
+        assertEquivalentWindowingMode(WINDOWING_MODE_FREEFORM, mResult.mWindowingMode,
+                WINDOWING_MODE_FULLSCREEN);
+    }
+
+    @Test
     public void testKeepsPictureInPictureLaunchModeInOptions() {
         final TestActivityDisplay freeformDisplay = createNewActivityDisplay(
                 WINDOWING_MODE_FREEFORM);
@@ -571,6 +585,23 @@
     }
 
     @Test
+    public void testRespectsLaunchBoundsWithFreeformSourceOnFullscreenDisplay() {
+        final TestActivityDisplay fullscreenDisplay = createNewActivityDisplay(
+                WINDOWING_MODE_FULLSCREEN);
+        final ActivityRecord source = createSourceActivity(fullscreenDisplay);
+        source.setWindowingMode(WINDOWING_MODE_FREEFORM);
+
+        final ActivityOptions options = ActivityOptions.makeBasic();
+        final Rect expected = new Rect(0, 0, 150, 150);
+        options.setLaunchBounds(expected);
+
+        assertEquals(RESULT_CONTINUE, mTarget.onCalculate(/* task */ null, /* layout */ null,
+                mActivity, source, options, mCurrent, mResult));
+
+        assertEquals(expected, mResult.mBounds);
+    }
+
+    @Test
     public void testNonEmptyLayoutBoundsRespectsGravityWithEmptySize_LeftGravity() {
         final TestActivityDisplay freeformDisplay = createNewActivityDisplay(
                 WINDOWING_MODE_FREEFORM);
@@ -1277,17 +1308,18 @@
     }
 
     private ActivityRecord createSourceActivity(TestActivityDisplay display) {
-        final TestActivityStack stack = display.createStack(display.getWindowingMode(),
+        final ActivityStack stack = display.createStack(display.getWindowingMode(),
                 ACTIVITY_TYPE_STANDARD, true);
         return new ActivityBuilder(mService).setStack(stack).setCreateTask(true).build();
     }
 
     private void addFreeformTaskTo(TestActivityDisplay display, Rect bounds) {
-        final TestActivityStack stack = display.createStack(display.getWindowingMode(),
+        final ActivityStack stack = display.createStack(display.getWindowingMode(),
                 ACTIVITY_TYPE_STANDARD, true);
         stack.setWindowingMode(WINDOWING_MODE_FREEFORM);
         final TaskRecord task = new TaskBuilder(mSupervisor).setStack(stack).build();
-        task.setBounds(bounds);
+        // Just work around the unnecessary adjustments for bounds.
+        task.getWindowConfiguration().setBounds(bounds);
     }
 
     private void assertEquivalentWindowingMode(int expected, int actual, int parentWindowingMode) {
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java
index a0302f6..c83401b 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java
@@ -28,14 +28,19 @@
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
 import static android.util.DisplayMetrics.DENSITY_DEFAULT;
 import static android.view.Surface.ROTATION_0;
+import static android.view.Surface.ROTATION_90;
 
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.eq;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
+import static com.android.server.policy.WindowManagerPolicy.USER_ROTATION_FREE;
+import static com.android.server.wm.DisplayRotation.FIXED_TO_USER_ROTATION_ENABLED;
 import static com.android.server.wm.WindowContainer.POSITION_TOP;
 
+import static com.google.common.truth.Truth.assertThat;
+
 import static org.hamcrest.Matchers.not;
 import static org.hamcrest.Matchers.sameInstance;
 import static org.junit.Assert.assertEquals;
@@ -64,6 +69,7 @@
 import android.util.Xml;
 import android.view.DisplayInfo;
 
+import androidx.test.filters.FlakyTest;
 import androidx.test.filters.MediumTest;
 
 import com.android.internal.app.IVoiceInteractor;
@@ -102,6 +108,7 @@
     public void setUp() throws Exception {
         TaskRecord.setTaskRecordFactory(null);
         mParentBounds = new Rect(10 /*left*/, 30 /*top*/, 80 /*right*/, 60 /*bottom*/);
+        removeGlobalMinSizeRestriction();
     }
 
     @Test
@@ -165,6 +172,7 @@
 
     /** Ensures that bounds on freeform stacks are not clipped. */
     @Test
+    @FlakyTest(bugId = 137879065)
     public void testAppBounds_FreeFormBounds() {
         final Rect freeFormBounds = new Rect(mParentBounds);
         freeFormBounds.offset(10, 10);
@@ -174,6 +182,7 @@
 
     /** Ensures that fully contained bounds are not clipped. */
     @Test
+    @FlakyTest(bugId = 137879065)
     public void testAppBounds_ContainedBounds() {
         final Rect insetBounds = new Rect(mParentBounds);
         insetBounds.inset(5, 5, 5, 5);
@@ -182,6 +191,7 @@
     }
 
     @Test
+    @FlakyTest(bugId = 137879065)
     public void testFitWithinBounds() {
         final Rect parentBounds = new Rect(10, 10, 200, 200);
         ActivityDisplay display = mService.mRootActivityContainer.getDefaultDisplay();
@@ -221,6 +231,7 @@
 
     /** Tests that the task bounds adjust properly to changes between FULLSCREEN and FREEFORM */
     @Test
+    @FlakyTest(bugId = 137879065)
     public void testBoundsOnModeChangeFreeformToFullscreen() {
         ActivityDisplay display = mService.mRootActivityContainer.getDefaultDisplay();
         ActivityStack stack = new StackBuilder(mRootActivityContainer).setDisplay(display)
@@ -248,18 +259,6 @@
     }
 
     /**
-     * This is a temporary hack to trigger an onConfigurationChange at the task level after an
-     * orientation is requested. Normally this is done by the onDescendentOrientationChanged call
-     * up the WM hierarchy, but since the WM hierarchy is mocked out, it doesn't happen here.
-     * TODO: remove this when we either get a WM hierarchy or when hierarchies are merged.
-     */
-    private void setActivityRequestedOrientation(ActivityRecord activity, int orientation) {
-        activity.setRequestedOrientation(orientation);
-        ConfigurationContainer taskRecord = activity.getParent();
-        taskRecord.onConfigurationChanged(taskRecord.getParent().getConfiguration());
-    }
-
-    /**
      * Tests that a task with forced orientation has orientation-consistent bounds within the
      * parent.
      */
@@ -268,49 +267,48 @@
         final Rect fullScreenBounds = new Rect(0, 0, 1920, 1080);
         final Rect fullScreenBoundsPort = new Rect(0, 0, 1080, 1920);
         DisplayInfo info = new DisplayInfo();
+        mService.mContext.getDisplay().getDisplayInfo(info);
         info.logicalWidth = fullScreenBounds.width();
         info.logicalHeight = fullScreenBounds.height();
         ActivityDisplay display = addNewActivityDisplayAt(info, POSITION_TOP);
         assertTrue(mRootActivityContainer.getActivityDisplay(display.mDisplayId) != null);
-        // Override display orientation. Normally this is available via DisplayContent, but DC
-        // is mocked-out.
-        display.getRequestedOverrideConfiguration().orientation =
-                Configuration.ORIENTATION_LANDSCAPE;
-        display.onRequestedOverrideConfigurationChanged(
-                display.getRequestedOverrideConfiguration());
+        // Fix the display orientation to landscape which is the natural rotation (0) for the test
+        // display.
+        final DisplayRotation dr = display.mDisplayContent.getDisplayRotation();
+        dr.setFixedToUserRotation(FIXED_TO_USER_ROTATION_ENABLED);
+        dr.setUserRotation(USER_ROTATION_FREE, ROTATION_0);
+
         ActivityStack stack = new StackBuilder(mRootActivityContainer)
                 .setWindowingMode(WINDOWING_MODE_FULLSCREEN).setDisplay(display).build();
         TaskRecord task = stack.getChildAt(0);
         ActivityRecord root = task.getTopActivity();
-        assertEquals(root, task.getTopActivity());
 
         assertEquals(fullScreenBounds, task.getBounds());
 
         // Setting app to fixed portrait fits within parent
-        setActivityRequestedOrientation(root, SCREEN_ORIENTATION_PORTRAIT);
+        root.setRequestedOrientation(SCREEN_ORIENTATION_PORTRAIT);
         assertEquals(root, task.getRootActivity());
         assertEquals(SCREEN_ORIENTATION_PORTRAIT, task.getRootActivity().getOrientation());
-        assertTrue(task.getBounds().width() < task.getBounds().height());
+        assertThat(task.getBounds().width()).isLessThan(task.getBounds().height());
         assertEquals(fullScreenBounds.height(), task.getBounds().height());
 
         // Top activity gets used
         ActivityRecord top = new ActivityBuilder(mService).setTask(task).setStack(stack).build();
         assertEquals(top, task.getTopActivity());
-        setActivityRequestedOrientation(top, SCREEN_ORIENTATION_LANDSCAPE);
-        assertTrue(task.getBounds().width() > task.getBounds().height());
+        top.setRequestedOrientation(SCREEN_ORIENTATION_LANDSCAPE);
+        assertThat(task.getBounds().width()).isGreaterThan(task.getBounds().height());
         assertEquals(task.getBounds().width(), fullScreenBounds.width());
 
         // Setting app to unspecified restores
-        setActivityRequestedOrientation(top, SCREEN_ORIENTATION_UNSPECIFIED);
+        top.setRequestedOrientation(SCREEN_ORIENTATION_UNSPECIFIED);
         assertEquals(fullScreenBounds, task.getBounds());
 
         // Setting app to fixed landscape and changing display
-        setActivityRequestedOrientation(top, SCREEN_ORIENTATION_LANDSCAPE);
-        // simulate display orientation changing (normally done via DisplayContent)
-        display.getRequestedOverrideConfiguration().orientation =
-                Configuration.ORIENTATION_PORTRAIT;
-        display.setBounds(fullScreenBoundsPort);
-        assertTrue(task.getBounds().width() > task.getBounds().height());
+        top.setRequestedOrientation(SCREEN_ORIENTATION_LANDSCAPE);
+        // Fix the display orientation to portrait which is 90 degrees for the test display.
+        dr.setUserRotation(USER_ROTATION_FREE, ROTATION_90);
+
+        assertThat(task.getBounds().width()).isGreaterThan(task.getBounds().height());
         assertEquals(fullScreenBoundsPort.width(), task.getBounds().width());
 
         // in FREEFORM, no constraint
@@ -323,7 +321,7 @@
 
         // FULLSCREEN letterboxes bounds
         stack.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
-        assertTrue(task.getBounds().width() > task.getBounds().height());
+        assertThat(task.getBounds().width()).isGreaterThan(task.getBounds().height());
         assertEquals(fullScreenBoundsPort.width(), task.getBounds().width());
 
         // FREEFORM restores bounds as before
@@ -335,6 +333,7 @@
     public void testIgnoresForcedOrientationWhenParentHandles() {
         final Rect fullScreenBounds = new Rect(0, 0, 1920, 1080);
         DisplayInfo info = new DisplayInfo();
+        mService.mContext.getDisplay().getDisplayInfo(info);
         info.logicalWidth = fullScreenBounds.width();
         info.logicalHeight = fullScreenBounds.height();
         ActivityDisplay display = addNewActivityDisplayAt(info, POSITION_TOP);
@@ -355,7 +354,7 @@
 
         // Setting app to fixed portrait fits within parent, but TaskRecord shouldn't adjust the
         // bounds because its parent says it will handle it at a later time.
-        setActivityRequestedOrientation(root, SCREEN_ORIENTATION_PORTRAIT);
+        root.setRequestedOrientation(SCREEN_ORIENTATION_PORTRAIT);
         assertEquals(root, task.getRootActivity());
         assertEquals(SCREEN_ORIENTATION_PORTRAIT, task.getRootActivity().getOrientation());
         assertEquals(fullScreenBounds, task.getBounds());
@@ -646,7 +645,7 @@
         final ActivityRecord activity0 = task0.getChildAt(0);
 
         final TaskRecord task1 = getTestTask();
-        final ActivityRecord activity1 = task0.getChildAt(0);
+        final ActivityRecord activity1 = task1.getChildAt(0);
 
         assertEquals(task0.taskId,
                 ActivityRecord.getTaskForActivityLocked(activity0.appToken, false /* onlyRoot */));
diff --git a/services/tests/wmtests/src/com/android/server/wm/TestActivityDisplay.java b/services/tests/wmtests/src/com/android/server/wm/TestActivityDisplay.java
new file mode 100644
index 0000000..c143969
--- /dev/null
+++ b/services/tests/wmtests/src/com/android/server/wm/TestActivityDisplay.java
@@ -0,0 +1,101 @@
+/*
+ * 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.wm;
+
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+import static android.view.Display.DEFAULT_DISPLAY;
+import static android.view.DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS;
+
+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.spyOn;
+
+import android.hardware.display.DisplayManagerGlobal;
+import android.view.Display;
+import android.view.DisplayInfo;
+
+class TestActivityDisplay extends ActivityDisplay {
+    private final ActivityStackSupervisor mSupervisor;
+
+    static TestActivityDisplay create(ActivityStackSupervisor supervisor) {
+        return create(supervisor, SystemServicesTestRule.sNextDisplayId++);
+    }
+
+    static TestActivityDisplay create(ActivityStackSupervisor supervisor, DisplayInfo info) {
+        return create(supervisor, SystemServicesTestRule.sNextDisplayId++, info);
+    }
+
+    static TestActivityDisplay create(ActivityStackSupervisor supervisor, int displayId) {
+        final DisplayInfo info = new DisplayInfo();
+        supervisor.mService.mContext.getDisplay().getDisplayInfo(info);
+        return create(supervisor, displayId, info);
+    }
+
+    static TestActivityDisplay create(ActivityStackSupervisor supervisor, int displayId,
+            DisplayInfo info) {
+        if (displayId == DEFAULT_DISPLAY) {
+            synchronized (supervisor.mService.mGlobalLock) {
+                return new TestActivityDisplay(supervisor,
+                        supervisor.mRootActivityContainer.mDisplayManager.getDisplay(displayId));
+            }
+        }
+        final Display display = new Display(DisplayManagerGlobal.getInstance(), displayId,
+                info, DEFAULT_DISPLAY_ADJUSTMENTS);
+
+        synchronized (supervisor.mService.mGlobalLock) {
+            return new TestActivityDisplay(supervisor, display);
+        }
+    }
+
+    private TestActivityDisplay(ActivityStackSupervisor supervisor, Display display) {
+        super(supervisor.mService.mRootActivityContainer, display);
+        // Normally this comes from display-properties as exposed by WM. Without that, just
+        // hard-code to FULLSCREEN for tests.
+        setWindowingMode(WINDOWING_MODE_FULLSCREEN);
+        mSupervisor = supervisor;
+        spyOn(this);
+        spyOn(mDisplayContent);
+        doAnswer(invocation -> {
+            // Bypass all the rotation animation and display freezing stuff for testing and just
+            // set the rotation we want for the display
+            final DisplayContent dc = mDisplayContent;
+            final int oldRotation = dc.getRotation();
+            final int rotation = dc.getDisplayRotation().rotationForOrientation(
+                    dc.getLastOrientation(), oldRotation);
+            if (oldRotation == rotation) {
+                return false;
+            }
+            dc.setLayoutNeeded();
+            dc.setRotation(rotation);
+            return true;
+        }).when(mDisplayContent).updateRotationUnchecked(anyBoolean());
+    }
+
+    @SuppressWarnings("TypeParameterUnusedInFormals")
+    @Override
+    ActivityStack createStackUnchecked(int windowingMode, int activityType,
+            int stackId, boolean onTop) {
+        return new ActivityTestsBase.StackBuilder(mSupervisor.mRootActivityContainer)
+                .setDisplay(this)
+                .setWindowingMode(windowingMode)
+                .setActivityType(activityType)
+                .setStackId(stackId)
+                .setOnTop(onTop)
+                .setCreateActivity(false)
+                .build();
+    }
+}
diff --git a/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java b/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java
index 2e5ce69..bb89446 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java
@@ -40,12 +40,14 @@
 import com.android.internal.policy.IKeyguardDismissCallback;
 import com.android.internal.policy.IShortcutService;
 import com.android.server.policy.WindowManagerPolicy;
+import com.android.server.wm.WindowState.PowerManagerWrapper;
 
 import java.io.PrintWriter;
 import java.util.function.Supplier;
 
 class TestWindowManagerPolicy implements WindowManagerPolicy {
     private final Supplier<WindowManagerService> mWmSupplier;
+    private final PowerManagerWrapper mPowerManagerWrapper;
 
     int mRotationToReport = 0;
     boolean mKeyguardShowingAndNotOccluded = false;
@@ -53,8 +55,10 @@
 
     private Runnable mRunnableWhenAddingSplashScreen;
 
-    TestWindowManagerPolicy(Supplier<WindowManagerService> wmSupplier) {
+    TestWindowManagerPolicy(Supplier<WindowManagerService> wmSupplier,
+            PowerManagerWrapper powerManagerWrapper) {
         mWmSupplier = wmSupplier;
+        mPowerManagerWrapper = powerManagerWrapper;
     }
 
     @Override
@@ -121,7 +125,7 @@
             doReturn(mock(IBinder.class)).when(iWindow).asBinder();
             window = WindowTestsBase.createWindow(null, TYPE_APPLICATION_STARTING, atoken,
                     "Starting window", 0 /* ownerId */, false /* internalWindows */, wm,
-                    mock(Session.class), iWindow);
+                    mock(Session.class), iWindow, mPowerManagerWrapper);
             atoken.startingWindow = window;
         }
         if (mRunnableWhenAddingSplashScreen != null) {
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java
index acfc2ea..921f105 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java
@@ -27,6 +27,7 @@
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.eq;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.never;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.reset;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.spy;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.times;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
@@ -113,6 +114,7 @@
 
     @Test
     public void testAddChildSetsSurfacePosition() {
+        reset(mTransaction);
         try (MockSurfaceBuildingContainer top = new MockSurfaceBuildingContainer(mWm)) {
             WindowContainer child = new WindowContainer(mWm);
             child.setBounds(1, 1, 10, 10);
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowFrameTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowFrameTests.java
index 06bcdf8..60cefe8 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowFrameTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowFrameTests.java
@@ -57,6 +57,7 @@
 
     private final IWindow mIWindow = new TestIWindow();
     private final Rect mEmptyRect = new Rect();
+    private DisplayContent mTestDisplayContent;
 
     static class FrameTestWindowState extends WindowState {
         boolean mDockedResizingForTest = false;
@@ -77,6 +78,9 @@
     @Before
     public void setUp() throws Exception {
         mStubStack = mock(TaskStack.class);
+        DisplayInfo testDisplayInfo = new DisplayInfo(mDisplayInfo);
+        testDisplayInfo.displayCutout = null;
+        mTestDisplayContent = createNewDisplay(testDisplayInfo);
     }
 
     // Do not use this function directly in the tests below. Instead, use more explicit function
@@ -100,6 +104,10 @@
         assertRect(w.getStableInsets(), left, top, right, bottom);
     }
 
+    private void assertFrame(WindowState w, Rect frame) {
+        assertEquals(w.getFrameLw(), frame);
+    }
+
     private void assertFrame(WindowState w, int left, int top, int right, int bottom) {
         assertRect(w.getFrameLw(), left, top, right, bottom);
     }
@@ -380,9 +388,10 @@
     }
 
     @Test
+    @FlakyTest(bugId = 137879065)
     public void testLayoutLetterboxedWindow() {
         // First verify task behavior in multi-window mode.
-        final DisplayInfo displayInfo = mWm.getDefaultDisplayContentLocked().getDisplayInfo();
+        final DisplayInfo displayInfo = mTestDisplayContent.getDisplayInfo();
         final int logicalWidth = displayInfo.logicalWidth;
         final int logicalHeight = displayInfo.logicalHeight;
 
@@ -413,13 +422,14 @@
         final Rect cf = new Rect(xInset, 0, logicalWidth - xInset, logicalHeight);
         Configuration config = new Configuration(w.mAppToken.getRequestedOverrideConfiguration());
         config.windowConfiguration.setBounds(cf);
+        config.windowConfiguration.setAppBounds(cf);
         w.mAppToken.onRequestedOverrideConfigurationChanged(config);
         pf.set(0, 0, logicalWidth, logicalHeight);
         task.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
         task.setBounds(null);
         windowFrames.setFrames(pf, pf, pf, cf, cf, pf, cf, mEmptyRect);
         w.computeFrameLw();
-        assertFrame(w, cf.left, cf.top, cf.right, cf.bottom);
+        assertFrame(w, cf);
         assertContentFrame(w, cf);
         assertContentInset(w, 0, 0, 0, 0);
     }
@@ -483,7 +493,7 @@
         w.mAttrs.gravity = Gravity.LEFT | Gravity.TOP;
         task.setWindowingMode(WINDOWING_MODE_FREEFORM);
 
-        DisplayContent dc = mWm.getDefaultDisplayContentLocked();
+        DisplayContent dc = mTestDisplayContent;
         dc.mInputMethodTarget = w;
         WindowState mockIme = mock(WindowState.class);
         Mockito.doReturn(true).when(mockIme).isVisibleNow();
@@ -537,7 +547,7 @@
         attrs.width = width;
         attrs.height = height;
 
-        AppWindowToken token = createAppWindowToken(mWm.getDefaultDisplayContentLocked(),
+        AppWindowToken token = createAppWindowToken(mTestDisplayContent,
                 WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
 
         FrameTestWindowState ws = new FrameTestWindowState(mWm, mIWindow, token, attrs);
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 36698ea..b731628 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
@@ -346,24 +346,28 @@
         firstWindow.mAttrs.flags |= WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON;
         secondWindow.mAttrs.flags |= WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON;
 
-        reset(sPowerManagerWrapper);
+        final WindowState.PowerManagerWrapper powerManagerWrapper =
+                mSystemServicesTestRule.getPowerManagerWrapper();
+        reset(powerManagerWrapper);
         firstWindow.prepareWindowToDisplayDuringRelayout(false /*wasVisible*/);
-        verify(sPowerManagerWrapper).wakeUp(anyLong(), anyInt(), anyString());
+        verify(powerManagerWrapper).wakeUp(anyLong(), anyInt(), anyString());
 
-        reset(sPowerManagerWrapper);
+        reset(powerManagerWrapper);
         secondWindow.prepareWindowToDisplayDuringRelayout(false /*wasVisible*/);
-        verify(sPowerManagerWrapper).wakeUp(anyLong(), anyInt(), anyString());
+        verify(powerManagerWrapper).wakeUp(anyLong(), anyInt(), anyString());
     }
 
     private void testPrepareWindowToDisplayDuringRelayout(WindowState appWindow,
             boolean expectedWakeupCalled, boolean expectedCurrentLaunchCanTurnScreenOn) {
-        reset(sPowerManagerWrapper);
+        final WindowState.PowerManagerWrapper powerManagerWrapper =
+                mSystemServicesTestRule.getPowerManagerWrapper();
+        reset(powerManagerWrapper);
         appWindow.prepareWindowToDisplayDuringRelayout(false /* wasVisible */);
 
         if (expectedWakeupCalled) {
-            verify(sPowerManagerWrapper).wakeUp(anyLong(), anyInt(), anyString());
+            verify(powerManagerWrapper).wakeUp(anyLong(), anyInt(), anyString());
         } else {
-            verify(sPowerManagerWrapper, never()).wakeUp(anyLong(), anyInt(), anyString());
+            verify(powerManagerWrapper, never()).wakeUp(anyLong(), anyInt(), anyString());
         }
         // If wakeup is expected to be called, the currentLaunchCanTurnScreenOn should be false
         // because the state will be consumed.
@@ -517,13 +521,19 @@
 
     @Test
     public void testGetTransformationMatrix() {
+        final int PARENT_WINDOW_OFFSET = 1;
+        final int DISPLAY_IN_PARENT_WINDOW_OFFSET = 2;
+        final int WINDOW_OFFSET = 3;
+        final float OFFSET_SUM =
+                PARENT_WINDOW_OFFSET + DISPLAY_IN_PARENT_WINDOW_OFFSET + WINDOW_OFFSET;
+
         synchronized (mWm.mGlobalLock) {
             final WindowState win0 = createWindow(null, TYPE_APPLICATION, "win0");
-            win0.getFrameLw().offsetTo(1, 0);
 
             final DisplayContent dc = createNewDisplay();
+            win0.getFrameLw().offsetTo(PARENT_WINDOW_OFFSET, 0);
             dc.reparentDisplayContent(win0, win0.getSurfaceControl());
-            dc.updateLocation(win0, 2, 0);
+            dc.updateLocation(win0, DISPLAY_IN_PARENT_WINDOW_OFFSET, 0);
 
             final float[] values = new float[9];
             final Matrix matrix = new Matrix();
@@ -531,12 +541,12 @@
             final WindowState win1 = createWindow(null, TYPE_APPLICATION, dc, "win1");
             win1.mHasSurface = true;
             win1.mSurfaceControl = mock(SurfaceControl.class);
-            win1.getFrameLw().offsetTo(3, 0);
+            win1.getFrameLw().offsetTo(WINDOW_OFFSET, 0);
             win1.updateSurfacePosition(t);
             win1.getTransformationMatrix(values, matrix);
 
             matrix.getValues(values);
-            assertEquals(6f, values[Matrix.MTRANS_X], 0f);
+            assertEquals(OFFSET_SUM, values[Matrix.MTRANS_X], 0f);
             assertEquals(0f, values[Matrix.MTRANS_Y], 0f);
         }
     }
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
index dc461d1..d1cf1c3 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
@@ -20,8 +20,6 @@
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
 import static android.os.Process.SYSTEM_UID;
-import static android.view.Display.DEFAULT_DISPLAY;
-import static android.view.DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS;
 import static android.view.View.VISIBLE;
 import static android.view.WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW;
 import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
@@ -38,33 +36,27 @@
 
 import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
 
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.spy;
+import static com.android.server.wm.ActivityDisplay.POSITION_TOP;
 
 import static org.mockito.Mockito.mock;
 
-
 import android.content.Context;
 import android.content.res.Configuration;
-import android.hardware.display.DisplayManagerGlobal;
 import android.testing.DexmakerShareClassLoaderRule;
 import android.util.Log;
 import android.view.Display;
 import android.view.DisplayInfo;
 import android.view.IWindow;
-import android.view.Surface;
 import android.view.SurfaceControl.Transaction;
 import android.view.WindowManager;
 
 import com.android.server.AttributeCache;
-import com.android.server.wm.utils.MockTracker;
 
 import org.junit.After;
-import org.junit.AfterClass;
 import org.junit.Before;
 import org.junit.BeforeClass;
 import org.junit.Rule;
 
-import java.io.IOException;
 import java.util.HashSet;
 import java.util.LinkedList;
 
@@ -79,10 +71,6 @@
     WindowManagerService mWm;
     private final IWindow mIWindow = new TestIWindow();
     private Session mMockSession;
-    // The default display is removed in {@link #setUp} and then we iterate over all displays to
-    // make sure we don't collide with any existing display. If we run into no other display, the
-    // added display should be treated as default. This cannot be the default display
-    private static int sNextDisplayId = DEFAULT_DISPLAY + 1;
     static int sNextStackId = 1000;
 
     /** Non-default display. */
@@ -99,8 +87,6 @@
     WindowState mChildAppWindowBelow;
     HashSet<WindowState> mCommonWindows;
 
-    private MockTracker mMockTracker;
-
     /**
      * Spied {@link Transaction} class than can be used to verify calls.
      */
@@ -112,49 +98,27 @@
     @Rule
     public final SystemServicesTestRule mSystemServicesTestRule = new SystemServicesTestRule();
 
-    static WindowState.PowerManagerWrapper sPowerManagerWrapper;
-
     @BeforeClass
     public static void setUpOnceBase() {
         AttributeCache.init(getInstrumentation().getTargetContext());
-
-        sPowerManagerWrapper = mock(WindowState.PowerManagerWrapper.class);
-    }
-
-    @AfterClass
-    public static void tearDownOnceBase() throws IOException {
-        sPowerManagerWrapper = null;
     }
 
     @Before
     public void setUpBase() {
-        mMockTracker = new MockTracker();
-
         // If @Before throws an exception, the error isn't logged. This will make sure any failures
         // in the set up are clear. This can be removed when b/37850063 is fixed.
         try {
             mMockSession = mock(Session.class);
-            mTransaction = spy(StubTransaction.class);
 
             final Context context = getInstrumentation().getTargetContext();
 
             mWm = mSystemServicesTestRule.getWindowManagerService();
-
-            // Setup factory classes to prevent calls to native code.
-
-            // Return a spied Transaction class than can be used to verify calls.
-            mWm.mTransactionFactory = () -> mTransaction;
-            // Return a SurfaceControl.Builder class that creates mocked SurfaceControl instances.
-            mWm.mSurfaceBuilderFactory = (unused) -> new MockSurfaceControlBuilder();
-            // Return mocked Surface instances.
-            mWm.mSurfaceFactory = () -> mock(Surface.class);
+            mTransaction = mSystemServicesTestRule.mTransaction;
 
             beforeCreateDisplay();
 
             context.getDisplay().getDisplayInfo(mDisplayInfo);
             mDisplayContent = createNewDisplay();
-            mWm.mDisplayEnabled = true;
-            mWm.mDisplayReady = true;
 
             // Set-up some common windows.
             mCommonWindows = new HashSet<>();
@@ -211,12 +175,6 @@
                     nonCommonWindows.pollLast().removeImmediately();
                 }
 
-                for (int i = mWm.mRoot.mChildren.size() - 1; i >= 0; --i) {
-                    final DisplayContent displayContent = mWm.mRoot.mChildren.get(i);
-                    if (!displayContent.isDefaultDisplay) {
-                        displayContent.removeImmediately();
-                    }
-                }
                 // Remove app transition & window freeze timeout callbacks to prevent unnecessary
                 // actions after test.
                 mWm.getDefaultDisplayContentLocked().mAppTransition
@@ -230,11 +188,7 @@
         } catch (Exception e) {
             Log.e(TAG, "Failed to tear down test", e);
             throw e;
-        } finally {
-            mMockTracker.close();
-            mMockTracker = null;
         }
-
     }
 
     private WindowState createCommonWindow(WindowState parent, int type, String name) {
@@ -356,12 +310,13 @@
     WindowState createWindow(WindowState parent, int type, WindowToken token, String name,
             int ownerId, boolean ownerCanAddInternalSystemWindow) {
         return createWindow(parent, type, token, name, ownerId, ownerCanAddInternalSystemWindow,
-                mWm, mMockSession, mIWindow);
+                mWm, mMockSession, mIWindow, mSystemServicesTestRule.getPowerManagerWrapper());
     }
 
     static WindowState createWindow(WindowState parent, int type, WindowToken token,
             String name, int ownerId, boolean ownerCanAddInternalSystemWindow,
-            WindowManagerService service, Session session, IWindow iWindow) {
+            WindowManagerService service, Session session, IWindow iWindow,
+            WindowState.PowerManagerWrapper powerManagerWrapper) {
         synchronized (service.mGlobalLock) {
             final WindowManager.LayoutParams attrs = new WindowManager.LayoutParams(type);
             attrs.setTitle(name);
@@ -369,7 +324,7 @@
             final WindowState w = new WindowState(service, session, iWindow, token, parent,
                     OP_NONE,
                     0, attrs, VISIBLE, ownerId, ownerCanAddInternalSystemWindow,
-                    sPowerManagerWrapper);
+                    powerManagerWrapper);
             // TODO: Probably better to make this call in the WindowState ctor to avoid errors with
             // adding it to the token...
             token.addWindow(w);
@@ -408,13 +363,11 @@
     }
 
     /** Creates a {@link DisplayContent} and adds it to the system. */
-    DisplayContent createNewDisplay(DisplayInfo displayInfo) {
-        final int displayId = sNextDisplayId++;
-        final Display display = new Display(DisplayManagerGlobal.getInstance(), displayId,
-                displayInfo, DEFAULT_DISPLAY_ADJUSTMENTS);
-        synchronized (mWm.mGlobalLock) {
-            return new DisplayContent(display, mWm, mock(ActivityDisplay.class));
-        }
+    DisplayContent createNewDisplay(DisplayInfo info) {
+        final ActivityDisplay display =
+                TestActivityDisplay.create(mWm.mAtmService.mStackSupervisor, info);
+        mWm.mAtmService.mRootActivityContainer.addChild(display, POSITION_TOP);
+        return display.mDisplayContent;
     }
 
     /**
@@ -428,17 +381,7 @@
         DisplayInfo displayInfo = new DisplayInfo();
         displayInfo.copyFrom(mDisplayInfo);
         displayInfo.state = displayState;
-        final int displayId = sNextDisplayId++;
-        final Display display = new Display(DisplayManagerGlobal.getInstance(), displayId,
-                displayInfo, DEFAULT_DISPLAY_ADJUSTMENTS);
-        synchronized (mWm.mGlobalLock) {
-            // Display creation is driven by DisplayWindowController via ActivityStackSupervisor.
-            // We skip those steps here.
-            final ActivityDisplay mockAd = mock(ActivityDisplay.class);
-            final DisplayContent displayContent = mWm.mRoot.createDisplayContent(display, mockAd);
-            displayContent.reconfigureDisplayLocked();
-            return displayContent;
-        }
+        return createNewDisplay(displayInfo);
     }
 
     /** Creates a {@link com.android.server.wm.WindowTestUtils.TestWindowState} */
diff --git a/services/tests/wmtests/src/com/android/server/wm/utils/MockTracker.java b/services/tests/wmtests/src/com/android/server/wm/utils/MockTracker.java
index a6e675a..7f09482 100644
--- a/services/tests/wmtests/src/com/android/server/wm/utils/MockTracker.java
+++ b/services/tests/wmtests/src/com/android/server/wm/utils/MockTracker.java
@@ -89,7 +89,7 @@
 
         for (final Object mock : mMocks.keySet()) {
             if (MockUtil.isMock(mock)) {
-                Mockito.reset(mock);
+                mMockitoFramework.clearInlineMock(mock);
             }
         }
         mMocks.clear();
diff --git a/telephony/java/android/provider/Telephony.java b/telephony/java/android/provider/Telephony.java
index 43fb304..42a5501 100644
--- a/telephony/java/android/provider/Telephony.java
+++ b/telephony/java/android/provider/Telephony.java
@@ -692,7 +692,7 @@
         }
 
         /**
-         * Contains all sent text-based SMS messages in the SMS app.
+         * Contains all draft text-based SMS messages in the SMS app.
          */
         public static final class Draft implements BaseColumns, TextBasedSmsColumns {
 
@@ -808,7 +808,15 @@
         }
 
         /**
-         * Contains all sent text-based SMS messages in the SMS app.
+         * Contains a view of SMS conversations (also referred to as threads). This is similar to
+         * {@link Threads}, but only includes SMS messages and columns relevant to SMS
+         * conversations.
+         * <p>
+         * Note that this view ignores any information about MMS messages, it is a
+         * view of conversations as if MMS messages did not exist at all. This means that all
+         * relevant information, such as snippets and message count, will ignore any MMS messages
+         * that might be in the same thread through other views and present only data based on the
+         * SMS messages in that thread.
          */
         public static final class Conversations
                 implements BaseColumns, TextBasedSmsColumns {
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 60de214..654b54d 100755
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -1026,6 +1026,18 @@
             "call_forwarding_map_non_number_to_voicemail_bool";
 
     /**
+     * When {@code true}, the phone will always tell the IMS stack to keep RTT enabled and
+     * determine on a per-call basis (based on extras from the dialer app) whether a call should be
+     * an RTT call or not.
+     *
+     * When {@code false}, the old behavior is used, where the toggle in accessibility settings is
+     * used to set the IMS stack's RTT enabled state.
+     * @hide
+     */
+    public static final String KEY_IGNORE_RTT_MODE_SETTING_BOOL =
+            "ignore_rtt_mode_setting_bool";
+
+    /**
      * Determines whether conference calls are supported by a carrier.  When {@code true},
      * conference calling is supported, {@code false otherwise}.
      */
@@ -1391,6 +1403,13 @@
             "read_only_apn_fields_string_array";
 
     /**
+     * Default value of APN types field if not specified by user when adding/modifying an APN.
+     * @hide
+     */
+    public static final String KEY_APN_SETTINGS_DEFAULT_APN_TYPES_STRING_ARRAY =
+            "apn_settings_default_apn_types_string_array";
+
+    /**
      * Boolean indicating if intent for emergency call state changes should be broadcast
      * @hide
      */
@@ -2656,25 +2675,34 @@
             "call_waiting_service_class_int";
 
     /**
-     * This configuration allow the system UI to display different 5G icon for different 5G status.
+     * This configuration allow the system UI to display different 5G icon for different 5G
+     * scenario.
      *
-     * There are four 5G status:
+     * There are five 5G scenarios:
      * 1. connected_mmwave: device currently connected to 5G cell as the secondary cell and using
      *    millimeter wave.
      * 2. connected: device currently connected to 5G cell as the secondary cell but not using
      *    millimeter wave.
-     * 3. not_restricted: device camped on a network that has 5G capability(not necessary to connect
-     *    a 5G cell as a secondary cell) and the use of 5G is not restricted.
-     * 4. restricted: device camped on a network that has 5G capability(not necessary to connect a
+     * 3. not_restricted_rrc_idle: device camped on a network that has 5G capability(not necessary
+     *    to connect a 5G cell as a secondary cell) and the use of 5G is not restricted and RRC
+     *    currently in IDLE state.
+     * 4. not_restricted_rrc_con: device camped on a network that has 5G capability(not necessary
+     *    to connect a 5G cell as a secondary cell) and the use of 5G is not restricted and RRC
+     *    currently in CONNECTED state.
+     * 5. restricted: device camped on a network that has 5G capability(not necessary to connect a
      *    5G cell as a secondary cell) but the use of 5G is restricted.
      *
      * The configured string contains multiple key-value pairs separated by comma. For each pair,
      * the key and value is separated by a colon. The key is corresponded to a 5G status above and
      * the value is the icon name. Use "None" as the icon name if no icon should be shown in a
-     * specific 5G status.
+     * specific 5G scenario. If the scenario is "None", config can skip this key and value.
      *
-     * Here is an example of the configuration:
-     * "connected_mmwave:5GPlus,connected:5G,not_restricted:None,restricted:None"
+     * Icon name options: "5G_Plus", "5G".
+     *
+     * Here is an example:
+     * UE want to display 5G_Plus icon for scenario#1, and 5G icon for scenario#2; otherwise no
+     * define.
+     * The configuration is: "connected_mmwave:5G_Plus,connected:5G"
      *
      * @hide
      */
@@ -3131,6 +3159,13 @@
     public static final String KEY_SUPPORT_WPS_OVER_IMS_BOOL =
             "support_wps_over_ims_bool";
 
+    /**
+     * Holds the list of carrier certificate hashes. Note that each carrier has its own certificates
+     * @hide
+     */
+    public static final String KEY_CARRIER_CERTIFICATE_STRING_ARRAY =
+            "carrier_certificate_string_array";
+
     /** The default value for every variable. */
     private final static PersistableBundle sDefaults;
 
@@ -3247,6 +3282,7 @@
         sDefaults.putBoolean(KEY_ALLOW_ADDING_APNS_BOOL, true);
         sDefaults.putStringArray(KEY_READ_ONLY_APN_TYPES_STRING_ARRAY, new String[] {"dun"});
         sDefaults.putStringArray(KEY_READ_ONLY_APN_FIELDS_STRING_ARRAY, null);
+        sDefaults.putStringArray(KEY_APN_SETTINGS_DEFAULT_APN_TYPES_STRING_ARRAY, null);
         sDefaults.putBoolean(KEY_BROADCAST_EMERGENCY_CALL_STATE_CHANGES_BOOL, false);
         sDefaults.putBoolean(KEY_ALWAYS_SHOW_EMERGENCY_ALERT_ONOFF_BOOL, false);
         sDefaults.putBoolean(KEY_DISABLE_SEVERE_WHEN_EXTREME_DISABLED_BOOL, true);
@@ -3290,6 +3326,7 @@
         sDefaults.putInt(KEY_IMS_DTMF_TONE_DELAY_INT, 0);
         sDefaults.putInt(KEY_CDMA_DTMF_TONE_DELAY_INT, 100);
         sDefaults.putBoolean(KEY_CALL_FORWARDING_MAP_NON_NUMBER_TO_VOICEMAIL_BOOL, false);
+        sDefaults.putBoolean(KEY_IGNORE_RTT_MODE_SETTING_BOOL, false);
         sDefaults.putInt(KEY_CDMA_3WAYCALL_FLASH_DELAY_INT , 0);
         sDefaults.putBoolean(KEY_SUPPORT_CONFERENCE_CALL_BOOL, true);
         sDefaults.putBoolean(KEY_SUPPORT_IMS_CONFERENCE_CALL_BOOL, true);
@@ -3509,7 +3546,7 @@
         sDefaults.putBoolean(KEY_USE_CALLER_ID_USSD_BOOL, false);
         sDefaults.putInt(KEY_CALL_WAITING_SERVICE_CLASS_INT, 1 /* SERVICE_CLASS_VOICE */);
         sDefaults.putString(KEY_5G_ICON_CONFIGURATION_STRING,
-                "connected_mmwave:None,connected:5G,not_restricted:None,restricted:None");
+                "connected_mmwave:5G,connected:5G");
         sDefaults.putInt(KEY_5G_ICON_DISPLAY_GRACE_PERIOD_SEC_INT, 0);
         sDefaults.putBoolean(KEY_ASCII_7_BIT_SUPPORT_FOR_LONG_MESSAGE_BOOL, false);
         /* Default value is minimum RSRP level needed for SIGNAL_STRENGTH_GOOD */
@@ -3537,7 +3574,7 @@
         sDefaults.putStringArray(KEY_EMERGENCY_NUMBER_PREFIX_STRING_ARRAY, new String[0]);
         sDefaults.putBoolean(KEY_USE_USIM_BOOL, false);
         sDefaults.putBoolean(KEY_SHOW_WFC_LOCATION_PRIVACY_POLICY_BOOL, false);
-        sDefaults.putBoolean(KEY_AUTO_CANCEL_CS_REJECT_NOTIFICATION, false);
+        sDefaults.putBoolean(KEY_AUTO_CANCEL_CS_REJECT_NOTIFICATION, true);
         sDefaults.putString(KEY_SMART_FORWARDING_CONFIG_COMPONENT_NAME_STRING, "");
         sDefaults.putBoolean(KEY_ALWAYS_SHOW_PRIMARY_SIGNAL_BAR_IN_OPPORTUNISTIC_NETWORK_BOOLEAN,
                 false);
@@ -3552,6 +3589,7 @@
                 });
         sDefaults.putBoolean(KEY_SUPPORT_WPS_OVER_IMS_BOOL, true);
         sDefaults.putAll(Ims.getDefaults());
+        sDefaults.putStringArray(KEY_CARRIER_CERTIFICATE_STRING_ARRAY, null);
     }
 
     /**
diff --git a/telephony/java/android/telephony/CellIdentity.java b/telephony/java/android/telephony/CellIdentity.java
index 258a873..432978d 100644
--- a/telephony/java/android/telephony/CellIdentity.java
+++ b/telephony/java/android/telephony/CellIdentity.java
@@ -17,6 +17,7 @@
 package android.telephony;
 
 import android.annotation.CallSuper;
+import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -61,7 +62,7 @@
         mType = type;
 
         // Only allow INT_MAX if unknown string mcc/mnc
-        if (mcc == null || mcc.matches("^[0-9]{3}$")) {
+        if (mcc == null || isMcc(mcc)) {
             mMccStr = mcc;
         } else if (mcc.isEmpty() || mcc.equals(String.valueOf(Integer.MAX_VALUE))) {
             // If the mccStr is empty or unknown, set it as null.
@@ -73,7 +74,7 @@
             log("invalid MCC format: " + mcc);
         }
 
-        if (mnc == null || mnc.matches("^[0-9]{2,3}$")) {
+        if (mnc == null || isMnc(mnc)) {
             mMncStr = mnc;
         } else if (mnc.isEmpty() || mnc.equals(String.valueOf(Integer.MAX_VALUE))) {
             // If the mncStr is empty or unknown, set it as null.
@@ -262,4 +263,30 @@
         if ((value < rangeMin || value > rangeMax) && value != special) return CellInfo.UNAVAILABLE;
         return value;
     }
+
+    /** @hide */
+    private static boolean isMcc(@NonNull String mcc) {
+        // ensure no out of bounds indexing
+        if (mcc.length() != 3) return false;
+
+        // Character.isDigit allows all unicode digits, not just [0-9]
+        for (int i = 0; i < 3; i++) {
+            if (mcc.charAt(i) < '0' || mcc.charAt(i) > '9') return false;
+        }
+
+        return true;
+    }
+
+    /** @hide */
+    private static boolean isMnc(@NonNull String mnc) {
+        // ensure no out of bounds indexing
+        if (mnc.length() < 2 || mnc.length() > 3) return false;
+
+        // Character.isDigit allows all unicode digits, not just [0-9]
+        for (int i = 0; i < mnc.length(); i++) {
+            if (mnc.charAt(i) < '0' || mnc.charAt(i) > '9') return false;
+        }
+
+        return true;
+    }
 }
diff --git a/telephony/java/android/telephony/NetworkServiceCallback.java b/telephony/java/android/telephony/NetworkServiceCallback.java
index 1c64bcd..89b9665 100644
--- a/telephony/java/android/telephony/NetworkServiceCallback.java
+++ b/telephony/java/android/telephony/NetworkServiceCallback.java
@@ -24,7 +24,6 @@
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
-import java.lang.ref.WeakReference;
 
 /**
  * Network service callback. Object of this class is passed to NetworkServiceProvider upon
@@ -61,11 +60,11 @@
     /** Request failed */
     public static final int RESULT_ERROR_FAILED         = 5;
 
-    private final WeakReference<INetworkServiceCallback> mCallback;
+    private final INetworkServiceCallback mCallback;
 
     /** @hide */
     public NetworkServiceCallback(INetworkServiceCallback callback) {
-        mCallback = new WeakReference<>(callback);
+        mCallback = callback;
     }
 
     /**
@@ -78,15 +77,14 @@
      */
     public void onRequestNetworkRegistrationInfoComplete(int result,
                                                          @Nullable NetworkRegistrationInfo state) {
-        INetworkServiceCallback callback = mCallback.get();
-        if (callback != null) {
+        if (mCallback != null) {
             try {
-                callback.onRequestNetworkRegistrationInfoComplete(result, state);
+                mCallback.onRequestNetworkRegistrationInfoComplete(result, state);
             } catch (RemoteException e) {
                 Rlog.e(mTag, "Failed to onRequestNetworkRegistrationInfoComplete on the remote");
             }
         } else {
-            Rlog.e(mTag, "Weak reference of callback is null.");
+            Rlog.e(mTag, "onRequestNetworkRegistrationInfoComplete callback is null.");
         }
     }
 }
\ No newline at end of file
diff --git a/telephony/java/android/telephony/SubscriptionInfo.java b/telephony/java/android/telephony/SubscriptionInfo.java
index a8491d3..bb2269f 100644
--- a/telephony/java/android/telephony/SubscriptionInfo.java
+++ b/telephony/java/android/telephony/SubscriptionInfo.java
@@ -38,6 +38,7 @@
 import android.util.DisplayMetrics;
 import android.util.Log;
 
+import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.List;
@@ -147,7 +148,14 @@
      * The access rules for this subscription, if it is embedded and defines any.
      */
     @Nullable
-    private UiccAccessRule[] mAccessRules;
+    private UiccAccessRule[] mNativeAccessRules;
+
+    /**
+     * The carrier certificates for this subscription that are saved in carrier configs.
+     * The other carrier certificates are embedded on Uicc and stored as part of mNativeAccessRules.
+     */
+    @Nullable
+    private UiccAccessRule[] mCarrierConfigAccessRules;
 
     /**
      * The string ID of the SIM card. It is the ICCID of the active profile for a UICC card and the
@@ -206,12 +214,12 @@
     public SubscriptionInfo(int id, String iccId, int simSlotIndex, CharSequence displayName,
             CharSequence carrierName, int nameSource, int iconTint, String number, int roaming,
             Bitmap icon, String mcc, String mnc, String countryIso, boolean isEmbedded,
-            @Nullable UiccAccessRule[] accessRules, String cardString) {
+            @Nullable UiccAccessRule[] nativeAccessRules, String cardString) {
         this(id, iccId, simSlotIndex, displayName, carrierName, nameSource, iconTint, number,
-                roaming, icon, mcc, mnc, countryIso, isEmbedded, accessRules, cardString, -1,
+                roaming, icon, mcc, mnc, countryIso, isEmbedded, nativeAccessRules, cardString, -1,
                 false, null, false, TelephonyManager.UNKNOWN_CARRIER_ID,
                 SubscriptionManager.PROFILE_CLASS_DEFAULT,
-                SubscriptionManager.SUBSCRIPTION_TYPE_LOCAL_SIM, null);
+                SubscriptionManager.SUBSCRIPTION_TYPE_LOCAL_SIM, null, null);
     }
 
     /**
@@ -220,12 +228,12 @@
     public SubscriptionInfo(int id, String iccId, int simSlotIndex, CharSequence displayName,
             CharSequence carrierName, int nameSource, int iconTint, String number, int roaming,
             Bitmap icon, String mcc, String mnc, String countryIso, boolean isEmbedded,
-            @Nullable UiccAccessRule[] accessRules, String cardString, boolean isOpportunistic,
-            @Nullable String groupUUID, int carrierId, int profileClass) {
+            @Nullable UiccAccessRule[] nativeAccessRules, String cardString,
+            boolean isOpportunistic, @Nullable String groupUUID, int carrierId, int profileClass) {
         this(id, iccId, simSlotIndex, displayName, carrierName, nameSource, iconTint, number,
-                roaming, icon, mcc, mnc, countryIso, isEmbedded, accessRules, cardString, -1,
+                roaming, icon, mcc, mnc, countryIso, isEmbedded, nativeAccessRules, cardString, -1,
                 isOpportunistic, groupUUID, false, carrierId, profileClass,
-                SubscriptionManager.SUBSCRIPTION_TYPE_LOCAL_SIM, null);
+                SubscriptionManager.SUBSCRIPTION_TYPE_LOCAL_SIM, null, null);
     }
 
     /**
@@ -234,9 +242,10 @@
     public SubscriptionInfo(int id, String iccId, int simSlotIndex, CharSequence displayName,
             CharSequence carrierName, int nameSource, int iconTint, String number, int roaming,
             Bitmap icon, String mcc, String mnc, String countryIso, boolean isEmbedded,
-            @Nullable UiccAccessRule[] accessRules, String cardString, int cardId,
+            @Nullable UiccAccessRule[] nativeAccessRules, String cardString, int cardId,
             boolean isOpportunistic, @Nullable String groupUUID, boolean isGroupDisabled,
-            int carrierId, int profileClass, int subType, @Nullable String groupOwner) {
+            int carrierId, int profileClass, int subType, @Nullable String groupOwner,
+            @Nullable UiccAccessRule[] carrierConfigAccessRules) {
         this.mId = id;
         this.mIccId = iccId;
         this.mSimSlotIndex = simSlotIndex;
@@ -251,7 +260,7 @@
         this.mMnc = mnc;
         this.mCountryIso = countryIso;
         this.mIsEmbedded = isEmbedded;
-        this.mAccessRules = accessRules;
+        this.mNativeAccessRules = nativeAccessRules;
         this.mCardString = cardString;
         this.mCardId = cardId;
         this.mIsOpportunistic = isOpportunistic;
@@ -261,6 +270,7 @@
         this.mProfileClass = profileClass;
         this.mSubscriptionType = subType;
         this.mGroupOwner = groupOwner;
+        this.mCarrierConfigAccessRules = carrierConfigAccessRules;
     }
 
     /**
@@ -541,7 +551,6 @@
      *
      * @param context Context of the application to check.
      * @return whether the app is authorized to manage this subscription per its metadata.
-     * @throws UnsupportedOperationException if this subscription is not embedded.
      * @hide
      * @deprecated - Do not use.
      */
@@ -557,16 +566,13 @@
      * @param context Any context.
      * @param packageName Package name of the app to check.
      * @return whether the app is authorized to manage this subscription per its metadata.
-     * @throws UnsupportedOperationException if this subscription is not embedded.
      * @hide
      * @deprecated - Do not use.
      */
     @Deprecated
     public boolean canManageSubscription(Context context, String packageName) {
-        if (!isEmbedded()) {
-            throw new UnsupportedOperationException("Not an embedded subscription");
-        }
-        if (mAccessRules == null) {
+        List<UiccAccessRule> allAccessRules = getAllAccessRules();
+        if (allAccessRules == null) {
             return false;
         }
         PackageManager packageManager = context.getPackageManager();
@@ -576,7 +582,7 @@
         } catch (PackageManager.NameNotFoundException e) {
             throw new IllegalArgumentException("Unknown package: " + packageName, e);
         }
-        for (UiccAccessRule rule : mAccessRules) {
+        for (UiccAccessRule rule : allAccessRules) {
             if (rule.getCarrierPrivilegeStatus(packageInfo)
                     == TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS) {
                 return true;
@@ -586,17 +592,31 @@
     }
 
     /**
-     * @return the {@link UiccAccessRule}s dictating who is authorized to manage this subscription.
+     * @return the {@link UiccAccessRule}s that are stored in Uicc, dictating who
+     * is authorized to manage this subscription.
+     * TODO and fix it properly in R / master: either deprecate this and have 3 APIs
+     *  native + carrier + all, or have this return all by default.
      * @throws UnsupportedOperationException if this subscription is not embedded.
      * @hide
      */
     @SystemApi
     public @Nullable List<UiccAccessRule> getAccessRules() {
-        if (!isEmbedded()) {
-            throw new UnsupportedOperationException("Not an embedded subscription");
+        if (mNativeAccessRules == null) return null;
+        return Arrays.asList(mNativeAccessRules);
+    }
+
+    /**
+     * @return the {@link UiccAccessRule}s that are both stored on Uicc and in carrierConfigs
+     * dictating who is authorized to manage this subscription.
+     * @hide
+     */
+    public @Nullable List<UiccAccessRule> getAllAccessRules() {
+        List<UiccAccessRule> merged = new ArrayList<>();
+        if (mNativeAccessRules != null) merged.addAll(getAccessRules());
+        if (mCarrierConfigAccessRules != null) {
+            merged.addAll(Arrays.asList(mCarrierConfigAccessRules));
         }
-        if (mAccessRules == null) return null;
-        return Arrays.asList(mAccessRules);
+        return merged.isEmpty() ? null : merged;
     }
 
     /**
@@ -651,7 +671,7 @@
             String countryIso = source.readString();
             Bitmap iconBitmap = source.readParcelable(Bitmap.class.getClassLoader());
             boolean isEmbedded = source.readBoolean();
-            UiccAccessRule[] accessRules = source.createTypedArray(UiccAccessRule.CREATOR);
+            UiccAccessRule[] nativeAccessRules = source.createTypedArray(UiccAccessRule.CREATOR);
             String cardString = source.readString();
             int cardId = source.readInt();
             boolean isOpportunistic = source.readBoolean();
@@ -663,11 +683,14 @@
             String[] ehplmns = source.readStringArray();
             String[] hplmns = source.readStringArray();
             String groupOwner = source.readString();
+            UiccAccessRule[] carrierConfigAccessRules = source.createTypedArray(
+                UiccAccessRule.CREATOR);
 
             SubscriptionInfo info = new SubscriptionInfo(id, iccId, simSlotIndex, displayName,
                     carrierName, nameSource, iconTint, number, dataRoaming, iconBitmap, mcc, mnc,
-                    countryIso, isEmbedded, accessRules, cardString, cardId, isOpportunistic,
-                    groupUUID, isGroupDisabled, carrierid, profileClass, subType, groupOwner);
+                    countryIso, isEmbedded, nativeAccessRules, cardString, cardId, isOpportunistic,
+                    groupUUID, isGroupDisabled, carrierid, profileClass, subType, groupOwner,
+                    carrierConfigAccessRules);
             info.setAssociatedPlmns(ehplmns, hplmns);
             return info;
         }
@@ -694,7 +717,7 @@
         dest.writeString(mCountryIso);
         dest.writeParcelable(mIconBitmap, flags);
         dest.writeBoolean(mIsEmbedded);
-        dest.writeTypedArray(mAccessRules, flags);
+        dest.writeTypedArray(mNativeAccessRules, flags);
         dest.writeString(mCardString);
         dest.writeInt(mCardId);
         dest.writeBoolean(mIsOpportunistic);
@@ -706,6 +729,7 @@
         dest.writeStringArray(mEhplmns);
         dest.writeStringArray(mHplmns);
         dest.writeString(mGroupOwner);
+        dest.writeTypedArray(mCarrierConfigAccessRules, flags);
     }
 
     @Override
@@ -736,9 +760,9 @@
                 + " carrierId=" + mCarrierId + " displayName=" + mDisplayName
                 + " carrierName=" + mCarrierName + " nameSource=" + mNameSource
                 + " iconTint=" + mIconTint + " mNumber=" + Rlog.pii(Build.IS_DEBUGGABLE, mNumber)
-                + " dataRoaming=" + mDataRoaming + " iconBitmap=" + mIconBitmap + " mcc=" + mMcc
-                + " mnc=" + mMnc + " mCountryIso=" + mCountryIso + " isEmbedded=" + mIsEmbedded
-                + " accessRules=" + Arrays.toString(mAccessRules)
+                + " dataRoaming=" + mDataRoaming + " iconBitmap=" + mIconBitmap + " mcc " + mMcc
+                + " mnc " + mMnc + "mCountryIso=" + mCountryIso + " isEmbedded " + mIsEmbedded
+                + " nativeAccessRules " + Arrays.toString(mNativeAccessRules)
                 + " cardString=" + cardStringToPrint + " cardId=" + mCardId
                 + " isOpportunistic=" + mIsOpportunistic + " mGroupUUID=" + mGroupUUID
                 + " mIsGroupDisabled=" + mIsGroupDisabled
@@ -746,14 +770,15 @@
                 + " ehplmns=" + Arrays.toString(mEhplmns)
                 + " hplmns=" + Arrays.toString(mHplmns)
                 + " subscriptionType=" + mSubscriptionType
-                + " mGroupOwner=" + mGroupOwner + "}";
+                + " mGroupOwner=" + mGroupOwner
+                + " carrierConfigAccessRules=" + mCarrierConfigAccessRules + "}";
     }
 
     @Override
     public int hashCode() {
         return Objects.hash(mId, mSimSlotIndex, mNameSource, mIconTint, mDataRoaming, mIsEmbedded,
                 mIsOpportunistic, mGroupUUID, mIccId, mNumber, mMcc, mMnc,
-                mCountryIso, mCardString, mCardId, mDisplayName, mCarrierName, mAccessRules,
+                mCountryIso, mCardString, mCardId, mDisplayName, mCarrierName, mNativeAccessRules,
                 mIsGroupDisabled, mCarrierId, mProfileClass, mGroupOwner);
     }
 
@@ -789,7 +814,7 @@
                 && Objects.equals(mGroupOwner, toCompare.mGroupOwner)
                 && TextUtils.equals(mDisplayName, toCompare.mDisplayName)
                 && TextUtils.equals(mCarrierName, toCompare.mCarrierName)
-                && Arrays.equals(mAccessRules, toCompare.mAccessRules)
+                && Arrays.equals(mNativeAccessRules, toCompare.mNativeAccessRules)
                 && mProfileClass == toCompare.mProfileClass
                 && Arrays.equals(mEhplmns, toCompare.mEhplmns)
                 && Arrays.equals(mHplmns, toCompare.mHplmns);
diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java
index 8c53c51..26574c4 100644
--- a/telephony/java/android/telephony/SubscriptionManager.java
+++ b/telephony/java/android/telephony/SubscriptionManager.java
@@ -571,6 +571,16 @@
     public static final String ACCESS_RULES = "access_rules";
 
     /**
+     * TelephonyProvider column name for the encoded {@link UiccAccessRule}s from
+     * {@link UiccAccessRule#encodeRules} but for the rules that come from CarrierConfigs.
+     * Only present if there are access rules in CarrierConfigs
+     * <p>TYPE: BLOB
+     * @hide
+     */
+    public static final String ACCESS_RULES_FROM_CARRIER_CONFIGS =
+            "access_rules_from_carrier_configs";
+
+    /**
      * TelephonyProvider column name identifying whether an embedded subscription is on a removable
      * card. Such subscriptions are marked inaccessible as soon as the current card is removed.
      * Otherwise, they will remain accessible unless explicitly deleted. Only present if
@@ -2578,7 +2588,6 @@
      *
      * @param info The subscription to check.
      * @return whether the app is authorized to manage this subscription per its metadata.
-     * @throws IllegalArgumentException if this subscription is not embedded.
      */
     public boolean canManageSubscription(SubscriptionInfo info) {
         return canManageSubscription(info, mContext.getPackageName());
@@ -2594,14 +2603,10 @@
      * @param info The subscription to check.
      * @param packageName Package name of the app to check.
      * @return whether the app is authorized to manage this subscription per its access rules.
-     * @throws IllegalArgumentException if this subscription is not embedded.
      * @hide
      */
     public boolean canManageSubscription(SubscriptionInfo info, String packageName) {
-        if (!info.isEmbedded()) {
-            throw new IllegalArgumentException("Not an embedded subscription");
-        }
-        if (info.getAccessRules() == null) {
+        if (info.getAllAccessRules() == null) {
             return false;
         }
         PackageManager packageManager = mContext.getPackageManager();
@@ -2611,7 +2616,7 @@
         } catch (PackageManager.NameNotFoundException e) {
             throw new IllegalArgumentException("Unknown package: " + packageName, e);
         }
-        for (UiccAccessRule rule : info.getAccessRules()) {
+        for (UiccAccessRule rule : info.getAllAccessRules()) {
             if (rule.getCarrierPrivilegeStatus(packageInfo)
                     == TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS) {
                 return true;
@@ -3002,7 +3007,7 @@
         // to the caller.
         boolean hasCarrierPrivilegePermission = TelephonyManager.from(mContext)
                 .hasCarrierPrivileges(info.getSubscriptionId())
-                || (info.isEmbedded() && canManageSubscription(info));
+                || canManageSubscription(info);
         return hasCarrierPrivilegePermission;
     }
 
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 35b435d..553bff2 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -11047,6 +11047,8 @@
      * The {@link #EXTRA_NETWORK_COUNTRY} extra indicates the country code of the current
      * network returned by {@link #getNetworkCountryIso()}.
      *
+     * <p>There may be a delay of several minutes before reporting that no country is detected.
+     *
      * @see #EXTRA_NETWORK_COUNTRY
      * @see #getNetworkCountryIso()
      */
diff --git a/telephony/java/android/telephony/data/DataServiceCallback.java b/telephony/java/android/telephony/data/DataServiceCallback.java
index 89d30c0d..11dc78a 100644
--- a/telephony/java/android/telephony/data/DataServiceCallback.java
+++ b/telephony/java/android/telephony/data/DataServiceCallback.java
@@ -27,7 +27,6 @@
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
-import java.lang.ref.WeakReference;
 import java.util.List;
 
 /**
@@ -64,11 +63,11 @@
     /** Request sent in illegal state */
     public static final int RESULT_ERROR_ILLEGAL_STATE  = 4;
 
-    private final WeakReference<IDataServiceCallback> mCallback;
+    private final IDataServiceCallback mCallback;
 
     /** @hide */
     public DataServiceCallback(IDataServiceCallback callback) {
-        mCallback = new WeakReference<>(callback);
+        mCallback = callback;
     }
 
     /**
@@ -80,14 +79,15 @@
      */
     public void onSetupDataCallComplete(@ResultCode int result,
                                         @Nullable DataCallResponse response) {
-        IDataServiceCallback callback = mCallback.get();
-        if (callback != null) {
+        if (mCallback != null) {
             try {
                 if (DBG) Rlog.d(TAG, "onSetupDataCallComplete");
-                callback.onSetupDataCallComplete(result, response);
+                mCallback.onSetupDataCallComplete(result, response);
             } catch (RemoteException e) {
                 Rlog.e(TAG, "Failed to onSetupDataCallComplete on the remote");
             }
+        } else {
+            Rlog.e(TAG, "onSetupDataCallComplete: callback is null!");
         }
     }
 
@@ -98,14 +98,15 @@
      * @param result The result code. Must be one of the {@link ResultCode}.
      */
     public void onDeactivateDataCallComplete(@ResultCode int result) {
-        IDataServiceCallback callback = mCallback.get();
-        if (callback != null) {
+        if (mCallback != null) {
             try {
                 if (DBG) Rlog.d(TAG, "onDeactivateDataCallComplete");
-                callback.onDeactivateDataCallComplete(result);
+                mCallback.onDeactivateDataCallComplete(result);
             } catch (RemoteException e) {
                 Rlog.e(TAG, "Failed to onDeactivateDataCallComplete on the remote");
             }
+        } else {
+            Rlog.e(TAG, "onDeactivateDataCallComplete: callback is null!");
         }
     }
 
@@ -116,13 +117,14 @@
      * @param result The result code. Must be one of the {@link ResultCode}.
      */
     public void onSetInitialAttachApnComplete(@ResultCode int result) {
-        IDataServiceCallback callback = mCallback.get();
-        if (callback != null) {
+        if (mCallback != null) {
             try {
-                callback.onSetInitialAttachApnComplete(result);
+                mCallback.onSetInitialAttachApnComplete(result);
             } catch (RemoteException e) {
                 Rlog.e(TAG, "Failed to onSetInitialAttachApnComplete on the remote");
             }
+        } else {
+            Rlog.e(TAG, "onSetInitialAttachApnComplete: callback is null!");
         }
     }
 
@@ -133,13 +135,14 @@
      * @param result The result code. Must be one of the {@link ResultCode}.
      */
     public void onSetDataProfileComplete(@ResultCode int result) {
-        IDataServiceCallback callback = mCallback.get();
-        if (callback != null) {
+        if (mCallback != null) {
             try {
-                callback.onSetDataProfileComplete(result);
+                mCallback.onSetDataProfileComplete(result);
             } catch (RemoteException e) {
                 Rlog.e(TAG, "Failed to onSetDataProfileComplete on the remote");
             }
+        } else {
+            Rlog.e(TAG, "onSetDataProfileComplete: callback is null!");
         }
     }
 
@@ -153,13 +156,14 @@
      */
     public void onRequestDataCallListComplete(@ResultCode int result,
                                               @NonNull List<DataCallResponse> dataCallList) {
-        IDataServiceCallback callback = mCallback.get();
-        if (callback != null) {
+        if (mCallback != null) {
             try {
-                callback.onRequestDataCallListComplete(result, dataCallList);
+                mCallback.onRequestDataCallListComplete(result, dataCallList);
             } catch (RemoteException e) {
                 Rlog.e(TAG, "Failed to onRequestDataCallListComplete on the remote");
             }
+        } else {
+            Rlog.e(TAG, "onRequestDataCallListComplete: callback is null!");
         }
     }
 
@@ -170,14 +174,15 @@
      * @param dataCallList List of the current active data connection.
      */
     public void onDataCallListChanged(@NonNull List<DataCallResponse> dataCallList) {
-        IDataServiceCallback callback = mCallback.get();
-        if (callback != null) {
+        if (mCallback != null) {
             try {
                 if (DBG) Rlog.d(TAG, "onDataCallListChanged");
-                callback.onDataCallListChanged(dataCallList);
+                mCallback.onDataCallListChanged(dataCallList);
             } catch (RemoteException e) {
                 Rlog.e(TAG, "Failed to onDataCallListChanged on the remote");
             }
+        } else {
+            Rlog.e(TAG, "onDataCallListChanged: callback is null!");
         }
     }
 }
diff --git a/tools/aapt2/optimize/ResourceDeduper.cpp b/tools/aapt2/optimize/ResourceDeduper.cpp
index 78ebcb9..0278b43 100644
--- a/tools/aapt2/optimize/ResourceDeduper.cpp
+++ b/tools/aapt2/optimize/ResourceDeduper.cpp
@@ -63,13 +63,14 @@
     // Compare compatible configs for this entry and ensure the values are
     // equivalent.
     const ConfigDescription& node_configuration = node_value->config;
-    for (const auto& sibling : entry_->values) {
-      if (!sibling->value) {
+    for (const auto& sibling : parent->children()) {
+      ResourceConfigValue* sibling_value = sibling->value();
+      if (!sibling_value->value) {
         // Sibling was already removed.
         continue;
       }
-      if (node_configuration.IsCompatibleWith(sibling->config) &&
-          !node_value->value->Equals(sibling->value.get())) {
+      if (node_configuration.IsCompatibleWith(sibling_value->config) &&
+          !node_value->value->Equals(sibling_value->value.get())) {
         // The configurations are compatible, but the value is
         // different, so we can't remove this value.
         return;
diff --git a/tools/aapt2/optimize/ResourceDeduper_test.cpp b/tools/aapt2/optimize/ResourceDeduper_test.cpp
index 2e098ae..048e318 100644
--- a/tools/aapt2/optimize/ResourceDeduper_test.cpp
+++ b/tools/aapt2/optimize/ResourceDeduper_test.cpp
@@ -80,11 +80,58 @@
           .Build();
 
   ASSERT_TRUE(ResourceDeduper().Consume(context.get(), table.get()));
+  EXPECT_THAT(table, HasValue("android:string/keep", default_config));
   EXPECT_THAT(table, HasValue("android:string/keep", ldrtl_config));
   EXPECT_THAT(table, HasValue("android:string/keep", ldrtl_v21_config));
   EXPECT_THAT(table, HasValue("android:string/keep", land_config));
 }
 
+TEST(ResourceDeduperTest, SameValuesAreDedupedIncompatibleSiblings) {
+  std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build();
+  const ConfigDescription default_config = {};
+  const ConfigDescription ldrtl_config = test::ParseConfigOrDie("ldrtl");
+  const ConfigDescription ldrtl_night_config = test::ParseConfigOrDie("ldrtl-night");
+  // Chosen because this configuration is not compatible with ldrtl-night.
+  const ConfigDescription ldrtl_notnight_config = test::ParseConfigOrDie("ldrtl-notnight");
+
+  std::unique_ptr<ResourceTable> table =
+      test::ResourceTableBuilder()
+          .AddString("android:string/keep", ResourceId{}, default_config, "keep")
+          .AddString("android:string/keep", ResourceId{}, ldrtl_config, "dedupe")
+          .AddString("android:string/keep", ResourceId{}, ldrtl_night_config, "dedupe")
+          .AddString("android:string/keep", ResourceId{}, ldrtl_notnight_config, "keep2")
+          .Build();
+
+  ASSERT_TRUE(ResourceDeduper().Consume(context.get(), table.get()));
+  EXPECT_THAT(table, HasValue("android:string/keep", default_config));
+  EXPECT_THAT(table, HasValue("android:string/keep", ldrtl_config));
+  EXPECT_THAT(table, Not(HasValue("android:string/keep", ldrtl_night_config)));
+  EXPECT_THAT(table, HasValue("android:string/keep", ldrtl_notnight_config));
+}
+
+TEST(ResourceDeduperTest, SameValuesAreDedupedCompatibleNonSiblings) {
+  std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build();
+  const ConfigDescription default_config = {};
+  const ConfigDescription ldrtl_config = test::ParseConfigOrDie("ldrtl");
+  const ConfigDescription ldrtl_night_config = test::ParseConfigOrDie("ldrtl-night");
+  // Chosen because this configuration is compatible with ldrtl.
+  const ConfigDescription land_config = test::ParseConfigOrDie("land");
+
+  std::unique_ptr<ResourceTable> table =
+      test::ResourceTableBuilder()
+          .AddString("android:string/keep", ResourceId{}, default_config, "keep")
+          .AddString("android:string/keep", ResourceId{}, ldrtl_config, "dedupe")
+          .AddString("android:string/keep", ResourceId{}, ldrtl_night_config, "dedupe")
+          .AddString("android:string/keep", ResourceId{}, land_config, "keep2")
+          .Build();
+
+  ASSERT_TRUE(ResourceDeduper().Consume(context.get(), table.get()));
+  EXPECT_THAT(table, HasValue("android:string/keep", default_config));
+  EXPECT_THAT(table, HasValue("android:string/keep", ldrtl_config));
+  EXPECT_THAT(table, Not(HasValue("android:string/keep", ldrtl_night_config)));
+  EXPECT_THAT(table, HasValue("android:string/keep", land_config));
+}
+
 TEST(ResourceDeduperTest, LocalesValuesAreKept) {
   std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build();
   const ConfigDescription default_config = {};
diff --git a/wifi/java/android/net/wifi/rtt/ResponderLocation.java b/wifi/java/android/net/wifi/rtt/ResponderLocation.java
index e1d82f8..970a75d 100644
--- a/wifi/java/android/net/wifi/rtt/ResponderLocation.java
+++ b/wifi/java/android/net/wifi/rtt/ResponderLocation.java
@@ -605,11 +605,11 @@
             // Negative 2's complement value
             // Note: The Math.pow(...) method cannot return a NaN value because the bitFieldSize
             // for Lat or Lng is limited to exactly 34 bits.
-            angle = Math.scalb(fields[offset] - Math.pow(2, bitFieldSizes[offset]),
+            angle = Math.scalb((double) fields[offset] - Math.pow(2, bitFieldSizes[offset]),
                     -LATLNG_FRACTION_BITS);
         } else {
             // Positive 2's complement value
-            angle = Math.scalb(fields[offset], -LATLNG_FRACTION_BITS);
+            angle = Math.scalb((double) fields[offset], -LATLNG_FRACTION_BITS);
         }
         if (angle > limit) {
             angle = limit;
@@ -732,10 +732,11 @@
 
         int maxBssidIndicator = (int) buffer[SUBELEMENT_BSSID_MAX_INDICATOR_INDEX] & BYTE_MASK;
         int bssidListLength = (buffer.length - 1) / BYTES_IN_A_BSSID;
-        // Check the max number of BSSIDs agrees with the list length.
-        if (maxBssidIndicator != bssidListLength) {
-            return false;
-        }
+        // The maxBSSIDIndicator is ignored. Its use is still being clarified in 802.11REVmd,
+        // which is not published at this time. This field will be used in a future
+        // release of Android after 802.11REVmd is public. Here, we interpret the following
+        // params as an explicit list of BSSIDs.
+
 
         int bssidOffset = SUBELEMENT_BSSID_LIST_INDEX;
         for (int i = 0; i < bssidListLength; i++) {
diff --git a/wifi/tests/src/android/net/wifi/rtt/ResponderLocationTest.java b/wifi/tests/src/android/net/wifi/rtt/ResponderLocationTest.java
index 47c30409..b02eebb 100644
--- a/wifi/tests/src/android/net/wifi/rtt/ResponderLocationTest.java
+++ b/wifi/tests/src/android/net/wifi/rtt/ResponderLocationTest.java
@@ -37,7 +37,7 @@
  */
 @RunWith(JUnit4.class)
 public class ResponderLocationTest {
-    private static final double LATLNG_TOLERANCE_DEGREES = 0.00001;
+    private static final double LATLNG_TOLERANCE_DEGREES = 0.000_000_05D; // 5E-8 = 6mm of meridian
     private static final double ALT_TOLERANCE_METERS = 0.01;
     private static final double HEIGHT_TOLERANCE_METERS = 0.01;
     private static final int INDEX_ELEMENT_TYPE = 2;
@@ -103,7 +103,7 @@
     private static final byte[] sTestBssidListSE = {
             (byte) 0x07, // Subelement BSSID list
             (byte) 13, // length dependent on number of BSSIDs in list
-            (byte) 0x02, // Number of BSSIDs in list
+            (byte) 0x00, // List is explicit; no expansion of list required
             (byte) 0x01, // BSSID #1 (MSB)
             (byte) 0x02,
             (byte) 0x03,
@@ -266,11 +266,11 @@
         assertTrue(valid);
         assertTrue(lciValid);
         assertFalse(zValid);
-        assertEquals(0.0009765625, responderLocation.getLatitudeUncertainty());
-        assertEquals(-33.857009, responderLocation.getLatitude(),
+        assertEquals(0.0009765625D, responderLocation.getLatitudeUncertainty());
+        assertEquals(-33.8570095D, responderLocation.getLatitude(),
                 LATLNG_TOLERANCE_DEGREES);
-        assertEquals(0.0009765625, responderLocation.getLongitudeUncertainty());
-        assertEquals(151.215200, responderLocation.getLongitude(),
+        assertEquals(0.0009765625D, responderLocation.getLongitudeUncertainty());
+        assertEquals(151.2152005D, responderLocation.getLongitude(),
                 LATLNG_TOLERANCE_DEGREES);
         assertEquals(1, responderLocation.getAltitudeType());
         assertEquals(64.0, responderLocation.getAltitudeUncertainty());
@@ -282,11 +282,11 @@
         assertEquals(1, responderLocation.getLciVersion());
 
         // Testing Location Object
-        assertEquals(-33.857009, location.getLatitude(),
+        assertEquals(-33.8570095D, location.getLatitude(),
                 LATLNG_TOLERANCE_DEGREES);
-        assertEquals(151.215200, location.getLongitude(),
+        assertEquals(151.2152005D, location.getLongitude(),
                 LATLNG_TOLERANCE_DEGREES);
-        assertEquals((0.0009765625 + 0.0009765625) / 2, location.getAccuracy(),
+        assertEquals((0.0009765625D + 0.0009765625D) / 2, location.getAccuracy(),
                 LATLNG_TOLERANCE_DEGREES);
         assertEquals(11.2, location.getAltitude(), ALT_TOLERANCE_METERS);
         assertEquals(64.0, location.getVerticalAccuracyMeters(), ALT_TOLERANCE_METERS);