Merge "Change permissionLevel of ACCESS_SHORTCUTS and UNLIMITED_SHORTCUTS_API_CALLS"
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..a16b24e 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);
   }
@@ -42240,6 +42241,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/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/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/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/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/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 213a125..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
@@ -8436,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.
@@ -8497,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.
@@ -8509,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.
@@ -8628,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.
@@ -8695,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.
@@ -8857,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.
@@ -8869,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.
@@ -10571,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 =
@@ -10721,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
@@ -12572,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
@@ -12602,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
@@ -12796,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
@@ -12808,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.
@@ -12823,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
@@ -12836,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
@@ -12849,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.
@@ -12999,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
@@ -13750,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.
@@ -13761,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
@@ -13796,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,
@@ -13865,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/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/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/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/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/proto/android/providers/settings/global.proto b/core/proto/android/providers/settings/global.proto
index a2d5d2a..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 ];
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/res/values/config.xml b/core/res/res/values/config.xml
index 40c2cbe..ceccd0d 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -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..cdfa0439 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -3377,8 +3377,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/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/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/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 b7eddc2..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,
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index c1aa0f9..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;
@@ -1717,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/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/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index de20972..8c5374a 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -1446,6 +1446,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 +1482,6 @@
 
     private void handleScreenTurnedOff() {
         checkIsHandlerThread();
-        mLockIconPressed = false;
         mHardwareFingerprintUnavailableRetryCount = 0;
         mHardwareFaceUnavailableRetryCount = 0;
         for (int i = 0; i < mCallbacks.size(); i++) {
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/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/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/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/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/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index d759be2..9caceae 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;
 
@@ -8201,6 +8210,53 @@
     @Deprecated
     @Override
     public void requestBugReport(int bugreportType) {
+        requestBugReportWithDescription(null, null, bugreportType);
+    }
+
+    /**
+     * @deprecated This method is only used by a few internal components and it will soon be
+     * replaced by a proper bug report API (which will be restricted to a few, pre-defined apps).
+     * No new code should be calling it.
+     */
+    @Deprecated
+    public void requestBugReportWithDescription(@Nullable String shareTitle,
+            @Nullable String shareDescription, int bugreportType) {
+        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);
+            }
+            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);
+
+        if (useApi) {
+            // 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);
+            }
+        }
         String extraOptions = null;
         switch (bugreportType) {
             case ActivityManager.BUGREPORT_OPTION_FULL:
@@ -8242,45 +8298,6 @@
      * No new code should be calling it.
      */
     @Deprecated
-    private void requestBugReportWithDescription(String shareTitle, String shareDescription,
-                                                 int bugreportType) {
-        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);
-            }
-        }
-
-        Slog.d(TAG, "Bugreport notification title " + shareTitle
-                + " description " + shareDescription);
-        requestBugReport(bugreportType);
-    }
-
-    /**
-     * @deprecated This method is only used by a few internal components and it will soon be
-     * replaced by a proper bug report API (which will be restricted to a few, pre-defined apps).
-     * No new code should be calling it.
-     */
-    @Deprecated
     @Override
     public void requestTelephonyBugReport(String shareTitle, String shareDescription) {
         requestBugReportWithDescription(shareTitle, shareDescription,
@@ -12213,11 +12230,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);
             }
@@ -12515,6 +12529,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);
 
@@ -12553,7 +12574,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;
@@ -12707,10 +12728,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();
@@ -13078,6 +13096,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);
 
@@ -13119,7 +13144,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;
@@ -13266,10 +13291,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/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/wm/ActivityDisplay.java b/services/core/java/com/android/server/wm/ActivityDisplay.java
index 5a0dfd0..ced3bd9 100644
--- a/services/core/java/com/android/server/wm/ActivityDisplay.java
+++ b/services/core/java/com/android/server/wm/ActivityDisplay.java
@@ -490,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;
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 2269537..292c400 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -122,7 +122,6 @@
 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.ActivityStackSupervisor.PRESERVE_WINDOWS;
-import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_ALL;
 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;
@@ -1592,8 +1591,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 */);
         }
     }
 
@@ -1650,14 +1648,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
@@ -1665,7 +1666,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
@@ -1704,20 +1705,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
@@ -1744,17 +1749,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,
@@ -1771,6 +1776,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);
@@ -1789,101 +1795,123 @@
         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) {
+        final boolean notFocusedStack = stack != mRootActivityContainer.getTopDisplayFocusedStack();
+        if (isVisible && next != null && !next.nowVisible) {
             if (!mStackSupervisor.mStoppingActivities.contains(this)) {
-                stack.addToStopping(this, false /* scheduleIdle */, false /* idleDelayed */,
-                        "finishCurrentActivityLocked");
+                getActivityStack().addToStopping(this, false /* scheduleIdle */,
+                        false /* idleDelayed */, "finishCurrentActivityLocked");
             }
             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.
+            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();
+
+        boolean activityRemoved = getActivityStack().destroyActivityLocked(this,
+                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 */);
-        }
-        return this;
     }
 
     void makeFinishingLocked() {
diff --git a/services/core/java/com/android/server/wm/ActivityStack.java b/services/core/java/com/android/server/wm/ActivityStack.java
index daf3286..f77e902 100644
--- a/services/core/java/com/android/server/wm/ActivityStack.java
+++ b/services/core/java/com/android/server/wm/ActivityStack.java
@@ -56,8 +56,6 @@
 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;
@@ -1771,8 +1769,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 +1787,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);
@@ -2782,8 +2778,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;
         }
 
@@ -3018,8 +3013,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 {
@@ -3446,8 +3440,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--;
                     }
@@ -3531,8 +3525,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 +3559,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 */);
                             }
                         }
                     }
@@ -3755,8 +3747,8 @@
             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) {
+                    if (r.finishIfPossible("stop-no-history", false /* oomAdj */)
+                            != FINISH_RESULT_CANCELLED) {
                         // {@link adjustFocusedActivityStack} must have been already called.
                         r.resumeKeyDispatchingLocked();
                         return;
@@ -3815,8 +3807,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 +3837,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 +3858,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 +3874,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 +3912,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 +4016,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 +4054,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);
@@ -4312,10 +4300,13 @@
     }
 
     /**
-     * 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.
+     * 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.
      */
     final boolean destroyActivityLocked(ActivityRecord r, boolean removeFromApp, String reason) {
         if (DEBUG_SWITCH || DEBUG_CLEANUP) Slog.v(TAG_SWITCH,
@@ -4890,7 +4881,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 +4925,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 +4979,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 +5015,7 @@
                     r.app = null;
                     getDisplay().mDisplayContent.prepareAppTransition(
                             TRANSIT_CRASHING_ACTIVITY_CLOSE, false /* alwaysKeepCurrent */);
-                    r.finishCurrentActivityLocked(FINISH_IMMEDIATELY, false /* oomAdj */,
-                            "handleAppCrashedLocked");
+                    r.destroyIfPossible("handleAppCrashedLocked");
                 }
             }
         }
diff --git a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
index 22f72a4..d0c70de 100644
--- a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
@@ -897,8 +897,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;
                 }
 
@@ -1347,8 +1346,8 @@
             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);
                 }
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index 9ed93c4..d6250f6 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -1426,8 +1426,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.
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index 7283ca5..2da2ebc 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -86,6 +86,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;
@@ -210,7 +211,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;
@@ -1614,8 +1615,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");
@@ -5862,9 +5863,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 ");
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/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/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/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/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/ActivityRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
index 0f04788..eaffd77 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
@@ -26,6 +26,7 @@
 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;
@@ -34,6 +35,7 @@
 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;
@@ -41,12 +43,16 @@
 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.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;
@@ -61,6 +67,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;
@@ -87,6 +94,7 @@
 import androidx.test.filters.MediumTest;
 
 import com.android.internal.R;
+import com.android.server.wm.ActivityStack.ActivityState;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -544,12 +552,16 @@
         final ActivityDisplay newDisplay =
                 addNewActivityDisplayAt(info, ActivityDisplay.POSITION_TOP);
 
-        mTask.getConfiguration().densityDpi = 200;
+        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;
@@ -586,7 +598,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();
 
@@ -726,12 +738,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());
 
@@ -739,21 +750,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());
 
@@ -762,8 +771,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());
     }
@@ -772,7 +780,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");
@@ -782,17 +790,300 @@
         // 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(mStack).destroyActivityLocked(eq(mActivity), 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(mStack, never()).destroyActivityLocked(eq(mActivity), eq(true) /* removeFromApp */,
+                anyString());
+        assertEquals(FINISHING, mActivity.getState());
+        assertTrue(mActivity.mStackSupervisor.mFinishingActivities.contains(mActivity));
+    }
+
     /** Setup {@link #mActivity} as a size-compat-mode-able activity without fixed orientation. */
     private void prepareFixedAspectRatioUnresizableActivity() {
         setupDisplayContentForCompatDisplayInsets();
         mActivity.info.resizeMode = RESIZE_MODE_UNRESIZEABLE;
         mActivity.info.maxAspectRatio = 1.5f;
+        mActivity.visible = true;
         ensureActivityConfiguration();
     }
 
@@ -805,8 +1096,12 @@
         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 ff7b1fa..fd552fc 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;
@@ -33,7 +32,6 @@
 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;
@@ -996,8 +994,7 @@
         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());
     }
@@ -1016,7 +1013,7 @@
         }
 
         // Home stack should not be destroyed immediately.
-        final ActivityRecord activity1 = finishCurrentActivity(homeStack);
+        final ActivityRecord activity1 = finishTopActivity(homeStack);
         assertEquals(FINISHING, activity1.getState());
     }
 
@@ -1032,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;
     }
 
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..10d4b8db 100755
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -2656,25 +2656,32 @@
             "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"
+     * Here is an example:
+     * UE want to display 5GPlus icon for scenario#1, and 5G icon for scenario#2; otherwise no
+     * define.
+     * The configuration is: "connected_mmwave:5GPlus,connected:5G"
      *
      * @hide
      */
@@ -3131,6 +3138,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;
 
@@ -3509,7 +3523,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 */
@@ -3552,6 +3566,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/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..36e8123 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;
     }
 
     /**
@@ -566,7 +576,8 @@
         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 +587,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,7 +597,10 @@
     }
 
     /**
-     * @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
      */
@@ -595,8 +609,25 @@
         if (!isEmbedded()) {
             throw new UnsupportedOperationException("Not an embedded subscription");
         }
-        if (mAccessRules == null) return null;
-        return Arrays.asList(mAccessRules);
+        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() {
+        if (!isEmbedded()) {
+            throw new UnsupportedOperationException("Not an embedded subscription");
+        }
+        List<UiccAccessRule> merged = new ArrayList<>();
+        if (mNativeAccessRules != null) merged.addAll(getAccessRules());
+        if (mCarrierConfigAccessRules != null) {
+            merged.addAll(Arrays.asList(mCarrierConfigAccessRules));
+        }
+        return merged.isEmpty() ? null : merged;
     }
 
     /**
@@ -651,7 +682,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 +694,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 +728,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 +740,7 @@
         dest.writeStringArray(mEhplmns);
         dest.writeStringArray(mHplmns);
         dest.writeString(mGroupOwner);
+        dest.writeTypedArray(mCarrierConfigAccessRules, flags);
     }
 
     @Override
@@ -736,9 +771,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 +781,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 +825,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..a84c916 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
@@ -2601,7 +2611,7 @@
         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 +2621,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;
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);