Merge "Migrate CheckedListItem missed in refactoring."
diff --git a/Android.bp b/Android.bp
index 9d05ffd..b2ed0de 100644
--- a/Android.bp
+++ b/Android.bp
@@ -261,6 +261,7 @@
     required: [
         "framework-platform-compat-config",
         "libcore-platform-compat-config",
+        "services-platform-compat-config",
     ],
     sdk_version: "core_platform",
 }
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/TimeController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/TimeController.java
index 4c11947..1bb9e96 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/TimeController.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/TimeController.java
@@ -18,13 +18,20 @@
 
 import static com.android.server.job.JobSchedulerService.sElapsedRealtimeClock;
 
+import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.AlarmManager;
 import android.app.AlarmManager.OnAlarmListener;
+import android.content.ContentResolver;
 import android.content.Context;
+import android.database.ContentObserver;
+import android.net.Uri;
+import android.os.Handler;
 import android.os.Process;
 import android.os.UserHandle;
 import android.os.WorkSource;
+import android.provider.Settings;
+import android.util.KeyValueListParser;
 import android.util.Log;
 import android.util.Slog;
 import android.util.TimeUtils;
@@ -32,6 +39,7 @@
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.IndentingPrintWriter;
+import com.android.server.job.ConstantsProto;
 import com.android.server.job.JobSchedulerService;
 import com.android.server.job.StateControllerProto;
 
@@ -55,6 +63,9 @@
     /** Delay alarm tag for logging purposes */
     private final String DELAY_TAG = "*job.delay*";
 
+    private final Handler mHandler;
+    private final TcConstants mTcConstants;
+
     private long mNextJobExpiredElapsedMillis;
     private long mNextDelayExpiredElapsedMillis;
 
@@ -70,6 +81,14 @@
         mNextJobExpiredElapsedMillis = Long.MAX_VALUE;
         mNextDelayExpiredElapsedMillis = Long.MAX_VALUE;
         mChainedAttributionEnabled = mService.isChainedAttributionEnabled();
+
+        mHandler = new Handler(mContext.getMainLooper());
+        mTcConstants = new TcConstants(mHandler);
+    }
+
+    @Override
+    public void onSystemServicesReady() {
+        mTcConstants.start(mContext.getContentResolver());
     }
 
     /**
@@ -294,8 +313,7 @@
                 } else {
                     if (!wouldBeReadyWithConstraintLocked(job, JobStatus.CONSTRAINT_TIMING_DELAY)) {
                         if (DEBUG) {
-                            Slog.i(TAG,
-                                    "Skipping " + job + " because delay won't make it ready.");
+                            Slog.i(TAG, "Skipping " + job + " because delay won't make it ready.");
                         }
                         continue;
                     }
@@ -354,7 +372,8 @@
     /**
      * Set an alarm with the {@link android.app.AlarmManager} for the next time at which a job's
      * delay will expire.
-     * This alarm <b>will</b> wake up the phone.
+     * This alarm <b>will not</b> wake up the phone if
+     * {@link TcConstants#USE_NON_WAKEUP_ALARM_FOR_DELAY} is true.
      */
     private void setDelayExpiredAlarmLocked(long alarmTimeElapsedMillis, WorkSource ws) {
         alarmTimeElapsedMillis = maybeAdjustAlarmTime(alarmTimeElapsedMillis);
@@ -362,8 +381,11 @@
             return;
         }
         mNextDelayExpiredElapsedMillis = alarmTimeElapsedMillis;
-        updateAlarmWithListenerLocked(DELAY_TAG, mNextDelayExpiredListener,
-                mNextDelayExpiredElapsedMillis, ws);
+        final int alarmType =
+                mTcConstants.USE_NON_WAKEUP_ALARM_FOR_DELAY
+                        ? AlarmManager.ELAPSED_REALTIME : AlarmManager.ELAPSED_REALTIME_WAKEUP;
+        updateAlarmWithListenerLocked(DELAY_TAG, alarmType,
+                mNextDelayExpiredListener, mNextDelayExpiredElapsedMillis, ws);
     }
 
     /**
@@ -377,16 +399,16 @@
             return;
         }
         mNextJobExpiredElapsedMillis = alarmTimeElapsedMillis;
-        updateAlarmWithListenerLocked(DEADLINE_TAG, mDeadlineExpiredListener,
-                mNextJobExpiredElapsedMillis, ws);
+        updateAlarmWithListenerLocked(DEADLINE_TAG, AlarmManager.ELAPSED_REALTIME_WAKEUP,
+                mDeadlineExpiredListener, mNextJobExpiredElapsedMillis, ws);
     }
 
     private long maybeAdjustAlarmTime(long proposedAlarmTimeElapsedMillis) {
         return Math.max(proposedAlarmTimeElapsedMillis, sElapsedRealtimeClock.millis());
     }
 
-    private void updateAlarmWithListenerLocked(String tag, OnAlarmListener listener,
-            long alarmTimeElapsed, WorkSource ws) {
+    private void updateAlarmWithListenerLocked(String tag, @AlarmManager.AlarmType int alarmType,
+            OnAlarmListener listener, long alarmTimeElapsed, WorkSource ws) {
         ensureAlarmServiceLocked();
         if (alarmTimeElapsed == Long.MAX_VALUE) {
             mAlarmService.cancel(listener);
@@ -394,7 +416,7 @@
             if (DEBUG) {
                 Slog.d(TAG, "Setting " + tag + " for: " + alarmTimeElapsed);
             }
-            mAlarmService.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, alarmTimeElapsed,
+            mAlarmService.set(alarmType, alarmTimeElapsed,
                     AlarmManager.WINDOW_HEURISTIC, 0, tag, listener, null, ws);
         }
     }
@@ -422,9 +444,77 @@
     };
 
     @VisibleForTesting
-    void recheckAlarmsLocked() {
-        checkExpiredDeadlinesAndResetAlarm();
-        checkExpiredDelaysAndResetAlarm();
+    class TcConstants extends ContentObserver {
+        private ContentResolver mResolver;
+        private final KeyValueListParser mParser = new KeyValueListParser(',');
+
+        private static final String KEY_USE_NON_WAKEUP_ALARM_FOR_DELAY =
+                "use_non_wakeup_delay_alarm";
+
+        private static final boolean DEFAULT_USE_NON_WAKEUP_ALARM_FOR_DELAY = true;
+
+        /**
+         * Whether or not TimeController should skip setting wakeup alarms for jobs that aren't
+         * ready now.
+         */
+        public boolean USE_NON_WAKEUP_ALARM_FOR_DELAY = DEFAULT_USE_NON_WAKEUP_ALARM_FOR_DELAY;
+
+        /**
+         * Creates a content observer.
+         *
+         * @param handler The handler to run {@link #onChange} on, or null if none.
+         */
+        TcConstants(Handler handler) {
+            super(handler);
+        }
+
+        private void start(ContentResolver resolver) {
+            mResolver = resolver;
+            mResolver.registerContentObserver(Settings.Global.getUriFor(
+                    Settings.Global.JOB_SCHEDULER_TIME_CONTROLLER_CONSTANTS), false, this);
+            onChange(true, null);
+        }
+
+        @Override
+        public void onChange(boolean selfChange, Uri uri) {
+            final String constants = Settings.Global.getString(
+                    mResolver, Settings.Global.JOB_SCHEDULER_TIME_CONTROLLER_CONSTANTS);
+
+            try {
+                mParser.setString(constants);
+            } catch (Exception e) {
+                // Failed to parse the settings string, log this and move on with defaults.
+                Slog.e(TAG, "Bad jobscheduler time controller settings", e);
+            }
+
+            USE_NON_WAKEUP_ALARM_FOR_DELAY = mParser.getBoolean(
+                    KEY_USE_NON_WAKEUP_ALARM_FOR_DELAY, DEFAULT_USE_NON_WAKEUP_ALARM_FOR_DELAY);
+            // Intentionally not calling checkExpiredDelaysAndResetAlarm() here. There's no need to
+            // iterate through the entire list again for this constant change. The next delay alarm
+            // that is set will make use of the new constant value.
+        }
+
+        private void dump(IndentingPrintWriter pw) {
+            pw.println();
+            pw.println("TimeController:");
+            pw.increaseIndent();
+            pw.printPair(KEY_USE_NON_WAKEUP_ALARM_FOR_DELAY,
+                    USE_NON_WAKEUP_ALARM_FOR_DELAY).println();
+            pw.decreaseIndent();
+        }
+
+        private void dump(ProtoOutputStream proto) {
+            final long tcToken = proto.start(ConstantsProto.TIME_CONTROLLER);
+            proto.write(ConstantsProto.TimeController.USE_NON_WAKEUP_ALARM_FOR_DELAY,
+                    USE_NON_WAKEUP_ALARM_FOR_DELAY);
+            proto.end(tcToken);
+        }
+    }
+
+    @VisibleForTesting
+    @NonNull
+    TcConstants getTcConstants() {
+        return mTcConstants;
     }
 
     @Override
@@ -501,4 +591,14 @@
         proto.end(mToken);
         proto.end(token);
     }
+
+    @Override
+    public void dumpConstants(IndentingPrintWriter pw) {
+        mTcConstants.dump(pw);
+    }
+
+    @Override
+    public void dumpConstants(ProtoOutputStream proto) {
+        mTcConstants.dump(proto);
+    }
 }
diff --git a/api/current.txt b/api/current.txt
index 593710314..c68120d 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -4278,6 +4278,7 @@
     method @Deprecated public int checkOpNoThrow(@NonNull String, int, @NonNull String);
     method public void checkPackage(int, @NonNull String);
     method public void finishOp(@NonNull String, int, @NonNull String);
+    method public boolean isOpActive(@NonNull String, int, @NonNull String);
     method public int noteOp(@NonNull String, int, @NonNull String);
     method public int noteOpNoThrow(@NonNull String, int, @NonNull String);
     method public int noteProxyOp(@NonNull String, @NonNull String);
@@ -4286,8 +4287,10 @@
     method public static String permissionToOp(String);
     method public int startOp(@NonNull String, int, @NonNull String);
     method public int startOpNoThrow(@NonNull String, int, @NonNull String);
+    method public void startWatchingActive(@NonNull String[], @NonNull java.util.concurrent.Executor, @NonNull android.app.AppOpsManager.OnOpActiveChangedListener);
     method public void startWatchingMode(@NonNull String, @Nullable String, @NonNull android.app.AppOpsManager.OnOpChangedListener);
     method public void startWatchingMode(@NonNull String, @Nullable String, int, @NonNull android.app.AppOpsManager.OnOpChangedListener);
+    method public void stopWatchingActive(@NonNull android.app.AppOpsManager.OnOpActiveChangedListener);
     method public void stopWatchingMode(@NonNull android.app.AppOpsManager.OnOpChangedListener);
     method public int unsafeCheckOp(@NonNull String, int, @NonNull String);
     method public int unsafeCheckOpNoThrow(@NonNull String, int, @NonNull String);
@@ -4335,6 +4338,10 @@
     field public static final int WATCH_FOREGROUND_CHANGES = 1; // 0x1
   }
 
+  public static interface AppOpsManager.OnOpActiveChangedListener {
+    method public void onOpActiveChanged(@NonNull String, int, @NonNull String, boolean);
+  }
+
   public static interface AppOpsManager.OnOpChangedListener {
     method public void onOpChanged(String, String);
   }
@@ -13460,6 +13467,7 @@
   public final class GestureLibraries {
     method public static android.gesture.GestureLibrary fromFile(String);
     method public static android.gesture.GestureLibrary fromFile(java.io.File);
+    method @NonNull public static android.gesture.GestureLibrary fromFileDescriptor(@NonNull android.os.ParcelFileDescriptor);
     method public static android.gesture.GestureLibrary fromPrivateFile(android.content.Context, String);
     method public static android.gesture.GestureLibrary fromRawResource(android.content.Context, @RawRes int);
   }
@@ -17073,6 +17081,7 @@
     field public static final int REQUEST_AVAILABLE_CAPABILITIES_RAW = 3; // 0x3
     field public static final int REQUEST_AVAILABLE_CAPABILITIES_READ_SENSOR_SETTINGS = 5; // 0x5
     field public static final int REQUEST_AVAILABLE_CAPABILITIES_SECURE_IMAGE_DATA = 13; // 0xd
+    field public static final int REQUEST_AVAILABLE_CAPABILITIES_SYSTEM_CAMERA = 14; // 0xe
     field public static final int REQUEST_AVAILABLE_CAPABILITIES_YUV_REPROCESSING = 7; // 0x7
     field public static final int SCALER_CROPPING_TYPE_CENTER_ONLY = 0; // 0x0
     field public static final int SCALER_CROPPING_TYPE_FREEFORM = 1; // 0x1
@@ -42240,6 +42249,7 @@
     method public int speak(CharSequence, int, android.os.Bundle, String);
     method @Deprecated public int speak(String, int, java.util.HashMap<java.lang.String,java.lang.String>);
     method public int stop();
+    method public int synthesizeToFile(@NonNull CharSequence, @NonNull android.os.Bundle, @NonNull android.os.ParcelFileDescriptor, @NonNull String);
     method public int synthesizeToFile(CharSequence, android.os.Bundle, java.io.File, String);
     method @Deprecated public int synthesizeToFile(String, java.util.HashMap<java.lang.String,java.lang.String>, String);
     field public static final String ACTION_TTS_QUEUE_PROCESSING_COMPLETED = "android.speech.tts.TTS_QUEUE_PROCESSING_COMPLETED";
diff --git a/api/system-current.txt b/api/system-current.txt
index 76d017c..86e5f2e 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -29,6 +29,7 @@
     field public static final String BIND_CONTENT_SUGGESTIONS_SERVICE = "android.permission.BIND_CONTENT_SUGGESTIONS_SERVICE";
     field public static final String BIND_DIRECTORY_SEARCH = "android.permission.BIND_DIRECTORY_SEARCH";
     field public static final String BIND_EUICC_SERVICE = "android.permission.BIND_EUICC_SERVICE";
+    field public static final String BIND_EXTERNAL_STORAGE_SERVICE = "android.permission.BIND_EXTERNAL_STORAGE_SERVICE";
     field public static final String BIND_IMS_SERVICE = "android.permission.BIND_IMS_SERVICE";
     field public static final String BIND_KEYGUARD_APPWIDGET = "android.permission.BIND_KEYGUARD_APPWIDGET";
     field public static final String BIND_NETWORK_RECOMMENDATION_SERVICE = "android.permission.BIND_NETWORK_RECOMMENDATION_SERVICE";
@@ -192,6 +193,7 @@
     field public static final String SUBSTITUTE_NOTIFICATION_APP_NAME = "android.permission.SUBSTITUTE_NOTIFICATION_APP_NAME";
     field public static final String SUBSTITUTE_SHARE_TARGET_APP_NAME_AND_ICON = "android.permission.SUBSTITUTE_SHARE_TARGET_APP_NAME_AND_ICON";
     field public static final String SUSPEND_APPS = "android.permission.SUSPEND_APPS";
+    field public static final String SYSTEM_CAMERA = "android.permission.SYSTEM_CAMERA";
     field public static final String TETHER_PRIVILEGED = "android.permission.TETHER_PRIVILEGED";
     field public static final String TV_INPUT_HARDWARE = "android.permission.TV_INPUT_HARDWARE";
     field public static final String TV_VIRTUAL_REMOTE_CONTROLLER = "android.permission.TV_VIRTUAL_REMOTE_CONTROLLER";
@@ -351,6 +353,9 @@
     field public static final String OPSTR_PROJECT_MEDIA = "android:project_media";
     field public static final String OPSTR_READ_CLIPBOARD = "android:read_clipboard";
     field public static final String OPSTR_READ_ICC_SMS = "android:read_icc_sms";
+    field public static final String OPSTR_READ_MEDIA_AUDIO = "android:read_media_audio";
+    field public static final String OPSTR_READ_MEDIA_IMAGES = "android:read_media_images";
+    field public static final String OPSTR_READ_MEDIA_VIDEO = "android:read_media_video";
     field public static final String OPSTR_RECEIVE_EMERGENCY_BROADCAST = "android:receive_emergency_broadcast";
     field public static final String OPSTR_REQUEST_DELETE_PACKAGES = "android:request_delete_packages";
     field public static final String OPSTR_REQUEST_INSTALL_PACKAGES = "android:request_install_packages";
@@ -366,6 +371,9 @@
     field public static final String OPSTR_WIFI_SCAN = "android:wifi_scan";
     field public static final String OPSTR_WRITE_CLIPBOARD = "android:write_clipboard";
     field public static final String OPSTR_WRITE_ICC_SMS = "android:write_icc_sms";
+    field public static final String OPSTR_WRITE_MEDIA_AUDIO = "android:write_media_audio";
+    field public static final String OPSTR_WRITE_MEDIA_IMAGES = "android:write_media_images";
+    field public static final String OPSTR_WRITE_MEDIA_VIDEO = "android:write_media_video";
     field public static final String OPSTR_WRITE_SMS = "android:write_sms";
     field public static final String OPSTR_WRITE_WALLPAPER = "android:write_wallpaper";
     field public static final int OP_FLAGS_ALL = 31; // 0x1f
@@ -513,6 +521,8 @@
   }
 
   public class Notification implements android.os.Parcelable {
+    method @Nullable public android.util.Pair<android.app.RemoteInput,android.app.Notification.Action> findRemoteInputActionPair(boolean);
+    method @NonNull public java.util.List<android.app.Notification.Action> getContextualActions();
     field public static final String CATEGORY_CAR_EMERGENCY = "car_emergency";
     field public static final String CATEGORY_CAR_INFORMATION = "car_information";
     field public static final String CATEGORY_CAR_WARNING = "car_warning";
@@ -521,6 +531,10 @@
     field public static final int FLAG_AUTOGROUP_SUMMARY = 1024; // 0x400
   }
 
+  public static final class Notification.MessagingStyle.Message {
+    method @Nullable public static android.app.Notification.MessagingStyle.Message getMessageFromBundle(@NonNull android.os.Bundle);
+  }
+
   public static final class Notification.TvExtender implements android.app.Notification.Extender {
     ctor public Notification.TvExtender();
     ctor public Notification.TvExtender(android.app.Notification);
@@ -543,6 +557,7 @@
     method public void populateFromXml(org.xmlpull.v1.XmlPullParser);
     method public org.json.JSONObject toJson() throws org.json.JSONException;
     method public void writeXml(org.xmlpull.v1.XmlSerializer) throws java.io.IOException;
+    field public static final int USER_LOCKED_SOUND = 32; // 0x20
   }
 
   public final class NotificationChannelGroup implements android.os.Parcelable {
@@ -6648,6 +6663,10 @@
     field @NonNull public static final android.os.Parcelable.Creator<android.service.notification.SnoozeCriterion> CREATOR;
   }
 
+  public class StatusBarNotification implements android.os.Parcelable {
+    method public boolean isAppGroup();
+  }
+
 }
 
 package android.service.oemlock {
@@ -6767,6 +6786,20 @@
 
 }
 
+package android.service.storage {
+
+  public abstract class ExternalStorageService extends android.app.Service {
+    ctor public ExternalStorageService();
+    method @NonNull public final android.os.IBinder onBind(@NonNull android.content.Intent);
+    method public abstract void onEndSession(@NonNull String) throws java.io.IOException;
+    method public abstract void onStartSession(@NonNull String, int, @NonNull android.os.ParcelFileDescriptor, @NonNull String, @NonNull String) throws java.io.IOException;
+    field public static final int FLAG_SESSION_ATTRIBUTE_INDEXABLE = 2; // 0x2
+    field public static final int FLAG_SESSION_TYPE_FUSE = 1; // 0x1
+    field public static final String SERVICE_INTERFACE = "android.service.storage.ExternalStorageService";
+  }
+
+}
+
 package android.service.textclassifier {
 
   public abstract class TextClassifierService extends android.app.Service {
diff --git a/api/test-current.txt b/api/test-current.txt
index e9615111..ec3cb06 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -160,8 +160,6 @@
     method @RequiresPermission("android.permission.MANAGE_APP_OPS_MODES") public void setMode(int, int, String, int);
     method @RequiresPermission("android.permission.MANAGE_APP_OPS_MODES") public void setMode(String, int, String, int);
     method @RequiresPermission("android.permission.MANAGE_APP_OPS_MODES") public void setUidMode(String, int, int);
-    method public void startWatchingActive(@NonNull int[], @NonNull android.app.AppOpsManager.OnOpActiveChangedListener);
-    method public void stopWatchingActive(@NonNull android.app.AppOpsManager.OnOpActiveChangedListener);
     method public static int strOpToOp(@NonNull String);
     field public static final int HISTORICAL_MODE_DISABLED = 0; // 0x0
     field public static final int HISTORICAL_MODE_ENABLED_ACTIVE = 1; // 0x1
@@ -193,6 +191,9 @@
     field public static final String OPSTR_PROJECT_MEDIA = "android:project_media";
     field public static final String OPSTR_READ_CLIPBOARD = "android:read_clipboard";
     field public static final String OPSTR_READ_ICC_SMS = "android:read_icc_sms";
+    field public static final String OPSTR_READ_MEDIA_AUDIO = "android:read_media_audio";
+    field public static final String OPSTR_READ_MEDIA_IMAGES = "android:read_media_images";
+    field public static final String OPSTR_READ_MEDIA_VIDEO = "android:read_media_video";
     field public static final String OPSTR_RECEIVE_EMERGENCY_BROADCAST = "android:receive_emergency_broadcast";
     field public static final String OPSTR_REQUEST_DELETE_PACKAGES = "android:request_delete_packages";
     field public static final String OPSTR_REQUEST_INSTALL_PACKAGES = "android:request_install_packages";
@@ -208,6 +209,9 @@
     field public static final String OPSTR_WIFI_SCAN = "android:wifi_scan";
     field public static final String OPSTR_WRITE_CLIPBOARD = "android:write_clipboard";
     field public static final String OPSTR_WRITE_ICC_SMS = "android:write_icc_sms";
+    field public static final String OPSTR_WRITE_MEDIA_AUDIO = "android:write_media_audio";
+    field public static final String OPSTR_WRITE_MEDIA_IMAGES = "android:write_media_images";
+    field public static final String OPSTR_WRITE_MEDIA_VIDEO = "android:write_media_video";
     field public static final String OPSTR_WRITE_SMS = "android:write_sms";
     field public static final String OPSTR_WRITE_WALLPAPER = "android:write_wallpaper";
     field public static final int OP_COARSE_LOCATION = 0; // 0x0
@@ -293,10 +297,6 @@
     field @NonNull public static final android.os.Parcelable.Creator<android.app.AppOpsManager.HistoricalUidOps> CREATOR;
   }
 
-  public static interface AppOpsManager.OnOpActiveChangedListener {
-    method public void onOpActiveChanged(int, int, String, boolean);
-  }
-
   public static final class AppOpsManager.OpEntry implements android.os.Parcelable {
     method public int describeContents();
     method public long getDuration();
@@ -619,10 +619,13 @@
     method public int describeContents();
     method public static android.content.AutofillOptions forWhitelistingItself();
     method public boolean isAugmentedAutofillEnabled(@NonNull android.content.Context);
+    method public boolean isAutofillDisabledLocked(@NonNull android.content.ComponentName);
     method public void writeToParcel(android.os.Parcel, int);
     field @NonNull public static final android.os.Parcelable.Creator<android.content.AutofillOptions> CREATOR;
+    field public long appDisabledExpiration;
     field public boolean augmentedAutofillEnabled;
     field public final boolean compatModeEnabled;
+    field @Nullable public android.util.ArrayMap<java.lang.String,java.lang.Long> disabledActivities;
     field public final int loggingLevel;
     field @Nullable public android.util.ArraySet<android.content.ComponentName> whitelistedActivitiesForAugmentedAutofill;
   }
diff --git a/cmds/incidentd/src/WorkDirectory.cpp b/cmds/incidentd/src/WorkDirectory.cpp
index 0570c3a..8dcb865 100644
--- a/cmds/incidentd/src/WorkDirectory.cpp
+++ b/cmds/incidentd/src/WorkDirectory.cpp
@@ -664,7 +664,7 @@
             nanosleep(&spec, nullptr);
         }
         clock_gettime(CLOCK_REALTIME, &spec);
-        timestampNs = (spec.tv_sec) * 1000 + spec.tv_nsec;
+        timestampNs = int64_t(spec.tv_sec) * 1000 + spec.tv_nsec;
     } while (file_exists_locked(timestampNs));
     return timestampNs;
 }
diff --git a/cmds/statsd/src/stats_log.proto b/cmds/statsd/src/stats_log.proto
index b875470..d9c04f2 100644
--- a/cmds/statsd/src/stats_log.proto
+++ b/cmds/statsd/src/stats_log.proto
@@ -66,13 +66,13 @@
 message CountMetricData {
   optional DimensionsValue dimensions_in_what = 1;
 
-  optional DimensionsValue dimensions_in_condition = 2;
+  optional DimensionsValue dimensions_in_condition = 2 [deprecated = true];
 
   repeated CountBucketInfo bucket_info = 3;
 
   repeated DimensionsValue dimension_leaf_values_in_what = 4;
 
-  repeated DimensionsValue dimension_leaf_values_in_condition = 5;
+  repeated DimensionsValue dimension_leaf_values_in_condition = 5 [deprecated = true];
 }
 
 message DurationBucketInfo {
@@ -92,13 +92,13 @@
 message DurationMetricData {
   optional DimensionsValue dimensions_in_what = 1;
 
-  optional DimensionsValue dimensions_in_condition = 2;
+  optional DimensionsValue dimensions_in_condition = 2 [deprecated = true];
 
   repeated DurationBucketInfo bucket_info = 3;
 
   repeated DimensionsValue dimension_leaf_values_in_what = 4;
 
-  repeated DimensionsValue dimension_leaf_values_in_condition = 5;
+  repeated DimensionsValue dimension_leaf_values_in_condition = 5 [deprecated = true];
 }
 
 message ValueBucketInfo {
@@ -136,13 +136,13 @@
 message ValueMetricData {
   optional DimensionsValue dimensions_in_what = 1;
 
-  optional DimensionsValue dimensions_in_condition = 2;
+  optional DimensionsValue dimensions_in_condition = 2 [deprecated = true];
 
   repeated ValueBucketInfo bucket_info = 3;
 
   repeated DimensionsValue dimension_leaf_values_in_what = 4;
 
-  repeated DimensionsValue dimension_leaf_values_in_condition = 5;
+  repeated DimensionsValue dimension_leaf_values_in_condition = 5 [deprecated = true];
 }
 
 message GaugeBucketInfo {
@@ -166,13 +166,13 @@
 message GaugeMetricData {
   optional DimensionsValue dimensions_in_what = 1;
 
-  optional DimensionsValue dimensions_in_condition = 2;
+  optional DimensionsValue dimensions_in_condition = 2 [deprecated = true];
 
   repeated GaugeBucketInfo bucket_info = 3;
 
   repeated DimensionsValue dimension_leaf_values_in_what = 4;
 
-  repeated DimensionsValue dimension_leaf_values_in_condition = 5;
+  repeated DimensionsValue dimension_leaf_values_in_condition = 5 [deprecated = true];
 }
 
 message StatsLogReport {
@@ -220,7 +220,7 @@
 
   optional DimensionsValue dimensions_path_in_what = 11;
 
-  optional DimensionsValue dimensions_path_in_condition = 12;
+  optional DimensionsValue dimensions_path_in_condition = 12 [deprecated = true];
 
   // DO NOT USE field 13.
 
@@ -363,7 +363,7 @@
         repeated ConditionStats condition_stats = 14;
         repeated MetricStats metric_stats = 15;
         repeated AlertStats alert_stats = 16;
-        repeated MetricStats metric_dimension_in_condition_stats = 17;
+        repeated MetricStats metric_dimension_in_condition_stats = 17 [deprecated = true];
         message Annotation {
             optional int64 field_int64 = 1;
             optional int32 field_int32 = 2;
@@ -483,7 +483,7 @@
     message MetricValue {
         optional int64 metric_id = 1;
         optional DimensionsValue dimension_in_what = 2;
-        optional DimensionsValue dimension_in_condition = 3;
+        optional DimensionsValue dimension_in_condition = 3 [deprecated = true];
         optional int64 value = 4;
     }
     oneof value {
diff --git a/cmds/statsd/src/statsd_config.proto b/cmds/statsd/src/statsd_config.proto
index 79c06b9..1b7f398 100644
--- a/cmds/statsd/src/statsd_config.proto
+++ b/cmds/statsd/src/statsd_config.proto
@@ -182,7 +182,7 @@
 
   optional FieldMatcher dimensions_in_what = 4;
 
-  optional FieldMatcher dimensions_in_condition = 7;
+  optional FieldMatcher dimensions_in_condition = 7 [deprecated = true];
 
   optional TimeUnit bucket = 5;
 
@@ -207,7 +207,7 @@
 
   optional FieldMatcher dimensions_in_what = 6;
 
-  optional FieldMatcher dimensions_in_condition = 8;
+  optional FieldMatcher dimensions_in_condition = 8 [deprecated = true];
 
   optional TimeUnit bucket = 7;
 }
@@ -225,7 +225,7 @@
 
   optional FieldMatcher dimensions_in_what = 5;
 
-  optional FieldMatcher dimensions_in_condition = 8;
+  optional FieldMatcher dimensions_in_condition = 8 [deprecated = true];
 
   optional TimeUnit bucket = 6;
 
@@ -259,7 +259,7 @@
 
   optional FieldMatcher dimensions_in_what = 5;
 
-  optional FieldMatcher dimensions_in_condition = 9;
+  optional FieldMatcher dimensions_in_condition = 9 [deprecated = true];
 
   optional TimeUnit bucket = 6;
 
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index d20cc41..34045c9 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -17,6 +17,7 @@
 package android.app;
 
 import android.Manifest;
+import android.annotation.CallbackExecutor;
 import android.annotation.IntDef;
 import android.annotation.IntRange;
 import android.annotation.NonNull;
@@ -29,6 +30,7 @@
 import android.app.usage.UsageStatsManager;
 import android.content.Context;
 import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.pm.ParceledListSlice;
 import android.media.AudioAttributes.AttributeUsage;
 import android.os.Binder;
@@ -1095,21 +1097,27 @@
             "android:sms_financial_transactions";
 
     /** @hide Read media of audio type. */
+    @SystemApi @TestApi
     public static final String OPSTR_READ_MEDIA_AUDIO = "android:read_media_audio";
     /** @hide Write media of audio type. */
+    @SystemApi @TestApi
     public static final String OPSTR_WRITE_MEDIA_AUDIO = "android:write_media_audio";
     /** @hide Read media of video type. */
+    @SystemApi @TestApi
     public static final String OPSTR_READ_MEDIA_VIDEO = "android:read_media_video";
     /** @hide Write media of video type. */
+    @SystemApi @TestApi
     public static final String OPSTR_WRITE_MEDIA_VIDEO = "android:write_media_video";
     /** @hide Read media of image type. */
+    @SystemApi @TestApi
     public static final String OPSTR_READ_MEDIA_IMAGES = "android:read_media_images";
     /** @hide Write media of image type. */
+    @SystemApi @TestApi
     public static final String OPSTR_WRITE_MEDIA_IMAGES = "android:write_media_images";
     /** @hide Has a legacy (non-isolated) view of storage. */
-    @TestApi
-    @SystemApi
+    @SystemApi @TestApi
     public static final String OPSTR_LEGACY_STORAGE = "android:legacy_storage";
+
     /** @hide Interact with accessibility. */
     @SystemApi
     public static final String OPSTR_ACCESS_ACCESSIBILITY = "android:access_accessibility";
@@ -4281,20 +4289,17 @@
 
     /**
      * Callback for notification of changes to operation active state.
-     *
-     * @hide
      */
-    @TestApi
     public interface OnOpActiveChangedListener {
         /**
          * Called when the active state of an app op changes.
          *
-         * @param code The op code.
-         * @param uid The UID performing the operation.
+         * @param op The operation that changed.
          * @param packageName The package performing the operation.
          * @param active Whether the operation became active or inactive.
          */
-        void onOpActiveChanged(int code, int uid, String packageName, boolean active);
+        void onOpActiveChanged(@NonNull String op, int uid, @NonNull String packageName,
+                boolean active);
     }
 
     /**
@@ -4324,6 +4329,16 @@
         public void onOpChanged(int op, String packageName) { }
     }
 
+    /**
+     * Callback for notification of changes to operation state.
+     * This allows you to see the raw op codes instead of strings.
+     * @hide
+     */
+    public interface OnOpActiveChangedInternalListener extends OnOpActiveChangedListener {
+        default void onOpActiveChanged(String op, int uid, String packageName, boolean active) { }
+        default void onOpActiveChanged(int op, int uid, String packageName, boolean active) { }
+    }
+
     AppOpsManager(Context context, IAppOpsService service) {
         mContext = context;
         mService = service;
@@ -4779,6 +4794,17 @@
         }
     }
 
+    /** {@hide} */
+    @Deprecated
+    public void startWatchingActive(@NonNull int[] ops,
+            @NonNull OnOpActiveChangedListener callback) {
+        final String[] strOps = new String[ops.length];
+        for (int i = 0; i < ops.length; i++) {
+            strOps[i] = opToPublicName(ops[i]);
+        }
+        startWatchingActive(strOps, mContext.getMainExecutor(), callback);
+    }
+
     /**
      * Start watching for changes to the active state of app ops. An app op may be
      * long running and it has a clear start and stop delimiters. If an op is being
@@ -4786,26 +4812,25 @@
      * watched ops for a registered callback you need to unregister and register it
      * again.
      *
-     * <p> If you don't hold the {@link android.Manifest.permission#WATCH_APPOPS} permission
+     * <p> If you don't hold the {@code android.Manifest.permission#WATCH_APPOPS} permission
      * you can watch changes only for your UID.
      *
-     * @param ops The ops to watch.
+     * @param ops The operations to watch.
      * @param callback Where to report changes.
      *
-     * @see #isOperationActive(int, int, String)
-     * @see #stopWatchingActive(OnOpActiveChangedListener)
+     * @see #isOperationActive
+     * @see #stopWatchingActive
      * @see #startOp(int, int, String)
      * @see #finishOp(int, int, String)
-     *
-     * @hide
      */
-    @TestApi
     // TODO: Uncomment below annotation once b/73559440 is fixed
     // @RequiresPermission(value=Manifest.permission.WATCH_APPOPS, conditional=true)
-    public void startWatchingActive(@NonNull int[] ops,
+    public void startWatchingActive(@NonNull String[] ops,
+            @CallbackExecutor @NonNull Executor executor,
             @NonNull OnOpActiveChangedListener callback) {
-        Preconditions.checkNotNull(ops, "ops cannot be null");
-        Preconditions.checkNotNull(callback, "callback cannot be null");
+        Objects.requireNonNull(ops);
+        Objects.requireNonNull(executor);
+        Objects.requireNonNull(callback);
         IAppOpsActiveCallback cb;
         synchronized (mActiveWatchers) {
             cb = mActiveWatchers.get(callback);
@@ -4815,13 +4840,25 @@
             cb = new IAppOpsActiveCallback.Stub() {
                 @Override
                 public void opActiveChanged(int op, int uid, String packageName, boolean active) {
-                    callback.onOpActiveChanged(op, uid, packageName, active);
+                    executor.execute(() -> {
+                        if (callback instanceof OnOpActiveChangedInternalListener) {
+                            ((OnOpActiveChangedInternalListener) callback).onOpActiveChanged(op,
+                                    uid, packageName, active);
+                        }
+                        if (sOpToString[op] != null) {
+                            callback.onOpActiveChanged(sOpToString[op], uid, packageName, active);
+                        }
+                    });
                 }
             };
             mActiveWatchers.put(callback, cb);
         }
+        final int[] rawOps = new int[ops.length];
+        for (int i = 0; i < ops.length; i++) {
+            rawOps[i] = strOpToOp(ops[i]);
+        }
         try {
-            mService.startWatchingActive(ops, cb);
+            mService.startWatchingActive(rawOps, cb);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -4832,14 +4869,11 @@
      * long running and it has a clear start and stop delimiters. Unregistering a
      * non-registered callback has no effect.
      *
-     * @see #isOperationActive#(int, int, String)
-     * @see #startWatchingActive(int[], OnOpActiveChangedListener)
+     * @see #isOperationActive
+     * @see #startWatchingActive
      * @see #startOp(int, int, String)
      * @see #finishOp(int, int, String)
-     *
-     * @hide
      */
-    @TestApi
     public void stopWatchingActive(@NonNull OnOpActiveChangedListener callback) {
         synchronized (mActiveWatchers) {
             final IAppOpsActiveCallback cb = mActiveWatchers.remove(callback);
@@ -5449,6 +5483,19 @@
     }
 
     /**
+     * Checks whether the given op for a package is active.
+     * <p>
+     * If you don't hold the {@code android.Manifest.permission#WATCH_APPOPS}
+     * permission you can query only for your UID.
+     *
+     * @see #finishOp(int)
+     * @see #startOp(int)
+     */
+    public boolean isOpActive(@NonNull String op, int uid, @NonNull String packageName) {
+        return isOperationActive(strOpToOp(op), uid, packageName);
+    }
+
+    /**
      * Checks whether the given op for a UID and package is active.
      *
      * <p> If you don't hold the {@link android.Manifest.permission#WATCH_APPOPS} permission
diff --git a/core/java/android/app/LoadedApk.java b/core/java/android/app/LoadedApk.java
index c49ea09..8987b23 100644
--- a/core/java/android/app/LoadedApk.java
+++ b/core/java/android/app/LoadedApk.java
@@ -883,7 +883,7 @@
             }
         }
 
-        // /aepx/com.android.runtime/lib, /vendor/lib, /odm/lib and /product/lib
+        // /apex/com.android.art/lib, /vendor/lib, /odm/lib and /product/lib
         // are added to the native lib search paths of the classloader.
         // Note that this is done AFTER the classloader is
         // created by ApplicationLoaders.getDefault().getClassLoader(...). The
@@ -904,8 +904,8 @@
         // (linker namespace).
         List<String> extraLibPaths = new ArrayList<>(4);
         String abiSuffix = VMRuntime.getRuntime().is64Bit() ? "64" : "";
-        if (!defaultSearchPaths.contains("/apex/com.android.runtime/lib")) {
-            extraLibPaths.add("/apex/com.android.runtime/lib" + abiSuffix);
+        if (!defaultSearchPaths.contains("/apex/com.android.art/lib")) {
+            extraLibPaths.add("/apex/com.android.art/lib" + abiSuffix);
         }
         if (!defaultSearchPaths.contains("/vendor/lib")) {
             extraLibPaths.add("/vendor/lib" + abiSuffix);
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 372eab2..ac53118 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -3278,6 +3278,7 @@
      * @hide
      */
     @Nullable
+    @SystemApi
     public Pair<RemoteInput, Action> findRemoteInputActionPair(boolean requiresFreeform) {
         if (actions == null) {
             return null;
@@ -3304,7 +3305,8 @@
      *
      * @hide
      */
-    public List<Notification.Action> getContextualActions() {
+    @SystemApi
+    public @NonNull List<Notification.Action> getContextualActions() {
         if (actions == null) return Collections.emptyList();
 
         List<Notification.Action> contextualActions = new ArrayList<>();
@@ -7730,7 +7732,8 @@
              * @hide
              */
             @Nullable
-            public static Message getMessageFromBundle(Bundle bundle) {
+            @SystemApi
+            public static Message getMessageFromBundle(@NonNull Bundle bundle) {
                 try {
                     if (!bundle.containsKey(KEY_TEXT) || !bundle.containsKey(KEY_TIMESTAMP)) {
                         return null;
diff --git a/core/java/android/app/NotificationChannel.java b/core/java/android/app/NotificationChannel.java
index 3effd11..93e4ddc 100644
--- a/core/java/android/app/NotificationChannel.java
+++ b/core/java/android/app/NotificationChannel.java
@@ -110,6 +110,7 @@
     /**
      * @hide
      */
+    @SystemApi
     public static final int USER_LOCKED_SOUND = 0x00000020;
 
     /**
diff --git a/core/java/android/app/UiAutomation.java b/core/java/android/app/UiAutomation.java
index ed2b991..f9b96c5 100644
--- a/core/java/android/app/UiAutomation.java
+++ b/core/java/android/app/UiAutomation.java
@@ -242,7 +242,7 @@
             mUiAutomationConnection.connect(mClient, flags);
             mFlags = flags;
         } catch (RemoteException re) {
-            throw new RuntimeException("Error while connecting UiAutomation", re);
+            throw new RuntimeException("Error while connecting " + this, re);
         }
 
         synchronized (mLock) {
@@ -255,7 +255,7 @@
                     final long elapsedTimeMillis = SystemClock.uptimeMillis() - startTimeMillis;
                     final long remainingTimeMillis = CONNECT_TIMEOUT_MILLIS - elapsedTimeMillis;
                     if (remainingTimeMillis <= 0) {
-                        throw new RuntimeException("Error while connecting UiAutomation");
+                        throw new RuntimeException("Error while connecting " + this);
                     }
                     try {
                         mLock.wait(remainingTimeMillis);
@@ -290,7 +290,7 @@
         synchronized (mLock) {
             if (mIsConnecting) {
                 throw new IllegalStateException(
-                        "Cannot call disconnect() while connecting!");
+                        "Cannot call disconnect() while connecting " + this);
             }
             throwIfNotConnectedLocked();
             mConnectionId = CONNECTION_ID_UNDEFINED;
@@ -299,7 +299,7 @@
             // Calling out without a lock held.
             mUiAutomationConnection.disconnect();
         } catch (RemoteException re) {
-            throw new RuntimeException("Error while disconnecting UiAutomation", re);
+            throw new RuntimeException("Error while disconnecting " + this, re);
         } finally {
             mRemoteCallbackThread.quit();
             mRemoteCallbackThread = null;
@@ -1184,19 +1184,29 @@
         return result;
     }
 
+    @Override
+    public String toString() {
+        final StringBuilder stringBuilder = new StringBuilder();
+        stringBuilder.append("UiAutomation@").append(Integer.toHexString(hashCode()));
+        stringBuilder.append("[id=").append(mConnectionId);
+        stringBuilder.append(", flags=").append(mFlags);
+        stringBuilder.append("]");
+        return stringBuilder.toString();
+    }
+
     private boolean isConnectedLocked() {
         return mConnectionId != CONNECTION_ID_UNDEFINED;
     }
 
     private void throwIfConnectedLocked() {
         if (mConnectionId != CONNECTION_ID_UNDEFINED) {
-            throw new IllegalStateException("UiAutomation not connected!");
+            throw new IllegalStateException("UiAutomation not connected, " + this);
         }
     }
 
     private void throwIfNotConnectedLocked() {
         if (!isConnectedLocked()) {
-            throw new IllegalStateException("UiAutomation not connected!");
+            throw new IllegalStateException("UiAutomation not connected, " + this);
         }
     }
 
@@ -1220,6 +1230,9 @@
                         mConnectionId = connectionId;
                         mLock.notifyAll();
                     }
+                    if (Build.IS_DEBUGGABLE) {
+                        Log.v(LOG_TAG, "Init " + UiAutomation.this);
+                    }
                 }
 
                 @Override
diff --git a/core/java/android/content/AutofillOptions.java b/core/java/android/content/AutofillOptions.java
index 8fb9501..082663e 100644
--- a/core/java/android/content/AutofillOptions.java
+++ b/core/java/android/content/AutofillOptions.java
@@ -21,6 +21,8 @@
 import android.app.ActivityThread;
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.os.SystemClock;
+import android.util.ArrayMap;
 import android.util.ArraySet;
 import android.util.Log;
 import android.view.autofill.AutofillManager;
@@ -62,6 +64,18 @@
     @Nullable
     public ArraySet<ComponentName> whitelistedActivitiesForAugmentedAutofill;
 
+    /**
+     * The package disable expiration by autofill service.
+     */
+    public long appDisabledExpiration;
+
+    /**
+     * The disabled Activities of the package. key is component name string, value is when they
+     * will be enabled.
+     */
+    @Nullable
+    public ArrayMap<String, Long> disabledActivities;
+
     public AutofillOptions(int loggingLevel, boolean compatModeEnabled) {
         this.loggingLevel = loggingLevel;
         this.compatModeEnabled = compatModeEnabled;
@@ -82,6 +96,27 @@
     }
 
     /**
+     * Returns if autofill is disabled by service to the given activity.
+     */
+    public boolean isAutofillDisabledLocked(@NonNull ComponentName componentName) {
+        final long elapsedTime = SystemClock.elapsedRealtime();
+        final String component = componentName.flattenToString();
+        // Check app first.
+        if (appDisabledExpiration >= elapsedTime) return true;
+
+        // Then check activities.
+        if (disabledActivities != null) {
+            final Long expiration = disabledActivities.get(component);
+            if (expiration != null) {
+                if (expiration >= elapsedTime) return true;
+                disabledActivities.remove(component);
+            }
+        }
+        appDisabledExpiration = 0;
+        return false;
+    }
+
+    /**
      * @hide
      */
     @TestApi
@@ -110,7 +145,8 @@
     @Override
     public String toString() {
         return "AutofillOptions [loggingLevel=" + loggingLevel + ", compatMode=" + compatModeEnabled
-                + ", augmentedAutofillEnabled=" + augmentedAutofillEnabled + "]";
+                + ", augmentedAutofillEnabled=" + augmentedAutofillEnabled
+                + ", appDisabledExpiration=" + appDisabledExpiration + "]";
     }
 
     /** @hide */
@@ -122,6 +158,11 @@
             pw.print(", whitelistedActivitiesForAugmentedAutofill=");
             pw.print(whitelistedActivitiesForAugmentedAutofill);
         }
+        pw.print(", appDisabledExpiration="); pw.print(appDisabledExpiration);
+        if (disabledActivities != null) {
+            pw.print(", disabledActivities=");
+            pw.print(disabledActivities);
+        }
     }
 
     @Override
@@ -135,6 +176,16 @@
         parcel.writeBoolean(compatModeEnabled);
         parcel.writeBoolean(augmentedAutofillEnabled);
         parcel.writeArraySet(whitelistedActivitiesForAugmentedAutofill);
+        parcel.writeLong(appDisabledExpiration);
+        final int size = disabledActivities != null ? disabledActivities.size() : 0;
+        parcel.writeInt(size);
+        if (size > 0) {
+            for (int i = 0; i < size; i++) {
+                final String key = disabledActivities.keyAt(i);
+                parcel.writeString(key);
+                parcel.writeLong(disabledActivities.get(key));
+            }
+        }
     }
 
     public static final @android.annotation.NonNull Parcelable.Creator<AutofillOptions> CREATOR =
@@ -148,6 +199,14 @@
                     options.augmentedAutofillEnabled = parcel.readBoolean();
                     options.whitelistedActivitiesForAugmentedAutofill =
                             (ArraySet<ComponentName>) parcel.readArraySet(null);
+                    options.appDisabledExpiration = parcel.readLong();
+                    final int size = parcel.readInt();
+                    if (size > 0) {
+                        options.disabledActivities = new ArrayMap<>();
+                        for (int i = 0; i < size; i++) {
+                            options.disabledActivities.put(parcel.readString(), parcel.readLong());
+                        }
+                    }
                     return options;
                 }
 
diff --git a/core/java/android/content/ContentProvider.java b/core/java/android/content/ContentProvider.java
index 65c11d7..f297c06 100644
--- a/core/java/android/content/ContentProvider.java
+++ b/core/java/android/content/ContentProvider.java
@@ -1033,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/gesture/GestureLibraries.java b/core/java/android/gesture/GestureLibraries.java
index 611d9ab..5e31ce6 100644
--- a/core/java/android/gesture/GestureLibraries.java
+++ b/core/java/android/gesture/GestureLibraries.java
@@ -16,14 +16,16 @@
 
 package android.gesture;
 
+import android.annotation.NonNull;
 import android.annotation.RawRes;
+import android.os.ParcelFileDescriptor;
 import android.util.Log;
 import static android.gesture.GestureConstants.*;
 import android.content.Context;
 
 import java.io.File;
+import java.io.FileDescriptor;
 import java.io.FileOutputStream;
-import java.io.FileNotFoundException;
 import java.io.IOException;
 import java.io.FileInputStream;
 import java.io.InputStream;
@@ -41,6 +43,11 @@
         return new FileGestureLibrary(path);
     }
 
+    @NonNull
+    public static GestureLibrary fromFileDescriptor(@NonNull ParcelFileDescriptor pfd) {
+        return new FileGestureLibrary(pfd.getFileDescriptor());
+    }
+
     public static GestureLibrary fromPrivateFile(Context context, String name) {
         return fromFile(context.getFileStreamPath(name));
     }
@@ -50,55 +57,83 @@
     }
 
     private static class FileGestureLibrary extends GestureLibrary {
+        // Either a file or an fd is used
         private final File mPath;
+        private final FileDescriptor mFd;
 
         public FileGestureLibrary(File path) {
             mPath = path;
+            mFd = null;
         }
 
+        public FileGestureLibrary(FileDescriptor fd) {
+            mPath = null;
+            mFd = fd;
+        }
+
+        /**
+         * <p>If this GestureLibrary was created using a FileDescriptor,
+         * this method will always return false.
+         */
         @Override
         public boolean isReadOnly() {
-            return !mPath.canWrite();
+            if (mPath != null) {
+                return !mPath.canWrite();
+            }
+            return false;
         }
 
         public boolean save() {
             if (!mStore.hasChanged()) return true;
+            boolean result = false;
 
-            final File file = mPath;
+            if (mPath != null) {
+                final File file = mPath;
 
-            final File parentFile = file.getParentFile();
-            if (!parentFile.exists()) {
-                if (!parentFile.mkdirs()) {
-                    return false;
+                final File parentFile = file.getParentFile();
+                if (!parentFile.exists()) {
+                    if (!parentFile.mkdirs()) {
+                        return false;
+                    }
+                }
+
+                try {
+                    //noinspection ResultOfMethodCallIgnored
+                    file.createNewFile();
+                    mStore.save(new FileOutputStream(file), true);
+                    result = true;
+                } catch (IOException e) {
+                    Log.d(LOG_TAG, "Could not save the gesture library in " + mPath, e);
+                }
+            } else {
+                try {
+                    mStore.save(new FileOutputStream(mFd), true);
+                    result = true;
+                } catch (IOException e) {
+                    Log.d(LOG_TAG, "Could not save the gesture library", e);
                 }
             }
-
-            boolean result = false;
-            try {
-                //noinspection ResultOfMethodCallIgnored
-                file.createNewFile();
-                mStore.save(new FileOutputStream(file), true);
-                result = true;
-            } catch (FileNotFoundException e) {
-                Log.d(LOG_TAG, "Could not save the gesture library in " + mPath, e);
-            } catch (IOException e) {
-                Log.d(LOG_TAG, "Could not save the gesture library in " + mPath, e);
-            }
-
             return result;
         }
 
         public boolean load() {
             boolean result = false;
-            final File file = mPath;
-            if (file.exists() && file.canRead()) {
+            if (mPath != null) {
+                final File file = mPath;
+                if (file.exists() && file.canRead()) {
+                    try {
+                        mStore.load(new FileInputStream(file), true);
+                        result = true;
+                    } catch (IOException e) {
+                        Log.d(LOG_TAG, "Could not load the gesture library from " + mPath, e);
+                    }
+                }
+            } else {
                 try {
-                    mStore.load(new FileInputStream(file), true);
+                    mStore.load(new FileInputStream(mFd), true);
                     result = true;
-                } catch (FileNotFoundException e) {
-                    Log.d(LOG_TAG, "Could not load the gesture library from " + mPath, e);
                 } catch (IOException e) {
-                    Log.d(LOG_TAG, "Could not load the gesture library from " + mPath, e);
+                    Log.d(LOG_TAG, "Could not load the gesture library", e);
                 }
             }
 
diff --git a/core/java/android/hardware/camera2/CameraCharacteristics.java b/core/java/android/hardware/camera2/CameraCharacteristics.java
index 1b31792..fbcc785 100644
--- a/core/java/android/hardware/camera2/CameraCharacteristics.java
+++ b/core/java/android/hardware/camera2/CameraCharacteristics.java
@@ -1955,6 +1955,7 @@
      *   <li>{@link #REQUEST_AVAILABLE_CAPABILITIES_LOGICAL_MULTI_CAMERA LOGICAL_MULTI_CAMERA}</li>
      *   <li>{@link #REQUEST_AVAILABLE_CAPABILITIES_MONOCHROME MONOCHROME}</li>
      *   <li>{@link #REQUEST_AVAILABLE_CAPABILITIES_SECURE_IMAGE_DATA SECURE_IMAGE_DATA}</li>
+     *   <li>{@link #REQUEST_AVAILABLE_CAPABILITIES_SYSTEM_CAMERA SYSTEM_CAMERA}</li>
      * </ul></p>
      * <p>This key is available on all devices.</p>
      *
@@ -1973,6 +1974,7 @@
      * @see #REQUEST_AVAILABLE_CAPABILITIES_LOGICAL_MULTI_CAMERA
      * @see #REQUEST_AVAILABLE_CAPABILITIES_MONOCHROME
      * @see #REQUEST_AVAILABLE_CAPABILITIES_SECURE_IMAGE_DATA
+     * @see #REQUEST_AVAILABLE_CAPABILITIES_SYSTEM_CAMERA
      */
     @PublicKey
     @NonNull
diff --git a/core/java/android/hardware/camera2/CameraMetadata.java b/core/java/android/hardware/camera2/CameraMetadata.java
index a0170da..2fa6125 100644
--- a/core/java/android/hardware/camera2/CameraMetadata.java
+++ b/core/java/android/hardware/camera2/CameraMetadata.java
@@ -996,6 +996,14 @@
      */
     public static final int REQUEST_AVAILABLE_CAPABILITIES_SECURE_IMAGE_DATA = 13;
 
+    /**
+     * <p>The camera device is only accessible by Android's system components and privileged
+     * applications. Processes need to have the android.permission.SYSTEM_CAMERA in
+     * addition to android.permission.CAMERA in order to connect to this camera device.</p>
+     * @see CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES
+     */
+    public static final int REQUEST_AVAILABLE_CAPABILITIES_SYSTEM_CAMERA = 14;
+
     //
     // Enumeration values for CameraCharacteristics#SCALER_CROPPING_TYPE
     //
diff --git a/core/java/android/hardware/camera2/legacy/SurfaceTextureRenderer.java b/core/java/android/hardware/camera2/legacy/SurfaceTextureRenderer.java
index 1b28d61..a4c65ae 100644
--- a/core/java/android/hardware/camera2/legacy/SurfaceTextureRenderer.java
+++ b/core/java/android/hardware/camera2/legacy/SurfaceTextureRenderer.java
@@ -28,7 +28,6 @@
 import android.opengl.GLES11Ext;
 import android.opengl.GLES20;
 import android.opengl.Matrix;
-import android.text.format.Time;
 import android.util.Log;
 import android.util.Pair;
 import android.util.Size;
@@ -39,9 +38,14 @@
 import java.nio.ByteBuffer;
 import java.nio.ByteOrder;
 import java.nio.FloatBuffer;
+import java.time.Instant;
+import java.time.LocalDateTime;
+import java.time.ZoneId;
+import java.time.format.DateTimeFormatter;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.List;
+import java.util.Locale;
 
 /**
  * A renderer class that manages the GL state, and can draw a frame into a set of output
@@ -63,6 +67,9 @@
     private static final int FLIP_TYPE_VERTICAL = 2;
     private static final int FLIP_TYPE_BOTH = FLIP_TYPE_HORIZONTAL | FLIP_TYPE_VERTICAL;
 
+    private static final DateTimeFormatter LOG_NAME_TIME_FORMATTER =
+            DateTimeFormatter.ofPattern("yyyyMMdd'T'HHmmss", Locale.ROOT);
+
     private EGLDisplay mEGLDisplay = EGL14.EGL_NO_DISPLAY;
     private EGLContext mEGLContext = EGL14.EGL_NO_CONTEXT;
     private EGLConfig mConfigs;
@@ -624,9 +631,7 @@
         path.append(File.separator);
         path.append("durations_");
 
-        Time now = new Time();
-        now.setToNow();
-        path.append(now.format2445());
+        path.append(formatTimestamp(System.currentTimeMillis()));
         path.append("_S");
         for (EGLSurfaceHolder surface : mSurfaces) {
             path.append(String.format("_%d_%d", surface.width, surface.height));
@@ -639,6 +644,15 @@
         mPerfMeasurer.dumpPerformanceData(path.toString());
     }
 
+    private static String formatTimestamp(long timeMillis) {
+        // This is a replacement for {@link Time#format2445()} that doesn't suffer from Y2038
+        // issues.
+        Instant instant = Instant.ofEpochMilli(timeMillis);
+        ZoneId zoneId = ZoneId.systemDefault();
+        LocalDateTime localDateTime = LocalDateTime.ofInstant(instant, zoneId);
+        return LOG_NAME_TIME_FORMATTER.format(localDateTime);
+    }
+
     private void setupGlTiming() {
         if (PerfMeasurement.isGlTimingSupported()) {
             Log.d(TAG, "Enabling GL performance measurement");
diff --git a/core/java/android/os/image/DynamicSystemManager.java b/core/java/android/os/image/DynamicSystemManager.java
index e4f88c5..77fd946 100644
--- a/core/java/android/os/image/DynamicSystemManager.java
+++ b/core/java/android/os/image/DynamicSystemManager.java
@@ -20,6 +20,7 @@
 import android.annotation.SystemService;
 import android.content.Context;
 import android.gsi.GsiProgress;
+import android.os.ParcelFileDescriptor;
 import android.os.RemoteException;
 
 /**
@@ -52,22 +53,39 @@
     /** The DynamicSystemManager.Session represents a started session for the installation. */
     public class Session {
         private Session() {}
+
         /**
-         * Write a chunk of the DynamicSystem system image
+         * Set the file descriptor that points to a ashmem which will be used
+         * to fetch data during the submitFromAshmem.
          *
-         * @return {@code true} if the call succeeds. {@code false} if there is any native runtime
-         *     error.
+         * @param ashmem fd that points to a ashmem
+         * @param size size of the ashmem file
          */
         @RequiresPermission(android.Manifest.permission.MANAGE_DYNAMIC_SYSTEM)
-        public boolean write(byte[] buf) {
+        public boolean setAshmem(ParcelFileDescriptor ashmem, long size) {
             try {
-                return mService.write(buf);
+                return mService.setAshmem(ashmem, size);
             } catch (RemoteException e) {
                 throw new RuntimeException(e.toString());
             }
         }
 
         /**
+         * Submit bytes to the DSU partition from the ashmem previously set with
+         * setAshmem.
+         *
+         * @param size Number of bytes
+         * @return true on success, false otherwise.
+         */
+        @RequiresPermission(android.Manifest.permission.MANAGE_DYNAMIC_SYSTEM)
+        public boolean submitFromAshmem(int size) {
+            try {
+                return mService.submitFromAshmem(size);
+            } catch (RemoteException e) {
+                throw new RuntimeException(e.toString());
+            }
+        }
+        /**
          * Finish write and make device to boot into the it after reboot.
          *
          * @return {@code true} if the call succeeds. {@code false} if there is any native runtime
diff --git a/core/java/android/os/image/IDynamicSystemService.aidl b/core/java/android/os/image/IDynamicSystemService.aidl
index 2f4ab2d..a6de170 100644
--- a/core/java/android/os/image/IDynamicSystemService.aidl
+++ b/core/java/android/os/image/IDynamicSystemService.aidl
@@ -79,10 +79,20 @@
     boolean setEnable(boolean enable, boolean oneShot);
 
     /**
-     * Write a chunk of the DynamicSystem system image
+     * Set the file descriptor that points to a ashmem which will be used
+     * to fetch data during the submitFromAshmem.
      *
-     * @return true if the call succeeds
+     * @param fd            fd that points to a ashmem
+     * @param size          size of the ashmem file
      */
-    boolean write(in byte[] buf);
+    boolean setAshmem(in ParcelFileDescriptor fd, long size);
 
+    /**
+     * Submit bytes to the DSU partition from the ashmem previously set with
+     * setAshmem.
+     *
+     * @param bytes         number of bytes that can be read from stream.
+     * @return              true on success, false otherwise.
+     */
+    boolean submitFromAshmem(long bytes);
 }
diff --git a/core/java/android/provider/DeviceConfig.java b/core/java/android/provider/DeviceConfig.java
index 4f7c8c5..bc03e51 100644
--- a/core/java/android/provider/DeviceConfig.java
+++ b/core/java/android/provider/DeviceConfig.java
@@ -706,16 +706,11 @@
         synchronized (sLock) {
             for (int i = 0; i < sListeners.size(); i++) {
                 if (namespace.equals(sListeners.valueAt(i).first)) {
-                    final int j = i;
-                    sListeners.valueAt(i).second.execute(new Runnable() {
-                        @Override
-                        public void run() {
-                            Map<String, String> propertyMap = new HashMap(1);
-                            propertyMap.put(name, value);
-                            sListeners.keyAt(j)
-                                    .onPropertiesChanged(new Properties(namespace, propertyMap));
-                        }
-
+                    final OnPropertiesChangedListener listener = sListeners.keyAt(i);
+                    sListeners.valueAt(i).second.execute(() -> {
+                        Map<String, String> propertyMap = new ArrayMap<>(1);
+                        propertyMap.put(name, value);
+                        listener.onPropertiesChanged(new Properties(namespace, propertyMap));
                     });
                 }
             }
diff --git a/core/java/android/service/notification/StatusBarNotification.java b/core/java/android/service/notification/StatusBarNotification.java
index 905c781..205df7e 100644
--- a/core/java/android/service/notification/StatusBarNotification.java
+++ b/core/java/android/service/notification/StatusBarNotification.java
@@ -17,6 +17,7 @@
 package android.service.notification;
 
 import android.annotation.NonNull;
+import android.annotation.SystemApi;
 import android.annotation.UnsupportedAppUsage;
 import android.app.Notification;
 import android.app.NotificationManager;
@@ -172,6 +173,7 @@
      *
      * @hide
      */
+    @SystemApi
     public boolean isAppGroup() {
         if (getNotification().getGroup() != null || getNotification().getSortKey() != null) {
             return true;
diff --git a/core/java/android/service/storage/ExternalStorageService.java b/core/java/android/service/storage/ExternalStorageService.java
new file mode 100644
index 0000000..cc8116d0
--- /dev/null
+++ b/core/java/android/service/storage/ExternalStorageService.java
@@ -0,0 +1,168 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.storage;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.SdkConstant;
+import android.annotation.SystemApi;
+import android.app.Service;
+import android.content.Intent;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.ParcelFileDescriptor;
+import android.os.ParcelableException;
+import android.os.RemoteCallback;
+import android.os.RemoteException;
+
+import java.io.IOException;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * A service to handle filesystem I/O from other apps.
+ *
+ * <p>To extend this class, you must declare the service in your manifest file with the
+ * {@link android.Manifest.permission#BIND_EXTERNAL_STORAGE_SERVICE} permission,
+ * and include an intent filter with the {@link #SERVICE_INTERFACE} action.
+ * For example:</p>
+ * <pre>
+ *     &lt;service android:name=".ExternalStorageServiceImpl"
+ *             android:exported="true"
+ *             android:priority="100"
+ *             android:permission="android.permission.BIND_EXTERNAL_STORAGE_SERVICE"&gt;
+ *         &lt;intent-filter&gt;
+ *             &lt;action android:name="android.service.storage.ExternalStorageService" /&gt;
+ *         &lt;/intent-filter&gt;
+ *     &lt;/service&gt;
+ * </pre>
+ * @hide
+ */
+@SystemApi
+public abstract class ExternalStorageService extends Service {
+    /**
+     * The Intent action that a service must respond to. Add it as an intent filter in the
+     * manifest declaration of the implementing service.
+     */
+    @SdkConstant(SdkConstant.SdkConstantType.SERVICE_ACTION)
+    public static final String SERVICE_INTERFACE = "android.service.storage.ExternalStorageService";
+    /**
+     * Whether the session associated with the device file descriptor when calling
+     * {@link #onStartSession} is a FUSE session.
+     */
+    public static final int FLAG_SESSION_TYPE_FUSE = 1 << 0;
+
+    /**
+     * Whether the upper file system path specified when calling {@link #onStartSession}
+     * should be indexed.
+     */
+    public static final int FLAG_SESSION_ATTRIBUTE_INDEXABLE = 1 << 1;
+
+    /**
+     * {@link Bundle} key for a {@link String} value.
+     *
+     * {@hide}
+     */
+    public static final String EXTRA_SESSION_ID =
+            "android.service.storage.extra.session_id";
+    /**
+     * {@link Bundle} key for a {@link ParcelableException} value.
+     *
+     * {@hide}
+     */
+    public static final String EXTRA_ERROR =
+            "android.service.storage.extra.error";
+
+    /** @hide */
+    @IntDef(flag = true, prefix = {"FLAG_SESSION_"},
+        value = {FLAG_SESSION_TYPE_FUSE, FLAG_SESSION_ATTRIBUTE_INDEXABLE})
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface SessionFlag {}
+
+    private final ExternalStorageServiceWrapper mWrapper = new ExternalStorageServiceWrapper();
+    private final Handler mHandler = new Handler(Looper.getMainLooper(), null, true);
+
+    /**
+     * Called when the system starts a session associated with {@code deviceFd}
+     * identified by {@code sessionId} to handle filesystem I/O for other apps. The type of
+     * session and other attributes are passed in {@code flag}.
+     *
+     * <p> I/O is received as requests originating from {@code upperFileSystemPath} on
+     * {@code deviceFd}. Implementors should handle the I/O by responding to these requests
+     * using the data on the {@code lowerFileSystemPath}.
+     *
+     * <p> Additional calls to start a session for the same {@code sessionId} while the session
+     * is still starting or already started should have no effect.
+     */
+    public abstract void onStartSession(@NonNull String sessionId, @SessionFlag int flag,
+            @NonNull ParcelFileDescriptor deviceFd, @NonNull String upperFileSystemPath,
+            @NonNull String lowerFileSystemPath) throws IOException;
+
+    /**
+     * Called when the system ends the session identified by {@code sessionId}. Implementors should
+     * stop handling filesystem I/O and clean up resources from the ended session.
+     *
+     * <p> Additional calls to end a session for the same {@code sessionId} while the session
+     * is still ending or has not started should have no effect.
+     */
+    public abstract void onEndSession(@NonNull String sessionId) throws IOException;
+
+    @Override
+    @NonNull
+    public final IBinder onBind(@NonNull Intent intent) {
+        return mWrapper;
+    }
+
+    private class ExternalStorageServiceWrapper extends IExternalStorageService.Stub {
+        @Override
+        public void startSession(String sessionId, @SessionFlag int flag,
+                ParcelFileDescriptor deviceFd, String upperPath, String lowerPath,
+                RemoteCallback callback) throws RemoteException {
+            mHandler.post(() -> {
+                try {
+                    onStartSession(sessionId, flag, deviceFd, upperPath, lowerPath);
+                    sendResult(sessionId, null /* throwable */, callback);
+                } catch (Throwable t) {
+                    sendResult(sessionId, t, callback);
+                }
+            });
+        }
+
+        @Override
+        public void endSession(String sessionId, RemoteCallback callback) throws RemoteException {
+            mHandler.post(() -> {
+                try {
+                    onEndSession(sessionId);
+                    sendResult(sessionId, null /* throwable */, callback);
+                } catch (Throwable t) {
+                    sendResult(sessionId, t, callback);
+                }
+            });
+        }
+
+        private void sendResult(String sessionId, Throwable throwable, RemoteCallback callback) {
+            Bundle bundle = new Bundle();
+            bundle.putString(EXTRA_SESSION_ID, sessionId);
+            if (throwable != null) {
+                bundle.putParcelable(EXTRA_ERROR, new ParcelableException(throwable));
+            }
+            callback.sendResult(bundle);
+        }
+    }
+}
diff --git a/core/java/android/service/storage/IExternalStorageService.aidl b/core/java/android/service/storage/IExternalStorageService.aidl
new file mode 100644
index 0000000..ae46f1f
--- /dev/null
+++ b/core/java/android/service/storage/IExternalStorageService.aidl
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.storage;
+
+import android.os.ParcelFileDescriptor;
+import android.os.RemoteCallback;
+
+/**
+ * @hide
+ */
+oneway interface IExternalStorageService
+{
+    void startSession(@utf8InCpp String sessionId, int type, in ParcelFileDescriptor deviceFd,
+         @utf8InCpp String upperPath, @utf8InCpp String lowerPath, in RemoteCallback callback);
+    void endSession(@utf8InCpp String sessionId, in RemoteCallback callback);
+}
\ No newline at end of file
diff --git a/core/java/android/service/textclassifier/TextClassifierService.java b/core/java/android/service/textclassifier/TextClassifierService.java
index 30c4e90..9108c33 100644
--- a/core/java/android/service/textclassifier/TextClassifierService.java
+++ b/core/java/android/service/textclassifier/TextClassifierService.java
@@ -460,6 +460,7 @@
                 return;
             }
             try {
+                Slog.w(LOG_TAG, "Request fail: " + error);
                 callback.onFailure();
             } catch (RemoteException e) {
                 Slog.d(LOG_TAG, "Error calling callback");
diff --git a/core/java/android/speech/tts/TextToSpeech.java b/core/java/android/speech/tts/TextToSpeech.java
index 100774c..44446ad0 100644
--- a/core/java/android/speech/tts/TextToSpeech.java
+++ b/core/java/android/speech/tts/TextToSpeech.java
@@ -16,6 +16,7 @@
 package android.speech.tts;
 
 import android.annotation.IntDef;
+import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.RawRes;
 import android.annotation.SdkConstant;
@@ -858,23 +859,20 @@
         }
 
         // Post connection case
-        runActionNoReconnect(new Action<Void>() {
-            @Override
-            public Void run(ITextToSpeechService service) throws RemoteException {
-                service.setCallback(getCallerIdentity(), null);
-                service.stop(getCallerIdentity());
-                mServiceConnection.disconnect();
-                // Context#unbindService does not result in a call to
-                // ServiceConnection#onServiceDisconnected. As a result, the
-                // service ends up being destroyed (if there are no other open
-                // connections to it) but the process lives on and the
-                // ServiceConnection continues to refer to the destroyed service.
-                //
-                // This leads to tons of log spam about SynthThread being dead.
-                mServiceConnection = null;
-                mCurrentEngine = null;
-                return null;
-            }
+        runActionNoReconnect((ITextToSpeechService service) -> {
+            service.setCallback(getCallerIdentity(), null);
+            service.stop(getCallerIdentity());
+            mServiceConnection.disconnect();
+            // Context#unbindService does not result in a call to
+            // ServiceConnection#onServiceDisconnected. As a result, the
+            // service ends up being destroyed (if there are no other open
+            // connections to it) but the process lives on and the
+            // ServiceConnection continues to refer to the destroyed service.
+            //
+            // This leads to tons of log spam about SynthThread being dead.
+            mServiceConnection = null;
+            mCurrentEngine = null;
+            return null;
         }, null, "shutdown", false);
     }
 
@@ -1105,17 +1103,14 @@
                      final int queueMode,
                      final Bundle params,
                      final String utteranceId) {
-        return runAction(new Action<Integer>() {
-            @Override
-            public Integer run(ITextToSpeechService service) throws RemoteException {
-                Uri utteranceUri = mUtterances.get(text);
-                if (utteranceUri != null) {
-                    return service.playAudio(getCallerIdentity(), utteranceUri, queueMode,
-                            getParams(params), utteranceId);
-                } else {
-                    return service.speak(getCallerIdentity(), text, queueMode, getParams(params),
-                            utteranceId);
-                }
+        return runAction((ITextToSpeechService service) -> {
+            Uri utteranceUri = mUtterances.get(text);
+            if (utteranceUri != null) {
+                return service.playAudio(getCallerIdentity(), utteranceUri, queueMode,
+                        getParams(params), utteranceId);
+            } else {
+                return service.speak(getCallerIdentity(), text, queueMode, getParams(params),
+                        utteranceId);
             }
         }, ERROR, "speak");
     }
@@ -1178,16 +1173,13 @@
      */
     public int playEarcon(final String earcon, final int queueMode,
             final Bundle params, final String utteranceId) {
-        return runAction(new Action<Integer>() {
-            @Override
-            public Integer run(ITextToSpeechService service) throws RemoteException {
-                Uri earconUri = mEarcons.get(earcon);
-                if (earconUri == null) {
-                    return ERROR;
-                }
-                return service.playAudio(getCallerIdentity(), earconUri, queueMode,
-                        getParams(params), utteranceId);
+        return runAction((ITextToSpeechService service) -> {
+            Uri earconUri = mEarcons.get(earcon);
+            if (earconUri == null) {
+                return ERROR;
             }
+            return service.playAudio(getCallerIdentity(), earconUri, queueMode,
+                    getParams(params), utteranceId);
         }, ERROR, "playEarcon");
     }
 
@@ -1242,12 +1234,9 @@
      */
     public int playSilentUtterance(final long durationInMs, final int queueMode,
             final String utteranceId) {
-        return runAction(new Action<Integer>() {
-            @Override
-            public Integer run(ITextToSpeechService service) throws RemoteException {
-                return service.playSilence(getCallerIdentity(), durationInMs,
-                                           queueMode, utteranceId);
-            }
+        return runAction((ITextToSpeechService service) -> {
+            return service.playSilence(getCallerIdentity(), durationInMs,
+                                        queueMode, utteranceId);
         }, ERROR, "playSilentUtterance");
     }
 
@@ -1302,26 +1291,23 @@
      */
     @Deprecated
     public Set<String> getFeatures(final Locale locale) {
-        return runAction(new Action<Set<String>>() {
-            @Override
-            public Set<String> run(ITextToSpeechService service) throws RemoteException {
-                String[] features = null;
-                try {
-                    features = service.getFeaturesForLanguage(
-                        locale.getISO3Language(), locale.getISO3Country(), locale.getVariant());
-                } catch(MissingResourceException e) {
-                    Log.w(TAG, "Couldn't retrieve 3 letter ISO 639-2/T language and/or ISO 3166 " +
-                            "country code for locale: " + locale, e);
-                    return null;
-                }
-
-                if (features != null) {
-                    final Set<String> featureSet = new HashSet<String>();
-                    Collections.addAll(featureSet, features);
-                    return featureSet;
-                }
+        return runAction((ITextToSpeechService service) -> {
+            String[] features = null;
+            try {
+                features = service.getFeaturesForLanguage(
+                    locale.getISO3Language(), locale.getISO3Country(), locale.getVariant());
+            } catch (MissingResourceException e) {
+                Log.w(TAG, "Couldn't retrieve 3 letter ISO 639-2/T language and/or ISO 3166 "
+                        + "country code for locale: " + locale, e);
                 return null;
             }
+
+            if (features != null) {
+                final Set<String> featureSet = new HashSet<String>();
+                Collections.addAll(featureSet, features);
+                return featureSet;
+            }
+            return null;
         }, null, "getFeatures");
     }
 
@@ -1334,11 +1320,8 @@
      * @return {@code true} if the TTS engine is speaking.
      */
     public boolean isSpeaking() {
-        return runAction(new Action<Boolean>() {
-            @Override
-            public Boolean run(ITextToSpeechService service) throws RemoteException {
-                return service.isSpeaking();
-            }
+        return runAction((ITextToSpeechService service) -> {
+            return service.isSpeaking();
         }, false, "isSpeaking");
     }
 
@@ -1349,11 +1332,8 @@
      * @return {@link #ERROR} or {@link #SUCCESS}.
      */
     public int stop() {
-        return runAction(new Action<Integer>() {
-            @Override
-            public Integer run(ITextToSpeechService service) throws RemoteException {
-                return service.stop(getCallerIdentity());
-            }
+        return runAction((ITextToSpeechService service) -> {
+            return service.stop(getCallerIdentity());
         }, ERROR, "stop");
     }
 
@@ -1447,13 +1427,10 @@
      */
     @Deprecated
     public Locale getDefaultLanguage() {
-        return runAction(new Action<Locale>() {
-            @Override
-            public Locale run(ITextToSpeechService service) throws RemoteException {
-                String[] defaultLanguage = service.getClientDefaultLanguage();
+        return runAction((ITextToSpeechService service) -> {
+            String[] defaultLanguage = service.getClientDefaultLanguage();
 
-                return new Locale(defaultLanguage[0], defaultLanguage[1], defaultLanguage[2]);
-            }
+            return new Locale(defaultLanguage[0], defaultLanguage[1], defaultLanguage[2]);
         }, null, "getDefaultLanguage");
     }
 
@@ -1474,83 +1451,80 @@
      *         {@link #LANG_MISSING_DATA} and {@link #LANG_NOT_SUPPORTED}.
      */
     public int setLanguage(final Locale loc) {
-        return runAction(new Action<Integer>() {
-            @Override
-            public Integer run(ITextToSpeechService service) throws RemoteException {
-                if (loc == null) {
-                    return LANG_NOT_SUPPORTED;
-                }
-                String language = null, country = null;
-                try {
-                    language = loc.getISO3Language();
-                } catch (MissingResourceException e) {
-                    Log.w(TAG, "Couldn't retrieve ISO 639-2/T language code for locale: " + loc, e);
-                    return LANG_NOT_SUPPORTED;
-                }
-
-                try {
-                    country = loc.getISO3Country();
-                } catch (MissingResourceException e) {
-                    Log.w(TAG, "Couldn't retrieve ISO 3166 country code for locale: " + loc, e);
-                    return LANG_NOT_SUPPORTED;
-                }
-
-                String variant = loc.getVariant();
-
-                // As of API level 21, setLanguage is implemented using setVoice.
-                // (which, in the default implementation, will call loadLanguage on the service
-                // interface).
-
-                // Sanitize locale using isLanguageAvailable.
-                int result = service.isLanguageAvailable(language, country, variant);
-                if (result >= LANG_AVAILABLE) {
-                    // Get the default voice for the locale.
-                    String voiceName = service.getDefaultVoiceNameFor(language, country, variant);
-                    if (TextUtils.isEmpty(voiceName)) {
-                        Log.w(TAG, "Couldn't find the default voice for " + language + "-" +
-                                country + "-" + variant);
-                        return LANG_NOT_SUPPORTED;
-                    }
-
-                    // Load it.
-                    if (service.loadVoice(getCallerIdentity(), voiceName) == TextToSpeech.ERROR) {
-                        Log.w(TAG, "The service claimed " + language + "-" + country + "-"
-                                + variant + " was available with voice name " + voiceName
-                                + " but loadVoice returned ERROR");
-                        return LANG_NOT_SUPPORTED;
-                    }
-
-                    // Set the language/country/variant of the voice, so #getLanguage will return
-                    // the currently set voice locale when called.
-                    Voice voice = getVoice(service, voiceName);
-                    if (voice == null) {
-                        Log.w(TAG, "getDefaultVoiceNameFor returned " + voiceName + " for locale "
-                                + language + "-" + country + "-" + variant
-                                + " but getVoice returns null");
-                        return LANG_NOT_SUPPORTED;
-                    }
-                    String voiceLanguage = "";
-                    try {
-                        voiceLanguage = voice.getLocale().getISO3Language();
-                    } catch (MissingResourceException e) {
-                        Log.w(TAG, "Couldn't retrieve ISO 639-2/T language code for locale: " +
-                                voice.getLocale(), e);
-                    }
-
-                    String voiceCountry = "";
-                    try {
-                        voiceCountry = voice.getLocale().getISO3Country();
-                    } catch (MissingResourceException e) {
-                        Log.w(TAG, "Couldn't retrieve ISO 3166 country code for locale: " +
-                                voice.getLocale(), e);
-                    }
-                    mParams.putString(Engine.KEY_PARAM_VOICE_NAME, voiceName);
-                    mParams.putString(Engine.KEY_PARAM_LANGUAGE, voiceLanguage);
-                    mParams.putString(Engine.KEY_PARAM_COUNTRY, voiceCountry);
-                    mParams.putString(Engine.KEY_PARAM_VARIANT, voice.getLocale().getVariant());
-                }
-                return result;
+        return runAction((ITextToSpeechService service) -> {
+            if (loc == null) {
+                return LANG_NOT_SUPPORTED;
             }
+            String language = null, country = null;
+            try {
+                language = loc.getISO3Language();
+            } catch (MissingResourceException e) {
+                Log.w(TAG, "Couldn't retrieve ISO 639-2/T language code for locale: " + loc, e);
+                return LANG_NOT_SUPPORTED;
+            }
+
+            try {
+                country = loc.getISO3Country();
+            } catch (MissingResourceException e) {
+                Log.w(TAG, "Couldn't retrieve ISO 3166 country code for locale: " + loc, e);
+                return LANG_NOT_SUPPORTED;
+            }
+
+            String variant = loc.getVariant();
+
+            // As of API level 21, setLanguage is implemented using setVoice.
+            // (which, in the default implementation, will call loadLanguage on the service
+            // interface).
+
+            // Sanitize locale using isLanguageAvailable.
+            int result = service.isLanguageAvailable(language, country, variant);
+            if (result >= LANG_AVAILABLE) {
+                // Get the default voice for the locale.
+                String voiceName = service.getDefaultVoiceNameFor(language, country, variant);
+                if (TextUtils.isEmpty(voiceName)) {
+                    Log.w(TAG, "Couldn't find the default voice for " + language + "-"
+                            + country + "-" + variant);
+                    return LANG_NOT_SUPPORTED;
+                }
+
+                // Load it.
+                if (service.loadVoice(getCallerIdentity(), voiceName) == TextToSpeech.ERROR) {
+                    Log.w(TAG, "The service claimed " + language + "-" + country + "-"
+                            + variant + " was available with voice name " + voiceName
+                            + " but loadVoice returned ERROR");
+                    return LANG_NOT_SUPPORTED;
+                }
+
+                // Set the language/country/variant of the voice, so #getLanguage will return
+                // the currently set voice locale when called.
+                Voice voice = getVoice(service, voiceName);
+                if (voice == null) {
+                    Log.w(TAG, "getDefaultVoiceNameFor returned " + voiceName + " for locale "
+                            + language + "-" + country + "-" + variant
+                            + " but getVoice returns null");
+                    return LANG_NOT_SUPPORTED;
+                }
+                String voiceLanguage = "";
+                try {
+                    voiceLanguage = voice.getLocale().getISO3Language();
+                } catch (MissingResourceException e) {
+                    Log.w(TAG, "Couldn't retrieve ISO 639-2/T language code for locale: "
+                            + voice.getLocale(), e);
+                }
+
+                String voiceCountry = "";
+                try {
+                    voiceCountry = voice.getLocale().getISO3Country();
+                } catch (MissingResourceException e) {
+                    Log.w(TAG, "Couldn't retrieve ISO 3166 country code for locale: "
+                            + voice.getLocale(), e);
+                }
+                mParams.putString(Engine.KEY_PARAM_VOICE_NAME, voiceName);
+                mParams.putString(Engine.KEY_PARAM_LANGUAGE, voiceLanguage);
+                mParams.putString(Engine.KEY_PARAM_COUNTRY, voiceCountry);
+                mParams.putString(Engine.KEY_PARAM_VARIANT, voice.getLocale().getVariant());
+            }
+            return result;
         }, LANG_NOT_SUPPORTED, "setLanguage");
     }
 
@@ -1582,16 +1556,13 @@
      */
     @Deprecated
     public Locale getLanguage() {
-        return runAction(new Action<Locale>() {
-            @Override
-            public Locale run(ITextToSpeechService service) {
-                /* No service call, but we're accessing mParams, hence need for
-                   wrapping it as an Action instance */
-                String lang = mParams.getString(Engine.KEY_PARAM_LANGUAGE, "");
-                String country = mParams.getString(Engine.KEY_PARAM_COUNTRY, "");
-                String variant = mParams.getString(Engine.KEY_PARAM_VARIANT, "");
-                return new Locale(lang, country, variant);
-            }
+        return runAction((ITextToSpeechService service) -> {
+            /* No service call, but we're accessing mParams, hence need for
+               wrapping it as an Action instance */
+            String lang = mParams.getString(Engine.KEY_PARAM_LANGUAGE, "");
+            String country = mParams.getString(Engine.KEY_PARAM_COUNTRY, "");
+            String variant = mParams.getString(Engine.KEY_PARAM_VARIANT, "");
+            return new Locale(lang, country, variant);
         }, null, "getLanguage");
     }
 
@@ -1599,19 +1570,16 @@
      * Query the engine about the set of available languages.
      */
     public Set<Locale> getAvailableLanguages() {
-        return runAction(new Action<Set<Locale>>() {
-            @Override
-            public Set<Locale> run(ITextToSpeechService service) throws RemoteException {
-                List<Voice> voices = service.getVoices();
-                if (voices == null) {
-                    return new HashSet<Locale>();
-                }
-                HashSet<Locale> locales = new HashSet<Locale>();
-                for (Voice voice : voices) {
-                    locales.add(voice.getLocale());
-                }
-                return locales;
+        return runAction((ITextToSpeechService service) -> {
+            List<Voice> voices = service.getVoices();
+            if (voices == null) {
+                return new HashSet<Locale>();
             }
+            HashSet<Locale> locales = new HashSet<Locale>();
+            for (Voice voice : voices) {
+                locales.add(voice.getLocale());
+            }
+            return locales;
         }, null, "getAvailableLanguages");
     }
 
@@ -1625,12 +1593,9 @@
      * @see Voice
      */
     public Set<Voice> getVoices() {
-        return runAction(new Action<Set<Voice>>() {
-            @Override
-            public Set<Voice> run(ITextToSpeechService service) throws RemoteException {
-                List<Voice> voices = service.getVoices();
-                return (voices != null)  ? new HashSet<Voice>(voices) : new HashSet<Voice>();
-            }
+        return runAction((ITextToSpeechService service) -> {
+            List<Voice> voices = service.getVoices();
+            return (voices != null)  ? new HashSet<Voice>(voices) : new HashSet<Voice>();
         }, null, "getVoices");
     }
 
@@ -1645,36 +1610,33 @@
      * @see Voice
      */
     public int setVoice(final Voice voice) {
-        return runAction(new Action<Integer>() {
-            @Override
-            public Integer run(ITextToSpeechService service) throws RemoteException {
-                int result = service.loadVoice(getCallerIdentity(), voice.getName());
-                if (result == SUCCESS) {
-                    mParams.putString(Engine.KEY_PARAM_VOICE_NAME, voice.getName());
+        return runAction((ITextToSpeechService service) -> {
+            int result = service.loadVoice(getCallerIdentity(), voice.getName());
+            if (result == SUCCESS) {
+                mParams.putString(Engine.KEY_PARAM_VOICE_NAME, voice.getName());
 
-                    // Set the language/country/variant, so #getLanguage will return the voice
-                    // locale when called.
-                    String language = "";
-                    try {
-                        language = voice.getLocale().getISO3Language();
-                    } catch (MissingResourceException e) {
-                        Log.w(TAG, "Couldn't retrieve ISO 639-2/T language code for locale: " +
-                                voice.getLocale(), e);
-                    }
-
-                    String country = "";
-                    try {
-                        country = voice.getLocale().getISO3Country();
-                    } catch (MissingResourceException e) {
-                        Log.w(TAG, "Couldn't retrieve ISO 3166 country code for locale: " +
-                                voice.getLocale(), e);
-                    }
-                    mParams.putString(Engine.KEY_PARAM_LANGUAGE, language);
-                    mParams.putString(Engine.KEY_PARAM_COUNTRY, country);
-                    mParams.putString(Engine.KEY_PARAM_VARIANT, voice.getLocale().getVariant());
+                // Set the language/country/variant, so #getLanguage will return the voice
+                // locale when called.
+                String language = "";
+                try {
+                    language = voice.getLocale().getISO3Language();
+                } catch (MissingResourceException e) {
+                    Log.w(TAG, "Couldn't retrieve ISO 639-2/T language code for locale: "
+                            + voice.getLocale(), e);
                 }
-                return result;
+
+                String country = "";
+                try {
+                    country = voice.getLocale().getISO3Country();
+                } catch (MissingResourceException e) {
+                    Log.w(TAG, "Couldn't retrieve ISO 3166 country code for locale: "
+                            + voice.getLocale(), e);
+                }
+                mParams.putString(Engine.KEY_PARAM_LANGUAGE, language);
+                mParams.putString(Engine.KEY_PARAM_COUNTRY, country);
+                mParams.putString(Engine.KEY_PARAM_VARIANT, voice.getLocale().getVariant());
             }
+            return result;
         }, LANG_NOT_SUPPORTED, "setVoice");
     }
 
@@ -1689,15 +1651,12 @@
      * @see Voice
      */
     public Voice getVoice() {
-        return runAction(new Action<Voice>() {
-            @Override
-            public Voice run(ITextToSpeechService service) throws RemoteException {
-                String voiceName = mParams.getString(Engine.KEY_PARAM_VOICE_NAME, "");
-                if (TextUtils.isEmpty(voiceName)) {
-                    return null;
-                }
-                return getVoice(service, voiceName);
+        return runAction((ITextToSpeechService service) -> {
+            String voiceName = mParams.getString(Engine.KEY_PARAM_VOICE_NAME, "");
+            if (TextUtils.isEmpty(voiceName)) {
+                return null;
             }
+            return getVoice(service, voiceName);
         }, null, "getVoice");
     }
 
@@ -1730,45 +1689,42 @@
      *     on error.
      */
     public Voice getDefaultVoice() {
-        return runAction(new Action<Voice>() {
-            @Override
-            public Voice run(ITextToSpeechService service) throws RemoteException {
+        return runAction((ITextToSpeechService service) -> {
 
-                String[] defaultLanguage = service.getClientDefaultLanguage();
+            String[] defaultLanguage = service.getClientDefaultLanguage();
 
-                if (defaultLanguage == null || defaultLanguage.length == 0) {
-                    Log.e(TAG, "service.getClientDefaultLanguage() returned empty array");
-                    return null;
-                }
-                String language = defaultLanguage[0];
-                String country = (defaultLanguage.length > 1) ? defaultLanguage[1] : "";
-                String variant = (defaultLanguage.length > 2) ? defaultLanguage[2] : "";
-
-                // Sanitize the locale using isLanguageAvailable.
-                int result = service.isLanguageAvailable(language, country, variant);
-                if (result < LANG_AVAILABLE) {
-                    // The default language is not supported.
-                    return null;
-                }
-
-                // Get the default voice name
-                String voiceName = service.getDefaultVoiceNameFor(language, country, variant);
-                if (TextUtils.isEmpty(voiceName)) {
-                    return null;
-                }
-
-                // Find it
-                List<Voice> voices = service.getVoices();
-                if (voices == null) {
-                    return null;
-                }
-                for (Voice voice : voices) {
-                    if (voice.getName().equals(voiceName)) {
-                        return voice;
-                    }
-                }
+            if (defaultLanguage == null || defaultLanguage.length == 0) {
+                Log.e(TAG, "service.getClientDefaultLanguage() returned empty array");
                 return null;
             }
+            String language = defaultLanguage[0];
+            String country = (defaultLanguage.length > 1) ? defaultLanguage[1] : "";
+            String variant = (defaultLanguage.length > 2) ? defaultLanguage[2] : "";
+
+            // Sanitize the locale using isLanguageAvailable.
+            int result = service.isLanguageAvailable(language, country, variant);
+            if (result < LANG_AVAILABLE) {
+                // The default language is not supported.
+                return null;
+            }
+
+            // Get the default voice name
+            String voiceName = service.getDefaultVoiceNameFor(language, country, variant);
+            if (TextUtils.isEmpty(voiceName)) {
+                return null;
+            }
+
+            // Find it
+            List<Voice> voices = service.getVoices();
+            if (voices == null) {
+                return null;
+            }
+            for (Voice voice : voices) {
+                if (voice.getName().equals(voiceName)) {
+                    return voice;
+                }
+            }
+            return null;
         }, null, "getDefaultVoice");
     }
 
@@ -1784,31 +1740,55 @@
      *         {@link #LANG_MISSING_DATA} and {@link #LANG_NOT_SUPPORTED}.
      */
     public int isLanguageAvailable(final Locale loc) {
-        return runAction(new Action<Integer>() {
-            @Override
-            public Integer run(ITextToSpeechService service) throws RemoteException {
-                String language = null, country = null;
+        return runAction((ITextToSpeechService service) -> {
+            String language = null, country = null;
 
-                try {
-                    language = loc.getISO3Language();
-                } catch (MissingResourceException e) {
-                    Log.w(TAG, "Couldn't retrieve ISO 639-2/T language code for locale: " + loc, e);
-                    return LANG_NOT_SUPPORTED;
-                }
-
-                try {
-                    country = loc.getISO3Country();
-                } catch (MissingResourceException e) {
-                    Log.w(TAG, "Couldn't retrieve ISO 3166 country code for locale: " + loc, e);
-                    return LANG_NOT_SUPPORTED;
-                }
-
-                return service.isLanguageAvailable(language, country, loc.getVariant());
+            try {
+                language = loc.getISO3Language();
+            } catch (MissingResourceException e) {
+                Log.w(TAG, "Couldn't retrieve ISO 639-2/T language code for locale: " + loc, e);
+                return LANG_NOT_SUPPORTED;
             }
+
+            try {
+                country = loc.getISO3Country();
+            } catch (MissingResourceException e) {
+                Log.w(TAG, "Couldn't retrieve ISO 3166 country code for locale: " + loc, e);
+                return LANG_NOT_SUPPORTED;
+            }
+
+            return service.isLanguageAvailable(language, country, loc.getVariant());
         }, LANG_NOT_SUPPORTED, "isLanguageAvailable");
     }
 
     /**
+     * Synthesizes the given text to a ParcelFileDescriptor using the specified parameters.
+     * This method is asynchronous, i.e. the method just adds the request to the queue of TTS
+     * requests and then returns. The synthesis might not have finished (or even started!) at the
+     * time when this method returns. In order to reliably detect errors during synthesis,
+     * we recommend setting an utterance progress listener (see
+     * {@link #setOnUtteranceProgressListener}).
+     *
+     * @param text The text that should be synthesized. No longer than
+     *            {@link #getMaxSpeechInputLength()} characters.
+     * @param params Parameters for the request. Can be null.
+     *            Engine specific parameters may be passed in but the parameter keys
+     *            must be prefixed by the name of the engine they are intended for. For example
+     *            the keys "com.svox.pico_foo" and "com.svox.pico:bar" will be passed to the engine
+     *            named "com.svox.pico" if it is being used.
+     * @param fileDescriptor ParcelFileDescriptor to write the generated audio data to.
+     * @param utteranceId An unique identifier for this request.
+     * @return {@link #ERROR} or {@link #SUCCESS} of <b>queuing</b> the synthesizeToFile operation.
+     */
+    public int synthesizeToFile(@NonNull final CharSequence text, @NonNull final Bundle params,
+            @NonNull final ParcelFileDescriptor fileDescriptor, @NonNull final String utteranceId) {
+        return runAction((ITextToSpeechService service) -> {
+            return service.synthesizeToFileDescriptor(getCallerIdentity(), text,
+                    fileDescriptor, getParams(params), utteranceId);
+        }, ERROR, "synthesizeToFile");
+    }
+
+    /**
      * Synthesizes the given text to a file using the specified parameters.
      * This method is asynchronous, i.e. the method just adds the request to the queue of TTS
      * requests and then returns. The synthesis might not have finished (or even started!) at the
@@ -1829,33 +1809,26 @@
      */
     public int synthesizeToFile(final CharSequence text, final Bundle params,
             final File file, final String utteranceId) {
-        return runAction(new Action<Integer>() {
-            @Override
-            public Integer run(ITextToSpeechService service) throws RemoteException {
-                ParcelFileDescriptor fileDescriptor;
-                int returnValue;
-                try {
-                    if(file.exists() && !file.canWrite()) {
-                        Log.e(TAG, "Can't write to " + file);
-                        return ERROR;
-                    }
-                    fileDescriptor = ParcelFileDescriptor.open(file,
-                            ParcelFileDescriptor.MODE_WRITE_ONLY |
-                            ParcelFileDescriptor.MODE_CREATE |
-                            ParcelFileDescriptor.MODE_TRUNCATE);
-                    returnValue = service.synthesizeToFileDescriptor(getCallerIdentity(), text,
-                            fileDescriptor, getParams(params), utteranceId);
-                    fileDescriptor.close();
-                    return returnValue;
-                } catch (FileNotFoundException e) {
-                    Log.e(TAG, "Opening file " + file + " failed", e);
-                    return ERROR;
-                } catch (IOException e) {
-                    Log.e(TAG, "Closing file " + file + " failed", e);
-                    return ERROR;
-                }
-            }
-        }, ERROR, "synthesizeToFile");
+        if (file.exists() && !file.canWrite()) {
+            Log.e(TAG, "Can't write to " + file);
+            return ERROR;
+        }
+        try (
+            ParcelFileDescriptor fileDescriptor = ParcelFileDescriptor.open(file,
+                ParcelFileDescriptor.MODE_WRITE_ONLY
+                | ParcelFileDescriptor.MODE_CREATE
+                | ParcelFileDescriptor.MODE_TRUNCATE);
+        ) {
+            int returnValue = synthesizeToFile(text, params, fileDescriptor, utteranceId);
+            fileDescriptor.close();
+            return returnValue;
+        } catch (FileNotFoundException e) {
+            Log.e(TAG, "Opening file " + file + " failed", e);
+            return ERROR;
+        } catch (IOException e) {
+            Log.e(TAG, "Closing file " + file + " failed", e);
+            return ERROR;
+        }
     }
 
     /**
diff --git a/core/java/android/text/format/DateUtils.java b/core/java/android/text/format/DateUtils.java
index e94b800..b00a938 100644
--- a/core/java/android/text/format/DateUtils.java
+++ b/core/java/android/text/format/DateUtils.java
@@ -32,6 +32,9 @@
 import libcore.icu.RelativeDateTimeFormatter;
 
 import java.io.IOException;
+import java.time.Instant;
+import java.time.LocalDateTime;
+import java.time.ZoneId;
 import java.util.Calendar;
 import java.util.Date;
 import java.util.Formatter;
@@ -502,17 +505,21 @@
      * @return true if the supplied when is today else false
      */
     public static boolean isToday(long when) {
-        Time time = new Time();
-        time.set(when);
+        return isSameDate(when, System.currentTimeMillis());
+    }
 
-        int thenYear = time.year;
-        int thenMonth = time.month;
-        int thenMonthDay = time.monthDay;
+    private static boolean isSameDate(long oneMillis, long twoMillis) {
+        ZoneId zoneId = ZoneId.systemDefault();
 
-        time.set(System.currentTimeMillis());
-        return (thenYear == time.year)
-                && (thenMonth == time.month)
-                && (thenMonthDay == time.monthDay);
+        Instant oneInstant = Instant.ofEpochMilli(oneMillis);
+        LocalDateTime oneLocalDateTime = LocalDateTime.ofInstant(oneInstant, zoneId);
+
+        Instant twoInstant = Instant.ofEpochMilli(twoMillis);
+        LocalDateTime twoLocalDateTime = LocalDateTime.ofInstant(twoInstant, zoneId);
+
+        return (oneLocalDateTime.getYear() == twoLocalDateTime.getYear())
+                && (oneLocalDateTime.getMonthValue() == twoLocalDateTime.getMonthValue())
+                && (oneLocalDateTime.getDayOfMonth() == twoLocalDateTime.getDayOfMonth());
     }
 
     /**
diff --git a/core/java/android/util/ArrayMap.java b/core/java/android/util/ArrayMap.java
index d899dde..3f44e48 100644
--- a/core/java/android/util/ArrayMap.java
+++ b/core/java/android/util/ArrayMap.java
@@ -48,6 +48,8 @@
  * you have no control over this shrinking -- if you set a capacity and then remove an
  * item, it may reduce the capacity to better match the current size.  In the future an
  * explicit call to set the capacity should turn off this aggressive shrinking behavior.</p>
+ *
+ * <p>This structure is <b>NOT</b> thread-safe.</p>
  */
 public final class ArrayMap<K, V> implements Map<K, V> {
     private static final boolean DEBUG = false;
@@ -103,15 +105,21 @@
     static Object[] mTwiceBaseCache;
     @UnsupportedAppUsage(maxTargetSdk = 28) // Allocations are an implementation detail.
     static int mTwiceBaseCacheSize;
+    /**
+     * Separate locks for each cache since each can be accessed independently of the other without
+     * risk of a deadlock.
+     */
+    private static final Object sBaseCacheLock = new Object();
+    private static final Object sTwiceBaseCacheLock = new Object();
 
-    final boolean mIdentityHashCode;
+    private final boolean mIdentityHashCode;
     @UnsupportedAppUsage(maxTargetSdk = 28) // Hashes are an implementation detail. Use public key/value API.
     int[] mHashes;
     @UnsupportedAppUsage(maxTargetSdk = 28) // Storage is an implementation detail. Use public key/value API.
     Object[] mArray;
     @UnsupportedAppUsage(maxTargetSdk = 28) // Use size()
     int mSize;
-    MapCollections<K, V> mCollections;
+    private MapCollections<K, V> mCollections;
 
     private static int binarySearchHashes(int[] hashes, int N, int hash) {
         try {
@@ -209,31 +217,57 @@
             throw new UnsupportedOperationException("ArrayMap is immutable");
         }
         if (size == (BASE_SIZE*2)) {
-            synchronized (ArrayMap.class) {
+            synchronized (sTwiceBaseCacheLock) {
                 if (mTwiceBaseCache != null) {
                     final Object[] array = mTwiceBaseCache;
                     mArray = array;
-                    mTwiceBaseCache = (Object[])array[0];
-                    mHashes = (int[])array[1];
-                    array[0] = array[1] = null;
-                    mTwiceBaseCacheSize--;
-                    if (DEBUG) Log.d(TAG, "Retrieving 2x cache " + mHashes
-                            + " now have " + mTwiceBaseCacheSize + " entries");
-                    return;
+                    try {
+                        mTwiceBaseCache = (Object[]) array[0];
+                        mHashes = (int[]) array[1];
+                        if (mHashes != null) {
+                            array[0] = array[1] = null;
+                            mTwiceBaseCacheSize--;
+                            if (DEBUG) {
+                                Log.d(TAG, "Retrieving 2x cache " + mHashes
+                                        + " now have " + mTwiceBaseCacheSize + " entries");
+                            }
+                            return;
+                        }
+                    } catch (ClassCastException e) {
+                    }
+                    // Whoops!  Someone trampled the array (probably due to not protecting
+                    // their access with a lock).  Our cache is corrupt; report and give up.
+                    Slog.wtf(TAG, "Found corrupt ArrayMap cache: [0]=" + array[0]
+                            + " [1]=" + array[1]);
+                    mTwiceBaseCache = null;
+                    mTwiceBaseCacheSize = 0;
                 }
             }
         } else if (size == BASE_SIZE) {
-            synchronized (ArrayMap.class) {
+            synchronized (sBaseCacheLock) {
                 if (mBaseCache != null) {
                     final Object[] array = mBaseCache;
                     mArray = array;
-                    mBaseCache = (Object[])array[0];
-                    mHashes = (int[])array[1];
-                    array[0] = array[1] = null;
-                    mBaseCacheSize--;
-                    if (DEBUG) Log.d(TAG, "Retrieving 1x cache " + mHashes
-                            + " now have " + mBaseCacheSize + " entries");
-                    return;
+                    try {
+                        mBaseCache = (Object[]) array[0];
+                        mHashes = (int[]) array[1];
+                        if (mHashes != null) {
+                            array[0] = array[1] = null;
+                            mBaseCacheSize--;
+                            if (DEBUG) {
+                                Log.d(TAG, "Retrieving 1x cache " + mHashes
+                                        + " now have " + mBaseCacheSize + " entries");
+                            }
+                            return;
+                        }
+                    } catch (ClassCastException e) {
+                    }
+                    // Whoops!  Someone trampled the array (probably due to not protecting
+                    // their access with a lock).  Our cache is corrupt; report and give up.
+                    Slog.wtf(TAG, "Found corrupt ArrayMap cache: [0]=" + array[0]
+                            + " [1]=" + array[1]);
+                    mBaseCache = null;
+                    mBaseCacheSize = 0;
                 }
             }
         }
@@ -242,10 +276,14 @@
         mArray = new Object[size<<1];
     }
 
+    /**
+     * Make sure <b>NOT</b> to call this method with arrays that can still be modified. In other
+     * words, don't pass mHashes or mArray in directly.
+     */
     @UnsupportedAppUsage(maxTargetSdk = 28) // Allocations are an implementation detail.
     private static void freeArrays(final int[] hashes, final Object[] array, final int size) {
         if (hashes.length == (BASE_SIZE*2)) {
-            synchronized (ArrayMap.class) {
+            synchronized (sTwiceBaseCacheLock) {
                 if (mTwiceBaseCacheSize < CACHE_SIZE) {
                     array[0] = mTwiceBaseCache;
                     array[1] = hashes;
@@ -259,7 +297,7 @@
                 }
             }
         } else if (hashes.length == BASE_SIZE) {
-            synchronized (ArrayMap.class) {
+            synchronized (sBaseCacheLock) {
                 if (mBaseCacheSize < CACHE_SIZE) {
                     array[0] = mBaseCache;
                     array[1] = hashes;
diff --git a/core/java/android/util/ArraySet.java b/core/java/android/util/ArraySet.java
index 3fa914f..44c5af2 100644
--- a/core/java/android/util/ArraySet.java
+++ b/core/java/android/util/ArraySet.java
@@ -23,6 +23,7 @@
 
 import java.lang.reflect.Array;
 import java.util.Collection;
+import java.util.ConcurrentModificationException;
 import java.util.Iterator;
 import java.util.Map;
 import java.util.Set;
@@ -46,6 +47,8 @@
  * you have no control over this shrinking -- if you set a capacity and then remove an
  * item, it may reduce the capacity to better match the current size.  In the future an
  * explicit call to set the capacity should turn off this aggressive shrinking behavior.</p>
+ *
+ * <p>This structure is <b>NOT</b> thread-safe.</p>
  */
 public final class ArraySet<E> implements Collection<E>, Set<E> {
     private static final boolean DEBUG = false;
@@ -72,15 +75,30 @@
     static int sBaseCacheSize;
     static Object[] sTwiceBaseCache;
     static int sTwiceBaseCacheSize;
+    /**
+     * Separate locks for each cache since each can be accessed independently of the other without
+     * risk of a deadlock.
+     */
+    private static final Object sBaseCacheLock = new Object();
+    private static final Object sTwiceBaseCacheLock = new Object();
 
-    final boolean mIdentityHashCode;
+    private final boolean mIdentityHashCode;
     @UnsupportedAppUsage(maxTargetSdk = 28) // Hashes are an implementation detail. Use public API.
     int[] mHashes;
     @UnsupportedAppUsage(maxTargetSdk = 28) // Storage is an implementation detail. Use public API.
     Object[] mArray;
     @UnsupportedAppUsage(maxTargetSdk = 28) // Use size()
     int mSize;
-    MapCollections<E, E> mCollections;
+    private MapCollections<E, E> mCollections;
+
+    private int binarySearch(int[] hashes, int hash) {
+        try {
+            return ContainerHelpers.binarySearch(hashes, mSize, hash);
+        } catch (ArrayIndexOutOfBoundsException e) {
+            throw new ConcurrentModificationException();
+        }
+    }
+
 
     @UnsupportedAppUsage(maxTargetSdk = 28) // Hashes are an implementation detail. Use indexOfKey(Object).
     private int indexOf(Object key, int hash) {
@@ -91,7 +109,7 @@
             return ~0;
         }
 
-        int index = ContainerHelpers.binarySearch(mHashes, N, hash);
+        int index = binarySearch(mHashes, hash);
 
         // If the hash code wasn't found, then we have no entry for this key.
         if (index < 0) {
@@ -130,7 +148,7 @@
             return ~0;
         }
 
-        int index = ContainerHelpers.binarySearch(mHashes, N, 0);
+        int index = binarySearch(mHashes, 0);
 
         // If the hash code wasn't found, then we have no entry for this key.
         if (index < 0) {
@@ -163,20 +181,22 @@
     @UnsupportedAppUsage(maxTargetSdk = 28) // Allocations are an implementation detail.
     private void allocArrays(final int size) {
         if (size == (BASE_SIZE * 2)) {
-            synchronized (ArraySet.class) {
+            synchronized (sTwiceBaseCacheLock) {
                 if (sTwiceBaseCache != null) {
                     final Object[] array = sTwiceBaseCache;
                     try {
                         mArray = array;
                         sTwiceBaseCache = (Object[]) array[0];
                         mHashes = (int[]) array[1];
-                        array[0] = array[1] = null;
-                        sTwiceBaseCacheSize--;
-                        if (DEBUG) {
-                            Log.d(TAG, "Retrieving 2x cache " + mHashes + " now have "
-                                    + sTwiceBaseCacheSize + " entries");
-                    }
-                    return;
+                        if (mHashes != null) {
+                            array[0] = array[1] = null;
+                            sTwiceBaseCacheSize--;
+                            if (DEBUG) {
+                                Log.d(TAG, "Retrieving 2x cache " + mHashes + " now have "
+                                        + sTwiceBaseCacheSize + " entries");
+                            }
+                            return;
+                        }
                     } catch (ClassCastException e) {
                     }
                     // Whoops!  Someone trampled the array (probably due to not protecting
@@ -188,20 +208,22 @@
                 }
             }
         } else if (size == BASE_SIZE) {
-            synchronized (ArraySet.class) {
+            synchronized (sBaseCacheLock) {
                 if (sBaseCache != null) {
                     final Object[] array = sBaseCache;
                     try {
                         mArray = array;
                         sBaseCache = (Object[]) array[0];
                         mHashes = (int[]) array[1];
-                        array[0] = array[1] = null;
-                        sBaseCacheSize--;
-                        if (DEBUG) {
-                            Log.d(TAG, "Retrieving 1x cache " + mHashes + " now have " + sBaseCacheSize
-                                    + " entries");
+                        if (mHashes != null) {
+                            array[0] = array[1] = null;
+                            sBaseCacheSize--;
+                            if (DEBUG) {
+                                Log.d(TAG, "Retrieving 1x cache " + mHashes + " now have "
+                                        + sBaseCacheSize + " entries");
+                            }
+                            return;
                         }
-                        return;
                     } catch (ClassCastException e) {
                     }
                     // Whoops!  Someone trampled the array (probably due to not protecting
@@ -218,10 +240,14 @@
         mArray = new Object[size];
     }
 
+    /**
+     * Make sure <b>NOT</b> to call this method with arrays that can still be modified. In other
+     * words, don't pass mHashes or mArray in directly.
+     */
     @UnsupportedAppUsage(maxTargetSdk = 28) // Allocations are an implementation detail.
     private static void freeArrays(final int[] hashes, final Object[] array, final int size) {
         if (hashes.length == (BASE_SIZE * 2)) {
-            synchronized (ArraySet.class) {
+            synchronized (sTwiceBaseCacheLock) {
                 if (sTwiceBaseCacheSize < CACHE_SIZE) {
                     array[0] = sTwiceBaseCache;
                     array[1] = hashes;
@@ -237,7 +263,7 @@
                 }
             }
         } else if (hashes.length == BASE_SIZE) {
-            synchronized (ArraySet.class) {
+            synchronized (sBaseCacheLock) {
                 if (sBaseCacheSize < CACHE_SIZE) {
                     array[0] = sBaseCache;
                     array[1] = hashes;
@@ -308,10 +334,16 @@
     @Override
     public void clear() {
         if (mSize != 0) {
-            freeArrays(mHashes, mArray, mSize);
+            final int[] ohashes = mHashes;
+            final Object[] oarray = mArray;
+            final int osize = mSize;
             mHashes = EmptyArray.INT;
             mArray = EmptyArray.OBJECT;
             mSize = 0;
+            freeArrays(ohashes, oarray, osize);
+        }
+        if (mSize != 0) {
+            throw new ConcurrentModificationException();
         }
     }
 
@@ -320,6 +352,7 @@
      * items.
      */
     public void ensureCapacity(int minimumCapacity) {
+        final int oSize = mSize;
         if (mHashes.length < minimumCapacity) {
             final int[] ohashes = mHashes;
             final Object[] oarray = mArray;
@@ -330,6 +363,9 @@
             }
             freeArrays(ohashes, oarray, mSize);
         }
+        if (mSize != oSize) {
+            throw new ConcurrentModificationException();
+        }
     }
 
     /**
@@ -400,11 +436,10 @@
      *
      * @param value the object to add.
      * @return {@code true} if this set is modified, {@code false} otherwise.
-     * @throws ClassCastException
-     *             when the class of the object is inappropriate for this set.
      */
     @Override
     public boolean add(E value) {
+        final int oSize = mSize;
         final int hash;
         int index;
         if (value == null) {
@@ -419,9 +454,9 @@
         }
 
         index = ~index;
-        if (mSize >= mHashes.length) {
-            final int n = mSize >= (BASE_SIZE * 2) ? (mSize + (mSize >> 1))
-                    : (mSize >= BASE_SIZE ? (BASE_SIZE * 2) : BASE_SIZE);
+        if (oSize >= mHashes.length) {
+            final int n = oSize >= (BASE_SIZE * 2) ? (oSize + (oSize >> 1))
+                    : (oSize >= BASE_SIZE ? (BASE_SIZE * 2) : BASE_SIZE);
 
             if (DEBUG) Log.d(TAG, "add: grow from " + mHashes.length + " to " + n);
 
@@ -429,21 +464,29 @@
             final Object[] oarray = mArray;
             allocArrays(n);
 
+            if (oSize != mSize) {
+                throw new ConcurrentModificationException();
+            }
+
             if (mHashes.length > 0) {
-                if (DEBUG) Log.d(TAG, "add: copy 0-" + mSize + " to 0");
+                if (DEBUG) Log.d(TAG, "add: copy 0-" + oSize + " to 0");
                 System.arraycopy(ohashes, 0, mHashes, 0, ohashes.length);
                 System.arraycopy(oarray, 0, mArray, 0, oarray.length);
             }
 
-            freeArrays(ohashes, oarray, mSize);
+            freeArrays(ohashes, oarray, oSize);
         }
 
-        if (index < mSize) {
+        if (index < oSize) {
             if (DEBUG) {
-                Log.d(TAG, "add: move " + index + "-" + (mSize - index) + " to " + (index + 1));
+                Log.d(TAG, "add: move " + index + "-" + (oSize - index) + " to " + (index + 1));
             }
-            System.arraycopy(mHashes, index, mHashes, index + 1, mSize - index);
-            System.arraycopy(mArray, index, mArray, index + 1, mSize - index);
+            System.arraycopy(mHashes, index, mHashes, index + 1, oSize - index);
+            System.arraycopy(mArray, index, mArray, index + 1, oSize - index);
+        }
+
+        if (oSize != mSize || index >= mHashes.length) {
+            throw new ConcurrentModificationException();
         }
 
         mHashes[index] = hash;
@@ -458,6 +501,7 @@
      * @hide
      */
     public void append(E value) {
+        final int oSize = mSize;
         final int index = mSize;
         final int hash = value == null ? 0
                 : (mIdentityHashCode ? System.identityHashCode(value) : value.hashCode());
@@ -476,6 +520,11 @@
             add(value);
             return;
         }
+
+        if (oSize != mSize) {
+            throw new ConcurrentModificationException();
+        }
+
         mSize = index + 1;
         mHashes[index] = hash;
         mArray[index] = value;
@@ -492,6 +541,9 @@
             if (N > 0) {
                 System.arraycopy(array.mHashes, 0, mHashes, 0, N);
                 System.arraycopy(array.mArray, 0, mArray, 0, N);
+                if (0 != mSize) {
+                    throw new ConcurrentModificationException();
+                }
                 mSize = N;
             }
         } else {
@@ -549,12 +601,14 @@
             // Check if exception should be thrown outside of the critical path.
             throw new ArrayIndexOutOfBoundsException(index);
         }
+        final int oSize = mSize;
         final Object old = mArray[index];
-        if (mSize <= 1) {
+        if (oSize <= 1) {
             // Now empty.
             if (DEBUG) Log.d(TAG, "remove: shrink from " + mHashes.length + " to 0");
             clear();
         } else {
+            final int nSize = oSize - 1;
             if (shouldShrink()) {
                 // Shrunk enough to reduce size of arrays.
                 final int n = getNewShrunkenSize();
@@ -565,31 +619,33 @@
                 final Object[] oarray = mArray;
                 allocArrays(n);
 
-                mSize--;
                 if (index > 0) {
                     if (DEBUG) Log.d(TAG, "remove: copy from 0-" + index + " to 0");
                     System.arraycopy(ohashes, 0, mHashes, 0, index);
                     System.arraycopy(oarray, 0, mArray, 0, index);
                 }
-                if (index < mSize) {
+                if (index < nSize) {
                     if (DEBUG) {
-                        Log.d(TAG, "remove: copy from " + (index + 1) + "-" + mSize
+                        Log.d(TAG, "remove: copy from " + (index + 1) + "-" + nSize
                                 + " to " + index);
                     }
-                    System.arraycopy(ohashes, index + 1, mHashes, index, mSize - index);
-                    System.arraycopy(oarray, index + 1, mArray, index, mSize - index);
+                    System.arraycopy(ohashes, index + 1, mHashes, index, nSize - index);
+                    System.arraycopy(oarray, index + 1, mArray, index, nSize - index);
                 }
             } else {
-                mSize--;
-                if (index < mSize) {
+                if (index < nSize) {
                     if (DEBUG) {
-                        Log.d(TAG, "remove: move " + (index + 1) + "-" + mSize + " to " + index);
+                        Log.d(TAG, "remove: move " + (index + 1) + "-" + nSize + " to " + index);
                     }
-                    System.arraycopy(mHashes, index + 1, mHashes, index, mSize - index);
-                    System.arraycopy(mArray, index + 1, mArray, index, mSize - index);
+                    System.arraycopy(mHashes, index + 1, mHashes, index, nSize - index);
+                    System.arraycopy(mArray, index + 1, mArray, index, nSize - index);
                 }
-                mArray[mSize] = null;
+                mArray[nSize] = null;
             }
+            if (oSize != mSize) {
+                throw new ConcurrentModificationException();
+            }
+            mSize = nSize;
         }
         return (E) old;
     }
diff --git a/core/java/android/util/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/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java
index 1f7ae0e..3bbd321 100644
--- a/core/java/android/view/autofill/AutofillManager.java
+++ b/core/java/android/view/autofill/AutofillManager.java
@@ -43,6 +43,7 @@
 import android.os.IBinder;
 import android.os.Parcelable;
 import android.os.RemoteException;
+import android.os.SystemClock;
 import android.service.autofill.AutofillService;
 import android.service.autofill.FillEventHistory;
 import android.service.autofill.UserData;
@@ -1737,6 +1738,26 @@
 
             final SyncResultReceiver receiver = new SyncResultReceiver(SYNC_CALLS_TIMEOUT_MS);
             final ComponentName componentName = client.autofillClientGetComponentName();
+
+            if (!mEnabledForAugmentedAutofillOnly && mOptions != null
+                    && mOptions.isAutofillDisabledLocked(componentName)) {
+                if (mOptions.isAugmentedAutofillEnabled(mContext)) {
+                    if (sDebug) {
+                        Log.d(TAG, "startSession(" + componentName + "): disabled by service but "
+                                + "whitelisted for augmented autofill");
+                        flags |= FLAG_ADD_CLIENT_ENABLED_FOR_AUGMENTED_AUTOFILL_ONLY;
+                    }
+                } else {
+                    if (sDebug) {
+                        Log.d(TAG, "startSession(" + componentName + "): ignored because "
+                                + "disabled by service and not whitelisted for augmented autofill");
+                    }
+                    setSessionFinished(AutofillManager.STATE_DISABLED_BY_SERVICE, null);
+                    client.autofillClientResetableStateAvailable();
+                    return;
+                }
+            }
+
             mService.startSession(client.autofillClientGetActivityToken(),
                     mServiceClient.asBinder(), id, bounds, value, mContext.getUserId(),
                     mCallback != null, flags, componentName,
@@ -2067,6 +2088,7 @@
                     mServiceClientCleaner.clean();
                     mServiceClientCleaner = null;
                 }
+                notifyReenableAutofill();
             }
         }
         sDebug = (flags & SET_STATE_FLAG_DEBUG) != 0;
@@ -2403,6 +2425,37 @@
         }
     }
 
+    private void notifyDisableAutofill(long disableDuration, ComponentName componentName) {
+        synchronized (mLock) {
+            if (mOptions == null) {
+                return;
+            }
+            long expiration = SystemClock.elapsedRealtime() + disableDuration;
+            // Protect it against overflow
+            if (expiration < 0) {
+                expiration = Long.MAX_VALUE;
+            }
+            if (componentName != null) {
+                if (mOptions.disabledActivities == null) {
+                    mOptions.disabledActivities = new ArrayMap<>();
+                }
+                mOptions.disabledActivities.put(componentName.flattenToString(), expiration);
+            } else {
+                mOptions.appDisabledExpiration = expiration;
+            }
+        }
+    }
+
+    void notifyReenableAutofill() {
+        synchronized (mLock) {
+            if (mOptions == null) {
+                return;
+            }
+            mOptions.appDisabledExpiration = 0;
+            mOptions.disabledActivities = null;
+        }
+    }
+
     private void notifyNoFillUi(int sessionId, AutofillId id, int sessionFinishedState) {
         if (sVerbose) {
             Log.v(TAG, "notifyNoFillUi(): sessionId=" + sessionId + ", autofillId=" + id
@@ -3181,6 +3234,15 @@
         }
 
         @Override
+        public void notifyDisableAutofill(long disableDuration, ComponentName componentName)
+                throws RemoteException {
+            final AutofillManager afm = mAfm.get();
+            if (afm != null) {
+                afm.post(() -> afm.notifyDisableAutofill(disableDuration, componentName));
+            }
+        }
+
+        @Override
         public void dispatchUnhandledKey(int sessionId, AutofillId id, KeyEvent fullScreen) {
             final AutofillManager afm = mAfm.get();
             if (afm != null) {
diff --git a/core/java/android/view/autofill/IAutoFillManagerClient.aidl b/core/java/android/view/autofill/IAutoFillManagerClient.aidl
index 85def7f..84949c8 100644
--- a/core/java/android/view/autofill/IAutoFillManagerClient.aidl
+++ b/core/java/android/view/autofill/IAutoFillManagerClient.aidl
@@ -18,6 +18,7 @@
 
 import java.util.List;
 
+import android.content.ComponentName;
 import android.content.Intent;
 import android.content.IntentSender;
 import android.graphics.Rect;
@@ -111,4 +112,8 @@
     */
    void getAugmentedAutofillClient(in IResultReceiver result);
 
+   /**
+    * Notifies disables autofill for the app or activity.
+    */
+   void notifyDisableAutofill(long disableDuration, in ComponentName componentName);
 }
diff --git a/core/java/android/view/textclassifier/SystemTextClassifier.java b/core/java/android/view/textclassifier/SystemTextClassifier.java
index a97c330..66f7504 100644
--- a/core/java/android/view/textclassifier/SystemTextClassifier.java
+++ b/core/java/android/view/textclassifier/SystemTextClassifier.java
@@ -309,7 +309,7 @@
         }
 
         public void onFailure() {
-            Log.e(LOG_TAG, "Request failed.", null);
+            Log.e(LOG_TAG, "Request failed at " + mName, null);
             mLatch.countDown();
         }
 
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index 88d0e85..8503ab8 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -4750,11 +4750,11 @@
             final long uptime = mClocks.uptimeMillis();
 
             boolean updateHistory = false;
-            if (isScreenDoze(state)) {
+            if (isScreenDoze(state) && !isScreenDoze(oldState)) {
                 mHistoryCur.states |= HistoryItem.STATE_SCREEN_DOZE_FLAG;
                 mScreenDozeTimer.startRunningLocked(elapsedRealtime);
                 updateHistory = true;
-            } else if (isScreenDoze(oldState)) {
+            } else if (isScreenDoze(oldState) && !isScreenDoze(state)) {
                 mHistoryCur.states &= ~HistoryItem.STATE_SCREEN_DOZE_FLAG;
                 mScreenDozeTimer.stopRunningLocked(elapsedRealtime);
                 updateHistory = true;
diff --git a/core/jni/Android.bp b/core/jni/Android.bp
index fb68bc7..4c54539 100644
--- a/core/jni/Android.bp
+++ b/core/jni/Android.bp
@@ -339,6 +339,8 @@
     cppflags: ["-Wno-conversion-null"],
 
     srcs: [
+        "android/graphics/apex/android_region.cpp",
+
         "android_graphics_Canvas.cpp",
         "android_graphics_ColorSpace.cpp",
         "android_graphics_drawable_AnimatedVectorDrawable.cpp",
@@ -386,10 +388,12 @@
     local_include_dirs: [
         "include",  // NEEDED FOR ANDROID RUNTIME
         "android/graphics",
+        "android/graphics/apex/include",
     ],
 
     export_include_dirs: [
         ".",
+        "android/graphics/apex/include",
     ],
 
     include_dirs: [
@@ -417,6 +421,8 @@
     target: {
         android: {
             srcs: [ // sources that depend on android only libraries
+                "android/graphics/apex/android_canvas.cpp",
+
                 "android_view_TextureLayer.cpp",
                 "android_view_ThreadedRenderer.cpp",
                 "android/graphics/BitmapRegionDecoder.cpp",
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index a3fd915..c12940a 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -245,6 +245,9 @@
 // Copying (CC) garbage collector.
 static const char* kNoGenerationalCCRuntimeOption = "-Xgc:nogenerational_cc";
 
+// Phenotype property name for enabling profiling the boot class path.
+static const char* PROFILE_BOOT_CLASS_PATH = "profilebootclasspath";
+
 // Feature flag name for running the JIT in Zygote experiment, b/119800099.
 static const char* ENABLE_APEX_IMAGE = "enable_apex_image";
 // Flag to pass to the runtime when using the apex image.
@@ -690,6 +693,24 @@
     char jdwpProviderBuf[sizeof("-XjdwpProvider:") - 1 + PROPERTY_VALUE_MAX];
     char bootImageBuf[sizeof("-Ximage:") - 1 + PROPERTY_VALUE_MAX];
 
+    // Read if we are using the profile configuration, do this at the start since the last ART args
+    // take precedence.
+    property_get("dalvik.vm.profilebootclasspath", propBuf, "");
+    std::string profile_boot_class_path = propBuf;
+    // Empty means the property is unset and we should default to the phenotype property.
+    // The possible values are {"true", "false", ""}
+    if (profile_boot_class_path.empty()) {
+        profile_boot_class_path = server_configurable_flags::GetServerConfigurableFlag(
+                RUNTIME_NATIVE_BOOT_NAMESPACE,
+                PROFILE_BOOT_CLASS_PATH,
+                /*default_value=*/ "");
+    }
+    if (profile_boot_class_path == "true") {
+        addOption("-Xps-profile-boot-class-path");
+        addOption("-Xps-profile-aot-code");
+        addOption("-Xjitsaveprofilinginfo");
+    }
+
     std::string use_apex_image =
         server_configurable_flags::GetServerConfigurableFlag(RUNTIME_NATIVE_BOOT_NAMESPACE,
                                                              ENABLE_APEX_IMAGE,
@@ -807,13 +828,6 @@
     parseRuntimeOption("dalvik.vm.jittransitionweight",
                        jittransitionweightOptBuf,
                        "-Xjittransitionweight:");
-
-    property_get("dalvik.vm.profilebootimage", propBuf, "");
-    if (strcmp(propBuf, "true") == 0) {
-        addOption("-Xps-profile-boot-class-path");
-        addOption("-Xps-profile-aot-code");
-    }
-
     /*
      * Madvise related options.
      */
diff --git a/core/jni/android/graphics/Region.cpp b/core/jni/android/graphics/Region.cpp
index 90ca3bb..1fb5fe3 100644
--- a/core/jni/android/graphics/Region.cpp
+++ b/core/jni/android/graphics/Region.cpp
@@ -360,8 +360,4 @@
                                 NELEM(gRegionIterMethods));
 }
 
-SkRegion* android_graphics_Region_getSkRegion(JNIEnv* env, jobject regionObj) {
-    return GetSkRegion(env, regionObj);
-}
-
 } // namespace android
diff --git a/core/jni/android/graphics/Region.h b/core/jni/android/graphics/Region.h
deleted file mode 100644
index 2e8e109..0000000
--- a/core/jni/android/graphics/Region.h
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef _ANDROID_GRAPHICS_REGION_H_
-#define _ANDROID_GRAPHICS_REGION_H_
-
-#include "jni.h"
-#include "SkRegion.h"
-
-namespace android {
-
-/* Gets the underlying SkRegion from a Region object. */
-extern SkRegion* android_graphics_Region_getSkRegion(JNIEnv* env, jobject regionObj);
-
-} // namespace android
-
-#endif // _ANDROID_GRAPHICS_REGION_H_
diff --git a/core/jni/android/graphics/apex/android_canvas.cpp b/core/jni/android/graphics/apex/android_canvas.cpp
new file mode 100644
index 0000000..7a4495f
--- /dev/null
+++ b/core/jni/android/graphics/apex/android_canvas.cpp
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "android/graphics/canvas.h"
+
+#include "GraphicsJNI.h"
+
+#include <hwui/Canvas.h>
+#include <utils/Color.h>
+
+#include <SkBitmap.h>
+
+using namespace android;
+
+static inline Canvas* toCanvas(ACanvas* aCanvas) {
+    return reinterpret_cast<Canvas*>(aCanvas);
+}
+
+static inline ACanvas* toACanvas(Canvas* canvas) {
+    return reinterpret_cast<ACanvas*>(canvas);
+}
+
+bool ACanvas_isSupportedPixelFormat(int32_t bufferFormat) {
+    ANativeWindow_Buffer buffer { 0, 0, 0, bufferFormat, nullptr, {0} };
+    const SkColorType colorType = uirenderer::ANativeWindowToImageInfo(buffer, nullptr).colorType();
+    return kUnknown_SkColorType != colorType;
+}
+
+ACanvas* ACanvas_getNativeHandleFromJava(JNIEnv* env, jobject canvasObj) {
+    return toACanvas(GraphicsJNI::getNativeCanvas(env, canvasObj));
+}
+
+void ACanvas_setBuffer(ACanvas* canvas, const ANativeWindow_Buffer* buffer,
+                       int32_t /*android_dataspace_t*/ dataspace) {
+    SkBitmap bitmap;
+    if (buffer != nullptr && buffer->width > 0 && buffer->height > 0) {
+        sk_sp<SkColorSpace> cs(uirenderer::DataSpaceToColorSpace((android_dataspace)dataspace));
+        SkImageInfo imageInfo = uirenderer::ANativeWindowToImageInfo(*buffer, cs);
+        ssize_t rowBytes = buffer->stride * imageInfo.bytesPerPixel();
+        bitmap.setInfo(imageInfo, rowBytes);
+        bitmap.setPixels(buffer->bits);
+    }
+
+    toCanvas(canvas)->setBitmap(bitmap);
+}
+
+void ACanvas_clipRect(ACanvas* canvas, const ARect& clipRect, bool /*doAA*/) {
+    //TODO update Canvas to take antialias param
+    toCanvas(canvas)->clipRect(clipRect.left, clipRect.top, clipRect.right, clipRect.bottom,
+                               SkClipOp::kIntersect);
+}
+
+void ACanvas_clipOutRect(ACanvas* canvas, const ARect& clipRect, bool /*doAA*/) {
+    //TODO update Canvas to take antialias param
+    toCanvas(canvas)->clipRect(clipRect.left, clipRect.top, clipRect.right, clipRect.bottom,
+                               SkClipOp::kDifference);
+}
diff --git a/core/jni/android/graphics/apex/android_region.cpp b/core/jni/android/graphics/apex/android_region.cpp
new file mode 100644
index 0000000..2030e7e
--- /dev/null
+++ b/core/jni/android/graphics/apex/android_region.cpp
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "android/graphics/region.h"
+
+#include "GraphicsJNI.h"
+
+#include <SkRegion.h>
+
+static inline SkRegion::Iterator* ARegionIter_to_SkRegionIter(ARegionIterator* iterator) {
+    return reinterpret_cast<SkRegion::Iterator*>(iterator);
+}
+
+static inline ARegionIterator* SkRegionIter_to_ARegionIter(SkRegion::Iterator* iterator) {
+    return reinterpret_cast<ARegionIterator*>(iterator);
+}
+
+ARegionIterator* ARegionIterator_acquireIterator(JNIEnv* env, jobject regionObj) {
+    SkRegion* region = GraphicsJNI::getNativeRegion(env, regionObj);
+    return (!region) ? nullptr : SkRegionIter_to_ARegionIter(new SkRegion::Iterator(*region));
+}
+
+void ARegionIterator_releaseIterator(ARegionIterator* iterator) {
+    delete ARegionIter_to_SkRegionIter(iterator);
+}
+
+bool ARegionIterator_isComplex(ARegionIterator* iterator) {
+    return ARegionIter_to_SkRegionIter(iterator)->rgn()->isComplex();
+}
+
+bool ARegionIterator_isDone(ARegionIterator* iterator) {
+    return ARegionIter_to_SkRegionIter(iterator)->done();
+}
+
+void ARegionIterator_next(ARegionIterator* iterator) {
+    ARegionIter_to_SkRegionIter(iterator)->next();
+}
+
+ARect ARegionIterator_getRect(ARegionIterator* iterator) {
+    const SkIRect& rect = ARegionIter_to_SkRegionIter(iterator)->rect();
+    return { rect.fLeft, rect.fTop, rect.fRight, rect.fBottom };
+}
+
+ARect ARegionIterator_getTotalBounds(ARegionIterator* iterator) {
+    const SkIRect& bounds = ARegionIter_to_SkRegionIter(iterator)->rgn()->getBounds();
+    return { bounds.fLeft, bounds.fTop, bounds.fRight, bounds.fBottom };
+}
diff --git a/core/jni/android/graphics/apex/include/android/graphics/canvas.h b/core/jni/android/graphics/apex/include/android/graphics/canvas.h
new file mode 100644
index 0000000..c35a7d6
--- /dev/null
+++ b/core/jni/android/graphics/apex/include/android/graphics/canvas.h
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#ifndef ANDROID_GRAPHICS_CANVAS_H
+#define ANDROID_GRAPHICS_CANVAS_H
+
+#include <android/native_window.h>
+#include <android/rect.h>
+#include <jni.h>
+
+__BEGIN_DECLS
+
+/**
+* Opaque handle for a native graphics canvas.
+*/
+typedef struct ACanvas ACanvas;
+
+//  One of AHardwareBuffer_Format.
+bool ACanvas_isSupportedPixelFormat(int32_t bufferFormat);
+
+/**
+ * Returns a native handle to a Java android.graphics.Canvas
+ *
+ * @param env
+ * @param canvas
+ * @return ACanvas* that is only valid for the life of the jobject.
+ */
+ACanvas* ACanvas_getNativeHandleFromJava(JNIEnv* env, jobject canvas);
+
+/**
+ * Updates the canvas to render into the pixels in the provided buffer
+ *
+ * @param canvas
+ * @param buffer The buffer that will provide the backing store for this canvas.  The buffer must
+ *               remain valid until the this method is called again with either another active
+ *               buffer or nullptr.  If nullptr is given the canvas will release the previous buffer
+ *               and set an empty backing store.
+ * @param dataspace
+ */
+void ACanvas_setBuffer(ACanvas* canvas, const ANativeWindow_Buffer* buffer,
+                       int32_t /*android_dataspace_t*/ dataspace);
+
+/**
+ * Clips operations on the canvas to the intersection of the current clip and the provided clipRect.
+ */
+void ACanvas_clipRect(ACanvas* canvas, const ARect& clipRect, bool doAntiAlias = false);
+
+/**
+ * Clips operations on the canvas to the difference of the current clip and the provided clipRect.
+ */
+void ACanvas_clipOutRect(ACanvas* canvas, const ARect& clipRect, bool doAntiAlias = false);
+
+__END_DECLS
+#endif // ANDROID_GRAPHICS_CANVAS_H
\ No newline at end of file
diff --git a/core/jni/android/graphics/apex/include/android/graphics/region.h b/core/jni/android/graphics/apex/include/android/graphics/region.h
new file mode 100644
index 0000000..961067a
--- /dev/null
+++ b/core/jni/android/graphics/apex/include/android/graphics/region.h
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#ifndef ANDROID_GRAPHICS_REGION_H
+#define ANDROID_GRAPHICS_REGION_H
+
+#include <android/rect.h>
+#include <sys/cdefs.h>
+#include <jni.h>
+
+__BEGIN_DECLS
+
+/**
+* Opaque handle for a native graphics region iterator.
+*/
+typedef struct ARegionIterator ARegionIterator;
+
+/**
+ * Returns a iterator for a Java android.graphics.Region
+ *
+ * @param env
+ * @param region
+ * @return ARegionIterator that must be closed and must not live longer than the life
+ *         of the jobject.  It returns nullptr if the region is not a valid object.
+ */
+ARegionIterator* ARegionIterator_acquireIterator(JNIEnv* env, jobject region);
+
+void ARegionIterator_releaseIterator(ARegionIterator* iterator);
+
+bool ARegionIterator_isComplex(ARegionIterator* iterator);
+
+bool ARegionIterator_isDone(ARegionIterator* iterator);
+
+void ARegionIterator_next(ARegionIterator* iterator);
+
+ARect ARegionIterator_getRect(ARegionIterator* iterator);
+
+ARect ARegionIterator_getTotalBounds(ARegionIterator* iterator);
+
+__END_DECLS
+
+#ifdef	__cplusplus
+namespace android {
+namespace graphics {
+    class RegionIterator {
+    public:
+        RegionIterator(JNIEnv* env, jobject region)
+                : mIterator(ARegionIterator_acquireIterator(env, region)) {}
+        ~RegionIterator() { ARegionIterator_releaseIterator(mIterator); }
+
+        bool isValid() const { return mIterator != nullptr; }
+        bool isComplex() { return ARegionIterator_isComplex(mIterator); }
+        bool isDone() { return ARegionIterator_isDone(mIterator); }
+        void next() { ARegionIterator_next(mIterator); }
+        ARect getRect() { return ARegionIterator_getRect(mIterator); }
+        ARect getTotalBounds() const { return ARegionIterator_getTotalBounds(mIterator); }
+    private:
+        ARegionIterator* mIterator;
+    };
+}; // namespace graphics
+}; // namespace android
+
+#endif // __cplusplus
+#endif // ANDROID_GRAPHICS_REGION_H
\ No newline at end of file
diff --git a/core/jni/android_graphics_GraphicBuffer.cpp b/core/jni/android_graphics_GraphicBuffer.cpp
index bb9254c..43d22eb 100644
--- a/core/jni/android_graphics_GraphicBuffer.cpp
+++ b/core/jni/android_graphics_GraphicBuffer.cpp
@@ -21,11 +21,6 @@
 #include <inttypes.h>
 
 #include "android_os_Parcel.h"
-#include "android/graphics/GraphicsJNI.h"
-
-#include <android_runtime/AndroidRuntime.h>
-#include <android_runtime/android_graphics_GraphicBuffer.h>
-
 #include <binder/Parcel.h>
 
 #include <log/log.h>
@@ -33,10 +28,10 @@
 #include <ui/GraphicBuffer.h>
 #include <ui/PixelFormat.h>
 
-#include <hwui/Bitmap.h>
-
-#include <SkCanvas.h>
-#include <SkBitmap.h>
+#include <android/native_window.h>
+#include <android/graphics/canvas.h>
+#include <android_runtime/android_graphics_GraphicBuffer.h>
+#include <private/android/AHardwareBufferHelpers.h>
 
 #include <private/gui/ComposerService.h>
 
@@ -146,23 +141,8 @@
 // Canvas management
 // ----------------------------------------------------------------------------
 
-static inline SkColorType convertPixelFormat(int32_t format) {
-    switch (format) {
-        case PIXEL_FORMAT_RGBA_8888:
-            return kN32_SkColorType;
-        case PIXEL_FORMAT_RGBX_8888:
-            return kN32_SkColorType;
-        case PIXEL_FORMAT_RGBA_FP16:
-            return kRGBA_F16_SkColorType;
-        case PIXEL_FORMAT_RGB_565:
-            return kRGB_565_SkColorType;
-        default:
-            return kUnknown_SkColorType;
-    }
-}
-
 static jboolean android_graphics_GraphicBuffer_lockCanvas(JNIEnv* env, jobject,
-        jlong wrapperHandle, jobject canvas, jobject dirtyRect) {
+        jlong wrapperHandle, jobject canvasObj, jobject dirtyRect) {
 
     GraphicBufferWrapper* wrapper =
                 reinterpret_cast<GraphicBufferWrapper*>(wrapperHandle);
@@ -191,24 +171,16 @@
         return JNI_FALSE;
     }
 
-    ssize_t bytesCount = buffer->getStride() * bytesPerPixel(buffer->getPixelFormat());
+    ANativeWindow_Buffer nativeBuffer;
+    nativeBuffer.width = buffer->getWidth();
+    nativeBuffer.height = buffer->getHeight();
+    nativeBuffer.stride = buffer->getStride();
+    nativeBuffer.format = AHardwareBuffer_convertFromPixelFormat(buffer->getPixelFormat());
+    nativeBuffer.bits = bits;
 
-    SkBitmap bitmap;
-    bitmap.setInfo(SkImageInfo::Make(buffer->getWidth(), buffer->getHeight(),
-                                     convertPixelFormat(buffer->getPixelFormat()),
-                                     kPremul_SkAlphaType),
-                   bytesCount);
-
-    if (buffer->getWidth() > 0 && buffer->getHeight() > 0) {
-        bitmap.setPixels(bits);
-    } else {
-        bitmap.setPixels(NULL);
-    }
-
-    Canvas* nativeCanvas = GraphicsJNI::getNativeCanvas(env, canvas);
-    nativeCanvas->setBitmap(bitmap);
-    nativeCanvas->clipRect(rect.left, rect.top, rect.right, rect.bottom,
-            SkClipOp::kIntersect);
+    ACanvas* canvas = ACanvas_getNativeHandleFromJava(env, canvasObj);
+    ACanvas_setBuffer(canvas, &nativeBuffer, ADATASPACE_UNKNOWN);
+    ACanvas_clipRect(canvas, {rect.left, rect.top, rect.right, rect.bottom});
 
     if (dirtyRect) {
         INVOKEV(dirtyRect, gRectClassInfo.set,
@@ -219,13 +191,13 @@
 }
 
 static jboolean android_graphics_GraphicBuffer_unlockCanvasAndPost(JNIEnv* env, jobject,
-        jlong wrapperHandle, jobject canvas) {
+        jlong wrapperHandle, jobject canvasObj) {
+    // release the buffer from the canvas
+    ACanvas* canvas = ACanvas_getNativeHandleFromJava(env, canvasObj);
+    ACanvas_setBuffer(canvas, nullptr, ADATASPACE_UNKNOWN);
 
     GraphicBufferWrapper* wrapper =
                 reinterpret_cast<GraphicBufferWrapper*>(wrapperHandle);
-    Canvas* nativeCanvas = GraphicsJNI::getNativeCanvas(env, canvas);
-    nativeCanvas->setBitmap(SkBitmap());
-
     if (wrapper) {
         status_t status = wrapper->get()->unlock();
         return status == 0 ? JNI_TRUE : JNI_FALSE;
diff --git a/core/jni/android_hardware_input_InputWindowHandle.cpp b/core/jni/android_hardware_input_InputWindowHandle.cpp
index 98680fa1e..64caf33 100644
--- a/core/jni/android_hardware_input_InputWindowHandle.cpp
+++ b/core/jni/android_hardware_input_InputWindowHandle.cpp
@@ -22,7 +22,7 @@
 #include <android_runtime/AndroidRuntime.h>
 #include <utils/threads.h>
 
-#include <android/graphics/Region.h>
+#include <android/graphics/region.h>
 #include <gui/SurfaceControl.h>
 #include <ui/Region.h>
 
@@ -128,10 +128,9 @@
     jobject regionObj = env->GetObjectField(obj,
             gInputWindowHandleClassInfo.touchableRegion);
     if (regionObj) {
-        SkRegion* region = android_graphics_Region_getSkRegion(env, regionObj);
-        for (SkRegion::Iterator it(*region); !it.done(); it.next()) {
-            const SkIRect& rect = it.rect();
-            mInfo.addTouchableRegion(Rect(rect.fLeft, rect.fTop, rect.fRight, rect.fBottom));
+        for (graphics::RegionIterator it(env, regionObj); !it.isDone(); it.next()) {
+            ARect rect = it.getRect();
+            mInfo.addTouchableRegion(Rect(rect.left, rect.top, rect.right, rect.bottom));
         }
         env->DeleteLocalRef(regionObj);
     }
diff --git a/core/jni/android_view_InputChannel.cpp b/core/jni/android_view_InputChannel.cpp
index 7e3b083..8553a2c 100644
--- a/core/jni/android_view_InputChannel.cpp
+++ b/core/jni/android_view_InputChannel.cpp
@@ -210,8 +210,7 @@
     if (parcel) {
         bool isInitialized = parcel->readInt32();
         if (isInitialized) {
-            InputChannel* inputChannel = new InputChannel();
-            inputChannel->read(*parcel);
+            sp<InputChannel> inputChannel = InputChannel::read(*parcel);
 
             NativeInputChannel* nativeInputChannel = new NativeInputChannel(inputChannel);
 
diff --git a/core/jni/android_view_Surface.cpp b/core/jni/android_view_Surface.cpp
index d65e252..4c2e91f 100644
--- a/core/jni/android_view_Surface.cpp
+++ b/core/jni/android_view_Surface.cpp
@@ -20,15 +20,16 @@
 
 #include "jni.h"
 #include <nativehelper/JNIHelp.h>
-#include "android_os_Parcel.h"
-#include "android/graphics/GraphicsJNI.h"
-
 #include "core_jni_helpers.h"
+
+#include <android/graphics/canvas.h>
 #include <android_runtime/android_graphics_GraphicBuffer.h>
 #include <android_runtime/android_graphics_SurfaceTexture.h>
 #include <android_runtime/android_view_Surface.h>
 #include <android_runtime/Log.h>
+#include <private/android/AHardwareBufferHelpers.h>
 
+#include "android_os_Parcel.h"
 #include <binder/Parcel.h>
 
 #include <gui/Surface.h>
@@ -39,11 +40,6 @@
 #include <ui/Rect.h>
 #include <ui/Region.h>
 
-#include <SkCanvas.h>
-#include <SkBitmap.h>
-#include <SkImage.h>
-#include <SkRegion.h>
-
 #include <utils/misc.h>
 #include <utils/Log.h>
 
@@ -232,37 +228,21 @@
         dirtyRectPtr = &dirtyRect;
     }
 
-    ANativeWindow_Buffer outBuffer;
-    status_t err = surface->lock(&outBuffer, dirtyRectPtr);
+    ANativeWindow_Buffer buffer;
+    status_t err = surface->lock(&buffer, dirtyRectPtr);
     if (err < 0) {
         const char* const exception = (err == NO_MEMORY) ?
-                OutOfResourcesException :
-                "java/lang/IllegalArgumentException";
+                OutOfResourcesException : IllegalArgumentException;
         jniThrowException(env, exception, NULL);
         return 0;
     }
 
-    SkImageInfo info = SkImageInfo::Make(outBuffer.width, outBuffer.height,
-                                         convertPixelFormat(outBuffer.format),
-                                         outBuffer.format == PIXEL_FORMAT_RGBX_8888
-                                                 ? kOpaque_SkAlphaType : kPremul_SkAlphaType);
-
-    SkBitmap bitmap;
-    ssize_t bpr = outBuffer.stride * bytesPerPixel(outBuffer.format);
-    bitmap.setInfo(info, bpr);
-    if (outBuffer.width > 0 && outBuffer.height > 0) {
-        bitmap.setPixels(outBuffer.bits);
-    } else {
-        // be safe with an empty bitmap.
-        bitmap.setPixels(NULL);
-    }
-
-    Canvas* nativeCanvas = GraphicsJNI::getNativeCanvas(env, canvasObj);
-    nativeCanvas->setBitmap(bitmap);
+    ACanvas* canvas = ACanvas_getNativeHandleFromJava(env, canvasObj);
+    ACanvas_setBuffer(canvas, &buffer, static_cast<int32_t>(surface->getBuffersDataSpace()));
 
     if (dirtyRectPtr) {
-        nativeCanvas->clipRect(dirtyRect.left, dirtyRect.top,
-                dirtyRect.right, dirtyRect.bottom, SkClipOp::kIntersect);
+        ACanvas_clipRect(canvas, {dirtyRect.left, dirtyRect.top,
+                                  dirtyRect.right, dirtyRect.bottom});
     }
 
     if (dirtyRectObj) {
@@ -288,8 +268,8 @@
     }
 
     // detach the canvas from the surface
-    Canvas* nativeCanvas = GraphicsJNI::getNativeCanvas(env, canvasObj);
-    nativeCanvas->setBitmap(SkBitmap());
+    ACanvas* canvas = ACanvas_getNativeHandleFromJava(env, canvasObj);
+    ACanvas_setBuffer(canvas, nullptr, ADATASPACE_UNKNOWN);
 
     // unlock surface
     status_t err = surface->unlockAndPost();
diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp
index 096bf69..67f52f4 100644
--- a/core/jni/android_view_SurfaceControl.cpp
+++ b/core/jni/android_view_SurfaceControl.cpp
@@ -20,9 +20,9 @@
 #include "android_os_Parcel.h"
 #include "android_util_Binder.h"
 #include "android_hardware_input_InputWindowHandle.h"
-#include "android/graphics/Region.h"
 #include "core_jni_helpers.h"
 
+#include <android/graphics/region.h>
 #include <android_runtime/AndroidRuntime.h>
 #include <android-base/chrono_utils.h>
 #include <nativehelper/JNIHelp.h>
@@ -425,20 +425,19 @@
 static void nativeSetTransparentRegionHint(JNIEnv* env, jclass clazz, jlong transactionObj,
         jlong nativeObject, jobject regionObj) {
     SurfaceControl* const ctrl = reinterpret_cast<SurfaceControl *>(nativeObject);
-    SkRegion* region = android_graphics_Region_getSkRegion(env, regionObj);
-    if (!region) {
+    graphics::RegionIterator iterator(env, regionObj);
+    if (!iterator.isValid()) {
         doThrowIAE(env);
         return;
     }
 
-    const SkIRect& b(region->getBounds());
-    Region reg(Rect(b.fLeft, b.fTop, b.fRight, b.fBottom));
-    if (region->isComplex()) {
-        SkRegion::Iterator it(*region);
-        while (!it.done()) {
-            const SkIRect& r(it.rect());
-            reg.addRectUnchecked(r.fLeft, r.fTop, r.fRight, r.fBottom);
-            it.next();
+    ARect bounds = iterator.getTotalBounds();
+    Region reg({bounds.left, bounds.top, bounds.right, bounds.bottom});
+    if (iterator.isComplex()) {
+        while (!iterator.isDone()) {
+            ARect rect = iterator.getRect();
+            reg.addRectUnchecked(rect.left, rect.top, rect.right, rect.bottom);
+            iterator.next();
         }
     }
 
diff --git a/core/jni/android_view_TextureView.cpp b/core/jni/android_view_TextureView.cpp
index 15319ad..1f69c8b 100644
--- a/core/jni/android_view_TextureView.cpp
+++ b/core/jni/android_view_TextureView.cpp
@@ -25,11 +25,7 @@
 #include <gui/GLConsumer.h>
 #include <gui/Surface.h>
 
-#include <SkBitmap.h>
-#include <SkCanvas.h>
-#include <SkImage.h>
-
-#include "android/graphics/GraphicsJNI.h"
+#include <android/graphics/canvas.h>
 
 #include "core_jni_helpers.h"
 
@@ -70,33 +66,6 @@
 // Native layer
 // ----------------------------------------------------------------------------
 
-// FIXME: consider exporting this to share (e.g. android_view_Surface.cpp)
-static inline SkImageInfo convertPixelFormat(const ANativeWindow_Buffer& buffer) {
-    SkColorType colorType = kUnknown_SkColorType;
-    SkAlphaType alphaType = kOpaque_SkAlphaType;
-    switch (buffer.format) {
-        case WINDOW_FORMAT_RGBA_8888:
-            colorType = kN32_SkColorType;
-            alphaType = kPremul_SkAlphaType;
-            break;
-        case WINDOW_FORMAT_RGBX_8888:
-            colorType = kN32_SkColorType;
-            alphaType = kOpaque_SkAlphaType;
-            break;
-        case AHARDWAREBUFFER_FORMAT_R16G16B16A16_FLOAT:
-            colorType = kRGBA_F16_SkColorType;
-            alphaType = kPremul_SkAlphaType;
-            break;
-        case WINDOW_FORMAT_RGB_565:
-            colorType = kRGB_565_SkColorType;
-            alphaType = kOpaque_SkAlphaType;
-            break;
-        default:
-            break;
-    }
-    return SkImageInfo::Make(buffer.width, buffer.height, colorType, alphaType);
-}
-
 /**
  * This is a private API, and this implementation is also provided in the NDK.
  * However, the NDK links against android_runtime, which means that using the
@@ -134,14 +103,12 @@
 }
 
 static jboolean android_view_TextureView_lockCanvas(JNIEnv* env, jobject,
-        jlong nativeWindow, jobject canvas, jobject dirtyRect) {
+        jlong nativeWindow, jobject canvasObj, jobject dirtyRect) {
 
     if (!nativeWindow) {
         return JNI_FALSE;
     }
 
-    ANativeWindow_Buffer buffer;
-
     Rect rect(Rect::EMPTY_RECT);
     if (dirtyRect) {
         rect.left = GET_INT(dirtyRect, gRectClassInfo.left);
@@ -152,25 +119,14 @@
         rect.set(Rect(0x3FFF, 0x3FFF));
     }
 
+    ANativeWindow_Buffer outBuffer;
     sp<ANativeWindow> window((ANativeWindow*) nativeWindow);
-    int32_t status = native_window_lock(window.get(), &buffer, &rect);
+    int32_t status = native_window_lock(window.get(), &outBuffer, &rect);
     if (status) return JNI_FALSE;
 
-    ssize_t bytesCount = buffer.stride * bytesPerPixel(buffer.format);
-
-    SkBitmap bitmap;
-    bitmap.setInfo(convertPixelFormat(buffer), bytesCount);
-
-    if (buffer.width > 0 && buffer.height > 0) {
-        bitmap.setPixels(buffer.bits);
-    } else {
-        bitmap.setPixels(NULL);
-    }
-
-    Canvas* nativeCanvas = GraphicsJNI::getNativeCanvas(env, canvas);
-    nativeCanvas->setBitmap(bitmap);
-    nativeCanvas->clipRect(rect.left, rect.top, rect.right, rect.bottom,
-            SkClipOp::kIntersect);
+    ACanvas* canvas = ACanvas_getNativeHandleFromJava(env, canvasObj);
+    ACanvas_setBuffer(canvas, &outBuffer, ANativeWindow_getBuffersDataSpace(window.get()));
+    ACanvas_clipRect(canvas, {rect.left, rect.top, rect.right, rect.bottom});
 
     if (dirtyRect) {
         INVOKEV(dirtyRect, gRectClassInfo.set,
@@ -181,10 +137,11 @@
 }
 
 static void android_view_TextureView_unlockCanvasAndPost(JNIEnv* env, jobject,
-        jlong nativeWindow, jobject canvas) {
+        jlong nativeWindow, jobject canvasObj) {
 
-    Canvas* nativeCanvas = GraphicsJNI::getNativeCanvas(env, canvas);
-    nativeCanvas->setBitmap(SkBitmap());
+    // release the buffer from the canvas
+    ACanvas* canvas = ACanvas_getNativeHandleFromJava(env, canvasObj);
+    ACanvas_setBuffer(canvas, nullptr, ADATASPACE_UNKNOWN);
 
     if (nativeWindow) {
         sp<ANativeWindow> window((ANativeWindow*) nativeWindow);
diff --git a/core/jni/fd_utils.cpp b/core/jni/fd_utils.cpp
index ea4b252..bb57805 100644
--- a/core/jni/fd_utils.cpp
+++ b/core/jni/fd_utils.cpp
@@ -80,9 +80,9 @@
     return true;
   }
 
-  // Jars from the runtime apex are allowed.
-  static const char* kRuntimeApexPrefix = "/apex/com.android.runtime/javalib/";
-  if (android::base::StartsWith(path, kRuntimeApexPrefix)
+  // Jars from the ART APEX are allowed.
+  static const char* kArtApexPrefix = "/apex/com.android.art/javalib/";
+  if (android::base::StartsWith(path, kArtApexPrefix)
       && android::base::EndsWith(path, kJarSuffix)) {
     return true;
   }
diff --git a/core/proto/android/server/activitymanagerservice.proto b/core/proto/android/server/activitymanagerservice.proto
index a1ff9b9..d54b6b0 100644
--- a/core/proto/android/server/activitymanagerservice.proto
+++ b/core/proto/android/server/activitymanagerservice.proto
@@ -367,10 +367,8 @@
         optional int32 id = 3;
         optional bool is_proc = 4;
         optional bool has_activities = 5;
-        oneof ss_kb {
-            int64 pss_kb = 6;
-            int64 rss_kb = 9;
-        }
+        optional int64 pss_kb = 6;
+        optional int64 rss_kb = 9;
         optional int64 swap_pss_kb = 7;
         repeated MemItem sub_items = 8;
     }
diff --git a/core/proto/android/server/jobscheduler.proto b/core/proto/android/server/jobscheduler.proto
index aac144c..79167ab 100644
--- a/core/proto/android/server/jobscheduler.proto
+++ b/core/proto/android/server/jobscheduler.proto
@@ -308,6 +308,8 @@
         // Whether or not TimeController should skip setting wakeup alarms for jobs that aren't
         // ready now.
         reserved 1; // skip_not_ready_jobs
+        // Whether or not TimeController will use a non-wakeup alarm for delay constraints.
+        optional bool use_non_wakeup_alarm_for_delay = 2;
     }
     optional TimeController time_controller = 25;
 
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 6a20484..9738759 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -1264,6 +1264,15 @@
         android:description="@string/permdesc_camera"
         android:protectionLevel="dangerous|instant" />
 
+      <!-- @SystemApi Required in addition to android.permission.CAMERA to be able to access
+           system only camera devices.
+           <p>Protection level: system|signature
+           @hide -->
+    <permission android:name="android.permission.SYSTEM_CAMERA"
+        android:permissionGroup="android.permission-group.UNDEFINED"
+        android:label="@string/permlab_systemCamera"
+        android:description="@string/permdesc_systemCamera"
+        android:protectionLevel="system|signature" />
 
     <!-- ====================================================================== -->
     <!-- Permissions for accessing the device sensors                           -->
@@ -4458,12 +4467,12 @@
     <!-- @SystemApi Allows to access all app shortcuts.
          @hide -->
     <permission android:name="android.permission.ACCESS_SHORTCUTS"
-        android:protectionLevel="signature|textClassifier" />
+        android:protectionLevel="signature|appPredictor" />
 
     <!-- @SystemApi Allows unlimited calls to shortcut mutation APIs.
          @hide -->
     <permission android:name="android.permission.UNLIMITED_SHORTCUTS_API_CALLS"
-        android:protectionLevel="signature|textClassifier" />
+        android:protectionLevel="signature|appPredictor" />
 
     <!-- @SystemApi Allows an application to read the runtime profiles of other apps.
          @hide <p>Not for use by third-party applications. -->
@@ -4524,6 +4533,13 @@
     <permission android:name="android.permission.BIND_EXPLICIT_HEALTH_CHECK_SERVICE"
         android:protectionLevel="signature|privileged" />
 
+    <!-- @SystemApi Must be required by an {@link android.service.storage.ExternalStorageService} to
+         ensure that only the system can bind to it.
+         @hide This is not a third-party API (intended for OEMs and system apps).
+    -->
+    <permission android:name="android.permission.BIND_EXTERNAL_STORAGE_SERVICE"
+        android:protectionLevel="signature" />
+
     <!-- @hide Permission that allows configuring appops.
      <p>Not for use by third-party applications. -->
     <permission android:name="android.permission.MANAGE_APPOPS"
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index ceccd0d..ab8a2b0 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -1703,16 +1703,16 @@
         <!-- Add packages here -->
     </string-array>
 
-    <!-- The set of system packages on device that are queryable regardless of the contents of their
-         manifest. -->
+    <!-- The set of system packages on device that are queryable by any app regardless of the
+         contents of its manifest. -->
     <string-array name="config_forceQueryablePackages" translatable="false">
         <item>com.android.settings</item>
         <item>com.android.providers.settings</item>
         <!-- Add packages here -->
     </string-array>
 
-    <!-- If true, will force all packages on any system partition as queryable regardless of the
-         contents of their manifest. -->
+    <!-- If true, will force all packages on any system partition as queryable by any app regardless
+         of the contents of its manifest. -->
     <bool name="config_forceSystemPackagesQueryable">false</bool>
 
     <!-- Component name of the default wallpaper. This will be ImageWallpaper if not
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index cdfa0439..2b97bf8 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -1173,6 +1173,11 @@
     <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
     <string name="permdesc_camera">This app can take pictures and record videos using the camera at any time.</string>
 
+    <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. [CHAR_LIMIT=NONE] -->
+    <string name="permlab_systemCamera">Allow an application or service access to system cameras to take pictures and videos</string>
+    <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. [CHAR_LIMIT=NONE] -->
+    <string name="permdesc_systemCamera">This privileged | system app can take pictures and record videos using a system camera at any time. Requires the android.permission.CAMERA permission to be held by the app as well</string>
+
     <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
     <string name="permlab_vibrate">control vibration</string>
     <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
diff --git a/core/tests/coretests/src/android/util/ArraySetTest.java b/core/tests/coretests/src/android/util/ArraySetTest.java
new file mode 100644
index 0000000..f1bebfb
--- /dev/null
+++ b/core/tests/coretests/src/android/util/ArraySetTest.java
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.util;
+
+import androidx.test.filters.LargeTest;
+
+import junit.framework.TestCase;
+
+import org.junit.After;
+import org.junit.Test;
+
+import java.util.ConcurrentModificationException;
+
+/**
+ * Unit tests for ArraySet that don't belong in CTS.
+ */
+@LargeTest
+public class ArraySetTest extends TestCase {
+    private static final String TAG = "ArraySetTest";
+    ArraySet<String> mSet = new ArraySet<>();
+
+    @After
+    public void tearDown() {
+        mSet = null;
+    }
+
+    /**
+     * Attempt to generate a ConcurrentModificationException in ArraySet.
+     * <p>
+     * ArraySet is explicitly documented to be non-thread-safe, yet it's easy to accidentally screw
+     * this up; ArraySet should (in the spirit of the core Java collection types) make an effort to
+     * catch this and throw ConcurrentModificationException instead of crashing somewhere in its
+     * internals.
+     */
+    @Test
+    public void testConcurrentModificationException() throws Exception {
+        final int testDurMs = 10_000;
+        System.out.println("Starting ArraySet concurrency test");
+        new Thread(() -> {
+            int i = 0;
+            while (mSet != null) {
+                try {
+                    mSet.add(String.format("key %d", i++));
+                } catch (ArrayIndexOutOfBoundsException e) {
+                    Log.e(TAG, "concurrent modification uncaught, causing indexing failure", e);
+                    fail("Concurrent modification uncaught, causing indexing failure: " + e);
+                } catch (ClassCastException e) {
+                    Log.e(TAG, "concurrent modification uncaught, causing cache corruption", e);
+                    fail("Concurrent modification uncaught, causing cache corruption: " + e);
+                } catch (ConcurrentModificationException e) {
+                    System.out.println("[successfully caught CME at put #" + i
+                            + " size=" + (mSet == null ? "??" : String.valueOf(mSet.size())) + "]");
+                    if (i % 200 == 0) {
+                        System.out.print(".");
+                    }
+                }
+            }
+        }).start();
+        for (int i = 0; i < (testDurMs / 100); i++) {
+            try {
+                if (mSet.size() % 4 == 0) {
+                    mSet.clear();
+                }
+                System.out.print("X");
+            } catch (ArrayIndexOutOfBoundsException e) {
+                Log.e(TAG, "concurrent modification uncaught, causing indexing failure", e);
+                fail("Concurrent modification uncaught, causing indexing failure: " + e);
+            } catch (ClassCastException e) {
+                Log.e(TAG, "concurrent modification uncaught, causing cache corruption", e);
+                fail("Concurrent modification uncaught, causing cache corruption: " + e);
+            } catch (ConcurrentModificationException e) {
+                System.out.println(
+                        "[successfully caught CME at clear #" + i + " size=" + mSet.size() + "]");
+            }
+        }
+    }
+
+    /**
+     * Check to make sure the same operations behave as expected in a single thread.
+     */
+    @Test
+    public void testNonConcurrentAccesses() throws Exception {
+        for (int i = 0; i < 100000; i++) {
+            try {
+                mSet.add(String.format("key %d", i++));
+                if (i % 200 == 0) {
+                    System.out.print(".");
+                }
+                if (i % 500 == 0) {
+                    mSet.clear();
+                    System.out.print("X");
+                }
+            } catch (ConcurrentModificationException e) {
+                Log.e(TAG, "concurrent modification caught on single thread", e);
+                fail();
+            }
+        }
+    }
+}
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index 4187c80..89523d6 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -322,6 +322,8 @@
         <permission name="android.permission.SET_WALLPAPER" />
         <permission name="android.permission.SET_WALLPAPER_COMPONENT" />
         <permission name="android.permission.REQUEST_NOTIFICATION_ASSISTANT_SERVICE" />
+        <!-- Permission required to test system only camera devices. -->
+        <permission name="android.permission.SYSTEM_CAMERA" />
         <!-- Permission required to test ExplicitHealthCheckServiceImpl. -->
         <permission name="android.permission.BIND_EXPLICIT_HEALTH_CHECK_SERVICE"/>
     </privapp-permissions>
diff --git a/libs/hwui/utils/Color.cpp b/libs/hwui/utils/Color.cpp
index 9a27f28..b93759f 100644
--- a/libs/hwui/utils/Color.cpp
+++ b/libs/hwui/utils/Color.cpp
@@ -21,6 +21,7 @@
 
 #ifdef __ANDROID__ // Layoutlib does not support hardware buffers or native windows
 #include <android/hardware_buffer.h>
+#include <android/native_window.h>
 #endif
 
 #include <algorithm>
@@ -30,11 +31,11 @@
 namespace uirenderer {
 
 #ifdef __ANDROID__ // Layoutlib does not support hardware buffers or native windows
-SkImageInfo BufferDescriptionToImageInfo(const AHardwareBuffer_Desc& bufferDesc,
-                                         sk_sp<SkColorSpace> colorSpace) {
+static inline SkImageInfo createImageInfo(int32_t width, int32_t height, int32_t format,
+                                          sk_sp<SkColorSpace> colorSpace) {
     SkColorType colorType = kUnknown_SkColorType;
     SkAlphaType alphaType = kOpaque_SkAlphaType;
-    switch (bufferDesc.format) {
+    switch (format) {
         case AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM:
             colorType = kN32_SkColorType;
             alphaType = kPremul_SkAlphaType;
@@ -56,10 +57,20 @@
             alphaType = kPremul_SkAlphaType;
             break;
         default:
-            ALOGV("Unsupported format: %d, return unknown by default", bufferDesc.format);
+            ALOGV("Unsupported format: %d, return unknown by default", format);
             break;
     }
-    return SkImageInfo::Make(bufferDesc.width, bufferDesc.height, colorType, alphaType, colorSpace);
+    return SkImageInfo::Make(width, height, colorType, alphaType, colorSpace);
+}
+
+SkImageInfo ANativeWindowToImageInfo(const ANativeWindow_Buffer& buffer,
+                                     sk_sp<SkColorSpace> colorSpace) {
+    return createImageInfo(buffer.width, buffer.height, buffer.format, colorSpace);
+}
+
+SkImageInfo BufferDescriptionToImageInfo(const AHardwareBuffer_Desc& bufferDesc,
+                                         sk_sp<SkColorSpace> colorSpace) {
+    return createImageInfo(bufferDesc.width, bufferDesc.height, bufferDesc.format, colorSpace);
 }
 #endif
 
diff --git a/libs/hwui/utils/Color.h b/libs/hwui/utils/Color.h
index 7c2378a..07b5ec8 100644
--- a/libs/hwui/utils/Color.h
+++ b/libs/hwui/utils/Color.h
@@ -25,6 +25,7 @@
 #include <SkColorSpace.h>
 #include <SkImageInfo.h>
 
+struct ANativeWindow_Buffer;
 struct AHardwareBuffer_Desc;
 
 namespace android {
@@ -91,8 +92,13 @@
     return srgb <= 0.04045f ? srgb / 12.92f : powf((srgb + 0.055f) / 1.055f, 2.4f);
 }
 
+#ifdef __ANDROID__ // Layoutlib does not support hardware buffers or native windows
+ANDROID_API SkImageInfo ANativeWindowToImageInfo(const ANativeWindow_Buffer& buffer,
+                                                 sk_sp<SkColorSpace> colorSpace);
+
 SkImageInfo BufferDescriptionToImageInfo(const AHardwareBuffer_Desc& bufferDesc,
                                          sk_sp<SkColorSpace> colorSpace);
+#endif
 
 android::PixelFormat ColorTypeToPixelFormat(SkColorType colorType);
 ANDROID_API SkColorType PixelFormatToColorType(android::PixelFormat format);
diff --git a/media/Android.bp b/media/Android.bp
index 29064ad..ef32239 100644
--- a/media/Android.bp
+++ b/media/Android.bp
@@ -67,6 +67,7 @@
         "apex/java/android/media/Session2Link.java",
         "apex/java/android/media/Session2Token.java",
     ],
+    path: "apex/java",
 }
 
 filegroup {
@@ -79,6 +80,7 @@
         "apex/java/android/media/IMediaSession2.aidl",
         "apex/java/android/media/IMediaSession2Service.aidl",
     ],
+    path: "apex/java",
 }
 
 filegroup {
@@ -98,6 +100,7 @@
         "apex/java/android/media/BufferingParams.java",
         "apex/java/android/media/ProxyDataSourceCallback.java",
     ],
+    path: "apex/java",
 }
 
 metalava_updatable_media_args = " --error UnhiddenSystemApi " +
diff --git a/media/java/android/media/Metadata.java b/media/java/android/media/Metadata.java
index be0d966..792a2ba 100644
--- a/media/java/android/media/Metadata.java
+++ b/media/java/android/media/Metadata.java
@@ -272,15 +272,6 @@
     @UnsupportedAppUsage
     public Metadata() { }
 
-    // Have to declare protected for finalize() since it is protected
-    // in the base class Object.
-    @Override
-    protected void finalize() throws Throwable {
-        if (mParcel != null) {
-            mParcel.recycle();
-        }
-    }
-
     /**
      * Go over all the records, collecting metadata keys and records'
      * type field offset in the Parcel. These are stored in
@@ -427,10 +418,6 @@
             parcel.setDataPosition(pin);
             return false;
         }
-
-        if (mParcel != null) {
-            mParcel.recycle();
-        }
         mParcel = parcel;
         return true;
     }
diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
index e95103b..7ea83f5 100644
--- a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
+++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
@@ -201,6 +201,11 @@
                 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();
@@ -247,9 +252,6 @@
         mPowerManagerHelper.connectToCarService();
 
         mSwitchToGuestTimer = new SwitchToGuestTimer(mContext);
-
-        mScreenLifecycle = Dependency.get(ScreenLifecycle.class);
-        mScreenLifecycle.addObserver(mScreenObserver);
     }
 
     /**
@@ -839,7 +841,11 @@
         }
     }
 
-    private void attachBottomNavBarWindow() {
+    /**
+     * 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;
         }
@@ -862,7 +868,11 @@
         mWindowManager.addView(mNavigationBarWindow, lp);
     }
 
-    private void detachBottomNavBarWindow() {
+    /**
+     * 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;
         }
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/SettingsLib/SearchWidget/res/values-pt-rPT/strings.xml b/packages/SettingsLib/SearchWidget/res/values-pt-rPT/strings.xml
index 7846be1..363d885 100644
--- a/packages/SettingsLib/SearchWidget/res/values-pt-rPT/strings.xml
+++ b/packages/SettingsLib/SearchWidget/res/values-pt-rPT/strings.xml
@@ -17,5 +17,5 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="search_menu" msgid="1604061903696928905">"Definições de pesquisa"</string>
+    <string name="search_menu" msgid="1604061903696928905">"Pesquisar definições"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-bn/strings.xml b/packages/SettingsLib/res/values-bn/strings.xml
index 13890e0..f1fc9f9 100644
--- a/packages/SettingsLib/res/values-bn/strings.xml
+++ b/packages/SettingsLib/res/values-bn/strings.xml
@@ -452,7 +452,7 @@
     <string name="cancel" msgid="6859253417269739139">"বাতিল"</string>
     <string name="okay" msgid="1997666393121016642">"ঠিক আছে"</string>
     <string name="zen_mode_enable_dialog_turn_on" msgid="8287824809739581837">"চালু করুন"</string>
-    <string name="zen_mode_settings_turn_on_dialog_title" msgid="2297134204747331078">"\'বিরক্ত করবেন না\' মোড চালু করুন"</string>
+    <string name="zen_mode_settings_turn_on_dialog_title" msgid="2297134204747331078">"\'বিরক্ত করবে না\' মোড চালু করুন"</string>
     <string name="zen_mode_settings_summary_off" msgid="6119891445378113334">"কখনও নয়"</string>
     <string name="zen_interruption_level_priority" msgid="2078370238113347720">"শুধুমাত্র অগ্রাধিকার"</string>
     <string name="zen_mode_and_condition" msgid="4927230238450354412">"<xliff:g id="ZEN_MODE">%1$s</xliff:g>. <xliff:g id="EXIT_CONDITION">%2$s</xliff:g>"</string>
diff --git a/packages/SettingsLib/res/values-es-rUS/strings.xml b/packages/SettingsLib/res/values-es-rUS/strings.xml
index 2734f4e..7874aeb 100644
--- a/packages/SettingsLib/res/values-es-rUS/strings.xml
+++ b/packages/SettingsLib/res/values-es-rUS/strings.xml
@@ -286,7 +286,7 @@
     <string name="wait_for_debugger_summary" msgid="1766918303462746804">"Esperar que se conecte el depurador para iniciar la aplicación"</string>
     <string name="debug_input_category" msgid="1811069939601180246">"Entrada"</string>
     <string name="debug_drawing_category" msgid="6755716469267367852">"Dibujo"</string>
-    <string name="debug_hw_drawing_category" msgid="6220174216912308658">"Representación acelerada mediante hardware"</string>
+    <string name="debug_hw_drawing_category" msgid="6220174216912308658">"Procesamiento acelerado mediante hardware"</string>
     <string name="media_category" msgid="4388305075496848353">"Multimedia"</string>
     <string name="debug_monitoring_category" msgid="7640508148375798343">"Supervisión"</string>
     <string name="strict_mode" msgid="1938795874357830695">"Modo estricto"</string>
diff --git a/packages/SettingsLib/res/values-hy/arrays.xml b/packages/SettingsLib/res/values-hy/arrays.xml
index 5cffafe..7368f1d 100644
--- a/packages/SettingsLib/res/values-hy/arrays.xml
+++ b/packages/SettingsLib/res/values-hy/arrays.xml
@@ -43,7 +43,7 @@
     <item msgid="8937994881315223448">"Միացված է <xliff:g id="NETWORK_NAME">%1$s</xliff:g>-ին"</item>
     <item msgid="1330262655415760617">"Անջատված"</item>
     <item msgid="7698638434317271902">"Անջատվում է <xliff:g id="NETWORK_NAME">%1$s</xliff:g>-ից…"</item>
-    <item msgid="197508606402264311">"Անջատած է"</item>
+    <item msgid="197508606402264311">"Անջատված է"</item>
     <item msgid="8578370891960825148">"Անհաջող"</item>
     <item msgid="5660739516542454527">"Արգելափակված"</item>
     <item msgid="1805837518286731242">"Վատ ցանցից ժամանակավոր խուսափում"</item>
diff --git a/packages/SettingsLib/res/values-hy/strings.xml b/packages/SettingsLib/res/values-hy/strings.xml
index bf58740..a74b4ae 100644
--- a/packages/SettingsLib/res/values-hy/strings.xml
+++ b/packages/SettingsLib/res/values-hy/strings.xml
@@ -263,7 +263,7 @@
     <string name="debug_view_attributes" msgid="6485448367803310384">"Միացնել ցուցադրման հատկանիշների ստուգումը"</string>
     <string name="mobile_data_always_on_summary" msgid="8149773901431697910">"Միշտ ակտիվացրած պահել բջջային տվյալները, նույնիսկ Wi‑Fi-ը միացրած ժամանակ (ցանցերի միջև արագ փոխարկման համար):"</string>
     <string name="tethering_hardware_offload_summary" msgid="7726082075333346982">"Օգտագործել սարքակազմի արագացման միացումը, եթե հասանելի է"</string>
-    <string name="adb_warning_title" msgid="6234463310896563253">"Թույլատրե՞լ USB-ի վրիպազերծումը:"</string>
+    <string name="adb_warning_title" msgid="6234463310896563253">"Թույլատրե՞լ USB վրիպազերծումը:"</string>
     <string name="adb_warning_message" msgid="7316799925425402244">"USB վրիպազերծումը միայն ծրագրավորման նպատակների համար է: Օգտագործեք այն ձեր համակարգչից տվյալները ձեր սարք պատճենելու համար, առանց ծանուցման ձեր սարքի վրա ծրագրեր տեղադրելու և տվյալների մատյանը ընթերցելու համար:"</string>
     <string name="adb_keys_warning_message" msgid="5659849457135841625">"Փակե՞լ USB-ի վրիպազերծման մուտքը` անջատելով այն բոլոր համակարգիչներից, որտեղ նախկինում թույլատրել էիք:"</string>
     <string name="dev_settings_warning_title" msgid="7244607768088540165">"Ընդունե՞լ ծրագրավորման կարգավորումներ:"</string>
diff --git a/packages/SettingsLib/res/values-ta/strings.xml b/packages/SettingsLib/res/values-ta/strings.xml
index cf442b7..57e690d 100644
--- a/packages/SettingsLib/res/values-ta/strings.xml
+++ b/packages/SettingsLib/res/values-ta/strings.xml
@@ -159,7 +159,7 @@
     <string name="tts_default_pitch_title" msgid="6135942113172488671">"ஒலித்திறன்"</string>
     <string name="tts_default_pitch_summary" msgid="1944885882882650009">"உருவாக்கப்படும் பேச்சின் டோன் பாதிக்கப்படும்"</string>
     <string name="tts_default_lang_title" msgid="8018087612299820556">"மொழி"</string>
-    <string name="tts_lang_use_system" msgid="2679252467416513208">"அமைப்பின் மொழியில்"</string>
+    <string name="tts_lang_use_system" msgid="2679252467416513208">"அமைப்பின் மொழியைப் பயன்படுத்தவும்"</string>
     <string name="tts_lang_not_selected" msgid="7395787019276734765">"மொழி தேர்ந்தெடுக்கப்படவில்லை"</string>
     <string name="tts_default_lang_summary" msgid="5219362163902707785">"பேசப்படும் உரைக்கு மொழி சார்ந்த குரலை அமைக்கிறது"</string>
     <string name="tts_play_example_title" msgid="7094780383253097230">"எடுத்துக்காட்டைக் கவனிக்கவும்"</string>
diff --git a/packages/SettingsLib/res/values-te/strings.xml b/packages/SettingsLib/res/values-te/strings.xml
index 83636dc..eb6160a 100644
--- a/packages/SettingsLib/res/values-te/strings.xml
+++ b/packages/SettingsLib/res/values-te/strings.xml
@@ -143,7 +143,7 @@
     <string name="data_usage_ota" msgid="5377889154805560860">"సిస్టమ్ అప్‌డేట్‌లు"</string>
     <string name="tether_settings_title_usb" msgid="6688416425801386511">"USB టీథరింగ్"</string>
     <string name="tether_settings_title_wifi" msgid="3277144155960302049">"పోర్టబుల్ హాట్‌స్పాట్"</string>
-    <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"బ్లూటూత్ టీథరింగ్"</string>
+    <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"బ్లూటూత్ టెథెరింగ్"</string>
     <string name="tether_settings_title_usb_bluetooth" msgid="5355828977109785001">"టీథరింగ్"</string>
     <string name="tether_settings_title_all" msgid="8356136101061143841">"టీథరింగ్ &amp; పోర్టబుల్ హాట్‌స్పాట్"</string>
     <string name="managed_user_title" msgid="8109605045406748842">"అన్ని కార్యాలయ అనువర్తనాలు"</string>
diff --git a/packages/SettingsLib/res/values-vi/strings.xml b/packages/SettingsLib/res/values-vi/strings.xml
index 4e2d6fa..5c06c80 100644
--- a/packages/SettingsLib/res/values-vi/strings.xml
+++ b/packages/SettingsLib/res/values-vi/strings.xml
@@ -201,7 +201,7 @@
     <string name="vpn_settings_not_available" msgid="956841430176985598">"Cài đặt VPN không khả dụng cho người dùng này"</string>
     <string name="tethering_settings_not_available" msgid="6765770438438291012">"Cài đặt chia sẻ kết nối không khả dụng cho người dùng này"</string>
     <string name="apn_settings_not_available" msgid="7873729032165324000">"Cài đặt tên điểm truy cập không khả dụng cho người dùng này"</string>
-    <string name="enable_adb" msgid="7982306934419797485">"Gỡ lỗi USB"</string>
+    <string name="enable_adb" msgid="7982306934419797485">"Gỡ lỗi qua USB"</string>
     <string name="enable_adb_summary" msgid="4881186971746056635">"Bật chế độ gỡ lỗi khi kết nối USB"</string>
     <string name="clear_adb_keys" msgid="4038889221503122743">"Thu hồi ủy quyền gỡ lỗi USB"</string>
     <string name="bugreport_in_power" msgid="7923901846375587241">"Phím tắt báo cáo lỗi"</string>
@@ -263,7 +263,7 @@
     <string name="debug_view_attributes" msgid="6485448367803310384">"Cho phép kiểm tra thuộc tính của chế độ xem"</string>
     <string name="mobile_data_always_on_summary" msgid="8149773901431697910">"Luôn bật dữ liệu di động ngay cả khi Wi-Fi đang hoạt động (để chuyển đổi mạng nhanh)."</string>
     <string name="tethering_hardware_offload_summary" msgid="7726082075333346982">"Sử dụng tính năng tăng tốc phần cứng khi chia sẻ kết nối nếu có"</string>
-    <string name="adb_warning_title" msgid="6234463310896563253">"Cho phép gỡ lỗi USB?"</string>
+    <string name="adb_warning_title" msgid="6234463310896563253">"Cho phép gỡ lỗi qua USB?"</string>
     <string name="adb_warning_message" msgid="7316799925425402244">"Gỡ lỗi USB chỉ dành cho mục đích phát triển. Hãy sử dụng tính năng này để sao chép dữ liệu giữa máy tính và thiết bị của bạn, cài đặt ứng dụng trên thiết bị của bạn mà không thông báo và đọc dữ liệu nhật ký."</string>
     <string name="adb_keys_warning_message" msgid="5659849457135841625">"Thu hồi quyền truy cập gỡ lỗi USB từ tất cả máy tính mà bạn đã ủy quyền trước đó?"</string>
     <string name="dev_settings_warning_title" msgid="7244607768088540165">"Cho phép cài đặt phát triển?"</string>
diff --git a/packages/SettingsLib/res/values-zh-rTW/strings.xml b/packages/SettingsLib/res/values-zh-rTW/strings.xml
index e85199f..c641302 100644
--- a/packages/SettingsLib/res/values-zh-rTW/strings.xml
+++ b/packages/SettingsLib/res/values-zh-rTW/strings.xml
@@ -329,7 +329,7 @@
     <string name="show_all_anrs" msgid="4924885492787069007">"顯示背景 ANR"</string>
     <string name="show_all_anrs_summary" msgid="6636514318275139826">"為背景應用程式顯示「應用程式無回應」對話方塊"</string>
     <string name="show_notification_channel_warnings" msgid="1399948193466922683">"顯示通知管道警告"</string>
-    <string name="show_notification_channel_warnings_summary" msgid="5536803251863694895">"當應用程式未經有效管道發佈通知時,在畫面上顯示警告"</string>
+    <string name="show_notification_channel_warnings_summary" msgid="5536803251863694895">"當應用程式未經有效管道發布通知時,在畫面上顯示警告"</string>
     <string name="force_allow_on_external" msgid="3215759785081916381">"強制允許將應用程式寫入外部儲存空間"</string>
     <string name="force_allow_on_external_summary" msgid="3640752408258034689">"允許將任何應用程式寫入外部儲存空間 (無論資訊清單值為何)"</string>
     <string name="force_resizable_activities" msgid="8615764378147824985">"將活動強制設為可調整大小"</string>
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index bb9a5e4..b2ff4b3 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -169,6 +169,9 @@
     <uses-permission android:name="android.permission.MANAGE_SENSORS" />
     <uses-permission android:name="android.permission.MANAGE_AUDIO_POLICY" />
     <uses-permission android:name="android.permission.MANAGE_CAMERA" />
+    <!-- Permissions needed to test system only camera devices -->
+    <uses-permission android:name="android.permission.CAMERA" />
+    <uses-permission android:name="android.permission.SYSTEM_CAMERA" />
     <!-- Permission needed to enable/disable Bluetooth/Wifi -->
     <uses-permission android:name="android.permission.MANAGE_BLUETOOTH_WHEN_WIRELESS_CONSENT_REQUIRED" />
     <uses-permission android:name="android.permission.MANAGE_WIFI_WHEN_WIRELESS_CONSENT_REQUIRED" />
@@ -235,6 +238,7 @@
 
         <activity
             android:name=".BugreportWarningActivity"
+            android:theme="@android:style/Theme.DeviceDefault.Dialog.Alert"
             android:finishOnCloseSystemDialogs="true"
             android:excludeFromRecents="true"
             android:exported="false" />
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/HomeControlsPlugin.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/HomeControlsPlugin.java
new file mode 100644
index 0000000..cac673f
--- /dev/null
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/HomeControlsPlugin.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.systemui.plugins;
+
+import android.view.ViewGroup;
+
+import com.android.systemui.plugins.annotations.ProvidesInterface;
+
+/**
+ * Test plugin for home controls
+ */
+@ProvidesInterface(action = HomeControlsPlugin.ACTION, version = HomeControlsPlugin.VERSION)
+public interface HomeControlsPlugin extends Plugin {
+
+    String ACTION = "com.android.systemui.action.PLUGIN_HOME_CONTROLS";
+    int VERSION = 1;
+
+    /**
+      * Pass the container for the plugin to use however it wants. Ideally the plugin impl
+      * will add home controls to this space.
+      */
+    void sendParentGroup(ViewGroup group);
+}
diff --git a/packages/SystemUI/res/layout/status_bar_expanded.xml b/packages/SystemUI/res/layout/status_bar_expanded.xml
index 7d403b2..7a94008 100644
--- a/packages/SystemUI/res/layout/status_bar_expanded.xml
+++ b/packages/SystemUI/res/layout/status_bar_expanded.xml
@@ -55,6 +55,19 @@
             android:clipChildren="false"
             systemui:viewType="com.android.systemui.plugins.qs.QS" />
 
+        <!-- Temporary area to test out home controls -->
+        <LinearLayout
+            android:id="@+id/home_controls_layout"
+            android:layout_width="match_parent"
+            android:layout_height="125dp"
+            android:layout_gravity="@integer/notification_panel_layout_gravity"
+            android:visibility="gone"
+            android:padding="8dp"
+            android:layout_margin="5dp"
+            android:background="?android:attr/colorBackgroundFloating"
+            android:orientation="vertical">
+        </LinearLayout>
+
         <com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout
             android:id="@+id/notification_stack_scroller"
             android:layout_marginTop="@dimen/notification_panel_margin_top"
diff --git a/packages/SystemUI/src/com/android/keyguard/CarrierTextController.java b/packages/SystemUI/src/com/android/keyguard/CarrierTextController.java
index 10d132a..45126f3 100644
--- a/packages/SystemUI/src/com/android/keyguard/CarrierTextController.java
+++ b/packages/SystemUI/src/com/android/keyguard/CarrierTextController.java
@@ -19,16 +19,12 @@
 import static android.telephony.PhoneStateListener.LISTEN_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGE;
 import static android.telephony.PhoneStateListener.LISTEN_NONE;
 
-import static com.android.internal.telephony.PhoneConstants.MAX_PHONE_COUNT_DUAL_SIM;
-
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.net.ConnectivityManager;
 import android.net.wifi.WifiManager;
 import android.os.Handler;
-import android.os.SystemProperties;
-import android.telephony.CarrierConfigManager;
 import android.telephony.PhoneStateListener;
 import android.telephony.ServiceState;
 import android.telephony.SubscriptionInfo;
@@ -37,19 +33,17 @@
 import android.text.TextUtils;
 import android.util.Log;
 
-import androidx.annotation.VisibleForTesting;
-
 import com.android.internal.telephony.IccCardConstants;
 import com.android.internal.telephony.TelephonyIntents;
-import com.android.internal.telephony.TelephonyProperties;
 import com.android.settingslib.WirelessUtils;
 import com.android.systemui.Dependency;
 import com.android.systemui.keyguard.WakefulnessLifecycle;
 
-import java.util.ArrayList;
 import java.util.List;
 import java.util.Objects;
 
+import androidx.annotation.VisibleForTesting;
+
 /**
  * Controller that generates text including the carrier names and/or the status of all the SIM
  * interfaces in the device. Through a callback, the updates can be retrieved either as a list or
@@ -72,8 +66,6 @@
     private Context mContext;
     private CharSequence mSeparator;
     private WakefulnessLifecycle mWakefulnessLifecycle;
-    @VisibleForTesting
-    protected boolean mDisplayOpportunisticSubscriptionCarrierText;
     private final WakefulnessLifecycle.Observer mWakefulnessObserver =
             new WakefulnessLifecycle.Observer() {
                 @Override
@@ -174,9 +166,6 @@
         mSimSlotsNumber = ((TelephonyManager) context.getSystemService(
                 Context.TELEPHONY_SERVICE)).getPhoneCount();
         mSimErrorState = new boolean[mSimSlotsNumber];
-        updateDisplayOpportunisticSubscriptionCarrierText(SystemProperties.getBoolean(
-                TelephonyProperties.DISPLAY_OPPORTUNISTIC_SUBSCRIPTION_CARRIER_TEXT_PROPERTY_NAME,
-                false));
     }
 
     /**
@@ -253,63 +242,8 @@
         }
     }
 
-    /**
-     * @param subscriptions
-     */
-    private void filterMobileSubscriptionInSameGroup(List<SubscriptionInfo> subscriptions) {
-        if (subscriptions.size() == MAX_PHONE_COUNT_DUAL_SIM) {
-            SubscriptionInfo info1 = subscriptions.get(0);
-            SubscriptionInfo info2 = subscriptions.get(1);
-            if (info1.getGroupUuid() != null && info1.getGroupUuid().equals(info2.getGroupUuid())) {
-                // If both subscriptions are primary, show both.
-                if (!info1.isOpportunistic() && !info2.isOpportunistic()) return;
-
-                // If carrier required, always show signal bar of primary subscription.
-                // Otherwise, show whichever subscription is currently active for Internet.
-                boolean alwaysShowPrimary = CarrierConfigManager.getDefaultConfig()
-                        .getBoolean(CarrierConfigManager
-                        .KEY_ALWAYS_SHOW_PRIMARY_SIGNAL_BAR_IN_OPPORTUNISTIC_NETWORK_BOOLEAN);
-                if (alwaysShowPrimary) {
-                    subscriptions.remove(info1.isOpportunistic() ? info1 : info2);
-                } else {
-                    subscriptions.remove(info1.getSubscriptionId() == mActiveMobileDataSubscription
-                            ? info2 : info1);
-                }
-
-            }
-        }
-    }
-
-    /**
-     * updates if opportunistic sub carrier text should be displayed or not
-     *
-     */
-    @VisibleForTesting
-    public void updateDisplayOpportunisticSubscriptionCarrierText(boolean isEnable) {
-        mDisplayOpportunisticSubscriptionCarrierText = isEnable;
-    }
-
     protected List<SubscriptionInfo> getSubscriptionInfo() {
-        List<SubscriptionInfo> subs;
-        if (mDisplayOpportunisticSubscriptionCarrierText) {
-            SubscriptionManager subscriptionManager = ((SubscriptionManager) mContext
-                    .getSystemService(
-                    Context.TELEPHONY_SUBSCRIPTION_SERVICE));
-            subs = subscriptionManager.getActiveSubscriptionInfoList(false);
-            if (subs == null) {
-                subs = new ArrayList<>();
-            } else {
-                filterMobileSubscriptionInSameGroup(subs);
-            }
-        } else {
-            subs = mKeyguardUpdateMonitor.getSubscriptionInfo(false);
-            if (subs == null) {
-                subs = new ArrayList<>();
-            } else {
-                filterMobileSubscriptionInSameGroup(subs);
-            }
-        }
-        return subs;
+        return mKeyguardUpdateMonitor.getFilteredSubscriptionInfo(false);
     }
 
     protected void updateCarrierText() {
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index 8c5374a..5d6cc83 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -30,6 +30,7 @@
 import static android.os.BatteryManager.EXTRA_STATUS;
 import static android.telephony.PhoneStateListener.LISTEN_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGE;
 
+import static com.android.internal.telephony.PhoneConstants.MAX_PHONE_COUNT_DUAL_SIM;
 import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_BOOT;
 import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_DPM_LOCK_NOW;
 import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_LOCKOUT;
@@ -77,6 +78,7 @@
 import android.provider.Settings;
 import android.service.dreams.DreamService;
 import android.service.dreams.IDreamManager;
+import android.telephony.CarrierConfigManager;
 import android.telephony.PhoneStateListener;
 import android.telephony.ServiceState;
 import android.telephony.SubscriptionInfo;
@@ -261,6 +263,7 @@
     private boolean mLogoutEnabled;
     // If the user long pressed the lock icon, disabling face auth for the current session.
     private boolean mLockIconPressed;
+    private int mActiveMobileDataSubscription = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
 
     /**
      * Short delay before restarting biometric authentication after a successful try
@@ -406,9 +409,11 @@
                 }
             };
 
-    private PhoneStateListener mPhoneStateListener = new PhoneStateListener() {
+    @VisibleForTesting
+    public PhoneStateListener mPhoneStateListener = new PhoneStateListener() {
         @Override
         public void onActiveDataSubscriptionIdChanged(int subId) {
+            mActiveMobileDataSubscription = subId;
             mHandler.sendEmptyMessage(MSG_SIM_SUBSCRIPTION_INFO_CHANGED);
         }
     };
@@ -511,7 +516,9 @@
         }
     }
 
-    /** @return List of SubscriptionInfo records, maybe empty but never null */
+    /**
+     * @return List of SubscriptionInfo records, maybe empty but never null.
+     */
     public List<SubscriptionInfo> getSubscriptionInfo(boolean forceReload) {
         List<SubscriptionInfo> sil = mSubscriptionInfo;
         if (sil == null || forceReload) {
@@ -523,7 +530,42 @@
         } else {
             mSubscriptionInfo = sil;
         }
-        return mSubscriptionInfo;
+        return new ArrayList<>(mSubscriptionInfo);
+    }
+
+    /**
+     * This method returns filtered list of SubscriptionInfo from {@link #getSubscriptionInfo}.
+     * above. Maybe empty but never null.
+     *
+     * In DSDS mode if both subscriptions are grouped and one is opportunistic, we filter out one
+     * of them based on carrier config. e.g. In this case we should only show one carrier name
+     * on the status bar and quick settings.
+     */
+    public List<SubscriptionInfo> getFilteredSubscriptionInfo(boolean forceReload) {
+        List<SubscriptionInfo> subscriptions = getSubscriptionInfo(false);
+        if (subscriptions.size() == MAX_PHONE_COUNT_DUAL_SIM) {
+            SubscriptionInfo info1 = subscriptions.get(0);
+            SubscriptionInfo info2 = subscriptions.get(1);
+            if (info1.getGroupUuid() != null && info1.getGroupUuid().equals(info2.getGroupUuid())) {
+                // If both subscriptions are primary, show both.
+                if (!info1.isOpportunistic() && !info2.isOpportunistic()) return subscriptions;
+
+                // If carrier required, always show signal bar of primary subscription.
+                // Otherwise, show whichever subscription is currently active for Internet.
+                boolean alwaysShowPrimary = CarrierConfigManager.getDefaultConfig()
+                        .getBoolean(CarrierConfigManager
+                        .KEY_ALWAYS_SHOW_PRIMARY_SIGNAL_BAR_IN_OPPORTUNISTIC_NETWORK_BOOLEAN);
+                if (alwaysShowPrimary) {
+                    subscriptions.remove(info1.isOpportunistic() ? info1 : info2);
+                } else {
+                    subscriptions.remove(info1.getSubscriptionId() == mActiveMobileDataSubscription
+                            ? info2 : info1);
+                }
+
+            }
+        }
+
+        return subscriptions;
     }
 
     @Override
@@ -2700,6 +2742,7 @@
                 pw.println("    " + mSubscriptionInfo.get(i));
             }
         }
+        pw.println("  Current active data subId=" + mActiveMobileDataSubscription);
         pw.println("  Service states:");
         for (int subId : mServiceStates.keySet()) {
             pw.println("    " + subId + "=" + mServiceStates.get(subId));
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
index 7e3b423..d5c928b 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
@@ -140,11 +140,12 @@
             LockPatternUtils lockPatternUtils,  ViewGroup container,
             DismissCallbackRegistry dismissCallbackRegistry,
             KeyguardBouncer.BouncerExpansionCallback expansionCallback,
-            FalsingManager falsingManager) {
+            FalsingManager falsingManager, KeyguardBypassController bypassController) {
         return new KeyguardBouncer(context, callback, lockPatternUtils, container,
                 dismissCallbackRegistry, falsingManager,
                 expansionCallback, UnlockMethodCache.getInstance(context),
-                KeyguardUpdateMonitor.getInstance(context), new Handler(Looper.getMainLooper()));
+                KeyguardUpdateMonitor.getInstance(context), bypassController,
+                new Handler(Looper.getMainLooper()));
     }
 
     public ScrimController createScrimController(ScrimView scrimBehind, ScrimView scrimInFront,
diff --git a/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java b/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java
index 858ed6d..ef171d3 100644
--- a/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java
@@ -50,7 +50,7 @@
  */
 @Singleton
 public class AppOpsControllerImpl implements AppOpsController,
-        AppOpsManager.OnOpActiveChangedListener,
+        AppOpsManager.OnOpActiveChangedInternalListener,
         AppOpsManager.OnOpNotedListener, Dumpable {
 
     private static final long NOTED_OP_TIME_DELAY_MS = 5000;
diff --git a/packages/SystemUI/src/com/android/systemui/glwallpaper/EglHelper.java b/packages/SystemUI/src/com/android/systemui/glwallpaper/EglHelper.java
index d7411260..aac721e 100644
--- a/packages/SystemUI/src/com/android/systemui/glwallpaper/EglHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/glwallpaper/EglHelper.java
@@ -146,7 +146,13 @@
      * @return true if EglSurface is ready.
      */
     public boolean createEglSurface(SurfaceHolder surfaceHolder) {
-        mEglSurface = eglCreateWindowSurface(mEglDisplay, mEglConfig, surfaceHolder, null, 0);
+        if (hasEglDisplay()) {
+            mEglSurface = eglCreateWindowSurface(mEglDisplay, mEglConfig, surfaceHolder, null, 0);
+        } else {
+            Log.w(TAG, "mEglDisplay is null");
+            return false;
+        }
+
         if (mEglSurface == null || mEglSurface == EGL_NO_SURFACE) {
             Log.w(TAG, "createWindowSurface failed: " + GLUtils.getEGLErrorString(eglGetError()));
             return false;
@@ -186,7 +192,13 @@
     public boolean createEglContext() {
         int[] attrib_list = new int[] {EGL_CONTEXT_CLIENT_VERSION, 2,
                 EGL_CONTEXT_PRIORITY_LEVEL_IMG, EGL_CONTEXT_PRIORITY_LOW_IMG, EGL_NONE};
-        mEglContext = eglCreateContext(mEglDisplay, mEglConfig, EGL_NO_CONTEXT, attrib_list, 0);
+        if (hasEglDisplay()) {
+            mEglContext = eglCreateContext(mEglDisplay, mEglConfig, EGL_NO_CONTEXT, attrib_list, 0);
+        } else {
+            Log.w(TAG, "mEglDisplay is null");
+            return false;
+        }
+
         if (mEglContext == EGL_NO_CONTEXT) {
             Log.w(TAG, "eglCreateContext failed: " + GLUtils.getEGLErrorString(eglGetError()));
             return false;
@@ -213,6 +225,14 @@
     }
 
     /**
+     * Check if we have EglDisplay.
+     * @return true if EglDisplay is ready.
+     */
+    public boolean hasEglDisplay() {
+        return mEglDisplay != null;
+    }
+
+    /**
      * Swap buffer to display.
      * @return true if swap successfully.
      */
@@ -235,7 +255,9 @@
         if (hasEglContext()) {
             destroyEglContext();
         }
-        eglTerminate(mEglDisplay);
+        if (hasEglDisplay()) {
+            eglTerminate(mEglDisplay);
+        }
         mEglReady = false;
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/OperatorNameView.java b/packages/SystemUI/src/com/android/systemui/statusbar/OperatorNameView.java
index d1b3c3c..2a5ccdb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/OperatorNameView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/OperatorNameView.java
@@ -134,7 +134,7 @@
 
     private void updateText() {
         CharSequence displayText = null;
-        List<SubscriptionInfo> subs = mKeyguardUpdateMonitor.getSubscriptionInfo(false);
+        List<SubscriptionInfo> subs = mKeyguardUpdateMonitor.getFilteredSubscriptionInfo(false);
         final int N = subs.size();
         for (int i = 0; i < N; i++) {
             int subId = subs.get(i).getSubscriptionId();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/DynamicPrivacyController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/DynamicPrivacyController.java
index d9328fa..e4bd4fa 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/DynamicPrivacyController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/DynamicPrivacyController.java
@@ -107,7 +107,7 @@
      */
     public boolean isInLockedDownShade() {
         if (!mStatusBarKeyguardViewManager.isShowing()
-                || !mStatusBarKeyguardViewManager.isSecure()) {
+                || !mUnlockMethodCache.isMethodSecure()) {
             return false;
         }
         int state = mStateController.getState();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
index c4d346c..dc9b373 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
@@ -77,6 +77,7 @@
                 }
             };
     private final Runnable mRemoveViewRunnable = this::removeView;
+    private final KeyguardBypassController mKeyguardBypassController;
     protected KeyguardHostView mKeyguardView;
     private final Runnable mResetRunnable = ()-> {
         if (mKeyguardView != null) {
@@ -97,7 +98,8 @@
             LockPatternUtils lockPatternUtils, ViewGroup container,
             DismissCallbackRegistry dismissCallbackRegistry, FalsingManager falsingManager,
             BouncerExpansionCallback expansionCallback, UnlockMethodCache unlockMethodCache,
-            KeyguardUpdateMonitor keyguardUpdateMonitor, Handler handler) {
+            KeyguardUpdateMonitor keyguardUpdateMonitor,
+            KeyguardBypassController keyguardBypassController, Handler handler) {
         mContext = context;
         mCallback = callback;
         mLockPatternUtils = lockPatternUtils;
@@ -109,6 +111,7 @@
         mHandler = handler;
         mUnlockMethodCache = unlockMethodCache;
         mKeyguardUpdateMonitor.registerCallback(mUpdateMonitorCallback);
+        mKeyguardBypassController = keyguardBypassController;
     }
 
     public void show(boolean resetSecuritySelection) {
@@ -171,7 +174,8 @@
         // Split up the work over multiple frames.
         DejankUtils.removeCallbacks(mResetRunnable);
         if (mUnlockMethodCache.isFaceAuthEnabled() && !needsFullscreenBouncer()
-                && !mKeyguardUpdateMonitor.userNeedsStrongAuth()) {
+                && !mKeyguardUpdateMonitor.userNeedsStrongAuth()
+                && !mKeyguardBypassController.getBypassEnabled()) {
             mHandler.postDelayed(mShowRunnable, BOUNCER_FACE_DELAY);
         } else {
             DejankUtils.postAfterTraversal(mShowRunnable);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
index c171730..c76cdcb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
@@ -53,6 +53,7 @@
 import android.view.WindowInsets;
 import android.view.accessibility.AccessibilityManager;
 import android.widget.FrameLayout;
+import android.widget.LinearLayout;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.config.sysui.SystemUiDeviceConfigFlags;
@@ -68,10 +69,13 @@
 import com.android.systemui.fragments.FragmentHostManager;
 import com.android.systemui.fragments.FragmentHostManager.FragmentListener;
 import com.android.systemui.plugins.FalsingManager;
+import com.android.systemui.plugins.HomeControlsPlugin;
+import com.android.systemui.plugins.PluginListener;
 import com.android.systemui.plugins.qs.QS;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.plugins.statusbar.StatusBarStateController.StateListener;
 import com.android.systemui.qs.QSFragment;
+import com.android.systemui.shared.plugins.PluginManager;
 import com.android.systemui.statusbar.CommandQueue;
 import com.android.systemui.statusbar.FlingAnimationUtils;
 import com.android.systemui.statusbar.GestureRecorder;
@@ -192,6 +196,7 @@
     private View mQsNavbarScrim;
     protected NotificationsQuickSettingsContainer mNotificationContainerParent;
     protected NotificationStackScrollLayout mNotificationStackScroller;
+    protected LinearLayout mHomeControlsLayout;
     private boolean mAnimateNextPositionUpdate;
 
     private int mTrackingPointer;
@@ -450,6 +455,7 @@
         mBigClockContainer = findViewById(R.id.big_clock_container);
         keyguardClockSwitch.setBigClockContainer(mBigClockContainer);
 
+        mHomeControlsLayout = findViewById(R.id.home_controls_layout);
         mNotificationContainerParent = findViewById(R.id.notification_container_parent);
         mNotificationStackScroller = findViewById(R.id.notification_stack_scroller);
         mNotificationStackScroller.setOnHeightChangedListener(this);
@@ -480,6 +486,21 @@
                 }
             }
         });
+
+        Dependency.get(PluginManager.class).addPluginListener(
+                new PluginListener<HomeControlsPlugin>() {
+
+                    @Override
+                    public void onPluginConnected(HomeControlsPlugin plugin,
+                                                  Context pluginContext) {
+                        plugin.sendParentGroup(mHomeControlsLayout);
+                    }
+
+                    @Override
+                    public void onPluginDisconnected(HomeControlsPlugin plugin) {
+
+                    }
+                }, HomeControlsPlugin.class, false);
     }
 
     @Override
@@ -1270,9 +1291,11 @@
             if (mQsExpandImmediate) {
                 mNotificationStackScroller.setVisibility(View.GONE);
                 mQsFrame.setVisibility(View.VISIBLE);
+                mHomeControlsLayout.setVisibility(View.VISIBLE);
             } else {
                 mNotificationStackScroller.setVisibility(View.VISIBLE);
                 mQsFrame.setVisibility(View.GONE);
+                mHomeControlsLayout.setVisibility(View.GONE);
             }
         }
         return false;
@@ -1551,6 +1574,7 @@
         if (mKeyguardShowing && isQsSplitEnabled()) {
             mNotificationStackScroller.setVisibility(View.VISIBLE);
             mQsFrame.setVisibility(View.VISIBLE);
+            mHomeControlsLayout.setVisibility(View.GONE);
         }
 
         if (oldState == StatusBarState.KEYGUARD
@@ -2099,8 +2123,10 @@
                 t = (expandedHeight - panelHeightQsCollapsed)
                         / (panelHeightQsExpanded - panelHeightQsCollapsed);
             }
-            setQsExpansion(mQsMinExpansionHeight
-                    + t * (mQsMaxExpansionHeight - mQsMinExpansionHeight));
+            float targetHeight = mQsMinExpansionHeight
+                    + t * (mQsMaxExpansionHeight - mQsMinExpansionHeight);
+            setQsExpansion(targetHeight);
+            mHomeControlsLayout.setTranslationY(targetHeight);
         }
         updateExpandedHeight(expandedHeight);
         updateHeader();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
index 3508c90..ccb85fa 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -221,7 +221,7 @@
         mBiometricUnlockController = biometricUnlockController;
         mBouncer = SystemUIFactory.getInstance().createKeyguardBouncer(mContext,
                 mViewMediatorCallback, mLockPatternUtils, container, dismissCallbackRegistry,
-                mExpansionCallback, falsingManager);
+                mExpansionCallback, falsingManager, bypassController);
         mNotificationPanelView = notificationPanelView;
         notificationPanelView.addExpansionListener(this);
         mBypassController = bypassController;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/EmergencyCryptkeeperText.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/EmergencyCryptkeeperText.java
index 0d6178b..f2c0434 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/EmergencyCryptkeeperText.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/EmergencyCryptkeeperText.java
@@ -97,7 +97,7 @@
         boolean allSimsMissing = true;
         CharSequence displayText = null;
 
-        List<SubscriptionInfo> subs = mKeyguardUpdateMonitor.getSubscriptionInfo(false);
+        List<SubscriptionInfo> subs = mKeyguardUpdateMonitor.getFilteredSubscriptionInfo(false);
         final int N = subs.size();
         for (int i = 0; i < N; i++) {
             int subId = subs.get(i).getSubscriptionId();
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/CarrierTextControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/CarrierTextControllerTest.java
index 0044ca7..1ae1b97 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/CarrierTextControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/CarrierTextControllerTest.java
@@ -70,15 +70,10 @@
     private static final CharSequence AIRPLANE_MODE_TEXT = "Airplane mode";
     private static final String TEST_CARRIER = "TEST_CARRIER";
     private static final String TEST_CARRIER_2 = "TEST_CARRIER_2";
-    private static final String TEST_GROUP_UUID = "59b5c870-fc4c-47a4-a99e-9db826b48b24";
     private static final int TEST_CARRIER_ID = 1;
     private static final SubscriptionInfo TEST_SUBSCRIPTION = new SubscriptionInfo(0, "", 0,
             TEST_CARRIER, TEST_CARRIER, NAME_SOURCE_DEFAULT_SOURCE, 0xFFFFFF, "",
-            DATA_ROAMING_DISABLE, null, null, null, null, false, null, "", false, TEST_GROUP_UUID,
-            TEST_CARRIER_ID, 0);
-    private static final SubscriptionInfo TEST_SUBSCRIPTION_2 = new SubscriptionInfo(0, "", 0,
-            TEST_CARRIER, TEST_CARRIER_2, NAME_SOURCE_DEFAULT_SOURCE, 0xFFFFFF, "",
-            DATA_ROAMING_DISABLE, null, null, null, null, false, null, "", true, TEST_GROUP_UUID,
+            DATA_ROAMING_DISABLE, null, null, null, null, false, null, "", false, null,
             TEST_CARRIER_ID, 0);
     private static final SubscriptionInfo TEST_SUBSCRIPTION_ROAMING = new SubscriptionInfo(0, "", 0,
             TEST_CARRIER, TEST_CARRIER, NAME_SOURCE_DEFAULT_SOURCE, 0xFFFFFF, "",
@@ -125,7 +120,6 @@
                 mKeyguardUpdateMonitor);
         // This should not start listening on any of the real dependencies
         mCarrierTextController.setListening(mCarrierTextCallback);
-        mCarrierTextController.updateDisplayOpportunisticSubscriptionCarrierText(false);
     }
 
     @Test
@@ -134,7 +128,7 @@
         reset(mCarrierTextCallback);
         List<SubscriptionInfo> list = new ArrayList<>();
         list.add(TEST_SUBSCRIPTION);
-        when(mKeyguardUpdateMonitor.getSubscriptionInfo(anyBoolean())).thenReturn(list);
+        when(mKeyguardUpdateMonitor.getFilteredSubscriptionInfo(anyBoolean())).thenReturn(list);
         when(mKeyguardUpdateMonitor.getSimState(0)).thenReturn(IccCardConstants.State.READY);
         mKeyguardUpdateMonitor.mServiceStates = new HashMap<>();
 
@@ -154,7 +148,7 @@
         reset(mCarrierTextCallback);
         List<SubscriptionInfo> list = new ArrayList<>();
         list.add(TEST_SUBSCRIPTION);
-        when(mKeyguardUpdateMonitor.getSubscriptionInfo(anyBoolean())).thenReturn(list);
+        when(mKeyguardUpdateMonitor.getFilteredSubscriptionInfo(anyBoolean())).thenReturn(list);
         when(mKeyguardUpdateMonitor.getSimState(0)).thenReturn(IccCardConstants.State.READY);
         when(mKeyguardUpdateMonitor.getSimState(1)).thenReturn(
                 IccCardConstants.State.CARD_IO_ERROR);
@@ -178,7 +172,7 @@
     @Test
     public void testWrongSlots() {
         reset(mCarrierTextCallback);
-        when(mKeyguardUpdateMonitor.getSubscriptionInfo(anyBoolean())).thenReturn(
+        when(mKeyguardUpdateMonitor.getFilteredSubscriptionInfo(anyBoolean())).thenReturn(
                 new ArrayList<>());
         when(mKeyguardUpdateMonitor.getSimState(anyInt())).thenReturn(
                 IccCardConstants.State.CARD_IO_ERROR);
@@ -192,7 +186,7 @@
     @Test
     public void testMoreSlotsThanSubs() {
         reset(mCarrierTextCallback);
-        when(mKeyguardUpdateMonitor.getSubscriptionInfo(anyBoolean())).thenReturn(
+        when(mKeyguardUpdateMonitor.getFilteredSubscriptionInfo(anyBoolean())).thenReturn(
                 new ArrayList<>());
 
         // STOPSHIP(b/130246708) This line makes sure that SubscriptionManager provides the
@@ -242,7 +236,7 @@
         List<SubscriptionInfo> list = new ArrayList<>();
         list.add(TEST_SUBSCRIPTION);
         when(mKeyguardUpdateMonitor.getSimState(anyInt())).thenReturn(IccCardConstants.State.READY);
-        when(mKeyguardUpdateMonitor.getSubscriptionInfo(anyBoolean())).thenReturn(list);
+        when(mKeyguardUpdateMonitor.getFilteredSubscriptionInfo(anyBoolean())).thenReturn(list);
 
         mKeyguardUpdateMonitor.mServiceStates = new HashMap<>();
 
@@ -266,7 +260,7 @@
         List<SubscriptionInfo> list = new ArrayList<>();
         list.add(TEST_SUBSCRIPTION_ROAMING);
         when(mKeyguardUpdateMonitor.getSimState(anyInt())).thenReturn(IccCardConstants.State.READY);
-        when(mKeyguardUpdateMonitor.getSubscriptionInfo(anyBoolean())).thenReturn(list);
+        when(mKeyguardUpdateMonitor.getFilteredSubscriptionInfo(anyBoolean())).thenReturn(list);
 
         mKeyguardUpdateMonitor.mServiceStates = new HashMap<>();
 
@@ -287,7 +281,7 @@
     @Test
     public void testCreateInfo_noSubscriptions() {
         reset(mCarrierTextCallback);
-        when(mKeyguardUpdateMonitor.getSubscriptionInfo(anyBoolean())).thenReturn(
+        when(mKeyguardUpdateMonitor.getFilteredSubscriptionInfo(anyBoolean())).thenReturn(
                 new ArrayList<>());
 
         ArgumentCaptor<CarrierTextController.CarrierTextCallbackInfo> captor =
@@ -311,7 +305,7 @@
         list.add(TEST_SUBSCRIPTION);
         list.add(TEST_SUBSCRIPTION);
         when(mKeyguardUpdateMonitor.getSimState(anyInt())).thenReturn(IccCardConstants.State.READY);
-        when(mKeyguardUpdateMonitor.getSubscriptionInfo(anyBoolean())).thenReturn(list);
+        when(mKeyguardUpdateMonitor.getFilteredSubscriptionInfo(anyBoolean())).thenReturn(list);
 
         mKeyguardUpdateMonitor.mServiceStates = new HashMap<>();
 
@@ -336,7 +330,7 @@
         when(mKeyguardUpdateMonitor.getSimState(anyInt()))
                 .thenReturn(IccCardConstants.State.READY)
                 .thenReturn(IccCardConstants.State.NOT_READY);
-        when(mKeyguardUpdateMonitor.getSubscriptionInfo(anyBoolean())).thenReturn(list);
+        when(mKeyguardUpdateMonitor.getFilteredSubscriptionInfo(anyBoolean())).thenReturn(list);
 
         mKeyguardUpdateMonitor.mServiceStates = new HashMap<>();
 
@@ -361,7 +355,7 @@
         when(mKeyguardUpdateMonitor.getSimState(anyInt()))
                 .thenReturn(IccCardConstants.State.NOT_READY)
                 .thenReturn(IccCardConstants.State.READY);
-        when(mKeyguardUpdateMonitor.getSubscriptionInfo(anyBoolean())).thenReturn(list);
+        when(mKeyguardUpdateMonitor.getFilteredSubscriptionInfo(anyBoolean())).thenReturn(list);
 
         mKeyguardUpdateMonitor.mServiceStates = new HashMap<>();
 
@@ -388,7 +382,7 @@
                 .thenReturn(IccCardConstants.State.READY)
                 .thenReturn(IccCardConstants.State.NOT_READY)
                 .thenReturn(IccCardConstants.State.READY);
-        when(mKeyguardUpdateMonitor.getSubscriptionInfo(anyBoolean())).thenReturn(list);
+        when(mKeyguardUpdateMonitor.getFilteredSubscriptionInfo(anyBoolean())).thenReturn(list);
         mKeyguardUpdateMonitor.mServiceStates = new HashMap<>();
 
         ArgumentCaptor<CarrierTextController.CarrierTextCallbackInfo> captor =
@@ -403,30 +397,6 @@
                 captor.getValue().carrierText);
     }
 
-    @Test
-    public void testCarrierText_GroupedSubWithOpportunisticCarrierText() {
-        reset(mCarrierTextCallback);
-        List<SubscriptionInfo> list = new ArrayList<>();
-        list.add(TEST_SUBSCRIPTION);
-        list.add(TEST_SUBSCRIPTION_2);
-        when(mKeyguardUpdateMonitor.getSimState(anyInt()))
-            .thenReturn(IccCardConstants.State.READY);
-
-        mKeyguardUpdateMonitor.mServiceStates = new HashMap<>();
-        mCarrierTextController.updateDisplayOpportunisticSubscriptionCarrierText(true);
-        when(mSubscriptionManager.getActiveSubscriptionInfoList(anyBoolean())).thenReturn(list);
-
-        ArgumentCaptor<CarrierTextController.CarrierTextCallbackInfo> captor =
-                ArgumentCaptor.forClass(
-                CarrierTextController.CarrierTextCallbackInfo.class);
-
-        mCarrierTextController.updateCarrierText();
-        mTestableLooper.processAllMessages();
-        verify(mCarrierTextCallback).updateCarrierInfo(captor.capture());
-
-        assertEquals(TEST_CARRIER_2, captor.getValue().carrierText);
-    }
-
     public static class TestCarrierTextController extends CarrierTextController {
         private KeyguardUpdateMonitor mKUM;
 
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
index db6177a..a3cb6c0 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
@@ -16,15 +16,18 @@
 
 package com.android.keyguard;
 
+import static android.telephony.SubscriptionManager.DATA_ROAMING_DISABLE;
+import static android.telephony.SubscriptionManager.NAME_SOURCE_DEFAULT_SOURCE;
+
 import static com.google.common.truth.Truth.assertThat;
 
 import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.Mockito.clearInvocations;
 import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.reset;
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
@@ -42,6 +45,7 @@
 import android.os.Bundle;
 import android.os.UserManager;
 import android.telephony.ServiceState;
+import android.telephony.SubscriptionInfo;
 import android.telephony.SubscriptionManager;
 import android.test.suitebuilder.annotation.SmallTest;
 import android.testing.AndroidTestingRunner;
@@ -62,6 +66,8 @@
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
+import java.util.ArrayList;
+import java.util.List;
 import java.util.concurrent.atomic.AtomicBoolean;
 
 @SmallTest
@@ -73,7 +79,18 @@
 // new tests.
 @RunWithLooper(setAsMainLooper = true)
 public class KeyguardUpdateMonitorTest extends SysuiTestCase {
-
+    private static final String TEST_CARRIER = "TEST_CARRIER";
+    private static final String TEST_CARRIER_2 = "TEST_CARRIER_2";
+    private static final int TEST_CARRIER_ID = 1;
+    private static final String TEST_GROUP_UUID = "59b5c870-fc4c-47a4-a99e-9db826b48b24";
+    private static final SubscriptionInfo TEST_SUBSCRIPTION = new SubscriptionInfo(1, "", 0,
+            TEST_CARRIER, TEST_CARRIER, NAME_SOURCE_DEFAULT_SOURCE, 0xFFFFFF, "",
+            DATA_ROAMING_DISABLE, null, null, null, null, false, null, "", false, TEST_GROUP_UUID,
+            TEST_CARRIER_ID, 0);
+    private static final SubscriptionInfo TEST_SUBSCRIPTION_2 = new SubscriptionInfo(2, "", 0,
+            TEST_CARRIER, TEST_CARRIER_2, NAME_SOURCE_DEFAULT_SOURCE, 0xFFFFFF, "",
+            DATA_ROAMING_DISABLE, null, null, null, null, false, null, "", true, TEST_GROUP_UUID,
+            TEST_CARRIER_ID, 0);
     @Mock
     private KeyguardUpdateMonitor.StrongAuthTracker mStrongAuthTracker;
     @Mock
@@ -92,6 +109,8 @@
     private DevicePolicyManager mDevicePolicyManager;
     @Mock
     private KeyguardBypassController mKeyguardBypassController;
+    @Mock
+    private SubscriptionManager mSubscriptionManager;
     private TestableLooper mTestableLooper;
     private TestableKeyguardUpdateMonitor mKeyguardUpdateMonitor;
 
@@ -119,6 +138,7 @@
         context.addMockSystemService(FaceManager.class, mFaceManager);
         context.addMockSystemService(UserManager.class, mUserManager);
         context.addMockSystemService(DevicePolicyManager.class, mDevicePolicyManager);
+        context.addMockSystemService(SubscriptionManager.class, mSubscriptionManager);
 
         mTestableLooper = TestableLooper.get(this);
         mKeyguardUpdateMonitor = new TestableKeyguardUpdateMonitor(context);
@@ -441,6 +461,22 @@
         assertThat(mKeyguardUpdateMonitor.getUserCanSkipBouncer(user)).isTrue();
     }
 
+    @Test
+    public void testGetSubscriptionInfo_whenInGroupedSubWithOpportunistic() {
+        List<SubscriptionInfo> list = new ArrayList<>();
+        list.add(TEST_SUBSCRIPTION);
+        list.add(TEST_SUBSCRIPTION_2);
+        when(mSubscriptionManager.getActiveSubscriptionInfoList(anyBoolean())).thenReturn(list);
+        mKeyguardUpdateMonitor.mPhoneStateListener.onActiveDataSubscriptionIdChanged(
+                TEST_SUBSCRIPTION_2.getSubscriptionId());
+        mTestableLooper.processAllMessages();
+
+        List<SubscriptionInfo> listToVerify = mKeyguardUpdateMonitor
+                .getFilteredSubscriptionInfo(false);
+        assertThat(listToVerify.size()).isEqualTo(1);
+        assertThat(listToVerify.get(0)).isEqualTo(TEST_SUBSCRIPTION_2);
+    }
+
     private Intent putPhoneInfo(Intent intent, Bundle data, Boolean simInited) {
         int subscription = simInited
                 ? 1/* mock subid=1 */ : SubscriptionManager.DUMMY_SUBSCRIPTION_ID_BASE;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/FalsingManagerProxyTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/FalsingManagerProxyTest.java
deleted file mode 100644
index 7ea6493..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/classifier/FalsingManagerProxyTest.java
+++ /dev/null
@@ -1,103 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.classifier;
-
-import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.BRIGHTLINE_FALSING_MANAGER_ENABLED;
-
-import static org.hamcrest.CoreMatchers.instanceOf;
-import static org.junit.Assert.assertThat;
-
-import android.os.Handler;
-import android.provider.DeviceConfig;
-import android.testing.AndroidTestingRunner;
-import android.testing.TestableLooper;
-
-import androidx.test.filters.SmallTest;
-
-import com.android.systemui.SysuiTestCase;
-import com.android.systemui.classifier.brightline.BrightLineFalsingManager;
-import com.android.systemui.shared.plugins.PluginManager;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-@SmallTest
-@RunWith(AndroidTestingRunner.class)
-@TestableLooper.RunWithLooper
-public class FalsingManagerProxyTest extends SysuiTestCase {
-    @Mock
-    PluginManager mPluginManager;
-    private boolean mDefaultConfigValue;
-    private Handler mHandler;
-    private TestableLooper mTestableLooper;
-
-    @Before
-    public void setup() {
-        MockitoAnnotations.initMocks(this);
-        mTestableLooper = TestableLooper.get(this);
-        mHandler = new Handler(mTestableLooper.getLooper());
-        mDefaultConfigValue = DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_SYSTEMUI,
-                BRIGHTLINE_FALSING_MANAGER_ENABLED, false);
-        // In case it runs on a device where it's been set to true, set it to false by hand.
-        DeviceConfig.setProperty(DeviceConfig.NAMESPACE_SYSTEMUI,
-                BRIGHTLINE_FALSING_MANAGER_ENABLED, "false", false);
-    }
-
-    @After
-    public void tearDown() {
-        DeviceConfig.setProperty(DeviceConfig.NAMESPACE_SYSTEMUI,
-                BRIGHTLINE_FALSING_MANAGER_ENABLED, mDefaultConfigValue ? "true" : "false", false);
-    }
-
-    @Test
-    public void test_brightLineFalsingManagerDisabled() {
-        FalsingManagerProxy proxy = new FalsingManagerProxy(getContext(), mPluginManager, mHandler);
-
-        assertThat(proxy.getInternalFalsingManager(), instanceOf(FalsingManagerImpl.class));
-    }
-
-    @Test
-    public void test_brightLineFalsingManagerEnabled() {
-        DeviceConfig.setProperty(DeviceConfig.NAMESPACE_SYSTEMUI,
-                BRIGHTLINE_FALSING_MANAGER_ENABLED, "true", false);
-        FalsingManagerProxy proxy = new FalsingManagerProxy(getContext(), mPluginManager, mHandler);
-
-        assertThat(proxy.getInternalFalsingManager(), instanceOf(BrightLineFalsingManager.class));
-    }
-
-    @Test
-    public void test_brightLineFalsingManagerToggled() {
-        FalsingManagerProxy proxy = new FalsingManagerProxy(getContext(), mPluginManager, mHandler);
-        assertThat(proxy.getInternalFalsingManager(), instanceOf(FalsingManagerImpl.class));
-
-        DeviceConfig.setProperty(DeviceConfig.NAMESPACE_SYSTEMUI,
-                BRIGHTLINE_FALSING_MANAGER_ENABLED, "true", false);
-        mTestableLooper.processAllMessages();
-        proxy.setupFalsingManager(getContext());
-        assertThat(proxy.getInternalFalsingManager(), instanceOf(BrightLineFalsingManager.class));
-
-        DeviceConfig.setProperty(DeviceConfig.NAMESPACE_SYSTEMUI,
-                BRIGHTLINE_FALSING_MANAGER_ENABLED, "false", false);
-        mTestableLooper.processAllMessages();
-        proxy.setupFalsingManager(getContext());
-        assertThat(proxy.getInternalFalsingManager(), instanceOf(FalsingManagerImpl.class));
-    }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/glwallpaper/EglHelperTest.java b/packages/SystemUI/tests/src/com/android/systemui/glwallpaper/EglHelperTest.java
new file mode 100644
index 0000000..b4a60d6
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/glwallpaper/EglHelperTest.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.glwallpaper;
+
+import static org.junit.Assert.*;
+import static org.mockito.Mockito.RETURNS_DEFAULTS;
+import static org.mockito.Mockito.mock;
+
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+import android.view.SurfaceHolder;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.systemui.SysuiTestCase;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper
+public class EglHelperTest extends SysuiTestCase {
+
+    @Mock
+    private EglHelper mEglHelper;
+    @Mock
+    private SurfaceHolder mSurfaceHolder;
+
+    @Before
+    public void setUp() throws Exception {
+        mEglHelper = mock(EglHelper.class, RETURNS_DEFAULTS);
+        mSurfaceHolder = mock(SurfaceHolder.class, RETURNS_DEFAULTS);
+    }
+
+    @Test
+    public void testInit_finish() {
+        mEglHelper.init(mSurfaceHolder);
+        mEglHelper.finish();
+    }
+
+    @Test
+    public void testFinish_shouldNotCrash() {
+        assertFalse(mEglHelper.hasEglDisplay());
+        assertFalse(mEglHelper.hasEglSurface());
+        assertFalse(mEglHelper.hasEglContext());
+
+        mEglHelper.finish();
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBouncerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBouncerTest.java
index 907e695..cd60e47 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBouncerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBouncerTest.java
@@ -21,6 +21,7 @@
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyFloat;
 import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyLong;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.never;
@@ -84,6 +85,8 @@
     @Mock
     private UnlockMethodCache mUnlockMethodCache;
     @Mock
+    private KeyguardBypassController mKeyguardBypassController;
+    @Mock
     private Handler mHandler;
 
     private KeyguardBouncer mBouncer;
@@ -98,7 +101,8 @@
         when(mKeyguardHostView.getHeight()).thenReturn(500);
         mBouncer = new KeyguardBouncer(getContext(), mViewMediatorCallback,
                 mLockPatternUtils, container, mDismissCallbackRegistry, mFalsingManager,
-                mExpansionCallback, mUnlockMethodCache, mKeyguardUpdateMonitor, mHandler) {
+                mExpansionCallback, mUnlockMethodCache, mKeyguardUpdateMonitor,
+                mKeyguardBypassController, mHandler) {
             @Override
             protected void inflateView() {
                 super.inflateView();
@@ -391,6 +395,15 @@
     }
 
     @Test
+    public void testShow_delaysIfFaceAuthIsRunning_unlessBypass() {
+        when(mUnlockMethodCache.isFaceAuthEnabled()).thenReturn(true);
+        when(mKeyguardBypassController.getBypassEnabled()).thenReturn(true);
+        mBouncer.show(true /* reset */);
+
+        verify(mHandler, never()).postDelayed(any(), anyLong());
+    }
+
+    @Test
     public void testRegisterUpdateMonitorCallback() {
         verify(mKeyguardUpdateMonitor).registerCallback(any());
     }
diff --git a/services/Android.bp b/services/Android.bp
index 54157d0..27f8d36 100644
--- a/services/Android.bp
+++ b/services/Android.bp
@@ -43,6 +43,10 @@
         "android.hidl.manager-V1.0-java",
     ],
 
+    plugins: [
+        "compat-changeid-annotation-processor",
+    ],
+
     // Uncomment to enable output of certain warnings (deprecated, unchecked)
     //javacflags: ["-Xlint"],
 
@@ -56,3 +60,9 @@
     defaults: ["libservices.core-libs"],
     whole_static_libs: ["libservices.core"],
 }
+
+platform_compat_config {
+    name: "services-platform-compat-config",
+    prefix: "services",
+    src: ":services",
+}
diff --git a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
index 1356157..5e9c08b 100644
--- a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
+++ b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
@@ -348,13 +348,13 @@
         }
     }
 
-    protected abstract boolean isCalledForCurrentUserLocked();
+    protected abstract boolean hasRightsToCurrentUserLocked();
 
     @Override
     public List<AccessibilityWindowInfo> getWindows() {
         ensureWindowsAvailableTimed();
         synchronized (mLock) {
-            if (!isCalledForCurrentUserLocked()) {
+            if (!hasRightsToCurrentUserLocked()) {
                 return null;
             }
             final boolean permissionGranted =
@@ -387,7 +387,7 @@
     public AccessibilityWindowInfo getWindow(int windowId) {
         ensureWindowsAvailableTimed();
         synchronized (mLock) {
-            if (!isCalledForCurrentUserLocked()) {
+            if (!hasRightsToCurrentUserLocked()) {
                 return null;
             }
             final boolean permissionGranted =
@@ -420,7 +420,7 @@
         MagnificationSpec spec;
         synchronized (mLock) {
             mUsesAccessibilityCache = true;
-            if (!isCalledForCurrentUserLocked()) {
+            if (!hasRightsToCurrentUserLocked()) {
                 return null;
             }
             resolvedWindowId = resolveAccessibilityWindowIdLocked(accessibilityWindowId);
@@ -481,7 +481,7 @@
         MagnificationSpec spec;
         synchronized (mLock) {
             mUsesAccessibilityCache = true;
-            if (!isCalledForCurrentUserLocked()) {
+            if (!hasRightsToCurrentUserLocked()) {
                 return null;
             }
             resolvedWindowId = resolveAccessibilityWindowIdLocked(accessibilityWindowId);
@@ -542,7 +542,7 @@
         MagnificationSpec spec;
         synchronized (mLock) {
             mUsesAccessibilityCache = true;
-            if (!isCalledForCurrentUserLocked()) {
+            if (!hasRightsToCurrentUserLocked()) {
                 return null;
             }
             resolvedWindowId = resolveAccessibilityWindowIdLocked(accessibilityWindowId);
@@ -602,7 +602,7 @@
         Region partialInteractiveRegion = Region.obtain();
         MagnificationSpec spec;
         synchronized (mLock) {
-            if (!isCalledForCurrentUserLocked()) {
+            if (!hasRightsToCurrentUserLocked()) {
                 return null;
             }
             resolvedWindowId = resolveAccessibilityWindowIdForFindFocusLocked(
@@ -663,7 +663,7 @@
         Region partialInteractiveRegion = Region.obtain();
         MagnificationSpec spec;
         synchronized (mLock) {
-            if (!isCalledForCurrentUserLocked()) {
+            if (!hasRightsToCurrentUserLocked()) {
                 return null;
             }
             resolvedWindowId = resolveAccessibilityWindowIdLocked(accessibilityWindowId);
@@ -728,7 +728,7 @@
             throws RemoteException {
         final int resolvedWindowId;
         synchronized (mLock) {
-            if (!isCalledForCurrentUserLocked()) {
+            if (!hasRightsToCurrentUserLocked()) {
                 return false;
             }
             resolvedWindowId = resolveAccessibilityWindowIdLocked(accessibilityWindowId);
@@ -748,7 +748,7 @@
     @Override
     public boolean performGlobalAction(int action) {
         synchronized (mLock) {
-            if (!isCalledForCurrentUserLocked()) {
+            if (!hasRightsToCurrentUserLocked()) {
                 return false;
             }
         }
@@ -771,7 +771,7 @@
     @Override
     public float getMagnificationScale(int displayId) {
         synchronized (mLock) {
-            if (!isCalledForCurrentUserLocked()) {
+            if (!hasRightsToCurrentUserLocked()) {
                 return 1.0f;
             }
         }
@@ -787,7 +787,7 @@
     public Region getMagnificationRegion(int displayId) {
         synchronized (mLock) {
             final Region region = Region.obtain();
-            if (!isCalledForCurrentUserLocked()) {
+            if (!hasRightsToCurrentUserLocked()) {
                 return region;
             }
             MagnificationController magnificationController =
@@ -810,7 +810,7 @@
     @Override
     public float getMagnificationCenterX(int displayId) {
         synchronized (mLock) {
-            if (!isCalledForCurrentUserLocked()) {
+            if (!hasRightsToCurrentUserLocked()) {
                 return 0.0f;
             }
             MagnificationController magnificationController =
@@ -832,7 +832,7 @@
     @Override
     public float getMagnificationCenterY(int displayId) {
         synchronized (mLock) {
-            if (!isCalledForCurrentUserLocked()) {
+            if (!hasRightsToCurrentUserLocked()) {
                 return 0.0f;
             }
             MagnificationController magnificationController =
@@ -864,7 +864,7 @@
     @Override
     public boolean resetMagnification(int displayId, boolean animate) {
         synchronized (mLock) {
-            if (!isCalledForCurrentUserLocked()) {
+            if (!hasRightsToCurrentUserLocked()) {
                 return false;
             }
             if (!mSecurityPolicy.canControlMagnification(this)) {
@@ -886,7 +886,7 @@
     public boolean setMagnificationScaleAndCenter(int displayId, float scale, float centerX,
             float centerY, boolean animate) {
         synchronized (mLock) {
-            if (!isCalledForCurrentUserLocked()) {
+            if (!hasRightsToCurrentUserLocked()) {
                 return false;
             }
             if (!mSecurityPolicy.canControlMagnification(this)) {
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilitySecurityPolicy.java b/services/accessibility/java/com/android/server/accessibility/AccessibilitySecurityPolicy.java
index 315d6fa..2032109 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilitySecurityPolicy.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilitySecurityPolicy.java
@@ -468,7 +468,13 @@
         }
     }
 
-    private boolean hasPermission(String permission) {
+    /**
+     * Permission check to caller.
+     *
+     * @param permission The permission to check
+     * @return true if caller has permission
+     */
+    public boolean hasPermission(@NonNull String permission) {
         return mContext.checkCallingPermission(permission) == PackageManager.PERMISSION_GRANTED;
     }
 
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java
index 02f7821..d7f61e5 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java
@@ -18,6 +18,7 @@
 
 import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
 
+import android.Manifest;
 import android.accessibilityservice.AccessibilityServiceInfo;
 import android.accessibilityservice.IAccessibilityServiceClient;
 import android.content.ComponentName;
@@ -27,6 +28,7 @@
 import android.os.Binder;
 import android.os.Handler;
 import android.os.IBinder;
+import android.os.Process;
 import android.os.RemoteException;
 import android.os.UserHandle;
 import android.provider.Settings;
@@ -211,19 +213,31 @@
     }
 
     @Override
-    protected boolean isCalledForCurrentUserLocked() {
+    protected boolean hasRightsToCurrentUserLocked() {
         // We treat calls from a profile as if made by its parent as profiles
         // share the accessibility state of the parent. The call below
         // performs the current profile parent resolution.
-        final int resolvedUserId = mSecurityPolicy
-                .resolveCallingUserIdEnforcingPermissionsLocked(UserHandle.USER_CURRENT);
-        return resolvedUserId == mSystemSupport.getCurrentUserIdLocked();
+        final int callingUid = Binder.getCallingUid();
+        if (callingUid == Process.ROOT_UID
+                || callingUid == Process.SYSTEM_UID
+                || callingUid == Process.SHELL_UID) {
+            return true;
+        }
+        if (mSecurityPolicy.resolveProfileParentLocked(UserHandle.getUserId(callingUid))
+                == mSystemSupport.getCurrentUserIdLocked()) {
+            return true;
+        }
+        if (mSecurityPolicy.hasPermission(Manifest.permission.INTERACT_ACROSS_USERS)
+                || mSecurityPolicy.hasPermission(Manifest.permission.INTERACT_ACROSS_USERS_FULL)) {
+            return true;
+        }
+        return false;
     }
 
     @Override
     public boolean setSoftKeyboardShowMode(int showMode) {
         synchronized (mLock) {
-            if (!isCalledForCurrentUserLocked()) {
+            if (!hasRightsToCurrentUserLocked()) {
                 return false;
             }
             final UserState userState = mUserStateWeakReference.get();
@@ -241,7 +255,7 @@
     @Override
     public boolean isAccessibilityButtonAvailable() {
         synchronized (mLock) {
-            if (!isCalledForCurrentUserLocked()) {
+            if (!hasRightsToCurrentUserLocked()) {
                 return false;
             }
             UserState userState = mUserStateWeakReference.get();
diff --git a/services/accessibility/java/com/android/server/accessibility/UiAutomationManager.java b/services/accessibility/java/com/android/server/accessibility/UiAutomationManager.java
index 2698b72..79d975d 100644
--- a/services/accessibility/java/com/android/server/accessibility/UiAutomationManager.java
+++ b/services/accessibility/java/com/android/server/accessibility/UiAutomationManager.java
@@ -66,6 +66,7 @@
                     mUiAutomationServiceOwner.unlinkToDeath(this, 0);
                     mUiAutomationServiceOwner = null;
                     destroyUiAutomationService();
+                    Slog.v(LOG_TAG, "UiAutomation service owner died");
                 }
             };
 
@@ -263,7 +264,7 @@
         }
 
         @Override
-        protected boolean isCalledForCurrentUserLocked() {
+        protected boolean hasRightsToCurrentUserLocked() {
             // Allow UiAutomation to work for any user
             return true;
         }
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
index a64f4e4..6b7c3e6 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
@@ -807,6 +807,7 @@
                     packageName, versionCode, userId);
             final AutofillOptions options = new AutofillOptions(loggingLevel, compatModeEnabled);
             mAugmentedAutofillState.injectAugmentedAutofillInfo(options, userId, packageName);
+            injectDisableAppInfo(options, userId, packageName);
             return options;
         }
 
@@ -820,6 +821,19 @@
             }
             return false;
         }
+
+        private void injectDisableAppInfo(@NonNull AutofillOptions options, int userId,
+                String packageName) {
+            synchronized (mLock) {
+                final AutofillManagerServiceImpl service = peekServiceForUserLocked(userId);
+                if (service != null) {
+                    options.appDisabledExpiration = service.getAppDisabledExpirationLocked(
+                            packageName);
+                    options.disabledActivities = service.getAppDisabledActivitiesLocked(
+                            packageName);
+                }
+            }
+        }
     }
 
     /**
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
index 1e1e07d..d7ed2e9 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
@@ -980,12 +980,12 @@
             for (int i = 0; i < size; i++) {
                 final String packageName = mDisabledApps.keyAt(i);
                 final long expiration = mDisabledApps.valueAt(i);
-                 builder.append(prefix).append(prefix)
-                     .append(i).append(". ").append(packageName).append(": ");
-                 TimeUtils.formatDuration((expiration - now), builder);
-                 builder.append('\n');
-             }
-             pw.println(builder);
+                builder.append(prefix).append(prefix)
+                        .append(i).append(". ").append(packageName).append(": ");
+                TimeUtils.formatDuration((expiration - now), builder);
+                builder.append('\n');
+            }
+            pw.println(builder);
         }
 
         pw.print(prefix); pw.print("Disabled activities: ");
@@ -1000,12 +1000,12 @@
             for (int i = 0; i < size; i++) {
                 final ComponentName component = mDisabledActivities.keyAt(i);
                 final long expiration = mDisabledActivities.valueAt(i);
-                 builder.append(prefix).append(prefix)
-                     .append(i).append(". ").append(component).append(": ");
-                 TimeUtils.formatDuration((expiration - now), builder);
-                 builder.append('\n');
-             }
-             pw.println(builder);
+                builder.append(prefix).append(prefix)
+                        .append(i).append(". ").append(component).append(": ");
+                TimeUtils.formatDuration((expiration - now), builder);
+                builder.append('\n');
+            }
+            pw.println(builder);
         }
 
         final int size = mSessions.size();
@@ -1418,6 +1418,36 @@
         }
     }
 
+    // Called by AutofillManagerService
+    long getAppDisabledExpirationLocked(@NonNull String packageName) {
+        if (mDisabledApps == null) {
+            return 0;
+        }
+        final Long expiration = mDisabledApps.get(packageName);
+        return expiration != null ? expiration : 0;
+    }
+
+    // Called by AutofillManagerService
+    @Nullable
+    ArrayMap<String, Long> getAppDisabledActivitiesLocked(@NonNull String packageName) {
+        if (mDisabledActivities != null) {
+            final int size = mDisabledActivities.size();
+            ArrayMap<String, Long> disabledList = null;
+            for (int i = 0; i < size; i++) {
+                final ComponentName component = mDisabledActivities.keyAt(i);
+                if (packageName.equals(component.getPackageName())) {
+                    if (disabledList == null) {
+                        disabledList = new ArrayMap<>();
+                    }
+                    final long expiration = mDisabledActivities.valueAt(i);
+                    disabledList.put(component.flattenToShortString(), expiration);
+                }
+            }
+            return disabledList;
+        }
+        return null;
+    }
+
     /**
      * Checks if autofill is disabled by service to the given activity.
      */
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index 4579a3d..48f16ac 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -772,13 +772,19 @@
         final long disableDuration = response.getDisableDuration();
         if (disableDuration > 0) {
             final int flags = response.getFlags();
-            if ((flags & FillResponse.FLAG_DISABLE_ACTIVITY_ONLY) != 0) {
+            final boolean disableActivityOnly =
+                    (flags & FillResponse.FLAG_DISABLE_ACTIVITY_ONLY) != 0;
+            notifyDisableAutofillToClient(disableDuration,
+                    disableActivityOnly ? mComponentName : null);
+
+            if (disableActivityOnly) {
                 mService.disableAutofillForActivity(mComponentName, disableDuration,
                         id, mCompatMode);
             } else {
                 mService.disableAutofillForApp(mComponentName.getPackageName(), disableDuration,
                         id, mCompatMode);
             }
+
             // Although "standard" autofill is disabled, it might still trigger augmented autofill
             if (triggerAugmentedAutofillLocked() != null) {
                 mForAugmentedAutofillOnly = true;
@@ -2567,6 +2573,17 @@
         }
     }
 
+    private void notifyDisableAutofillToClient(long disableDuration, ComponentName componentName) {
+        synchronized (mLock) {
+            if (mCurrentViewId == null) return;
+            try {
+                mClient.notifyDisableAutofill(disableDuration, componentName);
+            } catch (RemoteException e) {
+                Slog.e(TAG, "Error notifying client disable autofill: id=" + mCurrentViewId, e);
+            }
+        }
+    }
+
     @GuardedBy("mLock")
     private void updateTrackedIdsLocked() {
         // Only track the views of the last response as only those are reported back to the
diff --git a/services/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/VibratorService.java b/services/core/java/com/android/server/VibratorService.java
index 0748279..9936d73 100644
--- a/services/core/java/com/android/server/VibratorService.java
+++ b/services/core/java/com/android/server/VibratorService.java
@@ -1188,6 +1188,7 @@
     private static boolean isNotification(int usageHint) {
         switch (usageHint) {
             case AudioAttributes.USAGE_NOTIFICATION:
+            case AudioAttributes.USAGE_NOTIFICATION_EVENT:
             case AudioAttributes.USAGE_NOTIFICATION_COMMUNICATION_REQUEST:
             case AudioAttributes.USAGE_NOTIFICATION_COMMUNICATION_INSTANT:
             case AudioAttributes.USAGE_NOTIFICATION_COMMUNICATION_DELAYED:
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 2292889..5c0fe4e 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -8221,6 +8221,35 @@
     @Deprecated
     public void requestBugReportWithDescription(@Nullable String shareTitle,
             @Nullable String shareDescription, int bugreportType) {
+        String type = null;
+        switch (bugreportType) {
+            case ActivityManager.BUGREPORT_OPTION_FULL:
+                type = "bugreportfull";
+                break;
+            case ActivityManager.BUGREPORT_OPTION_INTERACTIVE:
+                type = "bugreportplus";
+                break;
+            case ActivityManager.BUGREPORT_OPTION_REMOTE:
+                type = "bugreportremote";
+                break;
+            case ActivityManager.BUGREPORT_OPTION_WEAR:
+                type = "bugreportwear";
+                break;
+            case ActivityManager.BUGREPORT_OPTION_TELEPHONY:
+                type = "bugreporttelephony";
+                break;
+            case ActivityManager.BUGREPORT_OPTION_WIFI:
+                type = "bugreportwifi";
+                break;
+            default:
+                throw new IllegalArgumentException(
+                    "Provided bugreport type is not correct, value: "
+                        + bugreportType);
+        }
+        // Always log caller, even if it does not have permission to dump.
+        Slog.i(TAG, type + " requested by UID " + Binder.getCallingUid());
+        enforceCallingPermission(android.Manifest.permission.DUMP, "requestBugReport");
+
         if (!TextUtils.isEmpty(shareTitle)) {
             if (shareTitle.length() > MAX_BUGREPORT_TITLE_SIZE) {
                 String errorStr = "shareTitle should be less than " +
@@ -8244,7 +8273,7 @@
         final boolean useApi = FeatureFlagUtils.isEnabled(mContext,
                 FeatureFlagUtils.USE_BUGREPORT_API);
 
-        if (useApi) {
+        if (useApi && bugreportType == ActivityManager.BUGREPORT_OPTION_INTERACTIVE) {
             // Create intent to trigger Bugreport API via Shell
             Intent triggerShellBugreport = new Intent();
             triggerShellBugreport.setAction(INTENT_BUGREPORT_REQUESTED);
@@ -8256,40 +8285,12 @@
             if (shareDescription != null) {
                 triggerShellBugreport.putExtra(EXTRA_DESCRIPTION, shareDescription);
             }
+            // Send broadcast to shell to trigger bugreport using Bugreport API
+            mContext.sendBroadcast(triggerShellBugreport);
+        } else {
+            SystemProperties.set("dumpstate.options", type);
+            SystemProperties.set("ctl.start", "bugreport");
         }
-        String extraOptions = null;
-        switch (bugreportType) {
-            case ActivityManager.BUGREPORT_OPTION_FULL:
-                extraOptions = "bugreportfull";
-                break;
-            case ActivityManager.BUGREPORT_OPTION_INTERACTIVE:
-                extraOptions = "bugreportplus";
-                break;
-            case ActivityManager.BUGREPORT_OPTION_REMOTE:
-                extraOptions = "bugreportremote";
-                break;
-            case ActivityManager.BUGREPORT_OPTION_WEAR:
-                extraOptions = "bugreportwear";
-                break;
-            case ActivityManager.BUGREPORT_OPTION_TELEPHONY:
-                extraOptions = "bugreporttelephony";
-                break;
-            case ActivityManager.BUGREPORT_OPTION_WIFI:
-                extraOptions = "bugreportwifi";
-                break;
-            default:
-                throw new IllegalArgumentException("Provided bugreport type is not correct, value: "
-                        + bugreportType);
-        }
-        // Always log caller, even if it does not have permission to dump.
-        String type = extraOptions == null ? "bugreport" : extraOptions;
-        Slog.i(TAG, type + " requested by UID " + Binder.getCallingUid());
-
-        enforceCallingPermission(android.Manifest.permission.DUMP, "requestBugReport");
-        if (extraOptions != null) {
-            SystemProperties.set("dumpstate.options", extraOptions);
-        }
-        SystemProperties.set("ctl.start", "bugreport");
     }
 
     /**
@@ -12230,11 +12231,8 @@
             proto.write(MemInfoDumpProto.MemItem.IS_PROC, mi.isProc);
             proto.write(MemInfoDumpProto.MemItem.ID, mi.id);
             proto.write(MemInfoDumpProto.MemItem.HAS_ACTIVITIES, mi.hasActivities);
-            if (dumpPss) {
-                proto.write(MemInfoDumpProto.MemItem.PSS_KB, mi.pss);
-            } else {
-                proto.write(MemInfoDumpProto.MemItem.RSS_KB, mi.mRss);
-            }
+            proto.write(MemInfoDumpProto.MemItem.PSS_KB, mi.pss);
+            proto.write(MemInfoDumpProto.MemItem.RSS_KB, mi.mRss);
             if (dumpSwapPss) {
                 proto.write(MemInfoDumpProto.MemItem.SWAP_PSS_KB, mi.swapPss);
             }
@@ -12532,6 +12530,13 @@
         if (!brief && !opts.oomOnly && (procs.size() == 1 || opts.isCheckinRequest || opts.packages)) {
             opts.dumpDetails = true;
         }
+        final int numProcs = procs.size();
+        final boolean collectNative = !opts.isCheckinRequest && numProcs > 1 && !opts.packages;
+        if (collectNative) {
+            // If we are showing aggregations, also look for native processes to
+            // include so that our aggregations are more accurate.
+            updateCpuStatsNow();
+        }
 
         dumpApplicationMemoryUsageHeader(pw, uptime, realtime, opts.isCheckinRequest, opts.isCompact);
 
@@ -12570,7 +12575,7 @@
         boolean hasSwapPss = false;
 
         Debug.MemoryInfo mi = null;
-        for (int i = procs.size() - 1 ; i >= 0 ; i--) {
+        for (int i = numProcs - 1; i >= 0; i--) {
             final ProcessRecord r = procs.get(i);
             final IApplicationThread thread;
             final int pid;
@@ -12724,10 +12729,7 @@
 
         long nativeProcTotalPss = 0;
 
-        if (!opts.isCheckinRequest && procs.size() > 1 && !opts.packages) {
-            // If we are showing aggregations, also look for native processes to
-            // include so that our aggregations are more accurate.
-            updateCpuStatsNow();
+        if (collectNative) {
             mi = null;
             synchronized (mProcessCpuTracker) {
                 final int N = mProcessCpuTracker.countStats();
@@ -13095,6 +13097,13 @@
         if (!brief && !opts.oomOnly && (procs.size() == 1 || opts.isCheckinRequest || opts.packages)) {
             opts.dumpDetails = true;
         }
+        final int numProcs = procs.size();
+        final boolean collectNative = numProcs > 1 && !opts.packages;
+        if (collectNative) {
+            // If we are showing aggregations, also look for native processes to
+            // include so that our aggregations are more accurate.
+            updateCpuStatsNow();
+        }
 
         ProtoOutputStream proto = new ProtoOutputStream(fd);
 
@@ -13136,7 +13145,7 @@
         boolean hasSwapPss = false;
 
         Debug.MemoryInfo mi = null;
-        for (int i = procs.size() - 1 ; i >= 0 ; i--) {
+        for (int i = numProcs - 1; i >= 0; i--) {
             final ProcessRecord r = procs.get(i);
             final IApplicationThread thread;
             final int pid;
@@ -13283,10 +13292,7 @@
 
         long nativeProcTotalPss = 0;
 
-        if (procs.size() > 1 && !opts.packages) {
-            // If we are showing aggregations, also look for native processes to
-            // include so that our aggregations are more accurate.
-            updateCpuStatsNow();
+        if (collectNative) {
             mi = null;
             synchronized (mProcessCpuTracker) {
                 final int N = mProcessCpuTracker.countStats();
diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java
index 5465309..c0af814 100644
--- a/services/core/java/com/android/server/am/ProcessList.java
+++ b/services/core/java/com/android/server/am/ProcessList.java
@@ -65,7 +65,6 @@
 import android.os.IBinder;
 import android.os.Looper;
 import android.os.Message;
-import android.os.MessageQueue;
 import android.os.Process;
 import android.os.RemoteException;
 import android.os.StrictMode;
@@ -101,11 +100,8 @@
 
 import dalvik.system.VMRuntime;
 
-import libcore.io.IoUtils;
-
 import java.io.File;
 import java.io.IOException;
-import java.io.InputStream;
 import java.io.OutputStream;
 import java.io.PrintWriter;
 import java.nio.ByteBuffer;
@@ -2087,10 +2083,10 @@
                     }
                 }
             }
-            if (lrui <= mLruProcessActivityStart) {
+            if (lrui < mLruProcessActivityStart) {
                 mLruProcessActivityStart--;
             }
-            if (lrui <= mLruProcessServiceStart) {
+            if (lrui < mLruProcessServiceStart) {
                 mLruProcessServiceStart--;
             }
             mLruProcesses.remove(lrui);
@@ -2622,7 +2618,7 @@
                         if (!moved) {
                             // Goes to the end of the group.
                             mLruProcesses.remove(i);
-                            mLruProcesses.add(endIndex - 1, subProc);
+                            mLruProcesses.add(endIndex, subProc);
                             if (DEBUG_LRU) Slog.d(TAG_LRU,
                                     "Moving " + subProc
                                             + " from position " + i + " to end of group @ "
@@ -2867,15 +2863,6 @@
                     pos--;
                 }
                 mLruProcesses.add(pos, app);
-                if (pos == mLruProcessActivityStart) {
-                    mLruProcessActivityStart++;
-                }
-                if (pos == mLruProcessServiceStart) {
-                    // Unless {@code #hasService} is implemented, currently the starting position
-                    // for activity and service are the same, so the incoming position may equal to
-                    // the starting position of service.
-                    mLruProcessServiceStart++;
-                }
                 // If this process is part of a group, need to pull up any other processes
                 // in that group to be with it.
                 int endIndex = pos - 1;
diff --git a/services/core/java/com/android/server/audio/FocusRequester.java b/services/core/java/com/android/server/audio/FocusRequester.java
index 65472c9..bd129f7 100644
--- a/services/core/java/com/android/server/audio/FocusRequester.java
+++ b/services/core/java/com/android/server/audio/FocusRequester.java
@@ -405,7 +405,7 @@
     @GuardedBy("MediaFocusControl.mAudioFocusLock")
     private boolean frameworkHandleFocusLoss(int focusLoss, @NonNull final FocusRequester frWinner,
                                              boolean forceDuck) {
-        if (frWinner.mCallingUid != this.mCallingUid) {
+        if (frWinner.mCallingUid == this.mCallingUid) {
             // the focus change is within the same app, so let the dispatching
             // happen as if the framework was not involved.
             return false;
diff --git a/services/core/java/com/android/server/biometrics/face/FaceService.java b/services/core/java/com/android/server/biometrics/face/FaceService.java
index a706521..ee49f58 100644
--- a/services/core/java/com/android/server/biometrics/face/FaceService.java
+++ b/services/core/java/com/android/server/biometrics/face/FaceService.java
@@ -223,7 +223,8 @@
 
         @Override
         public boolean wasUserDetected() {
-            return mLastAcquire != FaceManager.FACE_ACQUIRED_NOT_DETECTED;
+            return mLastAcquire != FaceManager.FACE_ACQUIRED_NOT_DETECTED
+                    && mLastAcquire != FaceManager.FACE_ACQUIRED_SENSOR_DIRTY;
         }
 
         @Override
diff --git a/services/core/java/com/android/server/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 e90db38..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;
 
@@ -911,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
@@ -4699,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)) {
@@ -4743,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.
@@ -5063,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;
         }
@@ -5075,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;
         }
 
diff --git a/services/core/java/com/android/server/notification/PreferencesHelper.java b/services/core/java/com/android/server/notification/PreferencesHelper.java
index 92ac1d4..d580bd6 100644
--- a/services/core/java/com/android/server/notification/PreferencesHelper.java
+++ b/services/core/java/com/android/server/notification/PreferencesHelper.java
@@ -74,6 +74,9 @@
     private static final String NON_BLOCKABLE_CHANNEL_DELIM = ":";
 
     @VisibleForTesting
+    static final int NOTIFICATION_CHANNEL_COUNT_LIMIT = 5000;
+
+    @VisibleForTesting
     static final String TAG_RANKING = "ranking";
     private static final String TAG_PACKAGE = "package";
     private static final String TAG_CHANNEL = "channel";
@@ -180,6 +183,7 @@
                                     // noop
                                 }
                             }
+                            boolean skipWarningLogged = false;
 
                             PackagePreferences r = getOrCreatePackagePreferencesLocked(name, uid,
                                     XmlUtils.readIntAttribute(
@@ -226,6 +230,14 @@
                                 }
                                 // Channels
                                 if (TAG_CHANNEL.equals(tagName)) {
+                                    if (r.channels.size() >= NOTIFICATION_CHANNEL_COUNT_LIMIT) {
+                                        if (!skipWarningLogged) {
+                                            Slog.w(TAG, "Skipping further channels for " + r.pkg
+                                                    + "; app has too many");
+                                            skipWarningLogged = true;
+                                        }
+                                        continue;
+                                    }
                                     String id = parser.getAttributeValue(null, ATT_ID);
                                     String channelName = parser.getAttributeValue(null, ATT_NAME);
                                     int channelImportance = XmlUtils.readIntAttribute(
@@ -696,6 +708,10 @@
                 return needsPolicyFileChange;
             }
 
+            if (r.channels.size() >= NOTIFICATION_CHANNEL_COUNT_LIMIT) {
+                throw new IllegalStateException("Limit exceed; cannot create more channels");
+            }
+
             needsPolicyFileChange = true;
 
             if (channel.getImportance() < IMPORTANCE_NONE
diff --git a/services/core/java/com/android/server/pm/ApexManager.java b/services/core/java/com/android/server/pm/ApexManager.java
index a1b6d49..1da5bc6 100644
--- a/services/core/java/com/android/server/pm/ApexManager.java
+++ b/services/core/java/com/android/server/pm/ApexManager.java
@@ -38,6 +38,7 @@
 import android.util.Slog;
 
 import com.android.internal.annotations.GuardedBy;
+import com.android.internal.os.BackgroundThread;
 import com.android.internal.util.IndentingPrintWriter;
 
 import java.io.File;
@@ -263,7 +264,8 @@
                     populateAllPackagesCacheIfNeeded();
                     mContext.unregisterReceiver(this);
                 }
-            }, new IntentFilter(Intent.ACTION_BOOT_COMPLETED));
+            }, new IntentFilter(Intent.ACTION_BOOT_COMPLETED), /* broadcastPermission */ null,
+                    BackgroundThread.getHandler());
         }
 
         private void populateAllPackagesCacheIfNeeded() {
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
index e2644ff..fda798c 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
@@ -43,7 +43,6 @@
 import static com.android.server.pm.PackageManagerService.DEBUG_PERMISSIONS;
 import static com.android.server.pm.PackageManagerService.DEBUG_REMOVE;
 import static com.android.server.pm.PackageManagerService.PLATFORM_PACKAGE_NAME;
-import static com.android.server.pm.permission.PermissionManagerService.killUid;
 import static com.android.server.pm.permission.PermissionsState.PERMISSION_OPERATION_FAILURE;
 
 import static java.util.concurrent.TimeUnit.SECONDS;
@@ -782,6 +781,14 @@
 
     @Override
     public int checkPermission(String permName, String pkgName, int userId) {
+        // Not using Preconditions.checkNotNull() here for compatibility reasons.
+        if (permName == null || pkgName == null) {
+            return PackageManager.PERMISSION_DENIED;
+        }
+        if (!mUserManagerInt.exists(userId)) {
+            return PackageManager.PERMISSION_DENIED;
+        }
+
         final CheckPermissionDelegate checkPermissionDelegate;
         synchronized (mLock) {
             if (mCheckPermissionDelegate == null)  {
@@ -794,43 +801,70 @@
     }
 
     private int checkPermissionImpl(String permName, String pkgName, int userId) {
-        return checkPermissionInternal(permName, pkgName, getCallingUid(), userId);
-    }
-
-    private int checkPermissionInternal(String permName, String packageName, int callingUid,
-            int userId) {
-        if (!mUserManagerInt.exists(userId)) {
+        final PackageParser.Package pkg = mPackageManagerInt.getPackage(pkgName);
+        if (pkg == null) {
             return PackageManager.PERMISSION_DENIED;
         }
-        final PackageParser.Package pkg = mPackageManagerInt.getPackage(packageName);
-        if (pkg != null && pkg.mExtras != null) {
+        return checkPermissionInternal(pkg, true, permName, userId);
+    }
+
+    private int checkPermissionInternal(@NonNull Package pkg, boolean isPackageExplicit,
+            @NonNull String permissionName, @UserIdInt int userId) {
+        final int callingUid = getCallingUid();
+        if (isPackageExplicit || pkg.mSharedUserId == null) {
             if (mPackageManagerInt.filterAppAccess(pkg, callingUid, userId)) {
                 return PackageManager.PERMISSION_DENIED;
             }
-            final PackageSetting ps = (PackageSetting) pkg.mExtras;
-            final boolean instantApp = ps.getInstantApp(userId);
-            final PermissionsState permissionsState = ps.getPermissionsState();
-            if (permissionsState.hasPermission(permName, userId)) {
-                if (instantApp) {
-                    synchronized (mLock) {
-                        BasePermission bp = mSettings.getPermissionLocked(permName);
-                        if (bp != null && bp.isInstant()) {
-                            return PackageManager.PERMISSION_GRANTED;
-                        }
-                    }
-                } else {
-                    return PackageManager.PERMISSION_GRANTED;
-                }
-            }
-            if (isImpliedPermissionGranted(permissionsState, permName, userId)) {
-                return PackageManager.PERMISSION_GRANTED;
+        } else {
+            if (mPackageManagerInt.getInstantAppPackageName(callingUid) != null) {
+                return PackageManager.PERMISSION_DENIED;
             }
         }
+
+        final int uid = UserHandle.getUid(userId, pkg.applicationInfo.uid);
+        final PackageSetting ps = (PackageSetting) pkg.mExtras;
+        if (ps == null) {
+            return PackageManager.PERMISSION_DENIED;
+        }
+        final PermissionsState permissionsState = ps.getPermissionsState();
+
+        if (checkSinglePermissionInternal(uid, permissionsState, permissionName)) {
+            return PackageManager.PERMISSION_GRANTED;
+        }
+
+        final String fullerPermissionName = FULLER_PERMISSION_MAP.get(permissionName);
+        if (fullerPermissionName != null
+                && checkSinglePermissionInternal(uid, permissionsState, fullerPermissionName)) {
+            return PackageManager.PERMISSION_GRANTED;
+        }
+
         return PackageManager.PERMISSION_DENIED;
     }
 
+    private boolean checkSinglePermissionInternal(int uid,
+            @NonNull PermissionsState permissionsState, @NonNull String permissionName) {
+        if (!permissionsState.hasPermission(permissionName, UserHandle.getUserId(uid))) {
+            return false;
+        }
+
+        if (mPackageManagerInt.getInstantAppPackageName(uid) != null) {
+            return mSettings.isPermissionInstant(permissionName);
+        }
+
+        return true;
+    }
+
     @Override
     public int checkUidPermission(String permName, int uid) {
+        // Not using Preconditions.checkNotNull() here for compatibility reasons.
+        if (permName == null) {
+            return PackageManager.PERMISSION_DENIED;
+        }
+        final int userId = UserHandle.getUserId(uid);
+        if (!mUserManagerInt.exists(userId)) {
+            return PackageManager.PERMISSION_DENIED;
+        }
+
         final CheckPermissionDelegate checkPermissionDelegate;
         synchronized (mLock) {
             if (mCheckPermissionDelegate == null)  {
@@ -842,11 +876,9 @@
                 PermissionManagerService.this::checkUidPermissionImpl);
     }
 
-    private int checkUidPermissionImpl(@NonNull String permName, int uid) {
+    private int checkUidPermissionImpl(String permName, int uid) {
         final PackageParser.Package pkg = mPackageManagerInt.getPackage(uid);
-        synchronized (mLock) {
-            return checkUidPermissionInternal(permName, pkg, uid, getCallingUid());
-        }
+        return checkUidPermissionInternal(pkg, uid, permName);
     }
 
     /**
@@ -856,55 +888,33 @@
      *
      * @see SystemConfig#getSystemPermissions()
      */
-    private int checkUidPermissionInternal(@NonNull String permName,
-            @Nullable PackageParser.Package pkg, int uid, int callingUid) {
-        final int callingUserId = UserHandle.getUserId(callingUid);
-        final boolean isCallerInstantApp =
-                mPackageManagerInt.getInstantAppPackageName(callingUid) != null;
-        final boolean isUidInstantApp =
-                mPackageManagerInt.getInstantAppPackageName(uid) != null;
-        final int userId = UserHandle.getUserId(uid);
-        if (!mUserManagerInt.exists(userId)) {
-            return PackageManager.PERMISSION_DENIED;
+    private int checkUidPermissionInternal(@Nullable Package pkg, int uid,
+            @NonNull String permissionName) {
+        if (pkg != null) {
+            final int userId = UserHandle.getUserId(uid);
+            return checkPermissionInternal(pkg, false, permissionName, userId);
         }
 
-        if (pkg != null) {
-            if (pkg.mSharedUserId != null) {
-                if (isCallerInstantApp) {
-                    return PackageManager.PERMISSION_DENIED;
-                }
-            } else if (mPackageManagerInt.filterAppAccess(pkg, callingUid, callingUserId)) {
-                return PackageManager.PERMISSION_DENIED;
-            }
-            final PermissionsState permissionsState =
-                    ((PackageSetting) pkg.mExtras).getPermissionsState();
-            if (permissionsState.hasPermission(permName, userId)) {
-                if (isUidInstantApp) {
-                    if (mSettings.isPermissionInstant(permName)) {
-                        return PackageManager.PERMISSION_GRANTED;
-                    }
-                } else {
-                    return PackageManager.PERMISSION_GRANTED;
-                }
-            }
-            if (isImpliedPermissionGranted(permissionsState, permName, userId)) {
-                return PackageManager.PERMISSION_GRANTED;
-            }
-        } else {
-            ArraySet<String> perms = mSystemPermissions.get(uid);
-            if (perms != null) {
-                if (perms.contains(permName)) {
-                    return PackageManager.PERMISSION_GRANTED;
-                }
-                if (FULLER_PERMISSION_MAP.containsKey(permName)
-                        && perms.contains(FULLER_PERMISSION_MAP.get(permName))) {
-                    return PackageManager.PERMISSION_GRANTED;
-                }
-            }
+        if (checkSingleUidPermissionInternal(uid, permissionName)) {
+            return PackageManager.PERMISSION_GRANTED;
         }
+
+        final String fullerPermissionName = FULLER_PERMISSION_MAP.get(permissionName);
+        if (fullerPermissionName != null
+                && checkSingleUidPermissionInternal(uid, fullerPermissionName)) {
+            return PackageManager.PERMISSION_GRANTED;
+        }
+
         return PackageManager.PERMISSION_DENIED;
     }
 
+    private boolean checkSingleUidPermissionInternal(int uid, @NonNull String permissionName) {
+        synchronized (mLock) {
+            ArraySet<String> permissions = mSystemPermissions.get(uid);
+            return permissions != null && permissions.contains(permissionName);
+        }
+    }
+
     @Override
     public void addOnPermissionsChangeListener(IOnPermissionsChangeListener listener) {
         mContext.enforceCallingOrSelfPermission(
@@ -1962,18 +1972,6 @@
         }
     }
 
-    /**
-     * Returns {@code true} if the permission can be implied from another granted permission.
-     * <p>Some permissions, such as ACCESS_FINE_LOCATION, imply other permissions,
-     * such as ACCESS_COURSE_LOCATION. If the caller holds an umbrella permission, give
-     * it access to any implied permissions.
-     */
-    private static boolean isImpliedPermissionGranted(PermissionsState permissionsState,
-            String permName, int userId) {
-        return FULLER_PERMISSION_MAP.containsKey(permName)
-                && permissionsState.hasPermission(FULLER_PERMISSION_MAP.get(permName), userId);
-    }
-
     private int adjustPermissionProtectionFlagsLocked(
             int protectionLevel, String packageName, int uid) {
         // Signature permission flags area always reported
@@ -2062,8 +2060,8 @@
                         final int numPackages = allPackageNames.size();
                         for (int packageNum = 0; packageNum < numPackages; packageNum++) {
                             final String packageName = allPackageNames.get(packageNum);
-                            final int permissionState = checkPermissionInternal(
-                                    permissionName, packageName, UserHandle.USER_SYSTEM, userId);
+                            final int permissionState = checkPermission(permissionName, packageName,
+                                    userId);
                             if (permissionState == PackageManager.PERMISSION_GRANTED) {
                                 EventLog.writeEvent(0x534e4554, "72710897",
                                         newPackage.applicationInfo.uid,
diff --git a/services/core/java/com/android/server/textclassifier/TextClassificationManagerService.java b/services/core/java/com/android/server/textclassifier/TextClassificationManagerService.java
index 89a5305..4c93564 100644
--- a/services/core/java/com/android/server/textclassifier/TextClassificationManagerService.java
+++ b/services/core/java/com/android/server/textclassifier/TextClassificationManagerService.java
@@ -142,11 +142,12 @@
         synchronized (mLock) {
             UserState userState = getUserStateLocked(userId);
             if (!userState.bindLocked()) {
+                Slog.d(LOG_TAG, "Unable to bind TextClassifierService at suggestSelection.");
                 callback.onFailure();
             } else if (userState.isBoundLocked()) {
                 userState.mService.onSuggestSelection(sessionId, request, callback);
             } else {
-                userState.mPendingRequests.add(new PendingRequest(
+                userState.mPendingRequests.add(new PendingRequest("suggestSelection",
                         () -> onSuggestSelection(sessionId, request, callback),
                         callback::onFailure, callback.asBinder(), this, userState));
             }
@@ -166,11 +167,12 @@
         synchronized (mLock) {
             UserState userState = getUserStateLocked(userId);
             if (!userState.bindLocked()) {
+                Slog.d(LOG_TAG, "Unable to bind TextClassifierService at classifyText.");
                 callback.onFailure();
             } else if (userState.isBoundLocked()) {
                 userState.mService.onClassifyText(sessionId, request, callback);
             } else {
-                userState.mPendingRequests.add(new PendingRequest(
+                userState.mPendingRequests.add(new PendingRequest("classifyText",
                         () -> onClassifyText(sessionId, request, callback),
                         callback::onFailure, callback.asBinder(), this, userState));
             }
@@ -190,11 +192,12 @@
         synchronized (mLock) {
             UserState userState = getUserStateLocked(userId);
             if (!userState.bindLocked()) {
+                Slog.d(LOG_TAG, "Unable to bind TextClassifierService at generateLinks.");
                 callback.onFailure();
             } else if (userState.isBoundLocked()) {
                 userState.mService.onGenerateLinks(sessionId, request, callback);
             } else {
-                userState.mPendingRequests.add(new PendingRequest(
+                userState.mPendingRequests.add(new PendingRequest("generateLinks",
                         () -> onGenerateLinks(sessionId, request, callback),
                         callback::onFailure, callback.asBinder(), this, userState));
             }
@@ -214,7 +217,7 @@
             if (userState.isBoundLocked()) {
                 userState.mService.onSelectionEvent(sessionId, event);
             } else {
-                userState.mPendingRequests.add(new PendingRequest(
+                userState.mPendingRequests.add(new PendingRequest("selectionEvent",
                         () -> onSelectionEvent(sessionId, event),
                         null /* onServiceFailure */, null /* binder */, this, userState));
             }
@@ -238,7 +241,7 @@
             if (userState.isBoundLocked()) {
                 userState.mService.onTextClassifierEvent(sessionId, event);
             } else {
-                userState.mPendingRequests.add(new PendingRequest(
+                userState.mPendingRequests.add(new PendingRequest("textClassifierEvent",
                         () -> onTextClassifierEvent(sessionId, event),
                         null /* onServiceFailure */, null /* binder */, this, userState));
             }
@@ -258,11 +261,12 @@
         synchronized (mLock) {
             UserState userState = getUserStateLocked(userId);
             if (!userState.bindLocked()) {
+                Slog.d(LOG_TAG, "Unable to bind TextClassifierService at detectLanguage.");
                 callback.onFailure();
             } else if (userState.isBoundLocked()) {
                 userState.mService.onDetectLanguage(sessionId, request, callback);
             } else {
-                userState.mPendingRequests.add(new PendingRequest(
+                userState.mPendingRequests.add(new PendingRequest("detectLanguage",
                         () -> onDetectLanguage(sessionId, request, callback),
                         callback::onFailure, callback.asBinder(), this, userState));
             }
@@ -282,11 +286,13 @@
         synchronized (mLock) {
             UserState userState = getUserStateLocked(userId);
             if (!userState.bindLocked()) {
+                Slog.d(LOG_TAG,
+                        "Unable to bind TextClassifierService at suggestConversationActions.");
                 callback.onFailure();
             } else if (userState.isBoundLocked()) {
                 userState.mService.onSuggestConversationActions(sessionId, request, callback);
             } else {
-                userState.mPendingRequests.add(new PendingRequest(
+                userState.mPendingRequests.add(new PendingRequest("suggestConversationActions",
                         () -> onSuggestConversationActions(sessionId, request, callback),
                         callback::onFailure, callback.asBinder(), this, userState));
             }
@@ -309,7 +315,7 @@
                         classificationContext, sessionId);
                 mSessionUserIds.put(sessionId, userId);
             } else {
-                userState.mPendingRequests.add(new PendingRequest(
+                userState.mPendingRequests.add(new PendingRequest("createTextClassificationSession",
                         () -> onCreateTextClassificationSession(classificationContext, sessionId),
                         null /* onServiceFailure */, null /* binder */, this, userState));
             }
@@ -332,9 +338,10 @@
                 userState.mService.onDestroyTextClassificationSession(sessionId);
                 mSessionUserIds.remove(sessionId);
             } else {
-                userState.mPendingRequests.add(new PendingRequest(
-                        () -> onDestroyTextClassificationSession(sessionId),
-                        null /* onServiceFailure */, null /* binder */, this, userState));
+                userState.mPendingRequests.add(
+                        new PendingRequest("destroyTextClassificationSession",
+                                () -> onDestroyTextClassificationSession(sessionId),
+                                null /* onServiceFailure */, null /* binder */, this, userState));
             }
         }
     }
@@ -379,6 +386,7 @@
 
     private static final class PendingRequest implements IBinder.DeathRecipient {
 
+        @Nullable private final String mName;
         @Nullable private final IBinder mBinder;
         @NonNull private final Runnable mRequest;
         @Nullable private final Runnable mOnServiceFailure;
@@ -394,11 +402,12 @@
          * @param service
          * @param owningUser
          */
-        PendingRequest(
+        PendingRequest(@Nullable String name,
                 @NonNull ThrowingRunnable request, @Nullable ThrowingRunnable onServiceFailure,
                 @Nullable IBinder binder,
                 TextClassificationManagerService service,
                 UserState owningUser) {
+            mName = name;
             mRequest =
                     logOnFailure(Preconditions.checkNotNull(request), "handling pending request");
             mOnServiceFailure =
@@ -499,6 +508,8 @@
                     request.mRequest.run();
                 } else {
                     if (request.mOnServiceFailure != null) {
+                        Slog.d(LOG_TAG, "Unable to bind TextClassifierService for PendingRequest "
+                                + request.mName);
                         request.mOnServiceFailure.run();
                     }
                 }
diff --git a/services/core/java/com/android/server/tv/TvInputManagerService.java b/services/core/java/com/android/server/tv/TvInputManagerService.java
index a2134be..8e66b14 100644
--- a/services/core/java/com/android/server/tv/TvInputManagerService.java
+++ b/services/core/java/com/android/server/tv/TvInputManagerService.java
@@ -842,6 +842,10 @@
     private void setStateLocked(String inputId, int state, int userId) {
         UserState userState = getOrCreateUserStateLocked(userId);
         TvInputState inputState = userState.inputMap.get(inputId);
+        if (inputState == null) {
+            Slog.e(TAG, "failed to setStateLocked - unknown input id " + inputId);
+            return;
+        }
         ServiceState serviceState = userState.serviceStateMap.get(inputState.info.getComponent());
         int oldState = inputState.state;
         inputState.state = state;
diff --git a/services/core/java/com/android/server/wm/ActivityDisplay.java b/services/core/java/com/android/server/wm/ActivityDisplay.java
index 5a0dfd0..e76078f 100644
--- a/services/core/java/com/android/server/wm/ActivityDisplay.java
+++ b/services/core/java/com/android/server/wm/ActivityDisplay.java
@@ -362,7 +362,7 @@
         return null;
     }
 
-    private boolean alwaysCreateStack(int windowingMode, int activityType) {
+    boolean alwaysCreateStack(int windowingMode, int activityType) {
         // Always create a stack for fullscreen, freeform, and split-screen-secondary windowing
         // modes so that we can manage visual ordering and return types correctly.
         return activityType == ACTIVITY_TYPE_STANDARD
@@ -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..fc36e99 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -106,6 +106,7 @@
 import static com.android.server.am.EventLogTags.AM_RELAUNCH_ACTIVITY;
 import static com.android.server.am.EventLogTags.AM_RELAUNCH_RESUME_ACTIVITY;
 import static com.android.server.wm.ActivityStack.ActivityState.DESTROYED;
+import static com.android.server.wm.ActivityStack.ActivityState.DESTROYING;
 import static com.android.server.wm.ActivityStack.ActivityState.FINISHING;
 import static com.android.server.wm.ActivityStack.ActivityState.INITIALIZING;
 import static com.android.server.wm.ActivityStack.ActivityState.PAUSED;
@@ -115,14 +116,13 @@
 import static com.android.server.wm.ActivityStack.ActivityState.STARTED;
 import static com.android.server.wm.ActivityStack.ActivityState.STOPPED;
 import static com.android.server.wm.ActivityStack.ActivityState.STOPPING;
-import static com.android.server.wm.ActivityStack.LAUNCH_TICK;
-import static com.android.server.wm.ActivityStack.LAUNCH_TICK_MSG;
-import static com.android.server.wm.ActivityStack.PAUSE_TIMEOUT_MSG;
+import static com.android.server.wm.ActivityStack.REMOVE_TASK_MODE_DESTROYING;
 import static com.android.server.wm.ActivityStack.STACK_VISIBILITY_VISIBLE;
-import static com.android.server.wm.ActivityStack.STOP_TIMEOUT_MSG;
 import static com.android.server.wm.ActivityStackSupervisor.PAUSE_IMMEDIATELY;
 import static com.android.server.wm.ActivityStackSupervisor.PRESERVE_WINDOWS;
-import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_ALL;
+import static com.android.server.wm.ActivityStackSupervisor.REMOVE_FROM_RECENTS;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_APP;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_CLEANUP;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_CONFIGURATION;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_CONTAINERS;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_FOCUS;
@@ -134,6 +134,8 @@
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_TRANSITION;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_USER_LEAVING;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_VISIBILITY;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_ADD_REMOVE;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_APP;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_CONFIGURATION;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_CONTAINERS;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_FOCUS;
@@ -178,8 +180,10 @@
 import android.app.servertransaction.ActivityConfigurationChangeItem;
 import android.app.servertransaction.ActivityLifecycleItem;
 import android.app.servertransaction.ActivityRelaunchItem;
+import android.app.servertransaction.ActivityResultItem;
 import android.app.servertransaction.ClientTransaction;
 import android.app.servertransaction.ClientTransactionItem;
+import android.app.servertransaction.DestroyActivityItem;
 import android.app.servertransaction.MoveToDisplayItem;
 import android.app.servertransaction.MultiWindowModeChangeItem;
 import android.app.servertransaction.NewIntentItem;
@@ -199,12 +203,12 @@
 import android.graphics.Bitmap;
 import android.graphics.GraphicBuffer;
 import android.graphics.Rect;
+import android.net.Uri;
 import android.os.Binder;
 import android.os.Build;
 import android.os.Bundle;
 import android.os.Debug;
 import android.os.IBinder;
-import android.os.Message;
 import android.os.PersistableBundle;
 import android.os.Process;
 import android.os.RemoteException;
@@ -261,6 +265,8 @@
  */
 final class ActivityRecord extends ConfigurationContainer {
     private static final String TAG = TAG_WITH_CLASS_NAME ? "ActivityRecord" : TAG_ATM;
+    private static final String TAG_ADD_REMOVE = TAG + POSTFIX_ADD_REMOVE;
+    private static final String TAG_APP = TAG + POSTFIX_APP;
     private static final String TAG_CONFIGURATION = TAG + POSTFIX_CONFIGURATION;
     private static final String TAG_CONTAINERS = TAG + POSTFIX_CONTAINERS;
     private static final String TAG_FOCUS = TAG + POSTFIX_FOCUS;
@@ -283,6 +289,9 @@
     private static final String ATTR_COMPONENTSPECIFIED = "component_specified";
     static final String ACTIVITY_ICON_SUFFIX = "_activity_icon_";
 
+    // How many activities have to be scheduled to stop to force a stop pass.
+    private static final int MAX_STOPPING_TO_FORCE = 3;
+
     final ActivityTaskManagerService mAtmService; // owner
     final IApplicationToken.Stub appToken; // window manager token
     // TODO: Remove after unification
@@ -1381,6 +1390,12 @@
             }
             // Keep track of the number of fullscreen activities in this task.
             task.numFullscreen += occludesParent ? +1 : -1;
+            fullscreen = occludesParent;
+        }
+        // Always ensure visibility if this activity doesn't occlude parent, so the
+        // {@link #returningOptions} of the activity under this one can be applied in
+        // {@link #handleAlreadyVisible()}.
+        if (changed || !occludesParent) {
             mRootActivityContainer.ensureActivitiesVisible(null, 0, !PRESERVE_WINDOWS);
         }
         return changed;
@@ -1592,8 +1607,7 @@
             if (!Objects.equals(cur.taskAffinity, taskAffinity)) {
                 break;
             }
-            cur.finishActivityLocked(Activity.RESULT_CANCELED, null /* resultData */,
-                    "request-affinity", true /* oomAdj */);
+            cur.finishIfPossible("request-affinity", true /* oomAdj */);
         }
     }
 
@@ -1650,14 +1664,17 @@
     @interface FinishRequest {}
 
     /**
-     * See {@link #finishActivityLocked(int, Intent, String, boolean, boolean)}
+     * See {@link #finishIfPossible(int, Intent, String, boolean, boolean)}
      */
-    @FinishRequest int finishActivityLocked(int resultCode, Intent resultData, String reason,
-            boolean oomAdj) {
-        return finishActivityLocked(resultCode, resultData, reason, oomAdj, !PAUSE_IMMEDIATELY);
+    @FinishRequest int finishIfPossible(String reason, boolean oomAdj) {
+        return finishIfPossible(Activity.RESULT_CANCELED, null /* resultData */, reason,
+                oomAdj, !PAUSE_IMMEDIATELY);
     }
 
     /**
+     * Finish activity if possible. If activity was resumed - we must first pause it to make the
+     * activity below resumed. Otherwise we will try to complete the request immediately by calling
+     * {@link #completeFinishing(String)}.
      * @return One of {@link FinishRequest} values:
      * {@link #FINISH_RESULT_REMOVED} if this activity has been removed from the history list.
      * {@link #FINISH_RESULT_REQUESTED} if removal process was started, but it is still in the list
@@ -1665,7 +1682,7 @@
      * {@link #FINISH_RESULT_CANCELLED} if activity is already finishing or in invalid state and the
      * request to finish it was not ignored.
      */
-    @FinishRequest int finishActivityLocked(int resultCode, Intent resultData, String reason,
+    @FinishRequest int finishIfPossible(int resultCode, Intent resultData, String reason,
             boolean oomAdj, boolean pauseImmediately) {
         if (DEBUG_RESULTS || DEBUG_STATES) {
             Slog.v(TAG_STATES, "Finishing activity r=" + this + ", result=" + resultCode
@@ -1704,20 +1721,24 @@
             pauseKeyDispatchingLocked();
 
             final ActivityStack stack = getActivityStack();
-            stack.adjustFocusedActivityStack(this, "finishActivity");
+            stack.adjustFocusedActivityStack(this, "finishIfPossible");
 
             finishActivityResults(resultCode, resultData);
 
             final boolean endTask = index <= 0 && !task.isClearingToReuseTask();
             final int transit = endTask ? TRANSIT_TASK_CLOSE : TRANSIT_ACTIVITY_CLOSE;
-            if (stack.getResumedActivity() == this) {
-                if (DEBUG_VISIBILITY || DEBUG_TRANSITION) {
-                    Slog.v(TAG_TRANSITION, "Prepare close transition: finishing " + this);
-                }
+            if (isState(RESUMED)) {
                 if (endTask) {
                     mAtmService.getTaskChangeNotificationController().notifyTaskRemovalStarted(
                             task.getTaskInfo());
                 }
+                // Prepare app close transition, but don't execute just yet. It is possible that
+                // an activity that will be made resumed in place of this one will immediately
+                // launch another new activity. In this case current closing transition will be
+                // combined with open transition for the new activity.
+                if (DEBUG_VISIBILITY || DEBUG_TRANSITION) {
+                    Slog.v(TAG_TRANSITION, "Prepare close transition: finishing " + this);
+                }
                 getDisplay().mDisplayContent.prepareAppTransition(transit, false);
 
                 // When finishing the activity preemptively take the snapshot before the app window
@@ -1744,17 +1765,17 @@
                     mAtmService.getLockTaskController().clearLockedTask(task);
                 }
             } else if (!isState(PAUSING)) {
-                // If the activity is PAUSING, we will complete the finish once
-                // it is done pausing; else we can just directly finish it here.
-                if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Finish not pausing: " + this);
                 if (visible) {
+                    // Prepare and execute close transition.
                     prepareActivityHideTransitionAnimation(transit);
                 }
 
-                final int finishMode = (visible || nowVisible) ? FINISH_AFTER_VISIBLE
-                        : FINISH_AFTER_PAUSE;
-                final boolean removedActivity = finishCurrentActivityLocked(finishMode, oomAdj,
-                        "finishActivityLocked") == null;
+                final boolean removedActivity = completeFinishing("finishIfPossible") == null;
+                // Performance optimization - only invoke OOM adjustment if the state changed to
+                // 'STOPPING'. Otherwise it will not change the OOM scores.
+                if (oomAdj && isState(STOPPING)) {
+                    mAtmService.updateOomAdj();
+                }
 
                 // The following code is an optimization. When the last non-task overlay activity
                 // is removed from the task, we remove the entire task from the stack. However,
@@ -1771,6 +1792,7 @@
                         taskOverlay.prepareActivityHideTransitionAnimation(transit);
                     }
                 }
+
                 return removedActivity ? FINISH_RESULT_REMOVED : FINISH_RESULT_REQUESTED;
             } else {
                 if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Finish waiting for pause of: " + this);
@@ -1789,101 +1811,298 @@
         dc.executeAppTransition();
     }
 
-    static final int FINISH_IMMEDIATELY = 0;
-    private static final int FINISH_AFTER_PAUSE = 1;
-    static final int FINISH_AFTER_VISIBLE = 2;
+    /**
+     * Complete activity finish request that was initiated earlier. If the activity is still
+     * pausing we will wait for it to complete its transition. If the activity that should appear in
+     * place of this one is not visible yet - we'll wait for it first. Otherwise - activity can be
+     * destroyed right away.
+     * @param reason Reason for finishing the activity.
+     * @return Flag indicating whether the activity was removed from history.
+     */
+    ActivityRecord completeFinishing(String reason) {
+        if (!finishing || isState(RESUMED)) {
+            throw new IllegalArgumentException(
+                    "Activity must be finishing and not resumed to complete, r=" + this
+                            + ", finishing=" + finishing + ", state=" + mState);
+        }
 
-    ActivityRecord finishCurrentActivityLocked(int mode, boolean oomAdj, String reason) {
-        // First things first: if this activity is currently visible,
-        // and the resumed activity is not yet visible, then hold off on
-        // finishing until the resumed one becomes visible.
+        if (isState(PAUSING)) {
+            // Activity is marked as finishing and will be processed once it completes.
+            return this;
+        }
 
+        boolean activityRemoved = false;
+
+        // If this activity is currently visible, and the resumed activity is not yet visible, then
+        // hold off on finishing until the resumed one becomes visible.
         // The activity that we are finishing may be over the lock screen. In this case, we do not
         // want to consider activities that cannot be shown on the lock screen as running and should
         // proceed with finishing the activity if there is no valid next top running activity.
         // Note that if this finishing activity is floating task, we don't need to wait the
         // next activity resume and can destroy it directly.
+        // TODO(b/137329632): find the next activity directly underneath this one, not just anywhere
+        final ActivityRecord next = getDisplay().topRunningActivity(
+                true /* considerKeyguardState */);
+        final boolean isVisible = visible || nowVisible;
         final ActivityStack stack = getActivityStack();
-        final ActivityDisplay display = getDisplay();
-        final ActivityRecord next = display.topRunningActivity(true /* considerKeyguardState */);
-        final boolean isFloating = getConfiguration().windowConfiguration.tasksAreFloating();
-
-        if (mode == FINISH_AFTER_VISIBLE && (visible || nowVisible)
-                && next != null && !next.nowVisible && !isFloating) {
-            if (!mStackSupervisor.mStoppingActivities.contains(this)) {
-                stack.addToStopping(this, false /* scheduleIdle */, false /* idleDelayed */,
-                        "finishCurrentActivityLocked");
-            }
+        final boolean notFocusedStack = stack != mRootActivityContainer.getTopDisplayFocusedStack();
+        if (isVisible && next != null && !next.nowVisible) {
+            addToStopping(false /* scheduleIdle */, false /* idleDelayed */,
+                    "completeFinishing");
             if (DEBUG_STATES) {
                 Slog.v(TAG_STATES, "Moving to STOPPING: " + this + " (finish requested)");
             }
-            setState(STOPPING, "finishCurrentActivityLocked");
-            if (oomAdj) {
-                mAtmService.updateOomAdj();
-            }
-            return this;
-        }
-
-        // make sure the record is cleaned out of other places.
-        mStackSupervisor.mStoppingActivities.remove(this);
-        mStackSupervisor.mGoingToSleepActivities.remove(this);
-        final ActivityState prevState = getState();
-        if (DEBUG_STATES) Slog.v(TAG_STATES, "Moving to FINISHING: " + this);
-
-        setState(FINISHING, "finishCurrentActivityLocked");
-
-        // Don't destroy activity immediately if the display contains home stack, although there is
-        // no next activity at the moment but another home activity should be started later. Keep
-        // this activity alive until next home activity is resumed then user won't see a temporary
-        // black screen.
-        final boolean noRunningStack = next == null && display.topRunningActivity() == null
-                && display.getHomeStack() == null;
-        final boolean noFocusedStack = stack != display.getFocusedStack();
-        final boolean finishingInNonFocusedStackOrNoRunning = mode == FINISH_AFTER_VISIBLE
-                && prevState == PAUSED && (noFocusedStack || noRunningStack);
-
-        if (mode == FINISH_IMMEDIATELY
-                || (prevState == PAUSED && (mode == FINISH_AFTER_PAUSE || inPinnedWindowingMode()))
-                || finishingInNonFocusedStackOrNoRunning
-                || prevState == STARTED
-                || prevState == STOPPING
-                || prevState == STOPPED
-                || prevState == ActivityState.INITIALIZING) {
-            makeFinishingLocked();
-            boolean activityRemoved = stack.destroyActivityLocked(this, true /* removeFromApp */,
-                    "finish-imm:" + reason);
-
-            if (finishingInNonFocusedStackOrNoRunning) {
-                // Finishing activity that was in paused state and it was in not currently focused
-                // stack, need to make something visible in its place. Also if the display does not
-                // have running activity, the configuration may need to be updated for restoring
-                // original orientation of the display.
+            setState(STOPPING, "completeFinishing");
+            if (notFocusedStack) {
                 mRootActivityContainer.ensureVisibilityAndConfig(next, getDisplayId(),
                         false /* markFrozenIfConfigChanged */, true /* deferResume */);
             }
-            if (activityRemoved) {
-                mRootActivityContainer.resumeFocusedStacksTopActivities();
-            }
-            if (DEBUG_CONTAINERS) {
-                Slog.d(TAG_CONTAINERS, "destroyActivityLocked: finishCurrentActivityLocked r="
-                        + this + " destroy returned removed=" + activityRemoved);
-            }
-            return activityRemoved ? null : this;
+        } else if (isVisible && isState(PAUSED) && getActivityStack().isFocusedStackOnDisplay()
+                && !inPinnedWindowingMode()) {
+            // TODO(b/137329632): Currently non-focused stack is handled differently.
+            addToFinishingAndWaitForIdle();
+        } else {
+            // Not waiting for the next one to become visible - finish right away.
+            activityRemoved = destroyIfPossible(reason);
         }
 
-        // Need to go through the full pause cycle to get this
-        // activity into the stopped state and then finish it.
-        if (DEBUG_ALL) Slog.v(TAG, "Enqueueing pending finish: " + this);
+        return activityRemoved ? null : this;
+    }
+
+    /**
+     * Destroy and cleanup the activity both on client and server if possible. If activity is the
+     * last one left on display with home stack and there is no other running activity - delay
+     * destroying it until the next one starts.
+     */
+    boolean destroyIfPossible(String reason) {
+        setState(FINISHING, "destroyIfPossible");
+
+        // Make sure the record is cleaned out of other places.
+        mStackSupervisor.mStoppingActivities.remove(this);
+        mStackSupervisor.mGoingToSleepActivities.remove(this);
+
+        final ActivityStack stack = getActivityStack();
+        final ActivityDisplay display = getDisplay();
+        // TODO(b/137329632): Exclude current activity when looking for the next one with
+        //  ActivityDisplay#topRunningActivity().
+        final ActivityRecord next = display.topRunningActivity();
+        final boolean isLastStackOverEmptyHome =
+                next == null && stack.isFocusedStackOnDisplay() && display.getHomeStack() != null;
+        if (isLastStackOverEmptyHome) {
+            // Don't destroy activity immediately if this is the last activity on the display and
+            // the display contains home stack. Although there is no next activity at the moment,
+            // another home activity should be started later. Keep this activity alive until next
+            // home activity is resumed. This way the user won't see a temporary black screen.
+            addToFinishingAndWaitForIdle();
+            return false;
+        }
+        makeFinishingLocked();
+
+        final boolean activityRemoved = destroyImmediately(true /* removeFromApp */,
+                "finish-imm:" + reason);
+
+        // If the display does not have running activity, the configuration may need to be
+        // updated for restoring original orientation of the display.
+        if (next == null) {
+            mRootActivityContainer.ensureVisibilityAndConfig(next, getDisplayId(),
+                    false /* markFrozenIfConfigChanged */, true /* deferResume */);
+        }
+        if (activityRemoved) {
+            mRootActivityContainer.resumeFocusedStacksTopActivities();
+        }
+
+        if (DEBUG_CONTAINERS) {
+            Slog.d(TAG_CONTAINERS, "destroyIfPossible: r=" + this + " destroy returned removed="
+                    + activityRemoved);
+        }
+
+        return activityRemoved;
+    }
+
+    @VisibleForTesting
+    void addToFinishingAndWaitForIdle() {
+        if (DEBUG_STATES) Slog.v(TAG, "Enqueueing pending finish: " + this);
+        setState(FINISHING, "addToFinishingAndWaitForIdle");
         mStackSupervisor.mFinishingActivities.add(this);
         resumeKeyDispatchingLocked();
         mRootActivityContainer.resumeFocusedStacksTopActivities();
-        // If activity was not paused at this point - explicitly pause it to start finishing
-        // process. Finishing will be completed once it reports pause back.
-        if (isState(RESUMED) && stack.mPausingActivity != null) {
-            stack.startPausingLocked(false /* userLeaving */, false /* uiSleeping */,
-                    next /* resuming */, false /* dontWait */);
+    }
+
+    /**
+     * Destroy the current CLIENT SIDE instance of an activity. This may be called both when
+     * actually finishing an activity, or when performing a configuration switch where we destroy
+     * the current client-side object but then create a new client-side object for this same
+     * HistoryRecord.
+     * Normally the server-side record will be removed when the client reports back after
+     * destruction. If, however, at this point there is no client process attached, the record will
+     * removed immediately.
+     */
+    boolean destroyImmediately(boolean removeFromApp, String reason) {
+        if (DEBUG_SWITCH || DEBUG_CLEANUP) {
+            Slog.v(TAG_SWITCH, "Removing activity from " + reason + ": token=" + this
+                    + ", app=" + (hasProcess() ? app.mName : "(null)"));
         }
-        return this;
+
+        if (isState(DESTROYING, DESTROYED)) {
+            if (DEBUG_STATES) {
+                Slog.v(TAG_STATES, "activity " + this + " already destroying."
+                        + "skipping request with reason:" + reason);
+            }
+            return false;
+        }
+
+        EventLog.writeEvent(EventLogTags.AM_DESTROY_ACTIVITY, mUserId,
+                System.identityHashCode(this), getTaskRecord().taskId, shortComponentName, reason);
+
+        boolean removedFromHistory = false;
+
+        cleanUp(false /* cleanServices */, false /* setState */);
+
+        final ActivityStack stack = getActivityStack();
+        final boolean hadApp = hasProcess();
+
+        if (hadApp) {
+            if (removeFromApp) {
+                app.removeActivity(this);
+                if (!app.hasActivities()) {
+                    mAtmService.clearHeavyWeightProcessIfEquals(app);
+                    // Update any services we are bound to that might care about whether
+                    // their client may have activities.
+                    // No longer have activities, so update LRU list and oom adj.
+                    app.updateProcessInfo(true /* updateServiceConnectionActivities */,
+                            false /* activityChange */, true /* updateOomAdj */);
+                }
+            }
+
+            boolean skipDestroy = false;
+
+            try {
+                if (DEBUG_SWITCH) Slog.i(TAG_SWITCH, "Destroying: " + this);
+                mAtmService.getLifecycleManager().scheduleTransaction(app.getThread(), appToken,
+                        DestroyActivityItem.obtain(finishing, configChangeFlags));
+            } catch (Exception e) {
+                // We can just ignore exceptions here...  if the process has crashed, our death
+                // notification will clean things up.
+                if (finishing) {
+                    removeFromHistory(reason + " exceptionInScheduleDestroy");
+                    removedFromHistory = true;
+                    skipDestroy = true;
+                }
+            }
+
+            nowVisible = false;
+
+            // If the activity is finishing, we need to wait on removing it from the list to give it
+            // a chance to do its cleanup.  During that time it may make calls back with its token
+            // so we need to be able to find it on the list and so we don't want to remove it from
+            // the list yet.  Otherwise, we can just immediately put it in the destroyed state since
+            // we are not removing it from the list.
+            if (finishing && !skipDestroy) {
+                if (DEBUG_STATES) {
+                    Slog.v(TAG_STATES, "Moving to DESTROYING: " + this + " (destroy requested)");
+                }
+                setState(DESTROYING,
+                        "destroyActivityLocked. finishing and not skipping destroy");
+                stack.scheduleDestroyTimeoutForActivity(this);
+            } else {
+                if (DEBUG_STATES) {
+                    Slog.v(TAG_STATES, "Moving to DESTROYED: " + this + " (destroy skipped)");
+                }
+                setState(DESTROYED,
+                        "destroyActivityLocked. not finishing or skipping destroy");
+                if (DEBUG_APP) Slog.v(TAG_APP, "Clearing app during destroy for activity " + this);
+                app = null;
+            }
+        } else {
+            // Remove this record from the history.
+            if (finishing) {
+                removeFromHistory(reason + " hadNoApp");
+                removedFromHistory = true;
+            } else {
+                if (DEBUG_STATES) Slog.v(TAG_STATES, "Moving to DESTROYED: " + this + " (no app)");
+                setState(DESTROYED, "destroyActivityLocked. not finishing and had no app");
+            }
+        }
+
+        configChangeFlags = 0;
+
+        if (!stack.removeActivityFromLRUList(this) && hadApp) {
+            Slog.w(TAG, "Activity " + this + " being finished, but not in LRU list");
+        }
+
+        return removedFromHistory;
+    }
+
+    boolean safelyDestroy(String reason) {
+        if (isDestroyable()) {
+            if (DEBUG_SWITCH) {
+                final ActivityStack stack = getActivityStack();
+                Slog.v(TAG_SWITCH, "Safely destroying " + this + " in state " + getState()
+                        + " resumed=" + stack.mResumedActivity
+                        + " pausing=" + stack.mPausingActivity
+                        + " for reason " + reason);
+            }
+            return destroyImmediately(true /* removeFromApp */, reason);
+        }
+        return false;
+    }
+
+    /** Note: call {@link #cleanUp(boolean, boolean)} before this method. */
+    void removeFromHistory(String reason) {
+        finishActivityResults(Activity.RESULT_CANCELED, null /* resultData */);
+        makeFinishingLocked();
+        if (ActivityTaskManagerDebugConfig.DEBUG_ADD_REMOVE) {
+            Slog.i(TAG_ADD_REMOVE, "Removing activity " + this + " from stack callers="
+                    + Debug.getCallers(5));
+        }
+
+        takeFromHistory();
+        final ActivityStack stack = getActivityStack();
+        stack.removeTimeoutsForActivity(this);
+        if (DEBUG_STATES) {
+            Slog.v(TAG_STATES, "Moving to DESTROYED: " + this + " (removed from history)");
+        }
+        setState(DESTROYED, "removeFromHistory");
+        if (DEBUG_APP) Slog.v(TAG_APP, "Clearing app during remove for activity " + this);
+        app = null;
+        removeWindowContainer();
+        final TaskRecord task = getTaskRecord();
+        final boolean lastActivity = task.removeActivity(this);
+        // If we are removing the last activity in the task, not including task overlay activities,
+        // then fall through into the block below to remove the entire task itself
+        final boolean onlyHasTaskOverlays =
+                task.onlyHasTaskOverlayActivities(false /* excludingFinishing */);
+
+        if (lastActivity || onlyHasTaskOverlays) {
+            if (DEBUG_STATES) {
+                Slog.i(TAG, "removeFromHistory: last activity removed from " + this
+                        + " onlyHasTaskOverlays=" + onlyHasTaskOverlays);
+            }
+
+            // The following block can be executed multiple times if there is more than one overlay.
+            // {@link ActivityStackSupervisor#removeTaskByIdLocked} handles this by reverse lookup
+            // of the task by id and exiting early if not found.
+            if (onlyHasTaskOverlays) {
+                // When destroying a task, tell the supervisor to remove it so that any activity it
+                // has can be cleaned up correctly. This is currently the only place where we remove
+                // a task with the DESTROYING mode, so instead of passing the onlyHasTaskOverlays
+                // state into removeTask(), we just clear the task here before the other residual
+                // work.
+                // TODO: If the callers to removeTask() changes such that we have multiple places
+                //       where we are destroying the task, move this back into removeTask()
+                mStackSupervisor.removeTaskByIdLocked(task.taskId, false /* killProcess */,
+                        !REMOVE_FROM_RECENTS, PAUSE_IMMEDIATELY, reason);
+            }
+
+            // We must keep the task around until all activities are destroyed. The following
+            // statement will only execute once since overlays are also considered activities.
+            if (lastActivity) {
+                stack.removeTask(task, reason, REMOVE_TASK_MODE_DESTROYING);
+            }
+        }
+
+        cleanUpActivityServices();
+        removeUriPermissionsLocked();
     }
 
     void makeFinishingLocked() {
@@ -1900,6 +2119,96 @@
         }
     }
 
+    /**
+     * This method is to only be called from the client via binder when the activity is destroyed
+     * AND finished.
+     */
+    void destroyed(String reason) {
+        getActivityStack().removeDestroyTimeoutForActivity(this);
+
+        if (DEBUG_CONTAINERS) Slog.d(TAG_CONTAINERS, "activityDestroyedLocked: r=" + this);
+
+        if (!isState(DESTROYING, DESTROYED)) {
+            throw new IllegalStateException(
+                    "Reported destroyed for activity that is not destroying: r=" + this);
+        }
+
+        if (isInStackLocked()) {
+            cleanUp(true /* cleanServices */, false /* setState */);
+            removeFromHistory(reason);
+        }
+
+        mRootActivityContainer.resumeFocusedStacksTopActivities();
+    }
+
+    /**
+     * Perform the common clean-up of an activity record.  This is called both as part of
+     * destroyActivityLocked() (when destroying the client-side representation) and cleaning things
+     * up as a result of its hosting processing going away, in which case there is no remaining
+     * client-side state to destroy so only the cleanup here is needed.
+     *
+     * Note: Call before {@link #removeFromHistory(String)}.
+     */
+    void cleanUp(boolean cleanServices, boolean setState) {
+        final ActivityStack stack = getActivityStack();
+        stack.onActivityRemovedFromStack(this);
+
+        deferRelaunchUntilPaused = false;
+        frozenBeforeDestroy = false;
+
+        if (setState) {
+            setState(DESTROYED, "cleanUp");
+            if (DEBUG_APP) Slog.v(TAG_APP, "Clearing app during cleanUp for activity " + this);
+            app = null;
+        }
+
+        // Inform supervisor the activity has been removed.
+        mStackSupervisor.cleanupActivity(this);
+
+        // Remove any pending results.
+        if (finishing && pendingResults != null) {
+            for (WeakReference<PendingIntentRecord> apr : pendingResults) {
+                PendingIntentRecord rec = apr.get();
+                if (rec != null) {
+                    mAtmService.mPendingIntentController.cancelIntentSender(rec,
+                            false /* cleanActivity */);
+                }
+            }
+            pendingResults = null;
+        }
+
+        if (cleanServices) {
+            cleanUpActivityServices();
+        }
+
+        // Get rid of any pending idle timeouts.
+        stack.removeTimeoutsForActivity(this);
+        // Clean-up activities are no longer relaunching (e.g. app process died). Notify window
+        // manager so it can update its bookkeeping.
+        mAtmService.mWindowManager.notifyAppRelaunchesCleared(appToken);
+    }
+
+    /**
+     * Perform clean-up of service connections in an activity record.
+     */
+    private void cleanUpActivityServices() {
+        if (mServiceConnectionsHolder == null) {
+            return;
+        }
+        // Throw away any services that have been bound by this activity.
+        mServiceConnectionsHolder.disconnectActivityFromServices();
+    }
+
+    void logStartActivity(int tag, TaskRecord task) {
+        final Uri data = intent.getData();
+        final String strData = data != null ? data.toSafeString() : null;
+
+        EventLog.writeEvent(tag,
+                mUserId, System.identityHashCode(this), task.taskId,
+                shortComponentName, intent.getAction(),
+                intent.getType(), strData, intent.getFlags());
+    }
+
     UriPermissionOwner getUriPermissionsLocked() {
         if (uriPermissions == null) {
             uriPermissions = new UriPermissionOwner(mAtmService.mUgmInternal, this);
@@ -1936,6 +2245,33 @@
         }
     }
 
+    void sendResult(int callingUid, String resultWho, int requestCode, int resultCode,
+            Intent data) {
+        if (callingUid > 0) {
+            mAtmService.mUgmInternal.grantUriPermissionFromIntent(callingUid, packageName,
+                    data, getUriPermissionsLocked(), mUserId);
+        }
+
+        if (DEBUG_RESULTS) {
+            Slog.v(TAG, "Send activity result to " + this
+                    + " : who=" + resultWho + " req=" + requestCode
+                    + " res=" + resultCode + " data=" + data);
+        }
+        if (isState(RESUMED) && attachedToProcess()) {
+            try {
+                final ArrayList<ResultInfo> list = new ArrayList<ResultInfo>();
+                list.add(new ResultInfo(resultWho, requestCode, resultCode, data));
+                mAtmService.getLifecycleManager().scheduleTransaction(app.getThread(), appToken,
+                        ActivityResultItem.obtain(list));
+                return;
+            } catch (Exception e) {
+                Slog.w(TAG, "Exception thrown sending result to " + this, e);
+            }
+        }
+
+        addResultLocked(null /* from */, resultWho, requestCode, resultCode, data);
+    }
+
     private void addNewIntentLocked(ReferrerIntent intent) {
         if (newIntents == null) {
             newIntents = new ArrayList<>();
@@ -2415,6 +2751,65 @@
         }
     }
 
+    void makeInvisible() {
+        if (!visible) {
+            if (DEBUG_VISIBILITY) Slog.v(TAG_VISIBILITY, "Already invisible: " + this);
+            return;
+        }
+        // Now for any activities that aren't visible to the user, make sure they no longer are
+        // keeping the screen frozen.
+        if (DEBUG_VISIBILITY) {
+            Slog.v(TAG_VISIBILITY, "Making invisible: " + this + ", state=" + getState());
+        }
+        try {
+            final boolean canEnterPictureInPicture = checkEnterPictureInPictureState(
+                    "makeInvisible", true /* beforeStopping */);
+            // Defer telling the client it is hidden if it can enter Pip and isn't current paused,
+            // stopped or stopping. This gives it a chance to enter Pip in onPause().
+            // TODO: There is still a question surrounding activities in multi-window mode that want
+            // to enter Pip after they are paused, but are still visible. I they should be okay to
+            // enter Pip in those cases, but not "auto-Pip" which is what this condition covers and
+            // the current contract for "auto-Pip" is that the app should enter it before onPause
+            // returns. Just need to confirm this reasoning makes sense.
+            final boolean deferHidingClient = canEnterPictureInPicture
+                    && !isState(STOPPING, STOPPED, PAUSED);
+            setDeferHidingClient(deferHidingClient);
+            setVisible(false);
+
+            switch (getState()) {
+                case STOPPING:
+                case STOPPED:
+                    if (attachedToProcess()) {
+                        if (DEBUG_VISIBILITY) {
+                            Slog.v(TAG_VISIBILITY, "Scheduling invisibility: " + this);
+                        }
+                        mAtmService.getLifecycleManager().scheduleTransaction(app.getThread(),
+                                appToken, WindowVisibilityItem.obtain(false /* showWindow */));
+                    }
+
+                    // Reset the flag indicating that an app can enter picture-in-picture once the
+                    // activity is hidden
+                    supportsEnterPipOnTaskSwitch = false;
+                    break;
+
+                case INITIALIZING:
+                case RESUMED:
+                case PAUSING:
+                case PAUSED:
+                case STARTED:
+                    addToStopping(true /* scheduleIdle */,
+                            canEnterPictureInPicture /* idleDelayed */, "makeInvisible");
+                    break;
+
+                default:
+                    break;
+            }
+        } catch (Exception e) {
+            // Just skip on any failure; we'll make it visible when it next restarts.
+            Slog.w(TAG, "Exception thrown making hidden: " + intent.getComponent(), e);
+        }
+    }
+
     /**
      * Make activity resumed or paused if needed.
      * @param activeActivity an activity that is resumed or just completed pause action.
@@ -2605,13 +3000,75 @@
         }
     }
 
+    void stopIfPossible() {
+        if (DEBUG_SWITCH) Slog.d(TAG_SWITCH, "Stopping: " + this);
+        final ActivityStack stack = getActivityStack();
+        if ((intent.getFlags() & Intent.FLAG_ACTIVITY_NO_HISTORY) != 0
+                || (info.flags & ActivityInfo.FLAG_NO_HISTORY) != 0) {
+            if (!finishing) {
+                if (!stack.shouldSleepActivities()) {
+                    if (DEBUG_STATES) Slog.d(TAG_STATES, "no-history finish of " + this);
+                    if (finishIfPossible("stop-no-history", false /* oomAdj */)
+                            != FINISH_RESULT_CANCELLED) {
+                        // {@link adjustFocusedActivityStack} must have been already called.
+                        resumeKeyDispatchingLocked();
+                        return;
+                    }
+                } else {
+                    if (DEBUG_STATES) {
+                        Slog.d(TAG_STATES, "Not finishing noHistory " + this
+                                + " on stop because we're just sleeping");
+                    }
+                }
+            }
+        }
+
+        if (!attachedToProcess()) {
+            return;
+        }
+        stack.adjustFocusedActivityStack(this, "stopActivity");
+        resumeKeyDispatchingLocked();
+        try {
+            stopped = false;
+            if (DEBUG_STATES) {
+                Slog.v(TAG_STATES, "Moving to STOPPING: " + this + " (stop requested)");
+            }
+            setState(STOPPING, "stopIfPossible");
+            if (DEBUG_VISIBILITY) {
+                Slog.v(TAG_VISIBILITY, "Stopping visible=" + visible + " for " + this);
+            }
+            if (!visible) {
+                setVisible(false);
+            }
+            EventLogTags.writeAmStopActivity(
+                    mUserId, System.identityHashCode(this), shortComponentName);
+            mAtmService.getLifecycleManager().scheduleTransaction(app.getThread(), appToken,
+                    StopActivityItem.obtain(visible, configChangeFlags));
+            if (stack.shouldSleepOrShutDownActivities()) {
+                setSleeping(true);
+            }
+            stack.scheduleStopTimeoutForActivity(this);
+        } catch (Exception e) {
+            // Maybe just ignore exceptions here...  if the process has crashed, our death
+            // notification will clean things up.
+            Slog.w(TAG, "Exception thrown during pause", e);
+            // Just in case, assume it to be stopped.
+            stopped = true;
+            if (DEBUG_STATES) Slog.v(TAG_STATES, "Stop failed; moving to STOPPED: " + this);
+            setState(STOPPED, "stopIfPossible");
+            if (deferRelaunchUntilPaused) {
+                destroyImmediately(true /* removeFromApp */, "stop-except");
+            }
+        }
+    }
+
     final void activityStoppedLocked(Bundle newIcicle, PersistableBundle newPersistentState,
             CharSequence description) {
         final ActivityStack stack = getActivityStack();
         final boolean isStopping = mState == STOPPING;
         if (!isStopping && mState != RESTARTING_PROCESS) {
             Slog.i(TAG, "Activity reported stop, but no longer stopping: " + this);
-            stack.mHandler.removeMessages(STOP_TIMEOUT_MSG, this);
+            stack.removeStopTimeoutForActivity(this);
             return;
         }
         if (newPersistentState != null) {
@@ -2629,7 +3086,7 @@
         if (DEBUG_SAVED_STATE) Slog.i(TAG_SAVED_STATE, "Saving icicle of " + this + ": " + mIcicle);
         if (!stopped) {
             if (DEBUG_STATES) Slog.v(TAG_STATES, "Moving to STOPPED: " + this + " (stop complete)");
-            stack.mHandler.removeMessages(STOP_TIMEOUT_MSG, this);
+            stack.removeStopTimeoutForActivity(this);
             stopped = true;
             if (isStopping) {
                 setState(STOPPED, "activityStoppedLocked");
@@ -2643,7 +3100,7 @@
                 clearOptionsLocked();
             } else {
                 if (deferRelaunchUntilPaused) {
-                    stack.destroyActivityLocked(this, true /* removeFromApp */, "stop-config");
+                    destroyImmediately(true /* removeFromApp */, "stop-config");
                     mRootActivityContainer.resumeFocusedStacksTopActivities();
                 } else {
                     mRootActivityContainer.updatePreviousProcess(this);
@@ -2652,6 +3109,34 @@
         }
     }
 
+    void addToStopping(boolean scheduleIdle, boolean idleDelayed, String reason) {
+        if (!mStackSupervisor.mStoppingActivities.contains(this)) {
+            EventLog.writeEvent(EventLogTags.AM_ADD_TO_STOPPING, mUserId,
+                    System.identityHashCode(this), shortComponentName, reason);
+            mStackSupervisor.mStoppingActivities.add(this);
+        }
+
+        final ActivityStack stack = getActivityStack();
+        // If we already have a few activities waiting to stop, then give up on things going idle
+        // and start clearing them out. Or if r is the last of activity of the last task the stack
+        // will be empty and must be cleared immediately.
+        boolean forceIdle = mStackSupervisor.mStoppingActivities.size() > MAX_STOPPING_TO_FORCE
+                || (isRootOfTask() && stack.getChildCount() <= 1);
+        if (scheduleIdle || forceIdle) {
+            if (DEBUG_PAUSE) {
+                Slog.v(TAG_PAUSE, "Scheduling idle now: forceIdle=" + forceIdle
+                        + "immediate=" + !idleDelayed);
+            }
+            if (!idleDelayed) {
+                mStackSupervisor.scheduleIdleLocked();
+            } else {
+                mStackSupervisor.scheduleIdleTimeoutLocked(this);
+            }
+        } else {
+            stack.checkReadyForSleep();
+        }
+    }
+
     void startLaunchTickingLocked() {
         if (Build.IS_USER) {
             return;
@@ -2672,18 +3157,18 @@
             return false;
         }
 
-        Message msg = stack.mHandler.obtainMessage(LAUNCH_TICK_MSG, this);
-        stack.mHandler.removeMessages(LAUNCH_TICK_MSG);
-        stack.mHandler.sendMessageDelayed(msg, LAUNCH_TICK);
+        stack.removeLaunchTickMessages();
+        stack.scheduleLaunchTickForActivity(this);
         return true;
     }
 
     void finishLaunchTickingLocked() {
         launchTickTime = 0;
         final ActivityStack stack = getActivityStack();
-        if (stack != null) {
-            stack.mHandler.removeMessages(LAUNCH_TICK_MSG);
+        if (stack == null) {
+            return;
         }
+        stack.removeLaunchTickMessages();
     }
 
     // IApplicationToken
@@ -3660,7 +4145,7 @@
             if (!attachedToProcess()) {
                 if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION,
                         "Config is destroying non-running " + this);
-                stack.destroyActivityLocked(this, true, "config");
+                destroyImmediately(true /* removeFromApp */, "config");
             } else if (mState == PAUSING) {
                 // A little annoying: we are waiting for this activity to finish pausing. Let's not
                 // do anything now, but just flag that it needs to be restarted when done pausing.
@@ -3844,7 +4329,7 @@
         } else {
             final ActivityStack stack = getActivityStack();
             if (stack != null) {
-                stack.mHandler.removeMessages(PAUSE_TIMEOUT_MSG, this);
+                stack.removePauseTimeoutForActivity(this);
             }
             setState(PAUSED, "relaunchActivityLocked");
         }
@@ -3884,7 +4369,7 @@
         if (!visible || mHaveState) {
             // Kill its process immediately because the activity should be in background.
             // The activity state will be update to {@link #DESTROYED} in
-            // {@link ActivityStack#cleanUpActivityLocked} when handling process died.
+            // {@link ActivityStack#cleanUp} when handling process died.
             mAtmService.mH.post(() -> {
                 final WindowProcessController wpc;
                 synchronized (mAtmService.mGlobalLock) {
diff --git a/services/core/java/com/android/server/wm/ActivityStack.java b/services/core/java/com/android/server/wm/ActivityStack.java
index daf3286..8bdedff 100644
--- a/services/core/java/com/android/server/wm/ActivityStack.java
+++ b/services/core/java/com/android/server/wm/ActivityStack.java
@@ -56,12 +56,7 @@
 import static com.android.server.am.ActivityStackProto.TASKS;
 import static com.android.server.wm.ActivityDisplay.POSITION_BOTTOM;
 import static com.android.server.wm.ActivityDisplay.POSITION_TOP;
-import static com.android.server.wm.ActivityRecord.FINISH_AFTER_VISIBLE;
-import static com.android.server.wm.ActivityRecord.FINISH_IMMEDIATELY;
-import static com.android.server.wm.ActivityRecord.FINISH_RESULT_CANCELLED;
 import static com.android.server.wm.ActivityRecord.FINISH_RESULT_REMOVED;
-import static com.android.server.wm.ActivityStack.ActivityState.DESTROYED;
-import static com.android.server.wm.ActivityStack.ActivityState.DESTROYING;
 import static com.android.server.wm.ActivityStack.ActivityState.PAUSED;
 import static com.android.server.wm.ActivityStack.ActivityState.PAUSING;
 import static com.android.server.wm.ActivityStack.ActivityState.RESUMED;
@@ -70,14 +65,12 @@
 import static com.android.server.wm.ActivityStack.ActivityState.STOPPING;
 import static com.android.server.wm.ActivityStackSupervisor.PAUSE_IMMEDIATELY;
 import static com.android.server.wm.ActivityStackSupervisor.PRESERVE_WINDOWS;
-import static com.android.server.wm.ActivityStackSupervisor.REMOVE_FROM_RECENTS;
 import static com.android.server.wm.ActivityStackSupervisor.dumpHistoryList;
 import static com.android.server.wm.ActivityStackSupervisor.printThisActivity;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_ADD_REMOVE;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_ALL;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_APP;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_CLEANUP;
-import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_CONTAINERS;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_PAUSE;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_RELEASE;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_RESULTS;
@@ -91,7 +84,6 @@
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_ADD_REMOVE;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_APP;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_CLEANUP;
-import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_CONTAINERS;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_PAUSE;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_RELEASE;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_RESULTS;
@@ -108,6 +100,7 @@
 import static com.android.server.wm.ActivityTaskManagerService.RELAUNCH_REASON_FREE_RESIZE;
 import static com.android.server.wm.ActivityTaskManagerService.RELAUNCH_REASON_WINDOWING_MODE_RESIZE;
 import static com.android.server.wm.RootActivityContainer.FindTaskResult;
+import static com.android.server.wm.TaskRecord.REPARENT_LEAVE_STACK_IN_PLACE;
 
 import static java.lang.Integer.MAX_VALUE;
 
@@ -124,12 +117,9 @@
 import android.app.WindowConfiguration.WindowingMode;
 import android.app.servertransaction.ActivityResultItem;
 import android.app.servertransaction.ClientTransaction;
-import android.app.servertransaction.DestroyActivityItem;
 import android.app.servertransaction.NewIntentItem;
 import android.app.servertransaction.PauseActivityItem;
 import android.app.servertransaction.ResumeActivityItem;
-import android.app.servertransaction.StopActivityItem;
-import android.app.servertransaction.WindowVisibilityItem;
 import android.content.ComponentName;
 import android.content.Intent;
 import android.content.pm.ActivityInfo;
@@ -164,11 +154,9 @@
 import com.android.server.am.ActivityManagerService.ItemMatcher;
 import com.android.server.am.AppTimeTracker;
 import com.android.server.am.EventLogTags;
-import com.android.server.am.PendingIntentRecord;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
-import java.lang.ref.WeakReference;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Objects;
@@ -182,7 +170,6 @@
     private static final String TAG_ADD_REMOVE = TAG + POSTFIX_ADD_REMOVE;
     private static final String TAG_APP = TAG + POSTFIX_APP;
     private static final String TAG_CLEANUP = TAG + POSTFIX_CLEANUP;
-    private static final String TAG_CONTAINERS = TAG + POSTFIX_CONTAINERS;
     private static final String TAG_PAUSE = TAG + POSTFIX_PAUSE;
     private static final String TAG_RELEASE = TAG + POSTFIX_RELEASE;
     private static final String TAG_RESULTS = TAG + POSTFIX_RESULTS;
@@ -195,7 +182,7 @@
     private static final String TAG_VISIBILITY = TAG + POSTFIX_VISIBILITY;
 
     // Ticks during which we check progress while waiting for an app to launch.
-    static final int LAUNCH_TICK = 500;
+    private static final int LAUNCH_TICK = 500;
 
     // How long we wait until giving up on the last activity to pause.  This
     // is short because it directly impacts the responsiveness of starting the
@@ -223,9 +210,6 @@
     // convertToTranslucent().
     private static final long TRANSLUCENT_CONVERSION_TIMEOUT = 2000;
 
-    // How many activities have to be scheduled to stop to force a stop pass.
-    private static final int MAX_STOPPING_TO_FORCE = 3;
-
     @IntDef(prefix = {"STACK_VISIBILITY"}, value = {
             STACK_VISIBILITY_VISIBLE,
             STACK_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT,
@@ -412,12 +396,12 @@
     private boolean mTopActivityOccludesKeyguard;
     private ActivityRecord mTopDismissingKeyguardActivity;
 
-    static final int PAUSE_TIMEOUT_MSG = FIRST_ACTIVITY_STACK_MSG + 1;
-    static final int DESTROY_TIMEOUT_MSG = FIRST_ACTIVITY_STACK_MSG + 2;
-    static final int LAUNCH_TICK_MSG = FIRST_ACTIVITY_STACK_MSG + 3;
-    static final int STOP_TIMEOUT_MSG = FIRST_ACTIVITY_STACK_MSG + 4;
-    static final int DESTROY_ACTIVITIES_MSG = FIRST_ACTIVITY_STACK_MSG + 5;
-    static final int TRANSLUCENT_TIMEOUT_MSG = FIRST_ACTIVITY_STACK_MSG + 6;
+    private static final int PAUSE_TIMEOUT_MSG = FIRST_ACTIVITY_STACK_MSG + 1;
+    private static final int DESTROY_TIMEOUT_MSG = FIRST_ACTIVITY_STACK_MSG + 2;
+    private static final int LAUNCH_TICK_MSG = FIRST_ACTIVITY_STACK_MSG + 3;
+    private static final int STOP_TIMEOUT_MSG = FIRST_ACTIVITY_STACK_MSG + 4;
+    private static final int DESTROY_ACTIVITIES_MSG = FIRST_ACTIVITY_STACK_MSG + 5;
+    private static final int TRANSLUCENT_TIMEOUT_MSG = FIRST_ACTIVITY_STACK_MSG + 6;
 
     // TODO: remove after unification.
     TaskStack mTaskStack;
@@ -431,7 +415,7 @@
         }
     }
 
-    final Handler mHandler;
+    private final Handler mHandler;
 
     private class ActivityStackHandler extends Handler {
 
@@ -468,7 +452,9 @@
                     // so we need to be conservative and assume it isn't.
                     Slog.w(TAG, "Activity destroy timeout for " + r);
                     synchronized (mService.mGlobalLock) {
-                        activityDestroyedLocked(r != null ? r.appToken : null, "destroyTimeout");
+                        if (r != null) {
+                            r.destroyed("destroyTimeout");
+                        }
                     }
                 } break;
                 case STOP_TIMEOUT_MSG: {
@@ -1215,12 +1201,17 @@
         return display != null && display.isSingleTaskInstance();
     }
 
-    final void removeActivitiesFromLRUListLocked(TaskRecord task) {
+    private void removeActivitiesFromLRUList(TaskRecord task) {
         for (ActivityRecord r : task.mActivities) {
             mLRUActivities.remove(r);
         }
     }
 
+    /** @return {@code true} if LRU list contained the specified activity. */
+    final boolean removeActivityFromLRUList(ActivityRecord activity) {
+        return mLRUActivities.remove(activity);
+    }
+
     final boolean updateLRUListLocked(ActivityRecord r) {
         final boolean hadit = mLRUActivities.remove(r);
         mLRUActivities.add(r);
@@ -1617,18 +1608,6 @@
     }
 
     /**
-     * Schedule a pause timeout in case the app doesn't respond. We don't give it much time because
-     * this directly impacts the responsiveness seen by the user.
-     */
-    private void schedulePauseTimeout(ActivityRecord r) {
-        final Message msg = mHandler.obtainMessage(PAUSE_TIMEOUT_MSG);
-        msg.obj = r;
-        r.pauseTime = SystemClock.uptimeMillis();
-        mHandler.sendMessageDelayed(msg, PAUSE_TIMEOUT);
-        if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Waiting for pause to complete...");
-    }
-
-    /**
      * Start pausing the currently resumed activity.  It is an error to call this if there
      * is already an activity being paused or there is no resumed activity.
      *
@@ -1728,7 +1707,7 @@
                 return false;
 
             } else {
-                schedulePauseTimeout(prev);
+                schedulePauseTimeoutForActivity(prev);
                 return true;
             }
 
@@ -1771,8 +1750,7 @@
                     if (r.finishing) {
                         if (DEBUG_PAUSE) Slog.v(TAG,
                                 "Executing finish of failed to pause activity: " + r);
-                        r.finishCurrentActivityLocked(FINISH_AFTER_VISIBLE, false,
-                                "activityPausedLocked");
+                        r.completeFinishing("activityPausedLocked");
                     }
                 }
             }
@@ -1790,8 +1768,7 @@
             prev.setState(PAUSED, "completePausedLocked");
             if (prev.finishing) {
                 if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Executing finish of activity: " + prev);
-                prev = prev.finishCurrentActivityLocked(FINISH_AFTER_VISIBLE, false /* oomAdj */,
-                        "completePausedLocked");
+                prev = prev.completeFinishing("completePausedLocked");
             } else if (prev.hasProcess()) {
                 if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Enqueue pending stop if needed: " + prev
                         + " wasStopping=" + wasStopping + " visible=" + prev.visible);
@@ -1810,7 +1787,7 @@
                     prev.setDeferHidingClient(false);
                     // If we were visible then resumeTopActivities will release resources before
                     // stopping.
-                    addToStopping(prev, true /* scheduleIdle */, false /* idleDelayed */,
+                    prev.addToStopping(true /* scheduleIdle */, false /* idleDelayed */,
                             "completePauseLocked");
                 }
             } else {
@@ -1872,32 +1849,6 @@
         mRootActivityContainer.ensureActivitiesVisible(resuming, 0, !PRESERVE_WINDOWS);
     }
 
-    void addToStopping(ActivityRecord r, boolean scheduleIdle, boolean idleDelayed, String reason) {
-        if (!mStackSupervisor.mStoppingActivities.contains(r)) {
-            EventLog.writeEvent(EventLogTags.AM_ADD_TO_STOPPING, r.mUserId,
-                    System.identityHashCode(r), r.shortComponentName, reason);
-            mStackSupervisor.mStoppingActivities.add(r);
-        }
-
-        // If we already have a few activities waiting to stop, then give up
-        // on things going idle and start clearing them out. Or if r is the
-        // last of activity of the last task the stack will be empty and must
-        // be cleared immediately.
-        boolean forceIdle = mStackSupervisor.mStoppingActivities.size() > MAX_STOPPING_TO_FORCE
-                || (r.isRootOfTask() && mTaskHistory.size() <= 1);
-        if (scheduleIdle || forceIdle) {
-            if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Scheduling idle now: forceIdle="
-                    + forceIdle + "immediate=" + !idleDelayed);
-            if (!idleDelayed) {
-                mStackSupervisor.scheduleIdleLocked();
-            } else {
-                mStackSupervisor.scheduleIdleTimeoutLocked(r);
-            }
-        } else {
-            checkReadyForSleep();
-        }
-    }
-
     /**
      * Returns true if the stack is translucent and can have other contents visible behind it if
      * needed. A stack is considered translucent if it don't contain a visible or
@@ -2207,7 +2158,7 @@
                                 + " stackShouldBeVisible=" + stackShouldBeVisible
                                 + " behindFullscreenActivity=" + behindFullscreenActivity
                                 + " mLaunchTaskBehind=" + r.mLaunchTaskBehind);
-                        makeInvisible(r);
+                        r.makeInvisible();
                     }
                 }
                 final int windowingMode = getWindowingMode();
@@ -2384,63 +2335,6 @@
         return false;
     }
 
-    // TODO: Should probably be moved into ActivityRecord.
-    private void makeInvisible(ActivityRecord r) {
-        if (!r.visible) {
-            if (DEBUG_VISIBILITY) Slog.v(TAG_VISIBILITY, "Already invisible: " + r);
-            return;
-        }
-        // Now for any activities that aren't visible to the user, make sure they no longer are
-        // keeping the screen frozen.
-        if (DEBUG_VISIBILITY) Slog.v(TAG_VISIBILITY, "Making invisible: " + r + " " + r.getState());
-        try {
-            final boolean canEnterPictureInPicture = r.checkEnterPictureInPictureState(
-                    "makeInvisible", true /* beforeStopping */);
-            // Defer telling the client it is hidden if it can enter Pip and isn't current paused,
-            // stopped or stopping. This gives it a chance to enter Pip in onPause().
-            // TODO: There is still a question surrounding activities in multi-window mode that want
-            // to enter Pip after they are paused, but are still visible. I they should be okay to
-            // enter Pip in those cases, but not "auto-Pip" which is what this condition covers and
-            // the current contract for "auto-Pip" is that the app should enter it before onPause
-            // returns. Just need to confirm this reasoning makes sense.
-            final boolean deferHidingClient = canEnterPictureInPicture
-                    && !r.isState(STOPPING, STOPPED, PAUSED);
-            r.setDeferHidingClient(deferHidingClient);
-            r.setVisible(false);
-
-            switch (r.getState()) {
-                case STOPPING:
-                case STOPPED:
-                    if (r.attachedToProcess()) {
-                        if (DEBUG_VISIBILITY) Slog.v(TAG_VISIBILITY,
-                                "Scheduling invisibility: " + r);
-                        mService.getLifecycleManager().scheduleTransaction(r.app.getThread(),
-                                r.appToken, WindowVisibilityItem.obtain(false /* showWindow */));
-                    }
-
-                    // Reset the flag indicating that an app can enter picture-in-picture once the
-                    // activity is hidden
-                    r.supportsEnterPipOnTaskSwitch = false;
-                    break;
-
-                case INITIALIZING:
-                case RESUMED:
-                case PAUSING:
-                case PAUSED:
-                case STARTED:
-                    addToStopping(r, true /* scheduleIdle */,
-                            canEnterPictureInPicture /* idleDelayed */, "makeInvisible");
-                    break;
-
-                default:
-                    break;
-            }
-        } catch (Exception e) {
-            // Just skip on any failure; we'll make it visible when it next restarts.
-            Slog.w(TAG, "Exception thrown making hidden: " + r.intent.getComponent(), e);
-        }
-    }
-
     private boolean updateBehindFullscreen(boolean stackInvisible, boolean behindFullscreenActivity,
             ActivityRecord r) {
         if (r.fullscreen) {
@@ -2782,8 +2676,7 @@
                 !mLastNoHistoryActivity.finishing) {
             if (DEBUG_STATES) Slog.d(TAG_STATES,
                     "no-history finish of " + mLastNoHistoryActivity + " on new resume");
-            mLastNoHistoryActivity.finishActivityLocked(Activity.RESULT_CANCELED,
-                    null /* resultData */, "resume-no-history", false /* oomAdj */);
+            mLastNoHistoryActivity.finishIfPossible("resume-no-history", false /* oomAdj */);
             mLastNoHistoryActivity = null;
         }
 
@@ -3018,8 +2911,7 @@
                 // If any exception gets thrown, toss away this
                 // activity and try the next one.
                 Slog.w(TAG, "Exception thrown during resume of " + next, e);
-                next.finishActivityLocked(Activity.RESULT_CANCELED, null /* resultData */,
-                        "resume-exception", true /* oomAdj */);
+                next.finishIfPossible("resume-exception", true /* oomAdj */);
                 return true;
             }
         } else {
@@ -3322,6 +3214,7 @@
         // to activities in the same chain will depend on what the end activity of the chain needs.
         int replyChainEnd = -1;
         boolean canMoveOptions = true;
+        int numTasksCreated = 0;
 
         // We only do this for activities that are not the root of the task (since if we finish
         // the root, we may no longer have the task!).
@@ -3386,6 +3279,7 @@
                             target.info, null /* intent */, null /* voiceSession */,
                             null /* voiceInteractor */, false /* toTop */);
                     targetTask.affinityIntent = target.intent;
+                    numTasksCreated++;
                     if (DEBUG_TASKS) Slog.v(TAG_TASKS, "Start pushing activity " + target
                             + " out to new task " + targetTask);
                 }
@@ -3414,6 +3308,7 @@
                 }
 
                 positionChildWindowContainerAtBottom(targetTask);
+                mStackSupervisor.mRecentTasks.add(targetTask);
                 replyChainEnd = -1;
             } else if (forceReset || finishOnTaskLaunch || clearWhenTaskReset) {
                 // If the activity should just be removed -- either
@@ -3446,8 +3341,8 @@
                     }
                     if (DEBUG_TASKS) Slog.w(TAG_TASKS,
                             "resetTaskIntendedTask: calling finishActivity on " + p);
-                    if (p.finishActivityLocked(Activity.RESULT_CANCELED, null /* resultData */,
-                            "reset-task", false /* oomAdj */) == FINISH_RESULT_REMOVED) {
+                    if (p.finishIfPossible("reset-task", false /* oomAdj */)
+                            == FINISH_RESULT_REMOVED) {
                         end--;
                         srcPos--;
                     }
@@ -3461,6 +3356,27 @@
             }
         }
 
+        // Create target stack for the newly created tasks if necessary
+        if (numTasksCreated > 0) {
+            ActivityDisplay display = getDisplay();
+            final boolean singleTaskInstanceDisplay = display.isSingleTaskInstance();
+            if (singleTaskInstanceDisplay) {
+                display = mRootActivityContainer.getDefaultDisplay();
+            }
+
+            if (singleTaskInstanceDisplay || display.alwaysCreateStack(getWindowingMode(),
+                    getActivityType())) {
+                for (int index = numTasksCreated - 1; index >= 0; index--) {
+                    final TaskRecord targetTask = mTaskHistory.get(index);
+                    final ActivityStack targetStack = display.getOrCreateStack(getWindowingMode(),
+                            getActivityType(), false /* onTop */);
+                    targetTask.reparent(targetStack, false /* toTop */,
+                            REPARENT_LEAVE_STACK_IN_PLACE, false /* animate */,
+                            true /* deferResume */, "resetTargetTask");
+                }
+            }
+        }
+
         return topOptions;
     }
 
@@ -3531,8 +3447,7 @@
                         if (p.finishing) {
                             continue;
                         }
-                        p.finishActivityLocked(Activity.RESULT_CANCELED, null /* resultData */,
-                                "move-affinity", false /* oomAdj */);
+                        p.finishIfPossible("move-affinity", false /* oomAdj */);
                     }
                 } else {
                     if (taskInsertionPoint < 0) {
@@ -3566,8 +3481,7 @@
                         if (targetNdx > 0) {
                             final ActivityRecord p = taskActivities.get(targetNdx - 1);
                             if (p.intent.getComponent().equals(target.intent.getComponent())) {
-                                p.finishActivityLocked(Activity.RESULT_CANCELED,
-                                        null /* resultData */, "replace", false /* oomAdj */);
+                                p.finishIfPossible("replace", false /* oomAdj */);
                             }
                         }
                     }
@@ -3579,6 +3493,16 @@
         return taskInsertionPoint;
     }
 
+    /**
+     * Reset the task by reparenting the activities that have same affinity to the task or
+     * reparenting the activities that have different affinityies out of the task, while these
+     * activities allow task reparenting.
+     *
+     * @param taskTop     Top activity of the task might be reset.
+     * @param newActivity The activity that going to be started.
+     * @return The non-finishing top activity of the task after reset or the original task top
+     *         activity if all activities within the task are finishing.
+     */
     final ActivityRecord resetTaskIfNeededLocked(ActivityRecord taskTop,
             ActivityRecord newActivity) {
         final boolean forceReset =
@@ -3609,9 +3533,10 @@
 
         int taskNdx = mTaskHistory.indexOf(task);
         if (taskNdx >= 0) {
-            do {
-                taskTop = mTaskHistory.get(taskNdx--).getTopActivity();
-            } while (taskTop == null && taskNdx >= 0);
+            ActivityRecord newTop = mTaskHistory.get(taskNdx).getTopActivity();
+            if (newTop != null) {
+                taskTop = newTop;
+            }
         }
 
         if (topOptions != null) {
@@ -3627,49 +3552,6 @@
         return taskTop;
     }
 
-    void sendActivityResultLocked(int callingUid, ActivityRecord r,
-            String resultWho, int requestCode, int resultCode, Intent data) {
-
-        if (callingUid > 0) {
-            mService.mUgmInternal.grantUriPermissionFromIntent(callingUid, r.packageName,
-                    data, r.getUriPermissionsLocked(), r.mUserId);
-        }
-
-        if (DEBUG_RESULTS) Slog.v(TAG, "Send activity result to " + r
-                + " : who=" + resultWho + " req=" + requestCode
-                + " res=" + resultCode + " data=" + data);
-        if (mResumedActivity == r && r.attachedToProcess()) {
-            try {
-                ArrayList<ResultInfo> list = new ArrayList<ResultInfo>();
-                list.add(new ResultInfo(resultWho, requestCode,
-                        resultCode, data));
-                mService.getLifecycleManager().scheduleTransaction(r.app.getThread(), r.appToken,
-                        ActivityResultItem.obtain(list));
-                return;
-            } catch (Exception e) {
-                Slog.w(TAG, "Exception thrown sending result to " + r, e);
-            }
-        }
-
-        r.addResultLocked(null, resultWho, requestCode, resultCode, data);
-    }
-
-    /** Returns true if the task is one of the task finishing on-top of the top running task. */
-    private boolean isATopFinishingTask(TaskRecord task) {
-        for (int i = mTaskHistory.size() - 1; i >= 0; --i) {
-            final TaskRecord current = mTaskHistory.get(i);
-            final ActivityRecord r = current.topRunningActivityLocked();
-            if (r != null) {
-                // We got a top running activity, so there isn't a top finishing task...
-                return false;
-            }
-            if (current == task) {
-                return true;
-            }
-        }
-        return false;
-    }
-
     void adjustFocusedActivityStack(ActivityRecord r, String reason) {
         if (!mRootActivityContainer.isTopDisplayFocusedStack(this) ||
                 ((mResumedActivity != r) && (mResumedActivity != null))) {
@@ -3748,64 +3630,6 @@
         return stack;
     }
 
-    final void stopActivityLocked(ActivityRecord r) {
-        if (DEBUG_SWITCH) Slog.d(TAG_SWITCH, "Stopping: " + r);
-        if ((r.intent.getFlags()&Intent.FLAG_ACTIVITY_NO_HISTORY) != 0
-                || (r.info.flags&ActivityInfo.FLAG_NO_HISTORY) != 0) {
-            if (!r.finishing) {
-                if (!shouldSleepActivities()) {
-                    if (DEBUG_STATES) Slog.d(TAG_STATES, "no-history finish of " + r);
-                    if (r.finishActivityLocked(Activity.RESULT_CANCELED, null /* resultData */,
-                            "stop-no-history", false /* oomAdj */) != FINISH_RESULT_CANCELLED) {
-                        // {@link adjustFocusedActivityStack} must have been already called.
-                        r.resumeKeyDispatchingLocked();
-                        return;
-                    }
-                } else {
-                    if (DEBUG_STATES) Slog.d(TAG_STATES, "Not finishing noHistory " + r
-                            + " on stop because we're just sleeping");
-                }
-            }
-        }
-
-        if (r.attachedToProcess()) {
-            adjustFocusedActivityStack(r, "stopActivity");
-            r.resumeKeyDispatchingLocked();
-            try {
-                r.stopped = false;
-                if (DEBUG_STATES) Slog.v(TAG_STATES,
-                        "Moving to STOPPING: " + r + " (stop requested)");
-                r.setState(STOPPING, "stopActivityLocked");
-                if (DEBUG_VISIBILITY) Slog.v(TAG_VISIBILITY,
-                        "Stopping visible=" + r.visible + " for " + r);
-                if (!r.visible) {
-                    r.setVisible(false);
-                }
-                EventLogTags.writeAmStopActivity(
-                        r.mUserId, System.identityHashCode(r), r.shortComponentName);
-                mService.getLifecycleManager().scheduleTransaction(r.app.getThread(), r.appToken,
-                        StopActivityItem.obtain(r.visible, r.configChangeFlags));
-                if (shouldSleepOrShutDownActivities()) {
-                    r.setSleeping(true);
-                }
-                Message msg = mHandler.obtainMessage(STOP_TIMEOUT_MSG, r);
-                mHandler.sendMessageDelayed(msg, STOP_TIMEOUT);
-            } catch (Exception e) {
-                // Maybe just ignore exceptions here...  if the process
-                // has crashed, our death notification will clean things
-                // up.
-                Slog.w(TAG, "Exception thrown during pause", e);
-                // Just in case, assume it to be stopped.
-                r.stopped = true;
-                if (DEBUG_STATES) Slog.v(TAG_STATES, "Stop failed; moving to STOPPED: " + r);
-                r.setState(STOPPED, "stopActivityLocked");
-                if (r.deferRelaunchUntilPaused) {
-                    destroyActivityLocked(r, true, "stop-except");
-                }
-            }
-        }
-    }
-
     /** Finish all activities that were started for result from the specified activity. */
     final void finishSubActivityLocked(ActivityRecord self, String resultWho, int requestCode) {
         for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
@@ -3815,8 +3639,7 @@
                 if (r.resultTo == self && r.requestCode == requestCode) {
                     if ((r.resultWho == null && resultWho == null) ||
                         (r.resultWho != null && r.resultWho.equals(resultWho))) {
-                        r.finishActivityLocked(Activity.RESULT_CANCELED, null /* resultData */,
-                                "request-sub", false /* oomAdj */);
+                        r.finishIfPossible("request-sub", false /* oomAdj */);
                     }
                 }
             }
@@ -3846,8 +3669,7 @@
         int activityNdx = task.mActivities.indexOf(r);
         getDisplay().mDisplayContent.prepareAppTransition(
                 TRANSIT_CRASHING_ACTIVITY_CLOSE, false /* alwaysKeepCurrent */);
-        r.finishActivityLocked(Activity.RESULT_CANCELED, null /* resultData */, reason,
-                false /* oomAdj */);
+        r.finishIfPossible(reason, false /* oomAdj */);
         finishedTask = task;
         // Also terminate any activities below it that aren't yet
         // stopped, to avoid a situation where one will get
@@ -3868,8 +3690,7 @@
                 if (!r.isActivityTypeHome() || mService.mHomeProcess != r.app) {
                     Slog.w(TAG, "  Force finishing activity "
                             + r.intent.getComponent().flattenToShortString());
-                    r.finishActivityLocked(Activity.RESULT_CANCELED, null /* resultData */, reason,
-                            false /* oomAdj */);
+                    r.finishIfPossible(reason, false /* oomAdj */);
                 }
             }
         }
@@ -3885,8 +3706,7 @@
                 for (int activityNdx = tr.mActivities.size() - 1; activityNdx >= 0; --activityNdx) {
                     ActivityRecord r = tr.mActivities.get(activityNdx);
                     if (!r.finishing) {
-                        r.finishActivityLocked(Activity.RESULT_CANCELED, null /* resultData */,
-                                "finish-voice", false /* oomAdj */);
+                        r.finishIfPossible("finish-voice", false /* oomAdj */);
                         didOne = true;
                     }
                 }
@@ -3924,8 +3744,7 @@
                 final ActivityRecord r = activities.get(activityNdx);
                 noActivitiesInStack = false;
                 Slog.d(TAG, "finishAllActivitiesImmediatelyLocked: finishing " + r);
-                r.finishCurrentActivityLocked(FINISH_IMMEDIATELY, false /* oomAdj */,
-                        "finishAllActivitiesImmediatelyLocked");
+                r.destroyIfPossible("finishAllActivitiesImmediatelyLocked");
             }
         }
         if (noActivitiesInStack) {
@@ -4029,7 +3848,8 @@
         final long origId = Binder.clearCallingIdentity();
         for (int i = start; i > finishTo; i--) {
             final ActivityRecord r = activities.get(i);
-            r.finishActivityLocked(resultCode, resultData, "navigate-up", true /* oomAdj */);
+            r.finishIfPossible(resultCode, resultData, "navigate-up", true /* oomAdj */,
+                    !PAUSE_IMMEDIATELY);
             // Only return the supplied result for the first activity finished
             resultCode = Activity.RESULT_CANCELED;
             resultData = null;
@@ -4066,8 +3886,8 @@
                 } catch (RemoteException e) {
                     foundParentInTask = false;
                 }
-                parent.finishActivityLocked(resultCode, resultData, "navigate-top",
-                        true /* oomAdj */);
+                parent.finishIfPossible(resultCode, resultData, "navigate-top",
+                        true /* oomAdj */, !PAUSE_IMMEDIATELY);
             }
         }
         Binder.restoreCallingIdentity(origId);
@@ -4079,7 +3899,7 @@
      * an activity moves away from the stack.
      */
     void onActivityRemovedFromStack(ActivityRecord r) {
-        removeTimeoutsForActivityLocked(r);
+        removeTimeoutsForActivity(r);
 
         if (mResumedActivity != null && mResumedActivity == r) {
             setResumedActivity(null, "onActivityRemovedFromStack");
@@ -4095,132 +3915,64 @@
         }
     }
 
-    /**
-     * Perform the common clean-up of an activity record.  This is called both
-     * as part of destroyActivityLocked() (when destroying the client-side
-     * representation) and cleaning things up as a result of its hosting
-     * processing going away, in which case there is no remaining client-side
-     * state to destroy so only the cleanup here is needed.
-     *
-     * Note: Call before #removeActivityFromHistoryLocked.
-     */
-    private void cleanUpActivityLocked(ActivityRecord r, boolean cleanServices, boolean setState) {
-        onActivityRemovedFromStack(r);
-
-        r.deferRelaunchUntilPaused = false;
-        r.frozenBeforeDestroy = false;
-
-        if (setState) {
-            if (DEBUG_STATES) Slog.v(TAG_STATES, "Moving to DESTROYED: " + r + " (cleaning up)");
-            r.setState(DESTROYED, "cleanupActivityLocked");
-            if (DEBUG_APP) Slog.v(TAG_APP, "Clearing app during cleanUp for activity " + r);
-            r.app = null;
-        }
-
-        // Inform supervisor the activity has been removed.
-        mStackSupervisor.cleanupActivity(r);
-
-
-        // Remove any pending results.
-        if (r.finishing && r.pendingResults != null) {
-            for (WeakReference<PendingIntentRecord> apr : r.pendingResults) {
-                PendingIntentRecord rec = apr.get();
-                if (rec != null) {
-                    mService.mPendingIntentController.cancelIntentSender(rec, false);
-                }
-            }
-            r.pendingResults = null;
-        }
-
-        if (cleanServices) {
-            cleanUpActivityServicesLocked(r);
-        }
-
-        // Get rid of any pending idle timeouts.
-        removeTimeoutsForActivityLocked(r);
-        // Clean-up activities are no longer relaunching (e.g. app process died). Notify window
-        // manager so it can update its bookkeeping.
-        mWindowManager.notifyAppRelaunchesCleared(r.appToken);
-    }
-
-    private void removeTimeoutsForActivityLocked(ActivityRecord r) {
+    /// HANDLER INTERFACE BEGIN
+    void removeTimeoutsForActivity(ActivityRecord r) {
         mStackSupervisor.removeTimeoutsForActivityLocked(r);
-        mHandler.removeMessages(PAUSE_TIMEOUT_MSG, r);
-        mHandler.removeMessages(STOP_TIMEOUT_MSG, r);
-        mHandler.removeMessages(DESTROY_TIMEOUT_MSG, r);
+        removePauseTimeoutForActivity(r);
+        removeStopTimeoutForActivity(r);
+        removeDestroyTimeoutForActivity(r);
         r.finishLaunchTickingLocked();
     }
 
-    private void removeActivityFromHistoryLocked(ActivityRecord r, String reason) {
-        r.finishActivityResults(Activity.RESULT_CANCELED, null /* resultData */);
-        r.makeFinishingLocked();
-        if (DEBUG_ADD_REMOVE) Slog.i(TAG_ADD_REMOVE,
-                "Removing activity " + r + " from stack callers=" + Debug.getCallers(5));
-
-        r.takeFromHistory();
-        removeTimeoutsForActivityLocked(r);
-        if (DEBUG_STATES) Slog.v(TAG_STATES,
-                "Moving to DESTROYED: " + r + " (removed from history)");
-        r.setState(DESTROYED, "removeActivityFromHistoryLocked");
-        if (DEBUG_APP) Slog.v(TAG_APP, "Clearing app during remove for activity " + r);
-        r.app = null;
-        r.removeWindowContainer();
-        final TaskRecord task = r.getTaskRecord();
-        final boolean lastActivity = task != null ? task.removeActivity(r) : false;
-        // If we are removing the last activity in the task, not including task overlay activities,
-        // then fall through into the block below to remove the entire task itself
-        final boolean onlyHasTaskOverlays = task != null
-                ? task.onlyHasTaskOverlayActivities(false /* excludingFinishing */) : false;
-
-        if (lastActivity || onlyHasTaskOverlays) {
-            if (DEBUG_STACK) {
-                Slog.i(TAG_STACK,
-                        "removeActivityFromHistoryLocked: last activity removed from " + this
-                                + " onlyHasTaskOverlays=" + onlyHasTaskOverlays);
-            }
-
-            // The following block can be executed multiple times if there is more than one overlay.
-            // {@link ActivityStackSupervisor#removeTaskByIdLocked} handles this by reverse lookup
-            // of the task by id and exiting early if not found.
-            if (onlyHasTaskOverlays) {
-                // When destroying a task, tell the supervisor to remove it so that any activity it
-                // has can be cleaned up correctly. This is currently the only place where we remove
-                // a task with the DESTROYING mode, so instead of passing the onlyHasTaskOverlays
-                // state into removeTask(), we just clear the task here before the other residual
-                // work.
-                // TODO: If the callers to removeTask() changes such that we have multiple places
-                //       where we are destroying the task, move this back into removeTask()
-                mStackSupervisor.removeTaskByIdLocked(task.taskId, false /* killProcess */,
-                        !REMOVE_FROM_RECENTS, PAUSE_IMMEDIATELY, reason);
-            }
-
-            // We must keep the task around until all activities are destroyed. The following
-            // statement will only execute once since overlays are also considered activities.
-            if (lastActivity) {
-                removeTask(task, reason, REMOVE_TASK_MODE_DESTROYING);
-            }
-        }
-        cleanUpActivityServicesLocked(r);
-        r.removeUriPermissionsLocked();
-    }
-
-    /**
-     * Perform clean-up of service connections in an activity record.
-     */
-    private void cleanUpActivityServicesLocked(ActivityRecord r) {
-        if (r.mServiceConnectionsHolder == null) {
-            return;
-        }
-        // Throw away any services that have been bound by this activity.
-        r.mServiceConnectionsHolder.disconnectActivityFromServices();
-    }
-
-    final void scheduleDestroyActivities(WindowProcessController owner, String reason) {
-        Message msg = mHandler.obtainMessage(DESTROY_ACTIVITIES_MSG);
+    void scheduleDestroyActivities(WindowProcessController owner, String reason) {
+        final Message msg = mHandler.obtainMessage(DESTROY_ACTIVITIES_MSG);
         msg.obj = new ScheduleDestroyArgs(owner, reason);
         mHandler.sendMessage(msg);
     }
 
+    void scheduleDestroyTimeoutForActivity(ActivityRecord r) {
+        final Message msg = mHandler.obtainMessage(DESTROY_TIMEOUT_MSG, r);
+        mHandler.sendMessageDelayed(msg, DESTROY_TIMEOUT);
+    }
+
+    void removeDestroyTimeoutForActivity(ActivityRecord r) {
+        mHandler.removeMessages(DESTROY_TIMEOUT_MSG, r);
+    }
+
+    void scheduleStopTimeoutForActivity(ActivityRecord r) {
+        final Message msg = mHandler.obtainMessage(STOP_TIMEOUT_MSG, r);
+        mHandler.sendMessageDelayed(msg, STOP_TIMEOUT);
+    }
+
+    void removeStopTimeoutForActivity(ActivityRecord r) {
+        mHandler.removeMessages(STOP_TIMEOUT_MSG, r);
+    }
+
+    /**
+     * Schedule a pause timeout in case the app doesn't respond. We don't give it much time because
+     * this directly impacts the responsiveness seen by the user.
+     */
+    private void schedulePauseTimeoutForActivity(ActivityRecord r) {
+        final Message msg = mHandler.obtainMessage(PAUSE_TIMEOUT_MSG, r);
+        r.pauseTime = SystemClock.uptimeMillis();
+        mHandler.sendMessageDelayed(msg, PAUSE_TIMEOUT);
+        if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Waiting for pause to complete...");
+    }
+
+    void removePauseTimeoutForActivity(ActivityRecord r) {
+        mHandler.removeMessages(PAUSE_TIMEOUT_MSG, r);
+    }
+
+    void scheduleLaunchTickForActivity(ActivityRecord r) {
+        final Message msg = mHandler.obtainMessage(LAUNCH_TICK_MSG, r);
+        mHandler.sendMessageDelayed(msg, LAUNCH_TICK);
+    }
+
+    void removeLaunchTickMessages() {
+        mHandler.removeMessages(LAUNCH_TICK_MSG);
+    }
+    /// HANDLER INTERFACE END
+
     private void destroyActivitiesLocked(WindowProcessController owner, String reason) {
         boolean lastIsOpaque = false;
         boolean activityRemoved = false;
@@ -4245,7 +3997,7 @@
                             + " in state " + r.getState()
                             + " resumed=" + mResumedActivity
                             + " pausing=" + mPausingActivity + " for reason " + reason);
-                    if (destroyActivityLocked(r, true, reason)) {
+                    if (r.destroyImmediately(true /* removeFromTask */, reason)) {
                         activityRemoved = true;
                     }
                 }
@@ -4256,16 +4008,6 @@
         }
     }
 
-    final boolean safelyDestroyActivityLocked(ActivityRecord r, String reason) {
-        if (r.isDestroyable()) {
-            if (DEBUG_SWITCH) Slog.v(TAG_SWITCH,
-                    "Destroying " + r + " in state " + r.getState() + " resumed=" + mResumedActivity
-                    + " pausing=" + mPausingActivity + " for reason " + reason);
-            return destroyActivityLocked(r, true, reason);
-        }
-        return false;
-    }
-
     final int releaseSomeActivitiesLocked(WindowProcessController app, ArraySet<TaskRecord> tasks,
             String reason) {
         // Iterate over tasks starting at the back (oldest) first.
@@ -4289,7 +4031,7 @@
                     if (DEBUG_RELEASE) Slog.v(TAG_RELEASE, "Destroying " + activity
                             + " in state " + activity.getState() + " resumed=" + mResumedActivity
                             + " pausing=" + mPausingActivity + " for reason " + reason);
-                    destroyActivityLocked(activity, true, reason);
+                    activity.destroyImmediately(true /* removeFromApp */, reason);
                     if (activities.get(actNdx) != activity) {
                         // Was removed from list, back up so we don't miss the next one.
                         actNdx--;
@@ -4311,142 +4053,6 @@
         return numReleased;
     }
 
-    /**
-     * Destroy the current CLIENT SIDE instance of an activity.  This may be
-     * called both when actually finishing an activity, or when performing
-     * a configuration switch where we destroy the current client-side object
-     * but then create a new client-side object for this same HistoryRecord.
-     */
-    final boolean destroyActivityLocked(ActivityRecord r, boolean removeFromApp, String reason) {
-        if (DEBUG_SWITCH || DEBUG_CLEANUP) Slog.v(TAG_SWITCH,
-                "Removing activity from " + reason + ": token=" + r
-                        + ", app=" + (r.hasProcess() ? r.app.mName : "(null)"));
-
-        if (r.isState(DESTROYING, DESTROYED)) {
-            if (DEBUG_STATES) Slog.v(TAG_STATES, "activity " + r + " already destroying."
-                    + "skipping request with reason:" + reason);
-            return false;
-        }
-
-        EventLog.writeEvent(EventLogTags.AM_DESTROY_ACTIVITY,
-                r.mUserId, System.identityHashCode(r),
-                r.getTaskRecord().taskId, r.shortComponentName, reason);
-
-        boolean removedFromHistory = false;
-
-        cleanUpActivityLocked(r, false, false);
-
-        final boolean hadApp = r.hasProcess();
-
-        if (hadApp) {
-            if (removeFromApp) {
-                r.app.removeActivity(r);
-                if (!r.app.hasActivities()) {
-                    mService.clearHeavyWeightProcessIfEquals(r.app);
-                }
-                if (!r.app.hasActivities()) {
-                    // Update any services we are bound to that might care about whether
-                    // their client may have activities.
-                    // No longer have activities, so update LRU list and oom adj.
-                    r.app.updateProcessInfo(true /* updateServiceConnectionActivities */,
-                            false /* activityChange */, true /* updateOomAdj */);
-                }
-            }
-
-            boolean skipDestroy = false;
-
-            try {
-                if (DEBUG_SWITCH) Slog.i(TAG_SWITCH, "Destroying: " + r);
-                mService.getLifecycleManager().scheduleTransaction(r.app.getThread(), r.appToken,
-                        DestroyActivityItem.obtain(r.finishing, r.configChangeFlags));
-            } catch (Exception e) {
-                // We can just ignore exceptions here...  if the process
-                // has crashed, our death notification will clean things
-                // up.
-                //Slog.w(TAG, "Exception thrown during finish", e);
-                if (r.finishing) {
-                    removeActivityFromHistoryLocked(r, reason + " exceptionInScheduleDestroy");
-                    removedFromHistory = true;
-                    skipDestroy = true;
-                }
-            }
-
-            r.nowVisible = false;
-
-            // If the activity is finishing, we need to wait on removing it
-            // from the list to give it a chance to do its cleanup.  During
-            // that time it may make calls back with its token so we need to
-            // be able to find it on the list and so we don't want to remove
-            // it from the list yet.  Otherwise, we can just immediately put
-            // it in the destroyed state since we are not removing it from the
-            // list.
-            if (r.finishing && !skipDestroy) {
-                if (DEBUG_STATES) Slog.v(TAG_STATES, "Moving to DESTROYING: " + r
-                        + " (destroy requested)");
-                r.setState(DESTROYING,
-                        "destroyActivityLocked. finishing and not skipping destroy");
-                Message msg = mHandler.obtainMessage(DESTROY_TIMEOUT_MSG, r);
-                mHandler.sendMessageDelayed(msg, DESTROY_TIMEOUT);
-            } else {
-                if (DEBUG_STATES) Slog.v(TAG_STATES,
-                        "Moving to DESTROYED: " + r + " (destroy skipped)");
-                r.setState(DESTROYED,
-                        "destroyActivityLocked. not finishing or skipping destroy");
-                if (DEBUG_APP) Slog.v(TAG_APP, "Clearing app during destroy for activity " + r);
-                r.app = null;
-            }
-        } else {
-            // remove this record from the history.
-            if (r.finishing) {
-                removeActivityFromHistoryLocked(r, reason + " hadNoApp");
-                removedFromHistory = true;
-            } else {
-                if (DEBUG_STATES) Slog.v(TAG_STATES, "Moving to DESTROYED: " + r + " (no app)");
-                r.setState(DESTROYED, "destroyActivityLocked. not finishing and had no app");
-                if (DEBUG_APP) Slog.v(TAG_APP, "Clearing app during destroy for activity " + r);
-                r.app = null;
-            }
-        }
-
-        r.configChangeFlags = 0;
-
-        if (!mLRUActivities.remove(r) && hadApp) {
-            Slog.w(TAG, "Activity " + r + " being finished, but not in LRU list");
-        }
-
-        return removedFromHistory;
-    }
-
-    final void activityDestroyedLocked(IBinder token, String reason) {
-        final long origId = Binder.clearCallingIdentity();
-        try {
-            activityDestroyedLocked(ActivityRecord.forTokenLocked(token), reason);
-        } finally {
-            Binder.restoreCallingIdentity(origId);
-        }
-    }
-
-    /**
-     * This method is to only be called from the client via binder when the activity is destroyed
-     * AND finished.
-     */
-    final void activityDestroyedLocked(ActivityRecord record, String reason) {
-        if (record != null) {
-            mHandler.removeMessages(DESTROY_TIMEOUT_MSG, record);
-        }
-
-        if (DEBUG_CONTAINERS) Slog.d(TAG_CONTAINERS, "activityDestroyedLocked: r=" + record);
-
-        if (isInStackLocked(record) != null) {
-            if (record.isState(DESTROYING, DESTROYED)) {
-                cleanUpActivityLocked(record, true, false);
-                removeActivityFromHistoryLocked(record, reason);
-            }
-        }
-
-        mRootActivityContainer.resumeFocusedStacksTopActivities();
-    }
-
     private void removeHistoryRecordsForAppLocked(ArrayList<ActivityRecord> list,
             WindowProcessController app, String listName) {
         int i = list.size();
@@ -4459,7 +4065,7 @@
             if (r.app == app) {
                 if (DEBUG_CLEANUP) Slog.v(TAG_CLEANUP, "---> REMOVING this entry!");
                 list.remove(i);
-                removeTimeoutsForActivityLocked(r);
+                removeTimeoutsForActivity(r);
             }
         }
     }
@@ -4554,9 +4160,9 @@
                         // other apps when user transfers focus to the restarted activity.
                         r.nowVisible = r.visible;
                     }
-                    cleanUpActivityLocked(r, true, true);
+                    r.cleanUp(true /* cleanServices */, true /* setState */);
                     if (remove) {
-                        removeActivityFromHistoryLocked(r, "appDied");
+                        r.removeFromHistory("appDied");
                     }
                 }
             }
@@ -4747,16 +4353,6 @@
         return true;
     }
 
-    static void logStartActivity(int tag, ActivityRecord r, TaskRecord task) {
-        final Uri data = r.intent.getData();
-        final String strData = data != null ? data.toSafeString() : null;
-
-        EventLog.writeEvent(tag,
-                r.mUserId, System.identityHashCode(r), task.taskId,
-                r.shortComponentName, r.intent.getAction(),
-                r.intent.getType(), strData, r.intent.getFlags());
-    }
-
     /**
      * Ensures all visible activities at or below the input activity have the right configuration.
      */
@@ -4890,7 +4486,7 @@
             for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) {
                 final ActivityRecord r = activities.get(activityNdx);
                 if ((r.info.flags&ActivityInfo.FLAG_FINISH_ON_CLOSE_SYSTEM_DIALOGS) != 0) {
-                    r.finishActivityLocked(Activity.RESULT_CANCELED, null, "close-sys", true);
+                    r.finishIfPossible("close-sys", true /* oomAdj */);
                 }
             }
         }
@@ -4934,7 +4530,7 @@
                     didSomething = true;
                     Slog.i(TAG, "  Force finishing activity " + r);
                     lastTask = r.getTaskRecord();
-                    r.finishActivityLocked(Activity.RESULT_CANCELED, null, "force-stop", true);
+                    r.finishIfPossible("force-stop", true);
                 }
             }
         }
@@ -4988,8 +4584,7 @@
             final ArrayList<ActivityRecord> activities = mTaskHistory.get(top).mActivities;
             int activityTop = activities.size() - 1;
             if (activityTop >= 0) {
-                activities.get(activityTop).finishActivityLocked(Activity.RESULT_CANCELED, null,
-                        "unhandled-back", true);
+                activities.get(activityTop).finishIfPossible("unhandled-back", true /* oomAdj */);
             }
         }
     }
@@ -5025,8 +4620,7 @@
                     r.app = null;
                     getDisplay().mDisplayContent.prepareAppTransition(
                             TRANSIT_CRASHING_ACTIVITY_CLOSE, false /* alwaysKeepCurrent */);
-                    r.finishCurrentActivityLocked(FINISH_IMMEDIATELY, false /* oomAdj */,
-                            "handleAppCrashedLocked");
+                    r.destroyIfPossible("handleAppCrashedLocked");
                 }
             }
         }
@@ -5167,7 +4761,7 @@
             EventLog.writeEvent(EventLogTags.AM_REMOVE_TASK, task.taskId, getStackId());
         }
 
-        removeActivitiesFromLRUListLocked(task);
+        removeActivitiesFromLRUList(task);
         updateTaskMovement(task, true);
 
         if (mode == REMOVE_TASK_MODE_DESTROYING) {
@@ -5350,7 +4944,7 @@
         // If the activity was previously pausing, then ensure we transfer that as well
         if (setPause) {
             mPausingActivity = r;
-            schedulePauseTimeout(r);
+            schedulePauseTimeoutForActivity(r);
         }
         // Move the stack in which we are placing the activity to the front.
         moveToFront(reason);
diff --git a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
index 22f72a4..a06b9ce 100644
--- a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
@@ -42,6 +42,7 @@
 import static android.content.pm.PackageManager.PERMISSION_GRANTED;
 import static android.graphics.Rect.copyOrNull;
 import static android.os.PowerManager.PARTIAL_WAKE_LOCK;
+import static android.os.Process.INVALID_UID;
 import static android.os.Process.SYSTEM_UID;
 import static android.os.Trace.TRACE_TAG_ACTIVITY_MANAGER;
 import static android.view.Display.DEFAULT_DISPLAY;
@@ -897,8 +898,7 @@
                     Slog.e(TAG, "Second failure launching "
                             + r.intent.getComponent().flattenToShortString() + ", giving up", e);
                     proc.appDied();
-                    r.finishActivityLocked(Activity.RESULT_CANCELED, null /* resultData */,
-                            "2nd-crash", false /* oomAdj */);
+                    r.finishIfPossible("2nd-crash", false /* oomAdj */);
                     return false;
                 }
 
@@ -1029,9 +1029,8 @@
         if (componentRestriction == ACTIVITY_RESTRICTION_PERMISSION
                 || actionRestriction == ACTIVITY_RESTRICTION_PERMISSION) {
             if (resultRecord != null) {
-                resultStack.sendActivityResultLocked(-1,
-                        resultRecord, resultWho, requestCode,
-                        Activity.RESULT_CANCELED, null);
+                resultRecord.sendResult(INVALID_UID, resultWho, requestCode,
+                        Activity.RESULT_CANCELED, null /* data */);
             }
             final String msg;
             if (actionRestriction == ACTIVITY_RESTRICTION_PERMISSION) {
@@ -1347,10 +1346,10 @@
             final ActivityStack stack = r.getActivityStack();
             if (stack != null) {
                 if (r.finishing) {
-                    r.finishCurrentActivityLocked(ActivityRecord.FINISH_IMMEDIATELY,
-                            false /* oomAdj */, "activityIdleInternalLocked");
+                    // TODO(b/137329632): Wait for idle of the right activity, not just any.
+                    r.destroyIfPossible("activityIdleInternalLocked");
                 } else {
-                    stack.stopActivityLocked(r);
+                    r.stopIfPossible();
                 }
             }
         }
@@ -1361,7 +1360,7 @@
             r = finishes.get(i);
             final ActivityStack stack = r.getActivityStack();
             if (stack != null) {
-                activityRemoved |= stack.destroyActivityLocked(r, true, "finish-idle");
+                activityRemoved |= r.destroyImmediately(true /* removeFromApp */, "finish-idle");
             }
         }
 
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index 9ed93c4..48bc963 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -55,6 +55,7 @@
 import static android.content.pm.ActivityInfo.LAUNCH_SINGLE_TASK;
 import static android.content.pm.ActivityInfo.LAUNCH_SINGLE_TOP;
 import static android.content.pm.PackageManager.PERMISSION_GRANTED;
+import static android.os.Process.INVALID_UID;
 import static android.view.Display.DEFAULT_DISPLAY;
 import static android.view.Display.INVALID_DISPLAY;
 
@@ -752,8 +753,8 @@
 
         if (err != START_SUCCESS) {
             if (resultRecord != null) {
-                resultStack.sendActivityResultLocked(
-                        -1, resultRecord, resultWho, requestCode, RESULT_CANCELED, null);
+                resultRecord.sendResult(INVALID_UID, resultWho, requestCode, RESULT_CANCELED,
+                        null /* data */);
             }
             SafeActivityOptions.abort(options);
             return err;
@@ -817,8 +818,8 @@
 
         if (abort) {
             if (resultRecord != null) {
-                resultStack.sendActivityResultLocked(-1, resultRecord, resultWho, requestCode,
-                        RESULT_CANCELED, null);
+                resultRecord.sendResult(INVALID_UID, resultWho, requestCode, RESULT_CANCELED,
+                        null /* data */);
             }
             // We pretend to the caller that it was really started, but
             // they will just get a cancel result.
@@ -1426,8 +1427,7 @@
                 // performing operations without a window container.
                 final ActivityStack stack = mStartActivity.getActivityStack();
                 if (stack != null) {
-                    mStartActivity.finishActivityLocked(RESULT_CANCELED,
-                            null /* intentResultData */, "startActivity", true /* oomAdj */);
+                    mStartActivity.finishIfPossible("startActivity", true /* oomAdj */);
                 }
 
                 // Stack should also be detached from display and be removed if it's empty.
@@ -1451,18 +1451,17 @@
      * TODO(b/131748165): Refactor the logic so we don't need to call this method everywhere.
      */
     private boolean handleBackgroundActivityAbort(ActivityRecord r) {
-        // TODO(b/131747138): Remove toast and refactor related code in Q release.
-        boolean abort = !mService.isBackgroundActivityStartsEnabled();
+        // TODO(b/131747138): Remove toast and refactor related code in R release.
+        final boolean abort = !mService.isBackgroundActivityStartsEnabled();
         if (!abort) {
             return false;
         }
-        ActivityRecord resultRecord = r.resultTo;
-        String resultWho = r.resultWho;
+        final ActivityRecord resultRecord = r.resultTo;
+        final String resultWho = r.resultWho;
         int requestCode = r.requestCode;
         if (resultRecord != null) {
-            ActivityStack resultStack = resultRecord.getActivityStack();
-            resultStack.sendActivityResultLocked(-1, resultRecord, resultWho, requestCode,
-                    RESULT_CANCELED, null);
+            resultRecord.sendResult(INVALID_UID, resultWho, requestCode, RESULT_CANCELED,
+                    null /* data */);
         }
         // We pretend to the caller that it was really started to make it backward compatible, but
         // they will just get a cancel result.
@@ -1627,12 +1626,9 @@
         }
 
         if (mStartActivity.packageName == null) {
-            final ActivityStack sourceStack = mStartActivity.resultTo != null
-                    ? mStartActivity.resultTo.getActivityStack() : null;
-            if (sourceStack != null) {
-                sourceStack.sendActivityResultLocked(-1 /* callingUid */, mStartActivity.resultTo,
-                        mStartActivity.resultWho, mStartActivity.requestCode, RESULT_CANCELED,
-                        null /* data */);
+            if (mStartActivity.resultTo != null) {
+                mStartActivity.resultTo.sendResult(INVALID_UID, mStartActivity.resultWho,
+                        mStartActivity.requestCode, RESULT_CANCELED, null /* data */);
             }
             ActivityOptions.abort(mOptions);
             return START_CLASS_NOT_FOUND;
@@ -1709,8 +1705,8 @@
             EventLog.writeEvent(EventLogTags.AM_CREATE_TASK, mStartActivity.mUserId,
                     mStartActivity.getTaskRecord().taskId);
         }
-        ActivityStack.logStartActivity(
-                EventLogTags.AM_CREATE_ACTIVITY, mStartActivity, mStartActivity.getTaskRecord());
+        mStartActivity.logStartActivity(
+                EventLogTags.AM_CREATE_ACTIVITY, mStartActivity.getTaskRecord());
         mTargetStack.mLastPausedActivity = null;
 
         mRootActivityContainer.sendPowerHintForLaunchStartIfNeeded(
@@ -1928,17 +1924,14 @@
     }
 
     private void sendNewTaskResultRequestIfNeeded() {
-        final ActivityStack sourceStack = mStartActivity.resultTo != null
-                ? mStartActivity.resultTo.getActivityStack() : null;
-        if (sourceStack != null && (mLaunchFlags & FLAG_ACTIVITY_NEW_TASK) != 0) {
+        if (mStartActivity.resultTo != null && (mLaunchFlags & FLAG_ACTIVITY_NEW_TASK) != 0) {
             // For whatever reason this activity is being launched into a new task...
             // yet the caller has requested a result back.  Well, that is pretty messed up,
             // so instead immediately send back a cancel and let the new task continue launched
             // as normal without a dependency on its originator.
             Slog.w(TAG, "Activity is launching as a new task, so cancelling activity result.");
-            sourceStack.sendActivityResultLocked(-1 /* callingUid */, mStartActivity.resultTo,
-                    mStartActivity.resultWho, mStartActivity.requestCode, RESULT_CANCELED,
-                    null /* data */);
+            mStartActivity.resultTo.sendResult(INVALID_UID, mStartActivity.resultWho,
+                    mStartActivity.requestCode, RESULT_CANCELED, null /* data */);
             mStartActivity.resultTo = null;
         }
     }
@@ -2363,7 +2356,7 @@
             return;
         }
 
-        ActivityStack.logStartActivity(AM_NEW_INTENT, activity, activity.getTaskRecord());
+        activity.logStartActivity(AM_NEW_INTENT, activity.getTaskRecord());
         activity.deliverNewIntentLocked(mCallingUid, mStartActivity.intent,
                 mStartActivity.launchedFromPackage);
         mIntentDelivered = true;
@@ -2430,7 +2423,7 @@
             ActivityRecord top = sourceTask.performClearTaskLocked(mStartActivity, mLaunchFlags);
             mKeepCurTransition = true;
             if (top != null) {
-                ActivityStack.logStartActivity(AM_NEW_INTENT, mStartActivity, top.getTaskRecord());
+                mStartActivity.logStartActivity(AM_NEW_INTENT, top.getTaskRecord());
                 deliverNewIntent(top);
                 // For paranoia, make sure we have correctly resumed the top activity.
                 mTargetStack.mLastPausedActivity = null;
@@ -2449,7 +2442,7 @@
                 final TaskRecord task = top.getTaskRecord();
                 task.moveActivityToFrontLocked(top);
                 top.updateOptionsLocked(mOptions);
-                ActivityStack.logStartActivity(AM_NEW_INTENT, mStartActivity, task);
+                mStartActivity.logStartActivity(AM_NEW_INTENT, task);
                 deliverNewIntent(top);
                 mTargetStack.mLastPausedActivity = null;
                 if (mDoResume) {
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index a7b6e0f..3c5947a 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -46,7 +46,6 @@
 import static android.content.pm.PackageManager.FEATURE_PICTURE_IN_PICTURE;
 import static android.content.pm.PackageManager.PERMISSION_GRANTED;
 import static android.content.res.Configuration.UI_MODE_TYPE_TELEVISION;
-import static android.os.Build.VERSION_CODES.N;
 import static android.os.FactoryTest.FACTORY_TEST_HIGH_LEVEL;
 import static android.os.FactoryTest.FACTORY_TEST_LOW_LEVEL;
 import static android.os.FactoryTest.FACTORY_TEST_OFF;
@@ -86,6 +85,7 @@
 import static com.android.server.wm.ActivityStack.REMOVE_TASK_MODE_DESTROYING;
 import static com.android.server.wm.ActivityStackSupervisor.DEFER_RESUME;
 import static com.android.server.wm.ActivityStackSupervisor.ON_TOP;
+import static com.android.server.wm.ActivityStackSupervisor.PAUSE_IMMEDIATELY;
 import static com.android.server.wm.ActivityStackSupervisor.PRESERVE_WINDOWS;
 import static com.android.server.wm.ActivityStackSupervisor.REMOVE_FROM_RECENTS;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_ALL;
@@ -1614,8 +1614,8 @@
                     // Explicitly dismissing the activity so reset its relaunch flag.
                     r.mRelaunchReason = RELAUNCH_REASON_NONE;
                 } else {
-                    r.finishActivityLocked(resultCode, resultData, "app-request",
-                            true /* oomAdj */);
+                    r.finishIfPossible(resultCode, resultData, "app-request",
+                            true /* oomAdj */, !PAUSE_IMMEDIATELY);
                     res = r.finishing;
                     if (!res) {
                         Slog.i(TAG, "Failed to finish by app-request");
@@ -1753,9 +1753,14 @@
     public final void activityDestroyed(IBinder token) {
         if (DEBUG_SWITCH) Slog.v(TAG_SWITCH, "ACTIVITY DESTROYED: " + token);
         synchronized (mGlobalLock) {
-            ActivityStack stack = ActivityRecord.getStackLocked(token);
-            if (stack != null) {
-                stack.activityDestroyedLocked(token, "activityDestroyed");
+            final long origId = Binder.clearCallingIdentity();
+            try {
+                final ActivityRecord activity = ActivityRecord.forTokenLocked(token);
+                if (activity != null) {
+                    activity.destroyed("activityDestroyed");
+                }
+            } finally {
+                Binder.restoreCallingIdentity(origId);
             }
         }
     }
@@ -3236,7 +3241,7 @@
                 if (r == null) {
                     return false;
                 }
-                return r.getActivityStack().safelyDestroyActivityLocked(r, "app-req");
+                return r.safelyDestroy("app-req");
             } finally {
                 Binder.restoreCallingIdentity(origId);
             }
@@ -6484,8 +6489,7 @@
             synchronized (mGlobalLock) {
                 final ActivityRecord r = ActivityRecord.isInStackLocked(activityToken);
                 if (r != null && r.getActivityStack() != null) {
-                    r.getActivityStack().sendActivityResultLocked(callingUid, r, resultWho,
-                            requestCode, resultCode, data);
+                    r.sendResult(callingUid, resultWho, requestCode, resultCode, data);
                 }
             }
         }
diff --git a/services/core/java/com/android/server/wm/RecentsAnimation.java b/services/core/java/com/android/server/wm/RecentsAnimation.java
index caa8363..1a8944a 100644
--- a/services/core/java/com/android/server/wm/RecentsAnimation.java
+++ b/services/core/java/com/android/server/wm/RecentsAnimation.java
@@ -149,8 +149,8 @@
             // traversal in non-stopped state (ViewRootImpl.mStopped) that would initialize more
             // things (e.g. the measure can be done earlier). The actual stop will be performed when
             // it reports idle.
-            targetStack.addToStopping(targetActivity, true /* scheduleIdle */,
-                    true /* idleDelayed */, "preloadRecents");
+            targetActivity.addToStopping(true /* scheduleIdle */, true /* idleDelayed */,
+                    "preloadRecents");
         }
     }
 
diff --git a/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java b/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java
index f51d4c4..1c015d0 100644
--- a/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java
+++ b/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java
@@ -159,11 +159,23 @@
         }
 
         // STEP 2: Resolve launch windowing mode.
-        // STEP 2.1: Determine if any parameter has specified initial bounds. That might be the
-        // launch bounds from activity options, or size/gravity passed in layout. It also treats the
-        // launch windowing mode in options as a suggestion for future resolution.
+        // STEP 2.1: Determine if any parameter can specify initial bounds/windowing mode. That
+        // might be the launch bounds from activity options, or size/gravity passed in layout. It
+        // also treats the launch windowing mode in options and source activity windowing mode in
+        // some cases as a suggestion for future resolution.
         int launchMode = options != null ? options.getLaunchWindowingMode()
                 : WINDOWING_MODE_UNDEFINED;
+        // In some cases we want to use the source's windowing mode as the default value, e.g. when
+        // source is a freeform window in a fullscreen display launching an activity on the same
+        // display.
+        if (launchMode == WINDOWING_MODE_UNDEFINED
+                && canInheritWindowingModeFromSource(display, source)) {
+            launchMode = source.getWindowingMode();
+            if (DEBUG) {
+                appendLog("inherit-from-source="
+                        + WindowConfiguration.windowingModeToString(launchMode));
+            }
+        }
         // hasInitialBounds is set if either activity options or layout has specified bounds. If
         // that's set we'll skip some adjustments later to avoid overriding the initial bounds.
         boolean hasInitialBounds = false;
@@ -342,6 +354,31 @@
                 ? displayId : DEFAULT_DISPLAY;
     }
 
+    private boolean canInheritWindowingModeFromSource(@NonNull ActivityDisplay display,
+            @Nullable ActivityRecord source) {
+        if (source == null) {
+            return false;
+        }
+
+        // There is not really any strong reason to tie the launching windowing mode and the source
+        // on freeform displays. The launching windowing mode is more tied to the content of the new
+        // activities.
+        if (display.inFreeformWindowingMode()) {
+            return false;
+        }
+
+        final int sourceWindowingMode = source.getWindowingMode();
+        if (sourceWindowingMode != WINDOWING_MODE_FULLSCREEN
+                && sourceWindowingMode != WINDOWING_MODE_FREEFORM) {
+            return false;
+        }
+
+        // Only inherit windowing mode if both source and target activities are on the same display.
+        // Otherwise we may have unintended freeform windows showing up if an activity in freeform
+        // window launches an activity on a fullscreen display by specifying display ID.
+        return display.mDisplayId == source.getDisplayId();
+    }
+
     private boolean canApplyFreeformWindowPolicy(@NonNull ActivityDisplay display, int launchMode) {
         return mSupervisor.mService.mSupportsFreeformWindowManagement
                 && (display.inFreeformWindowingMode() || launchMode == WINDOWING_MODE_FREEFORM);
diff --git a/services/core/java/com/android/server/wm/TaskRecord.java b/services/core/java/com/android/server/wm/TaskRecord.java
index 882f411..ede2f56 100644
--- a/services/core/java/com/android/server/wm/TaskRecord.java
+++ b/services/core/java/com/android/server/wm/TaskRecord.java
@@ -1419,8 +1419,9 @@
                 mActivities.remove(activityNdx);
                 --activityNdx;
                 --numActivities;
-            } else if (r.finishActivityLocked(Activity.RESULT_CANCELED, null,
-                    reason, false /* oomAdj */, pauseImmediately) == FINISH_RESULT_REMOVED) {
+            } else if (r.finishIfPossible(Activity.RESULT_CANCELED,
+                    null /* resultData */, reason, false /* oomAdj */, pauseImmediately)
+                    == FINISH_RESULT_REMOVED) {
                 --activityNdx;
                 --numActivities;
             }
@@ -1474,8 +1475,8 @@
                     if (opts != null) {
                         ret.updateOptionsLocked(opts);
                     }
-                    if (r.finishActivityLocked(Activity.RESULT_CANCELED, null /* resultData */,
-                            "clear-task-stack", false /* oomAdj */) == FINISH_RESULT_REMOVED) {
+                    if (r.finishIfPossible("clear-task-stack", false /* oomAdj */)
+                            == FINISH_RESULT_REMOVED) {
                         --activityNdx;
                         --numActivities;
                     }
@@ -1488,8 +1489,7 @@
                         && (launchFlags & Intent.FLAG_ACTIVITY_SINGLE_TOP) == 0
                         && !ActivityStarter.isDocumentLaunchesIntoExisting(launchFlags)) {
                     if (!ret.finishing) {
-                        ret.finishActivityLocked(Activity.RESULT_CANCELED, null /* resultData */,
-                                "clear-task-top", false /* oomAdj */);
+                        ret.finishIfPossible("clear-task-top", false /* oomAdj */);
                         return null;
                     }
                 }
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index d8d6841..3874fdc 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -7717,8 +7717,7 @@
         clientChannel.transferTo(outInputChannel);
         clientChannel.dispose();
 
-        IBinder token = new Binder();
-        mInputManager.registerInputChannel(inputChannel, token);
+        mInputManager.registerInputChannel(inputChannel, null /* generate new token */);
 
         // Prevent the java finalizer from breaking the input channel. But we won't
         // do any further management so we just release the java ref and let the
@@ -7726,7 +7725,7 @@
         inputChannel.release();
 
         InputWindowHandle h = new InputWindowHandle(null, null, displayId);
-        h.token = token;
+        h.token = inputChannel.getToken();
         h.name = name;
         h.layoutParamsFlags = WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
         h.layoutParamsType = 0;
diff --git a/services/core/java/com/android/server/wm/WindowProcessController.java b/services/core/java/com/android/server/wm/WindowProcessController.java
index cf8e1e8..242b116 100644
--- a/services/core/java/com/android/server/wm/WindowProcessController.java
+++ b/services/core/java/com/android/server/wm/WindowProcessController.java
@@ -41,7 +41,6 @@
 import static com.android.server.wm.ActivityTaskManagerService.RELAUNCH_REASON_NONE;
 
 import android.annotation.NonNull;
-import android.app.Activity;
 import android.app.ActivityThread;
 import android.app.IApplicationThread;
 import android.app.ProfilerInfo;
@@ -644,8 +643,7 @@
         for (int i = 0; i < activities.size(); i++) {
             final ActivityRecord r = activities.get(i);
             if (!r.finishing && r.isInStackLocked()) {
-                r.finishActivityLocked(Activity.RESULT_CANCELED, null /* resultData */,
-                        "finish-heavy", true /* oomAdj */);
+                r.finishIfPossible("finish-heavy", true /* oomAdj */);
             }
         }
     }
diff --git a/services/tests/mockingservicestests/src/com/android/server/job/controllers/TimeControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/job/controllers/TimeControllerTest.java
index 3614763..5d041b7 100644
--- a/services/tests/mockingservicestests/src/com/android/server/job/controllers/TimeControllerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/job/controllers/TimeControllerTest.java
@@ -71,6 +71,7 @@
     private static final String SOURCE_PACKAGE = "com.android.frameworks.mockingservicestests";
     private static final int SOURCE_USER_ID = 0;
 
+    private TimeController.TcConstants mConstants;
     private TimeController mTimeController;
 
     private MockitoSession mMockingSession;
@@ -110,6 +111,7 @@
 
         // Initialize real objects.
         mTimeController = new TimeController(mJobSchedulerService);
+        mConstants = mTimeController.getTcConstants();
         spyOn(mTimeController);
     }
 
@@ -528,6 +530,46 @@
     }
 
     @Test
+    public void testJobDelayWakeupAlarmToggling() {
+        final long now = JobSchedulerService.sElapsedRealtimeClock.millis();
+
+        JobStatus job = createJobStatus(
+                "testMaybeStartTrackingJobLocked_DeadlineReverseOrder",
+                createJob().setMinimumLatency(HOUR_IN_MILLIS));
+
+        doReturn(true).when(mTimeController)
+                .wouldBeReadyWithConstraintLocked(eq(job), anyInt());
+
+        // Starting off with using a wakeup alarm.
+        mConstants.USE_NON_WAKEUP_ALARM_FOR_DELAY = false;
+        InOrder inOrder = inOrder(mAlarmManager);
+
+        mTimeController.maybeStartTrackingJobLocked(job, null);
+        inOrder.verify(mAlarmManager, times(1))
+                .set(eq(AlarmManager.ELAPSED_REALTIME_WAKEUP), eq(now + HOUR_IN_MILLIS), anyLong(),
+                        anyLong(),
+                        eq(TAG_DELAY), any(), any(), any());
+
+        // Use a non wakeup alarm.
+        mConstants.USE_NON_WAKEUP_ALARM_FOR_DELAY = true;
+
+        mTimeController.maybeStartTrackingJobLocked(job, null);
+        inOrder.verify(mAlarmManager, times(1))
+                .set(eq(AlarmManager.ELAPSED_REALTIME), eq(now + HOUR_IN_MILLIS), anyLong(),
+                        anyLong(), eq(TAG_DELAY),
+                        any(), any(), any());
+
+        // Back off, use a wakeup alarm.
+        mConstants.USE_NON_WAKEUP_ALARM_FOR_DELAY = false;
+
+        mTimeController.maybeStartTrackingJobLocked(job, null);
+        inOrder.verify(mAlarmManager, times(1))
+                .set(eq(AlarmManager.ELAPSED_REALTIME_WAKEUP), eq(now + HOUR_IN_MILLIS), anyLong(),
+                        anyLong(),
+                        eq(TAG_DELAY), any(), any(), any());
+    }
+
+    @Test
     public void testCheckExpiredDeadlinesAndResetAlarm_AllReady() {
         doReturn(true).when(mTimeController).wouldBeReadyWithConstraintLocked(any(), anyInt());
 
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AbstractAccessibilityServiceConnectionTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AbstractAccessibilityServiceConnectionTest.java
index 1084d62..939aafa 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/AbstractAccessibilityServiceConnectionTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AbstractAccessibilityServiceConnectionTest.java
@@ -752,7 +752,7 @@
         }
 
         @Override
-        protected boolean isCalledForCurrentUserLocked() {
+        protected boolean hasRightsToCurrentUserLocked() {
             return mResolvedUserId == mSystemSupport.getCurrentUserIdLocked();
         }
 
diff --git a/services/tests/servicestests/src/com/android/server/appop/AppOpsActiveWatcherTest.java b/services/tests/servicestests/src/com/android/server/appop/AppOpsActiveWatcherTest.java
index 65c5781..41142f6 100644
--- a/services/tests/servicestests/src/com/android/server/appop/AppOpsActiveWatcherTest.java
+++ b/services/tests/servicestests/src/com/android/server/appop/AppOpsActiveWatcherTest.java
@@ -57,15 +57,15 @@
 
         // Start watching active ops
         final AppOpsManager appOpsManager = getContext().getSystemService(AppOpsManager.class);
-        appOpsManager.startWatchingActive(new int[] {AppOpsManager.OP_CAMERA,
-                AppOpsManager.OP_RECORD_AUDIO}, listener);
+        appOpsManager.startWatchingActive(new String[] {AppOpsManager.OPSTR_CAMERA,
+                AppOpsManager.OPSTR_RECORD_AUDIO}, getContext().getMainExecutor(), listener);
 
         // Start the op
         appOpsManager.startOp(AppOpsManager.OP_CAMERA);
 
         // Verify that we got called for the op being active
         verify(listener, timeout(NOTIFICATION_TIMEOUT_MILLIS)
-                .times(1)).onOpActiveChanged(eq(AppOpsManager.OP_CAMERA),
+                .times(1)).onOpActiveChanged(eq(AppOpsManager.OPSTR_CAMERA),
                 eq(Process.myUid()), eq(getContext().getPackageName()), eq(true));
 
         // This should be the only callback we got
@@ -83,7 +83,7 @@
 
         // Verify that we got called for the op being active
         verify(listener, timeout(NOTIFICATION_TIMEOUT_MILLIS)
-                .times(1)).onOpActiveChanged(eq(AppOpsManager.OP_CAMERA),
+                .times(1)).onOpActiveChanged(eq(AppOpsManager.OPSTR_CAMERA),
                 eq(Process.myUid()), eq(getContext().getPackageName()), eq(false));
 
         // Verify that the op is not active
@@ -155,4 +155,4 @@
     private static Context getContext() {
         return InstrumentationRegistry.getContext();
     }
-}
\ No newline at end of file
+}
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 57caa1d..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;
@@ -4040,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/PreferencesHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
index 8f8b746..365cd80 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
@@ -22,6 +22,8 @@
 import static android.app.NotificationManager.IMPORTANCE_NONE;
 import static android.app.NotificationManager.IMPORTANCE_UNSPECIFIED;
 
+import static com.android.server.notification.PreferencesHelper.NOTIFICATION_CHANNEL_COUNT_LIMIT;
+
 import static junit.framework.Assert.assertNull;
 import static junit.framework.Assert.fail;
 
@@ -2690,4 +2692,51 @@
         assertFalse(mHelper.areBubblesAllowed(PKG_O, UID_O));
         verify(mHandler, times(1)).requestSort();
     }
+
+    @Test
+    public void testTooManyChannels() {
+        for (int i = 0; i < NOTIFICATION_CHANNEL_COUNT_LIMIT; i++) {
+            NotificationChannel channel = new NotificationChannel(String.valueOf(i),
+                    String.valueOf(i), NotificationManager.IMPORTANCE_HIGH);
+            mHelper.createNotificationChannel(PKG_O, UID_O, channel, true, true);
+        }
+        try {
+            NotificationChannel channel = new NotificationChannel(
+                    String.valueOf(NOTIFICATION_CHANNEL_COUNT_LIMIT),
+                    String.valueOf(NOTIFICATION_CHANNEL_COUNT_LIMIT),
+                    NotificationManager.IMPORTANCE_HIGH);
+            mHelper.createNotificationChannel(PKG_O, UID_O, channel, true, true);
+            fail("Allowed to create too many notification channels");
+        } catch (IllegalStateException e) {
+            // great
+        }
+    }
+
+    @Test
+    public void testTooManyChannels_xml() throws Exception {
+        String extraChannel = "EXTRA";
+        String extraChannel1 = "EXTRA1";
+
+        // create first... many... directly so we don't need a big xml blob in this test
+        for (int i = 0; i < NOTIFICATION_CHANNEL_COUNT_LIMIT; i++) {
+            NotificationChannel channel = new NotificationChannel(String.valueOf(i),
+                    String.valueOf(i), NotificationManager.IMPORTANCE_HIGH);
+            mHelper.createNotificationChannel(PKG_O, UID_O, channel, true, true);
+        }
+
+        final String xml = "<ranking version=\"1\">\n"
+                + "<package name=\"" + PKG_O + "\" uid=\"" + UID_O + "\" >\n"
+                + "<channel id=\"" + extraChannel + "\" name=\"hi\" importance=\"3\"/>"
+                + "<channel id=\"" + extraChannel1 + "\" name=\"hi\" importance=\"3\"/>"
+                + "</package>"
+                + "</ranking>";
+        XmlPullParser parser = Xml.newPullParser();
+        parser.setInput(new BufferedInputStream(new ByteArrayInputStream(xml.getBytes())),
+                null);
+        parser.nextTag();
+        mHelper.readXml(parser, false, UserHandle.USER_ALL);
+
+        assertNull(mHelper.getNotificationChannel(PKG_O, UID_O, extraChannel, true));
+        assertNull(mHelper.getNotificationChannel(PKG_O, UID_O, extraChannel1, true));
+    }
 }
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..13748cb 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,17 @@
 import static com.android.server.wm.ActivityRecord.FINISH_RESULT_CANCELLED;
 import static com.android.server.wm.ActivityRecord.FINISH_RESULT_REMOVED;
 import static com.android.server.wm.ActivityRecord.FINISH_RESULT_REQUESTED;
+import static com.android.server.wm.ActivityStack.ActivityState.DESTROYED;
+import static com.android.server.wm.ActivityStack.ActivityState.DESTROYING;
+import static com.android.server.wm.ActivityStack.ActivityState.FINISHING;
 import static com.android.server.wm.ActivityStack.ActivityState.INITIALIZING;
+import static com.android.server.wm.ActivityStack.ActivityState.PAUSED;
 import static com.android.server.wm.ActivityStack.ActivityState.PAUSING;
 import static com.android.server.wm.ActivityStack.ActivityState.RESUMED;
 import static com.android.server.wm.ActivityStack.ActivityState.STARTED;
 import static com.android.server.wm.ActivityStack.ActivityState.STOPPED;
 import static com.android.server.wm.ActivityStack.ActivityState.STOPPING;
+import static com.android.server.wm.ActivityStack.REMOVE_TASK_MODE_DESTROYING;
 import static com.android.server.wm.ActivityStack.REMOVE_TASK_MODE_MOVING;
 import static com.android.server.wm.ActivityStack.STACK_VISIBILITY_INVISIBLE;
 import static com.android.server.wm.ActivityStack.STACK_VISIBILITY_VISIBLE;
@@ -61,6 +68,7 @@
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.never;
 
 import android.app.ActivityManager;
 import android.app.ActivityManagerInternal;
@@ -87,6 +95,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 +553,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 +599,7 @@
     public void testSizeCompatMode_FixedScreenLayoutSizeBits() {
         final int fixedScreenLayout = Configuration.SCREENLAYOUT_LONG_NO
                 | Configuration.SCREENLAYOUT_SIZE_NORMAL;
-        mTask.getConfiguration().screenLayout = fixedScreenLayout
+        mTask.getRequestedOverrideConfiguration().screenLayout = fixedScreenLayout
                 | Configuration.SCREENLAYOUT_LAYOUTDIR_LTR;
         prepareFixedAspectRatioUnresizableActivity();
 
@@ -726,12 +739,11 @@
      * incorrect state.
      */
     @Test
-    public void testFinishActivityLocked_cancelled() {
+    public void testFinishActivityIfPossible_cancelled() {
         // Mark activity as finishing
         mActivity.finishing = true;
         assertEquals("Duplicate finish request must be ignored", FINISH_RESULT_CANCELLED,
-                mActivity.finishActivityLocked(0 /* resultCode */, null /* resultData */, "test",
-                        false /* oomAdj */));
+                mActivity.finishIfPossible("test", false /* oomAdj */));
         assertTrue(mActivity.finishing);
         assertTrue(mActivity.isInStackLocked());
 
@@ -739,21 +751,19 @@
         mActivity.finishing = false;
         mActivity.setTask(null);
         assertEquals("Activity outside of task/stack cannot be finished", FINISH_RESULT_CANCELLED,
-                mActivity.finishActivityLocked(0 /* resultCode */, null /* resultData */, "test",
-                        false /* oomAdj */));
+                mActivity.finishIfPossible("test", false /* oomAdj */));
         assertFalse(mActivity.finishing);
     }
 
     /**
-     * Verify that activity finish request is requested, but not executed immediately if activity is
+     * Verify that activity finish request is placed, but not executed immediately if activity is
      * not ready yet.
      */
     @Test
-    public void testFinishActivityLocked_requested() {
+    public void testFinishActivityIfPossible_requested() {
         mActivity.finishing = false;
-        assertEquals("Currently resumed activity be paused removal", FINISH_RESULT_REQUESTED,
-                mActivity.finishActivityLocked(0 /* resultCode */, null /* resultData */, "test",
-                        false /* oomAdj */));
+        assertEquals("Currently resumed activity must be prepared removal", FINISH_RESULT_REQUESTED,
+                mActivity.finishIfPossible("test", false /* oomAdj */));
         assertTrue(mActivity.finishing);
         assertTrue(mActivity.isInStackLocked());
 
@@ -762,8 +772,7 @@
         mActivity.finishing = false;
         mActivity.setState(STOPPED, "test");
         assertEquals("Activity outside of task/stack cannot be finished", FINISH_RESULT_REQUESTED,
-                mActivity.finishActivityLocked(0 /* resultCode */, null /* resultData */, "test",
-                        false /* oomAdj */));
+                mActivity.finishIfPossible("test", false /* oomAdj */));
         assertTrue(mActivity.finishing);
         assertTrue(mActivity.isInStackLocked());
     }
@@ -772,7 +781,7 @@
      * Verify that activity finish request removes activity immediately if it's ready.
      */
     @Test
-    public void testFinishActivityLocked_removed() {
+    public void testFinishActivityIfPossible_removed() {
         // Prepare the activity record to be ready for immediate removal. It should be invisible and
         // have no process. Otherwise, request to finish it will send a message to client first.
         mActivity.setState(STOPPED, "test");
@@ -782,17 +791,427 @@
         // this will cause NPE when updating task's process.
         mActivity.app = null;
         assertEquals("Activity outside of task/stack cannot be finished", FINISH_RESULT_REMOVED,
-                mActivity.finishActivityLocked(0 /* resultCode */, null /* resultData */, "test",
-                        false /* oomAdj */));
+                mActivity.finishIfPossible("test", false /* oomAdj */));
         assertTrue(mActivity.finishing);
         assertFalse(mActivity.isInStackLocked());
     }
 
+    /**
+     * Verify that resumed activity is paused due to finish request.
+     */
+    @Test
+    public void testFinishActivityIfPossible_resumedStartsPausing() {
+        mActivity.finishing = false;
+        mActivity.setState(RESUMED, "test");
+        assertEquals("Currently resumed activity must be paused before removal",
+                FINISH_RESULT_REQUESTED, mActivity.finishIfPossible("test", false /* oomAdj */));
+        assertEquals(PAUSING, mActivity.getState());
+        verify(mActivity).setVisibility(eq(false));
+        verify(mActivity.getDisplay().mDisplayContent)
+                .prepareAppTransition(eq(TRANSIT_TASK_CLOSE), eq(false) /* alwaysKeepCurrent */);
+    }
+
+    /**
+     * Verify that finish request will be completed immediately for non-resumed activity.
+     */
+    @Test
+    public void testFinishActivityIfPossible_nonResumedFinishCompletesImmediately() {
+        final ActivityState[] states = {INITIALIZING, STARTED, PAUSED, STOPPING, STOPPED};
+        for (ActivityState state : states) {
+            mActivity.finishing = false;
+            mActivity.setState(state, "test");
+            reset(mActivity);
+            assertEquals("Finish must be requested", FINISH_RESULT_REQUESTED,
+                    mActivity.finishIfPossible("test", false /* oomAdj */));
+            verify(mActivity).completeFinishing(anyString());
+        }
+    }
+
+    /**
+     * Verify that finishing will not be completed in PAUSING state.
+     */
+    @Test
+    public void testFinishActivityIfPossible_pausing() {
+        mActivity.finishing = false;
+        mActivity.setState(PAUSING, "test");
+        assertEquals("Finish must be requested", FINISH_RESULT_REQUESTED,
+                mActivity.finishIfPossible("test", false /* oomAdj */));
+        verify(mActivity, never()).completeFinishing(anyString());
+    }
+
+    /**
+     * Verify that finish request for resumed activity will prepare an app transition but not
+     * execute it immediately.
+     */
+    @Test
+    public void testFinishActivityIfPossible_visibleResumedPreparesAppTransition() {
+        mActivity.finishing = false;
+        mActivity.visible = true;
+        mActivity.setState(RESUMED, "test");
+        mActivity.finishIfPossible("test", false /* oomAdj */);
+
+        verify(mActivity).setVisibility(eq(false));
+        verify(mActivity.getDisplay().mDisplayContent)
+                .prepareAppTransition(eq(TRANSIT_TASK_CLOSE), eq(false) /* alwaysKeepCurrent */);
+        verify(mActivity.getDisplay().mDisplayContent, never()).executeAppTransition();
+    }
+
+    /**
+     * Verify that finish request for paused activity will prepare and execute an app transition.
+     */
+    @Test
+    public void testFinishActivityIfPossible_visibleNotResumedExecutesAppTransition() {
+        mActivity.finishing = false;
+        mActivity.visible = true;
+        mActivity.setState(PAUSED, "test");
+        mActivity.finishIfPossible("test", false /* oomAdj */);
+
+        verify(mActivity).setVisibility(eq(false));
+        verify(mActivity.getDisplay().mDisplayContent)
+                .prepareAppTransition(eq(TRANSIT_TASK_CLOSE), eq(false) /* alwaysKeepCurrent */);
+        verify(mActivity.getDisplay().mDisplayContent).executeAppTransition();
+    }
+
+    /**
+     * Verify that finish request for non-visible activity will not prepare any transitions.
+     */
+    @Test
+    public void testFinishActivityIfPossible_nonVisibleNoAppTransition() {
+        // Put an activity on top of test activity to make it invisible and prevent us from
+        // accidentally resuming the topmost one again.
+        new ActivityBuilder(mService).build();
+        mActivity.visible = false;
+        mActivity.setState(STOPPED, "test");
+
+        mActivity.finishIfPossible("test", false /* oomAdj */);
+
+        verify(mActivity.getDisplay().mDisplayContent, never())
+                .prepareAppTransition(eq(TRANSIT_TASK_CLOSE), eq(false) /* alwaysKeepCurrent */);
+    }
+
+    /**
+     * Verify that complete finish request for non-finishing activity is invalid.
+     */
+    @Test(expected = IllegalArgumentException.class)
+    public void testCompleteFinishing_failNotFinishing() {
+        mActivity.finishing = false;
+        mActivity.completeFinishing("test");
+    }
+
+    /**
+     * Verify that complete finish request for resumed activity is invalid.
+     */
+    @Test(expected = IllegalArgumentException.class)
+    public void testCompleteFinishing_failResumed() {
+        mActivity.setState(RESUMED, "test");
+        mActivity.completeFinishing("test");
+    }
+
+    /**
+     * Verify that finish request for pausing activity must be a no-op - activity will finish
+     * once it completes pausing.
+     */
+    @Test
+    public void testCompleteFinishing_pausing() {
+        mActivity.setState(PAUSING, "test");
+        mActivity.finishing = true;
+
+        assertEquals("Activity must not be removed immediately - waiting for paused",
+                mActivity, mActivity.completeFinishing("test"));
+        assertEquals(PAUSING, mActivity.getState());
+        verify(mActivity, never()).destroyIfPossible(anyString());
+    }
+
+    /**
+     * Verify that complete finish request for visible activity must be delayed before the next one
+     * becomes visible.
+     */
+    @Test
+    public void testCompleteFinishing_waitForNextVisible() {
+        final ActivityRecord topActivity = new ActivityBuilder(mService).setTask(mTask).build();
+        topActivity.visible = true;
+        topActivity.nowVisible = true;
+        topActivity.finishing = true;
+        topActivity.setState(PAUSED, "true");
+        // Mark the bottom activity as not visible, so that we will wait for it before removing
+        // the top one.
+        mActivity.visible = false;
+        mActivity.nowVisible = false;
+        mActivity.setState(STOPPED, "test");
+
+        assertEquals("Activity must not be removed immediately - waiting for next visible",
+                topActivity, topActivity.completeFinishing("test"));
+        assertEquals("Activity must be stopped to make next one visible", STOPPING,
+                topActivity.getState());
+        assertTrue("Activity must be stopped to make next one visible",
+                topActivity.mStackSupervisor.mStoppingActivities.contains(topActivity));
+        verify(topActivity, never()).destroyIfPossible(anyString());
+    }
+
+    /**
+     * Verify that complete finish request for invisible activity must not be delayed.
+     */
+    @Test
+    public void testCompleteFinishing_noWaitForNextVisible_alreadyInvisible() {
+        final ActivityRecord topActivity = new ActivityBuilder(mService).setTask(mTask).build();
+        topActivity.visible = false;
+        topActivity.nowVisible = false;
+        topActivity.finishing = true;
+        topActivity.setState(PAUSED, "true");
+        // Mark the bottom activity as not visible, so that we would wait for it before removing
+        // the top one.
+        mActivity.visible = false;
+        mActivity.nowVisible = false;
+        mActivity.setState(STOPPED, "test");
+
+        topActivity.completeFinishing("test");
+
+        verify(topActivity).destroyIfPossible(anyString());
+    }
+
+    /**
+     * Verify that paused finishing activity will be added to finishing list and wait for next one
+     * to idle.
+     */
+    @Test
+    public void testCompleteFinishing_waitForIdle() {
+        final ActivityRecord topActivity = new ActivityBuilder(mService).setTask(mTask).build();
+        topActivity.visible = true;
+        topActivity.nowVisible = true;
+        topActivity.finishing = true;
+        topActivity.setState(PAUSED, "true");
+        // Mark the bottom activity as already visible, so that there is no need to wait for it.
+        mActivity.visible = true;
+        mActivity.nowVisible = true;
+        mActivity.setState(RESUMED, "test");
+
+        topActivity.completeFinishing("test");
+
+        verify(topActivity).addToFinishingAndWaitForIdle();
+    }
+
+    /**
+     * Verify that complete finish request for visible activity must not be delayed if the next one
+     * is already visible and it's not the focused stack.
+     */
+    @Test
+    public void testCompleteFinishing_noWaitForNextVisible_stopped() {
+        final ActivityRecord topActivity = new ActivityBuilder(mService).setTask(mTask).build();
+        topActivity.visible = false;
+        topActivity.nowVisible = false;
+        topActivity.finishing = true;
+        topActivity.setState(STOPPED, "true");
+        // Mark the bottom activity as already visible, so that there is no need to wait for it.
+        mActivity.visible = true;
+        mActivity.nowVisible = true;
+        mActivity.setState(RESUMED, "test");
+
+        topActivity.completeFinishing("test");
+
+        verify(topActivity).destroyIfPossible(anyString());
+    }
+
+    /**
+     * Verify that complete finish request for visible activity must not be delayed if the next one
+     * is already visible and it's not the focused stack.
+     */
+    @Test
+    public void testCompleteFinishing_noWaitForNextVisible_nonFocusedStack() {
+        final ActivityRecord topActivity = new ActivityBuilder(mService).setTask(mTask).build();
+        topActivity.visible = true;
+        topActivity.nowVisible = true;
+        topActivity.finishing = true;
+        topActivity.setState(PAUSED, "true");
+        // Mark the bottom activity as already visible, so that there is no need to wait for it.
+        mActivity.visible = true;
+        mActivity.nowVisible = true;
+        mActivity.setState(RESUMED, "test");
+
+        // Add another stack to become focused and make the activity there visible. This way it
+        // simulates finishing in non-focused stack in split-screen.
+        final ActivityStack stack = new StackBuilder(mRootActivityContainer).build();
+        stack.getChildAt(0).getChildAt(0).nowVisible = true;
+
+        topActivity.completeFinishing("test");
+
+        verify(topActivity).destroyIfPossible(anyString());
+    }
+
+    /**
+     * Verify destroy activity request completes successfully.
+     */
+    @Test
+    public void testDestroyIfPossible() {
+        doReturn(false).when(mRootActivityContainer).resumeFocusedStacksTopActivities();
+        spyOn(mStack);
+        mActivity.destroyIfPossible("test");
+
+        assertEquals(DESTROYING, mActivity.getState());
+        assertTrue(mActivity.finishing);
+        verify(mActivity).destroyImmediately(eq(true) /* removeFromApp */, anyString());
+    }
+
+    /**
+     * Verify that complete finish request for visible activity must not destroy it immediately if
+     * it is the last running activity on a display with a home stack. We must wait for home
+     * activity to come up to avoid a black flash in this case.
+     */
+    @Test
+    public void testDestroyIfPossible_lastActivityAboveEmptyHomeStack() {
+        // Empty the home stack.
+        final ActivityStack homeStack = mActivity.getDisplay().getHomeStack();
+        for (TaskRecord t : homeStack.getAllTasks()) {
+            homeStack.removeTask(t, "test", REMOVE_TASK_MODE_DESTROYING);
+        }
+        mActivity.finishing = true;
+        doReturn(false).when(mRootActivityContainer).resumeFocusedStacksTopActivities();
+        spyOn(mStack);
+
+        // Try to destroy the last activity above the home stack.
+        mActivity.destroyIfPossible("test");
+
+        // Verify that the activity was not actually destroyed, but waits for next one to come up
+        // instead.
+        verify(mActivity, never()).destroyImmediately(eq(true) /* removeFromApp */, anyString());
+        assertEquals(FINISHING, mActivity.getState());
+        assertTrue(mActivity.mStackSupervisor.mFinishingActivities.contains(mActivity));
+    }
+
+    /**
+     * Test that the activity will be moved to destroying state and the message to destroy will be
+     * sent to the client.
+     */
+    @Test
+    public void testDestroyImmediately_hadApp_finishing() {
+        mActivity.finishing = true;
+        mActivity.destroyImmediately(false /* removeFromApp */, "test");
+
+        assertEquals(DESTROYING, mActivity.getState());
+    }
+
+    /**
+     * Test that the activity will be moved to destroyed state immediately if it was not marked as
+     * finishing before {@link ActivityRecord#destroyImmediately(boolean, String)}.
+     */
+    @Test
+    public void testDestroyImmediately_hadApp_notFinishing() {
+        mActivity.finishing = false;
+        mActivity.destroyImmediately(false /* removeFromApp */, "test");
+
+        assertEquals(DESTROYED, mActivity.getState());
+    }
+
+    /**
+     * Test that an activity with no process attached and that is marked as finishing will be
+     * removed from task when {@link ActivityRecord#destroyImmediately(boolean, String)} is called.
+     */
+    @Test
+    public void testDestroyImmediately_noApp_finishing() {
+        mActivity.app = null;
+        mActivity.finishing = true;
+        final TaskRecord task = mActivity.getTaskRecord();
+
+        mActivity.destroyImmediately(false /* removeFromApp */, "test");
+
+        assertEquals(DESTROYED, mActivity.getState());
+        assertNull(mActivity.getTaskRecord());
+        assertEquals(0, task.getChildCount());
+    }
+
+    /**
+     * Test that an activity with no process attached and that is not marked as finishing will be
+     * marked as DESTROYED but not removed from task.
+     */
+    @Test
+    public void testDestroyImmediately_noApp_notFinishing() {
+        mActivity.app = null;
+        mActivity.finishing = false;
+        final TaskRecord task = mActivity.getTaskRecord();
+
+        mActivity.destroyImmediately(false /* removeFromApp */, "test");
+
+        assertEquals(DESTROYED, mActivity.getState());
+        assertEquals(task, mActivity.getTaskRecord());
+        assertEquals(1, task.getChildCount());
+    }
+
+    /**
+     * Test that an activity will not be destroyed if it is marked as non-destroyable.
+     */
+    @Test
+    public void testSafelyDestroy_nonDestroyable() {
+        doReturn(false).when(mActivity).isDestroyable();
+
+        mActivity.safelyDestroy("test");
+
+        verify(mActivity, never()).destroyImmediately(eq(true) /* removeFromApp */, anyString());
+    }
+
+    /**
+     * Test that an activity will not be destroyed if it is marked as non-destroyable.
+     */
+    @Test
+    public void testSafelyDestroy_destroyable() {
+        doReturn(true).when(mActivity).isDestroyable();
+
+        mActivity.safelyDestroy("test");
+
+        verify(mActivity).destroyImmediately(eq(true) /* removeFromApp */, anyString());
+    }
+
+    @Test
+    public void testRemoveFromHistory() {
+        final ActivityStack stack = mActivity.getActivityStack();
+        final TaskRecord task = mActivity.getTaskRecord();
+
+        mActivity.removeFromHistory("test");
+
+        assertEquals(DESTROYED, mActivity.getState());
+        assertNull(mActivity.app);
+        assertNull(mActivity.getTaskRecord());
+        assertEquals(0, task.getChildCount());
+        assertNull(task.getStack());
+        assertEquals(0, stack.getChildCount());
+    }
+
+    /**
+     * Test that it's not allowed to call {@link ActivityRecord#destroyed(String)} if activity is
+     * not in destroying or destroyed state.
+     */
+    @Test(expected = IllegalStateException.class)
+    public void testDestroyed_notDestroying() {
+        mActivity.setState(STOPPED, "test");
+        mActivity.destroyed("test");
+    }
+
+    /**
+     * Test that {@link ActivityRecord#destroyed(String)} can be called if an activity is destroying
+     */
+    @Test
+    public void testDestroyed_destroying() {
+        mActivity.setState(DESTROYING, "test");
+        mActivity.destroyed("test");
+
+        verify(mActivity).removeFromHistory(anyString());
+    }
+
+    /**
+     * Test that {@link ActivityRecord#destroyed(String)} can be called if an activity is destroyed.
+     */
+    @Test
+    public void testDestroyed_destroyed() {
+        mActivity.setState(DESTROYED, "test");
+        mActivity.destroyed("test");
+
+        verify(mActivity).removeFromHistory(anyString());
+    }
+
     /** Setup {@link #mActivity} as a size-compat-mode-able activity without fixed orientation. */
     private void prepareFixedAspectRatioUnresizableActivity() {
         setupDisplayContentForCompatDisplayInsets();
         mActivity.info.resizeMode = RESIZE_MODE_UNRESIZEABLE;
         mActivity.info.maxAspectRatio = 1.5f;
+        mActivity.visible = true;
         ensureActivityConfiguration();
     }
 
@@ -805,8 +1224,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..60c5f0b 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java
@@ -16,7 +16,6 @@
 
 package com.android.server.wm;
 
-import static android.app.Activity.RESULT_CANCELED;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
@@ -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;
@@ -235,7 +233,7 @@
         final ActivityRecord r = new ActivityBuilder(mService).setTask(mTask).build();
         r.info.flags |= ActivityInfo.FLAG_NO_HISTORY;
         mStack.moveToFront("testStopActivityWithDestroy");
-        mStack.stopActivityLocked(r);
+        r.stopIfPossible();
         // Mostly testing to make sure there is a crash in the call part, so if we get here we are
         // good-to-go!
     }
@@ -881,7 +879,7 @@
         final ActivityRecord overlayActivity = new ActivityBuilder(mService).setTask(mTask)
                 .setComponent(new ComponentName("package.overlay", ".OverlayActivity")).build();
         // If the task only remains overlay activity, the task should also be removed.
-        // See {@link ActivityStack#removeActivityFromHistoryLocked}.
+        // See {@link ActivityStack#removeFromHistory}.
         overlayActivity.mTaskOverlay = true;
 
         // The activity without an app means it will be removed immediately.
@@ -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;
     }
 
@@ -1113,6 +1109,19 @@
         assertTrue(listener.mChanged);
     }
 
+    @Test
+    public void testResetTaskWithFinishingActivities() {
+        final ActivityRecord taskTop =
+                new ActivityBuilder(mService).setStack(mStack).setCreateTask(true).build();
+        // Make all activities in the task are finishing to simulate TaskRecord#getTopActivity
+        // returns null.
+        taskTop.finishing = true;
+
+        final ActivityRecord newR = new ActivityBuilder(mService).build();
+        final ActivityRecord result = mStack.resetTaskIfNeededLocked(taskTop, newR);
+        assertThat(result).isEqualTo(taskTop);
+    }
+
     private void verifyShouldSleepActivities(boolean focusedStack,
             boolean keyguardGoingAway, boolean displaySleeping, boolean expected) {
         final ActivityDisplay display = mock(ActivityDisplay.class);
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java
index bcff704..e6c9b9f 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java
@@ -249,6 +249,20 @@
     }
 
     @Test
+    public void testInheritsFreeformModeFromSourceOnFullscreenDisplay() {
+        final TestActivityDisplay fullscreenDisplay = createNewActivityDisplay(
+                WINDOWING_MODE_FULLSCREEN);
+        final ActivityRecord source = createSourceActivity(fullscreenDisplay);
+        source.setWindowingMode(WINDOWING_MODE_FREEFORM);
+
+        assertEquals(RESULT_CONTINUE, mTarget.onCalculate(/* task */ null, /* layout */ null,
+                mActivity, source, /* options */ null, mCurrent, mResult));
+
+        assertEquivalentWindowingMode(WINDOWING_MODE_FREEFORM, mResult.mWindowingMode,
+                WINDOWING_MODE_FULLSCREEN);
+    }
+
+    @Test
     public void testKeepsPictureInPictureLaunchModeInOptions() {
         final TestActivityDisplay freeformDisplay = createNewActivityDisplay(
                 WINDOWING_MODE_FREEFORM);
@@ -571,6 +585,23 @@
     }
 
     @Test
+    public void testRespectsLaunchBoundsWithFreeformSourceOnFullscreenDisplay() {
+        final TestActivityDisplay fullscreenDisplay = createNewActivityDisplay(
+                WINDOWING_MODE_FULLSCREEN);
+        final ActivityRecord source = createSourceActivity(fullscreenDisplay);
+        source.setWindowingMode(WINDOWING_MODE_FREEFORM);
+
+        final ActivityOptions options = ActivityOptions.makeBasic();
+        final Rect expected = new Rect(0, 0, 150, 150);
+        options.setLaunchBounds(expected);
+
+        assertEquals(RESULT_CONTINUE, mTarget.onCalculate(/* task */ null, /* layout */ null,
+                mActivity, source, options, mCurrent, mResult));
+
+        assertEquals(expected, mResult.mBounds);
+    }
+
+    @Test
     public void testNonEmptyLayoutBoundsRespectsGravityWithEmptySize_LeftGravity() {
         final TestActivityDisplay freeformDisplay = createNewActivityDisplay(
                 WINDOWING_MODE_FREEFORM);
diff --git a/services/usb/java/com/android/server/usb/descriptors/ByteStream.java b/services/usb/java/com/android/server/usb/descriptors/ByteStream.java
index 1e823b6..56dc3e0 100644
--- a/services/usb/java/com/android/server/usb/descriptors/ByteStream.java
+++ b/services/usb/java/com/android/server/usb/descriptors/ByteStream.java
@@ -185,12 +185,14 @@
             // Positive offsets only
             throw new IllegalArgumentException();
         }
-        // do arithmetic and comparison in long to ovoid potention integer overflow
+        // do arithmetic and comparison in long to avoid potential integer overflow
         long longNewIndex = (long) mIndex + (long) numBytes;
-        if (longNewIndex < (long) mBytes.length) {
+        if (longNewIndex <= (long) mBytes.length) {
             mReadCount += numBytes;
             mIndex += numBytes;
         } else {
+            // Position the stream to the end so available() will return 0
+            mIndex = mBytes.length;
             throw new IndexOutOfBoundsException();
         }
     }
@@ -210,6 +212,7 @@
             mReadCount -= numBytes;
             mIndex -= numBytes;
         } else {
+            mIndex = 0;
             throw new IndexOutOfBoundsException();
         }
     }
diff --git a/services/usb/java/com/android/server/usb/descriptors/UsbACEndpoint.java b/services/usb/java/com/android/server/usb/descriptors/UsbACEndpoint.java
index 7ebccf3..0535d71 100644
--- a/services/usb/java/com/android/server/usb/descriptors/UsbACEndpoint.java
+++ b/services/usb/java/com/android/server/usb/descriptors/UsbACEndpoint.java
@@ -45,7 +45,6 @@
     @Override
     public int parseRawDescriptors(ByteStream stream) {
         mSubtype = stream.getByte();
-
         return mLength;
     }
 
@@ -55,12 +54,21 @@
         int subClass = interfaceDesc.getUsbSubclass();
         switch (subClass) {
             case AUDIO_AUDIOCONTROL:
+                if (UsbDescriptorParser.DEBUG) {
+                    Log.d(TAG, "---> AUDIO_AUDIOCONTROL");
+                }
                 return new UsbACAudioControlEndpoint(length, type, subClass);
 
             case AUDIO_AUDIOSTREAMING:
+                if (UsbDescriptorParser.DEBUG) {
+                    Log.d(TAG, "---> AUDIO_AUDIOSTREAMING");
+                }
                 return new UsbACAudioStreamEndpoint(length, type, subClass);
 
             case AUDIO_MIDISTREAMING:
+                if (UsbDescriptorParser.DEBUG) {
+                    Log.d(TAG, "---> AUDIO_MIDISTREAMING");
+                }
                 return new UsbACMidiEndpoint(length, type, subClass);
 
             default:
diff --git a/services/usb/java/com/android/server/usb/descriptors/UsbACInterface.java b/services/usb/java/com/android/server/usb/descriptors/UsbACInterface.java
index 38c12a1..82fbfb8 100644
--- a/services/usb/java/com/android/server/usb/descriptors/UsbACInterface.java
+++ b/services/usb/java/com/android/server/usb/descriptors/UsbACInterface.java
@@ -100,8 +100,14 @@
         switch (subtype) {
             case ACI_HEADER:
             {
+                if (UsbDescriptorParser.DEBUG) {
+                    Log.d(TAG, " ---> ACI_HEADER");
+                }
                 int acInterfaceSpec = stream.unpackUsbShort();
                 parser.setACInterfaceSpec(acInterfaceSpec);
+                if (UsbDescriptorParser.DEBUG) {
+                    Log.d(TAG, "  acInterfaceSpec:0x" + Integer.toHexString(acInterfaceSpec));
+                }
                 if (acInterfaceSpec == UsbDeviceDescriptor.USBSPEC_2_0) {
                     return new Usb20ACHeader(length, type, subtype, subClass, acInterfaceSpec);
                 } else {
@@ -111,7 +117,13 @@
 
             case ACI_INPUT_TERMINAL:
             {
+                if (UsbDescriptorParser.DEBUG) {
+                    Log.d(TAG, " ---> ACI_INPUT_TERMINAL");
+                }
                 int acInterfaceSpec = parser.getACInterfaceSpec();
+                if (UsbDescriptorParser.DEBUG) {
+                    Log.d(TAG, "  acInterfaceSpec:0x" + Integer.toHexString(acInterfaceSpec));
+                }
                 if (acInterfaceSpec == UsbDeviceDescriptor.USBSPEC_2_0) {
                     return new Usb20ACInputTerminal(length, type, subtype, subClass);
                 } else {
@@ -121,7 +133,13 @@
 
             case ACI_OUTPUT_TERMINAL:
             {
+                if (UsbDescriptorParser.DEBUG) {
+                    Log.d(TAG, " ---> ACI_OUTPUT_TERMINAL");
+                }
                 int acInterfaceSpec = parser.getACInterfaceSpec();
+                if (UsbDescriptorParser.DEBUG) {
+                    Log.d(TAG, "  acInterfaceSpec:0x" + Integer.toHexString(acInterfaceSpec));
+                }
                 if (acInterfaceSpec == UsbDeviceDescriptor.USBSPEC_2_0) {
                     return new Usb20ACOutputTerminal(length, type, subtype, subClass);
                 } else {
@@ -130,14 +148,26 @@
             }
 
             case ACI_SELECTOR_UNIT:
+                if (UsbDescriptorParser.DEBUG) {
+                    Log.d(TAG, " ---> ACI_SELECTOR_UNIT");
+                }
                 return new UsbACSelectorUnit(length, type, subtype, subClass);
 
             case ACI_FEATURE_UNIT:
+                if (UsbDescriptorParser.DEBUG) {
+                    Log.d(TAG, " ---> ACI_FEATURE_UNIT");
+                }
                 return new UsbACFeatureUnit(length, type, subtype, subClass);
 
             case ACI_MIXER_UNIT:
             {
+                if (UsbDescriptorParser.DEBUG) {
+                    Log.d(TAG, " ---> ACI_MIXER_UNIT");
+                }
                 int acInterfaceSpec = parser.getACInterfaceSpec();
+                if (UsbDescriptorParser.DEBUG) {
+                    Log.d(TAG, "  acInterfaceSpec:0x" + Integer.toHexString(acInterfaceSpec));
+                }
                 if (acInterfaceSpec == UsbDeviceDescriptor.USBSPEC_2_0) {
                     return new Usb20ACMixerUnit(length, type, subtype, subClass);
                 } else {
@@ -215,14 +245,23 @@
         int subClass = interfaceDesc.getUsbSubclass();
         switch (subClass) {
             case AUDIO_AUDIOCONTROL:
+                if (UsbDescriptorParser.DEBUG) {
+                    Log.d(TAG, "  AUDIO_AUDIOCONTROL");
+                }
                 return allocAudioControlDescriptor(
                         parser, stream, length, type, subtype, subClass);
 
             case AUDIO_AUDIOSTREAMING:
+                if (UsbDescriptorParser.DEBUG) {
+                    Log.d(TAG, "  AUDIO_AUDIOSTREAMING");
+                }
                 return allocAudioStreamingDescriptor(
                         parser, stream, length, type, subtype, subClass);
 
             case AUDIO_MIDISTREAMING:
+                if (UsbDescriptorParser.DEBUG) {
+                    Log.d(TAG, "  AUDIO_MIDISTREAMING");
+                }
                 return allocMidiStreamingDescriptor(length, type, subtype, subClass);
 
             default:
diff --git a/services/usb/java/com/android/server/usb/descriptors/UsbConfigDescriptor.java b/services/usb/java/com/android/server/usb/descriptors/UsbConfigDescriptor.java
index 639aa4e..de2dd10 100644
--- a/services/usb/java/com/android/server/usb/descriptors/UsbConfigDescriptor.java
+++ b/services/usb/java/com/android/server/usb/descriptors/UsbConfigDescriptor.java
@@ -30,7 +30,6 @@
  */
 public final class UsbConfigDescriptor extends UsbDescriptor {
     private static final String TAG = "UsbConfigDescriptor";
-    private static final boolean DEBUG = false;
 
     private int mTotalLength;    // 2:2 Total length in bytes of data returned
     private byte mNumInterfaces; // 4:1 Number of Interfaces
@@ -79,14 +78,14 @@
     }
 
     UsbConfiguration toAndroid(UsbDescriptorParser parser) {
-        if (DEBUG) {
+        if (UsbDescriptorParser.DEBUG) {
             Log.d(TAG, "  toAndroid()");
         }
         String name = parser.getDescriptorString(mConfigIndex);
         UsbConfiguration config = new
                 UsbConfiguration(mConfigValue, name, mAttribs, mMaxPower);
         UsbInterface[] interfaces = new UsbInterface[mInterfaceDescriptors.size()];
-        if (DEBUG) {
+        if (UsbDescriptorParser.DEBUG) {
             Log.d(TAG, "    " + mInterfaceDescriptors.size() + " interfaces.");
         }
         for (int index = 0; index < mInterfaceDescriptors.size(); index++) {
diff --git a/services/usb/java/com/android/server/usb/descriptors/UsbDescriptor.java b/services/usb/java/com/android/server/usb/descriptors/UsbDescriptor.java
index ff67667..44422a2 100644
--- a/services/usb/java/com/android/server/usb/descriptors/UsbDescriptor.java
+++ b/services/usb/java/com/android/server/usb/descriptors/UsbDescriptor.java
@@ -43,7 +43,7 @@
     protected int mHierarchyLevel;
 
     protected final int mLength;    // 0:1 bLength Number Size of the Descriptor in Bytes (18 bytes)
-                                    // we store this as an int because Java bytes are SIGNED.
+    // we store this as an int because Java bytes are SIGNED.
     protected final byte mType;     // 1:1 bDescriptorType Constant Device Descriptor (0x01)
 
     private byte[] mRawData;
@@ -52,11 +52,11 @@
     private static byte[] sStringBuffer = new byte[SIZE_STRINGBUFFER];
 
     // Status
-    public static final int STATUS_UNPARSED         = 0;
-    public static final int STATUS_PARSED_OK        = 1;
-    public static final int STATUS_PARSED_UNDERRUN  = 2;
-    public static final int STATUS_PARSED_OVERRUN   = 3;
-    public static final int STATUS_PARSE_EXCEPTION  = 4;
+    public static final int STATUS_UNPARSED = 0;
+    public static final int STATUS_PARSED_OK = 1;
+    public static final int STATUS_PARSED_UNDERRUN = 2;
+    public static final int STATUS_PARSED_OVERRUN = 3;
+    public static final int STATUS_PARSE_EXCEPTION = 4;
 
     private int mStatus = STATUS_UNPARSED;
 
@@ -78,53 +78,53 @@
     public static final byte DESCRIPTORTYPE_HID = 0x21;                // 33
     public static final byte DESCRIPTORTYPE_REPORT = 0x22;             // 34
     public static final byte DESCRIPTORTYPE_PHYSICAL = 0x23;           // 35
-    public static final byte DESCRIPTORTYPE_AUDIO_INTERFACE = 0x24;    // 36
-    public static final byte DESCRIPTORTYPE_AUDIO_ENDPOINT = 0x25;     // 37
+    public static final byte DESCRIPTORTYPE_CLASSSPECIFIC_INTERFACE = 0x24;    // 36
+    public static final byte DESCRIPTORTYPE_CLASSSPECIFIC_ENDPOINT = 0x25;     // 37
     public static final byte DESCRIPTORTYPE_HUB = 0x29;                // 41
     public static final byte DESCRIPTORTYPE_SUPERSPEED_HUB = 0x2A;     // 42
     public static final byte DESCRIPTORTYPE_ENDPOINT_COMPANION = 0x30; // 48
 
     // Class IDs
-    public static final int CLASSID_DEVICE  =      0x00;
-    public static final int CLASSID_AUDIO =        0x01;
-    public static final int CLASSID_COM =          0x02;
-    public static final int CLASSID_HID =          0x03;
+    public static final int CLASSID_DEVICE = 0x00;
+    public static final int CLASSID_AUDIO = 0x01;
+    public static final int CLASSID_COM = 0x02;
+    public static final int CLASSID_HID = 0x03;
     // public static final int CLASSID_??? =       0x04;
-    public static final int CLASSID_PHYSICAL =     0x05;
-    public static final int CLASSID_IMAGE =        0x06;
-    public static final int CLASSID_PRINTER =      0x07;
-    public static final int CLASSID_STORAGE =      0x08;
-    public static final int CLASSID_HUB =          0x09;
-    public static final int CLASSID_CDC_CONTROL =  0x0A;
-    public static final int CLASSID_SMART_CARD =   0x0B;
+    public static final int CLASSID_PHYSICAL = 0x05;
+    public static final int CLASSID_IMAGE = 0x06;
+    public static final int CLASSID_PRINTER = 0x07;
+    public static final int CLASSID_STORAGE = 0x08;
+    public static final int CLASSID_HUB = 0x09;
+    public static final int CLASSID_CDC_CONTROL = 0x0A;
+    public static final int CLASSID_SMART_CARD = 0x0B;
     //public static final int CLASSID_??? =        0x0C;
-    public static final int CLASSID_SECURITY =     0x0D;
-    public static final int CLASSID_VIDEO =        0x0E;
-    public static final int CLASSID_HEALTHCARE =   0x0F;
-    public static final int CLASSID_AUDIOVIDEO =   0x10;
-    public static final int CLASSID_BILLBOARD =    0x11;
-    public static final int CLASSID_TYPECBRIDGE =  0x12;
-    public static final int CLASSID_DIAGNOSTIC =   0xDC;
-    public static final int CLASSID_WIRELESS =     0xE0;
-    public static final int CLASSID_MISC =         0xEF;
-    public static final int CLASSID_APPSPECIFIC =  0xFE;
+    public static final int CLASSID_SECURITY = 0x0D;
+    public static final int CLASSID_VIDEO = 0x0E;
+    public static final int CLASSID_HEALTHCARE = 0x0F;
+    public static final int CLASSID_AUDIOVIDEO = 0x10;
+    public static final int CLASSID_BILLBOARD = 0x11;
+    public static final int CLASSID_TYPECBRIDGE = 0x12;
+    public static final int CLASSID_DIAGNOSTIC = 0xDC;
+    public static final int CLASSID_WIRELESS = 0xE0;
+    public static final int CLASSID_MISC = 0xEF;
+    public static final int CLASSID_APPSPECIFIC = 0xFE;
     public static final int CLASSID_VENDSPECIFIC = 0xFF;
 
     // Audio Subclass codes
-    public static final int AUDIO_SUBCLASS_UNDEFINED   = 0x00;
-    public static final int AUDIO_AUDIOCONTROL         = 0x01;
-    public static final int AUDIO_AUDIOSTREAMING       = 0x02;
-    public static final int AUDIO_MIDISTREAMING        = 0x03;
+    public static final int AUDIO_SUBCLASS_UNDEFINED = 0x00;
+    public static final int AUDIO_AUDIOCONTROL = 0x01;
+    public static final int AUDIO_AUDIOSTREAMING = 0x02;
+    public static final int AUDIO_MIDISTREAMING = 0x03;
 
     // Request IDs
-    public static final int REQUEST_GET_STATUS         = 0x00;
-    public static final int REQUEST_CLEAR_FEATURE      = 0x01;
-    public static final int REQUEST_SET_FEATURE        = 0x03;
-    public static final int REQUEST_GET_ADDRESS        = 0x05;
-    public static final int REQUEST_GET_DESCRIPTOR     = 0x06;
-    public static final int REQUEST_SET_DESCRIPTOR     = 0x07;
-    public static final int REQUEST_GET_CONFIGURATION  = 0x08;
-    public static final int REQUEST_SET_CONFIGURATION  = 0x09;
+    public static final int REQUEST_GET_STATUS = 0x00;
+    public static final int REQUEST_CLEAR_FEATURE = 0x01;
+    public static final int REQUEST_SET_FEATURE = 0x03;
+    public static final int REQUEST_GET_ADDRESS = 0x05;
+    public static final int REQUEST_GET_DESCRIPTOR = 0x06;
+    public static final int REQUEST_SET_DESCRIPTOR = 0x07;
+    public static final int REQUEST_GET_CONFIGURATION = 0x08;
+    public static final int REQUEST_SET_CONFIGURATION = 0x09;
 
     // USB control transfer timeout
     public static final int USB_CONTROL_TRANSFER_TIMEOUT_MS = 200;
@@ -163,7 +163,6 @@
     public int getOverUnderRunCount() {
         return mOverUnderRunCount;
     }
-
     public String getStatusString() {
         return sStatusStrings[mStatus];
     }
@@ -278,4 +277,24 @@
                 + " Len: " + getLength();
         canvas.writeParagraph(text, false);
     }
+
+    /*
+     * Logging Helpers
+     */
+    static String getDescriptorName(byte descriptorType, int descriptorLength) {
+        String name = UsbStrings.getDescriptorName(descriptorType);
+        if (name != null) {
+            return name;
+        } else {
+            return "Unknown Descriptor Type " + descriptorType
+                + " 0x" + Integer.toHexString(descriptorType)
+                + " length:" + descriptorLength;
+        }
+    }
+
+    static void logDescriptorName(byte descriptorType, int descriptorLength) {
+        if (UsbDescriptorParser.DEBUG) {
+            Log.d(TAG, "----> " + getDescriptorName(descriptorType, descriptorLength));
+        }
+    }
 }
diff --git a/services/usb/java/com/android/server/usb/descriptors/UsbDescriptorParser.java b/services/usb/java/com/android/server/usb/descriptors/UsbDescriptorParser.java
index c021101..4f62d5a 100644
--- a/services/usb/java/com/android/server/usb/descriptors/UsbDescriptorParser.java
+++ b/services/usb/java/com/android/server/usb/descriptors/UsbDescriptorParser.java
@@ -26,7 +26,7 @@
  */
 public final class UsbDescriptorParser {
     private static final String TAG = "UsbDescriptorParser";
-    private static final boolean DEBUG = false;
+    public static final boolean DEBUG = false;
 
     private final String mDeviceAddr;
 
@@ -115,6 +115,8 @@
         int length = stream.getUnsignedByte();
         byte type = stream.getByte();
 
+        UsbDescriptor.logDescriptorName(type, length);
+
         UsbDescriptor descriptor = null;
         switch (type) {
             /*
@@ -174,14 +176,56 @@
                 break;
 
             /*
-             * Audio Class Specific
+             * Various Class Specific
              */
-            case UsbDescriptor.DESCRIPTORTYPE_AUDIO_INTERFACE:
-                descriptor = UsbACInterface.allocDescriptor(this, stream, length, type);
+            case UsbDescriptor.DESCRIPTORTYPE_CLASSSPECIFIC_INTERFACE:
+                if (mCurInterfaceDescriptor != null) {
+                    switch (mCurInterfaceDescriptor.getUsbClass()) {
+                        case UsbDescriptor.CLASSID_AUDIO:
+                            descriptor = UsbACInterface.allocDescriptor(this, stream, length, type);
+                            break;
+
+                        case UsbDescriptor.CLASSID_VIDEO:
+                            Log.d(TAG, "  UsbDescriptor.CLASSID_VIDEO subType:0x"
+                                    + Integer.toHexString(stream.getByte()));
+                            descriptor = UsbVCInterface.allocDescriptor(this, stream, length, type);
+                            break;
+
+                        case UsbDescriptor.CLASSID_AUDIOVIDEO:
+                            Log.d(TAG, "  UsbDescriptor.CLASSID_AUDIOVIDEO subType:0x"
+                                    + Integer.toHexString(stream.getByte()));
+                            break;
+
+                        default:
+                            Log.d(TAG, "  Unparsed Class-specific Interface:0x"
+                                    + Integer.toHexString(mCurInterfaceDescriptor.getUsbClass()));
+                            break;
+                    }
+                }
                 break;
 
-            case UsbDescriptor.DESCRIPTORTYPE_AUDIO_ENDPOINT:
-                descriptor = UsbACEndpoint.allocDescriptor(this, length, type);
+            case UsbDescriptor.DESCRIPTORTYPE_CLASSSPECIFIC_ENDPOINT:
+                if (mCurInterfaceDescriptor != null) {
+                    switch (mCurInterfaceDescriptor.getUsbClass()) {
+                        case UsbDescriptor.CLASSID_AUDIO:
+                            descriptor = UsbACEndpoint.allocDescriptor(this, length, type);
+                            break;
+                        case UsbDescriptor.CLASSID_VIDEO:
+                            Log.d(TAG, "UsbDescriptor.CLASSID_VIDEO subType:0x"
+                                    + Integer.toHexString(stream.getByte()));
+                            descriptor = UsbVCEndpoint.allocDescriptor(this, length, type);
+                            break;
+
+                        case UsbDescriptor.CLASSID_AUDIOVIDEO:
+                            Log.d(TAG, "UsbDescriptor.CLASSID_AUDIOVIDEO subType:0x"
+                                    + Integer.toHexString(stream.getByte()));
+                            break;
+                        default:
+                            Log.d(TAG, "  Unparsed Class-specific Endpoint:0x"
+                                    + Integer.toHexString(mCurInterfaceDescriptor.getUsbClass()));
+                            break;
+                    }
+                }
                 break;
 
             default:
@@ -190,8 +234,6 @@
 
         if (descriptor == null) {
             // Unknown Descriptor
-            Log.i(TAG, "Unknown Descriptor len: " + length + " type:0x"
-                    + Integer.toHexString(type));
             descriptor = new UsbUnknown(length, type);
         }
 
@@ -210,10 +252,6 @@
      * @hide
      */
     public void parseDescriptors(byte[] descriptors) {
-        if (DEBUG) {
-            Log.d(TAG, "parseDescriptors() - start");
-        }
-
         ByteStream stream = new ByteStream(descriptors);
         while (stream.available() > 0) {
             UsbDescriptor descriptor = null;
@@ -325,8 +363,8 @@
     public ArrayList<UsbDescriptor> getACInterfaceDescriptors(byte subtype, int subclass) {
         ArrayList<UsbDescriptor> list = new ArrayList<UsbDescriptor>();
         for (UsbDescriptor descriptor : mDescriptors) {
-            if (descriptor.getType() == UsbDescriptor.DESCRIPTORTYPE_AUDIO_INTERFACE) {
-                // ensure that this isn't an unrecognized DESCRIPTORTYPE_AUDIO_INTERFACE
+            if (descriptor.getType() == UsbDescriptor.DESCRIPTORTYPE_CLASSSPECIFIC_INTERFACE) {
+                // ensure that this isn't an unrecognized DESCRIPTORTYPE_CLASSSPECIFIC_INTERFACE
                 if (descriptor instanceof UsbACInterface) {
                     UsbACInterface acDescriptor = (UsbACInterface) descriptor;
                     if (acDescriptor.getSubtype() == subtype
@@ -334,8 +372,8 @@
                         list.add(descriptor);
                     }
                 } else {
-                    Log.w(TAG, "Unrecognized Audio Interface l: " + descriptor.getLength()
-                            + " t:0x" + Integer.toHexString(descriptor.getType()));
+                    Log.w(TAG, "Unrecognized Audio Interface len: " + descriptor.getLength()
+                            + " type:0x" + Integer.toHexString(descriptor.getType()));
                 }
             }
         }
diff --git a/services/usb/java/com/android/server/usb/descriptors/UsbDeviceDescriptor.java b/services/usb/java/com/android/server/usb/descriptors/UsbDeviceDescriptor.java
index f50b9cb..e6e10fe 100644
--- a/services/usb/java/com/android/server/usb/descriptors/UsbDeviceDescriptor.java
+++ b/services/usb/java/com/android/server/usb/descriptors/UsbDeviceDescriptor.java
@@ -31,7 +31,6 @@
  */
 public final class UsbDeviceDescriptor extends UsbDescriptor {
     private static final String TAG = "UsbDeviceDescriptor";
-    private static final boolean DEBUG = false;
 
     public static final int USBSPEC_1_0 = 0x0100;
     public static final int USBSPEC_1_1 = 0x0110;
@@ -136,19 +135,19 @@
      * @hide
      */
     public UsbDevice.Builder toAndroid(UsbDescriptorParser parser) {
-        if (DEBUG) {
+        if (UsbDescriptorParser.DEBUG) {
             Log.d(TAG, "toAndroid()");
         }
 
         String mfgName = getMfgString(parser);
         String prodName = getProductString(parser);
-        if (DEBUG) {
+        if (UsbDescriptorParser.DEBUG) {
             Log.d(TAG, "  mfgName:" + mfgName + " prodName:" + prodName);
         }
 
         String versionString = getDeviceReleaseString();
         String serialStr = getSerialString(parser);
-        if (DEBUG) {
+        if (UsbDescriptorParser.DEBUG) {
             Log.d(TAG, "  versionString:" + versionString + " serialStr:" + serialStr);
         }
 
diff --git a/services/usb/java/com/android/server/usb/descriptors/UsbEndpointDescriptor.java b/services/usb/java/com/android/server/usb/descriptors/UsbEndpointDescriptor.java
index 4da31ea..5eb0a2f 100644
--- a/services/usb/java/com/android/server/usb/descriptors/UsbEndpointDescriptor.java
+++ b/services/usb/java/com/android/server/usb/descriptors/UsbEndpointDescriptor.java
@@ -27,7 +27,6 @@
  */
 public class UsbEndpointDescriptor extends UsbDescriptor {
     private static final String TAG = "UsbEndpointDescriptor";
-    private static final boolean DEBUG = false;
 
     public static final int MASK_ENDPOINT_ADDRESS = 0b000000000001111;
     public static final int MASK_ENDPOINT_DIRECTION = (byte) 0b0000000010000000;
@@ -110,7 +109,7 @@
     }
 
     /* package */ UsbEndpoint toAndroid(UsbDescriptorParser parser) {
-        if (DEBUG) {
+        if (UsbDescriptorParser.DEBUG) {
             Log.d(TAG, "toAndroid() type:"
                     + Integer.toHexString(mAttributes & MASK_ATTRIBS_TRANSTYPE)
                     + " sync:" + Integer.toHexString(mAttributes & MASK_ATTRIBS_SYNCTYPE)
diff --git a/services/usb/java/com/android/server/usb/descriptors/UsbInterfaceDescriptor.java b/services/usb/java/com/android/server/usb/descriptors/UsbInterfaceDescriptor.java
index 632e3dc..1dc6069 100644
--- a/services/usb/java/com/android/server/usb/descriptors/UsbInterfaceDescriptor.java
+++ b/services/usb/java/com/android/server/usb/descriptors/UsbInterfaceDescriptor.java
@@ -31,7 +31,6 @@
  */
 public class UsbInterfaceDescriptor extends UsbDescriptor {
     private static final String TAG = "UsbInterfaceDescriptor";
-    private static final boolean DEBUG = false;
 
     protected int mInterfaceNumber;   // 2:1 Number of Interface
     protected byte mAlternateSetting; // 3:1 Value used to select alternative setting
@@ -95,7 +94,7 @@
     }
 
     UsbInterface toAndroid(UsbDescriptorParser parser) {
-        if (DEBUG) {
+        if (UsbDescriptorParser.DEBUG) {
             Log.d(TAG, "toAndroid() class:" + Integer.toHexString(mUsbClass)
                     + " subclass:" + Integer.toHexString(mUsbSubclass)
                     + " " + mEndpointDescriptors.size() + " endpoints.");
diff --git a/services/usb/java/com/android/server/usb/descriptors/UsbVCEndpoint.java b/services/usb/java/com/android/server/usb/descriptors/UsbVCEndpoint.java
new file mode 100644
index 0000000..39fbc0d
--- /dev/null
+++ b/services/usb/java/com/android/server/usb/descriptors/UsbVCEndpoint.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.usb.descriptors;
+
+import android.util.Log;
+
+/**
+ * @hide
+ * A video class-specific Endpoint
+ * see
+ */
+abstract class UsbVCEndpoint extends UsbDescriptor {
+    private static final String TAG = "UsbVCEndpoint";
+
+    UsbVCEndpoint(int length, byte type, int subclass) {
+        super(length, type);
+        // mSubclass = subclass;
+    }
+
+    public static UsbDescriptor allocDescriptor(UsbDescriptorParser parser,
+                                                int length, byte type) {
+        UsbInterfaceDescriptor interfaceDesc = parser.getCurInterface();
+        int subClass = interfaceDesc.getUsbSubclass();
+        switch (subClass) {
+//            case AUDIO_AUDIOCONTROL:
+//                if (UsbDescriptorParser.DEBUG) {
+//                    Log.i(TAG, "---> AUDIO_AUDIOCONTROL");
+//                }
+//                return new UsbACAudioControlEndpoint(length, type, subClass);
+//
+//            case AUDIO_AUDIOSTREAMING:
+//                if (UsbDescriptorParser.DEBUG) {
+//                    Log.i(TAG, "---> AUDIO_AUDIOSTREAMING");
+//                }
+//                return new UsbACAudioStreamEndpoint(length, type, subClass);
+//
+//            case AUDIO_MIDISTREAMING:
+//                if (UsbDescriptorParser.DEBUG) {
+//                    Log.i(TAG, "---> AUDIO_MIDISTREAMING");
+//                }
+//                return new UsbACMidiEndpoint(length, type, subClass);
+
+            default:
+                Log.w(TAG, "Unknown Video Class Endpoint id:0x" + Integer.toHexString(subClass));
+                return null;
+        }
+    }
+}
diff --git a/services/usb/java/com/android/server/usb/descriptors/UsbVCInterface.java b/services/usb/java/com/android/server/usb/descriptors/UsbVCInterface.java
new file mode 100644
index 0000000..c9eb1ec
--- /dev/null
+++ b/services/usb/java/com/android/server/usb/descriptors/UsbVCInterface.java
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.usb.descriptors;
+
+import android.util.Log;
+
+/**
+ * @hide
+ * A video class-specific Interface.
+ * see USB_Video_Class_1.1.pdf, section 3.7.2
+ */
+public abstract class UsbVCInterface extends UsbDescriptor {
+    private static final String TAG = "UsbVCInterface";
+
+    // Class-specific Video Subtypes
+    public static final byte VCI_UNDEFINED          = 0x00;
+    public static final byte VCI_VEADER             = 0x01;
+    public static final byte VCI_INPUT_TERMINAL     = 0x02;
+    public static final byte VCI_VOUTPUT_TERMINAL   = 0x03;
+    public static final byte VCI_SELECTOR_UNIT      = 0x04;
+    public static final byte VCI_VROCESSING_UNIT    = 0x05;
+    public static final byte VCI_VEXTENSION_UNIT    = 0x06;
+
+   // See “Universal Serial Bus Device Class Definition for Video
+    protected final byte mSubtype;  // 2:1 HEADER descriptor subtype
+    protected final int mSubclass;  // from the mSubclass member of the
+    // "enclosing" Interface Descriptor
+
+    public UsbVCInterface(int length, byte type, byte subtype, int subclass) {
+        super(length, type);
+        mSubtype = subtype;
+        mSubclass = subclass;
+    }
+
+    /**
+     * Allocates an audio class interface subtype based on subtype and subclass.
+     */
+    public static UsbDescriptor allocDescriptor(UsbDescriptorParser parser, ByteStream stream,
+                                                int length, byte type) {
+        byte subtype = stream.getByte();
+        UsbInterfaceDescriptor interfaceDesc = parser.getCurInterface();
+        int subClass = interfaceDesc.getUsbSubclass();
+        if (UsbDescriptorParser.DEBUG) {
+            Log.d(TAG, "  Video Class-specific Interface subClass:0x"
+                    + Integer.toHexString(subClass));
+        }
+        switch (subClass) {
+            // TODO - Create descriptor classes and parse these...
+            case VCI_UNDEFINED:
+                if (UsbDescriptorParser.DEBUG) {
+                    Log.d(TAG, " ---> VCI_UNDEFINED");
+                }
+                break;
+
+            case VCI_VEADER:
+                if (UsbDescriptorParser.DEBUG) {
+                    Log.d(TAG, " ---> VCI_VEADER");
+                }
+                break;
+
+            case VCI_INPUT_TERMINAL:
+                if (UsbDescriptorParser.DEBUG) {
+                    Log.d(TAG, " ---> VCI_INPUT_TERMINAL");
+                }
+                break;
+
+            case VCI_VOUTPUT_TERMINAL:
+                if (UsbDescriptorParser.DEBUG) {
+                    Log.d(TAG, " ---> VCI_VOUTPUT_TERMINAL");
+                }
+                break;
+
+            case VCI_SELECTOR_UNIT:
+                if (UsbDescriptorParser.DEBUG) {
+                    Log.d(TAG, " ---> VCI_SELECTOR_UNIT");
+                }
+                break;
+
+            case VCI_VROCESSING_UNIT:
+                if (UsbDescriptorParser.DEBUG) {
+                    Log.d(TAG, " ---> VCI_VROCESSING_UNIT");
+                }
+                break;
+
+            case VCI_VEXTENSION_UNIT:
+                if (UsbDescriptorParser.DEBUG) {
+                    Log.d(TAG, " ---> VCI_VEXTENSION_UNIT");
+                }
+                break;
+
+            default:
+                Log.w(TAG, "Unknown Video Class Interface Subclass: 0x"
+                        + Integer.toHexString(subClass));
+                return null;
+        }
+
+        return null;
+    }
+}
diff --git a/services/usb/java/com/android/server/usb/descriptors/report/UsbStrings.java b/services/usb/java/com/android/server/usb/descriptors/report/UsbStrings.java
index fb4576a..918ba2cc 100644
--- a/services/usb/java/com/android/server/usb/descriptors/report/UsbStrings.java
+++ b/services/usb/java/com/android/server/usb/descriptors/report/UsbStrings.java
@@ -56,9 +56,10 @@
         sDescriptorNames.put(UsbDescriptor.DESCRIPTORTYPE_HID, "HID");
         sDescriptorNames.put(UsbDescriptor.DESCRIPTORTYPE_REPORT, "Report");
         sDescriptorNames.put(UsbDescriptor.DESCRIPTORTYPE_PHYSICAL, "Physical");
-        sDescriptorNames.put(UsbDescriptor.DESCRIPTORTYPE_AUDIO_INTERFACE,
-                "Audio Class Interface");
-        sDescriptorNames.put(UsbDescriptor.DESCRIPTORTYPE_AUDIO_ENDPOINT, "Audio Class Endpoint");
+        sDescriptorNames.put(UsbDescriptor.DESCRIPTORTYPE_CLASSSPECIFIC_INTERFACE,
+                "Class-specific Interface");
+        sDescriptorNames.put(UsbDescriptor.DESCRIPTORTYPE_CLASSSPECIFIC_ENDPOINT,
+                "Class-specific Endpoint");
         sDescriptorNames.put(UsbDescriptor.DESCRIPTORTYPE_HUB, "Hub");
         sDescriptorNames.put(UsbDescriptor.DESCRIPTORTYPE_SUPERSPEED_HUB, "Superspeed Hub");
         sDescriptorNames.put(UsbDescriptor.DESCRIPTORTYPE_ENDPOINT_COMPANION,
diff --git a/services/usb/java/com/android/server/usb/descriptors/tree/UsbDescriptorsTree.java b/services/usb/java/com/android/server/usb/descriptors/tree/UsbDescriptorsTree.java
index 1aa30fa..72fa897 100644
--- a/services/usb/java/com/android/server/usb/descriptors/tree/UsbDescriptorsTree.java
+++ b/services/usb/java/com/android/server/usb/descriptors/tree/UsbDescriptorsTree.java
@@ -126,11 +126,13 @@
                 //
                 // Audio Class Descriptors
                 //
-                case UsbDescriptor.DESCRIPTORTYPE_AUDIO_INTERFACE:
-                    addACInterface((UsbACInterface) descriptor);
+                case UsbDescriptor.DESCRIPTORTYPE_CLASSSPECIFIC_INTERFACE:
+                    //TODO: This needs to be parsed out to Audio/Video...
+                    // addACInterface((UsbACInterface) descriptor);
                     break;
 
-                case UsbDescriptor.DESCRIPTORTYPE_AUDIO_ENDPOINT:
+                case UsbDescriptor.DESCRIPTORTYPE_CLASSSPECIFIC_ENDPOINT:
+                    //TODO: This needs to be parsed out to Audio/Video...
                     break;
             }
         }
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 10d4b8db..654b54d 100755
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -1026,6 +1026,18 @@
             "call_forwarding_map_non_number_to_voicemail_bool";
 
     /**
+     * When {@code true}, the phone will always tell the IMS stack to keep RTT enabled and
+     * determine on a per-call basis (based on extras from the dialer app) whether a call should be
+     * an RTT call or not.
+     *
+     * When {@code false}, the old behavior is used, where the toggle in accessibility settings is
+     * used to set the IMS stack's RTT enabled state.
+     * @hide
+     */
+    public static final String KEY_IGNORE_RTT_MODE_SETTING_BOOL =
+            "ignore_rtt_mode_setting_bool";
+
+    /**
      * Determines whether conference calls are supported by a carrier.  When {@code true},
      * conference calling is supported, {@code false otherwise}.
      */
@@ -1391,6 +1403,13 @@
             "read_only_apn_fields_string_array";
 
     /**
+     * Default value of APN types field if not specified by user when adding/modifying an APN.
+     * @hide
+     */
+    public static final String KEY_APN_SETTINGS_DEFAULT_APN_TYPES_STRING_ARRAY =
+            "apn_settings_default_apn_types_string_array";
+
+    /**
      * Boolean indicating if intent for emergency call state changes should be broadcast
      * @hide
      */
@@ -2678,10 +2697,12 @@
      * the value is the icon name. Use "None" as the icon name if no icon should be shown in a
      * specific 5G scenario. If the scenario is "None", config can skip this key and value.
      *
+     * Icon name options: "5G_Plus", "5G".
+     *
      * Here is an example:
-     * UE want to display 5GPlus icon for scenario#1, and 5G icon for scenario#2; otherwise no
+     * UE want to display 5G_Plus icon for scenario#1, and 5G icon for scenario#2; otherwise no
      * define.
-     * The configuration is: "connected_mmwave:5GPlus,connected:5G"
+     * The configuration is: "connected_mmwave:5G_Plus,connected:5G"
      *
      * @hide
      */
@@ -3261,6 +3282,7 @@
         sDefaults.putBoolean(KEY_ALLOW_ADDING_APNS_BOOL, true);
         sDefaults.putStringArray(KEY_READ_ONLY_APN_TYPES_STRING_ARRAY, new String[] {"dun"});
         sDefaults.putStringArray(KEY_READ_ONLY_APN_FIELDS_STRING_ARRAY, null);
+        sDefaults.putStringArray(KEY_APN_SETTINGS_DEFAULT_APN_TYPES_STRING_ARRAY, null);
         sDefaults.putBoolean(KEY_BROADCAST_EMERGENCY_CALL_STATE_CHANGES_BOOL, false);
         sDefaults.putBoolean(KEY_ALWAYS_SHOW_EMERGENCY_ALERT_ONOFF_BOOL, false);
         sDefaults.putBoolean(KEY_DISABLE_SEVERE_WHEN_EXTREME_DISABLED_BOOL, true);
@@ -3304,6 +3326,7 @@
         sDefaults.putInt(KEY_IMS_DTMF_TONE_DELAY_INT, 0);
         sDefaults.putInt(KEY_CDMA_DTMF_TONE_DELAY_INT, 100);
         sDefaults.putBoolean(KEY_CALL_FORWARDING_MAP_NON_NUMBER_TO_VOICEMAIL_BOOL, false);
+        sDefaults.putBoolean(KEY_IGNORE_RTT_MODE_SETTING_BOOL, false);
         sDefaults.putInt(KEY_CDMA_3WAYCALL_FLASH_DELAY_INT , 0);
         sDefaults.putBoolean(KEY_SUPPORT_CONFERENCE_CALL_BOOL, true);
         sDefaults.putBoolean(KEY_SUPPORT_IMS_CONFERENCE_CALL_BOOL, true);
@@ -3551,7 +3574,7 @@
         sDefaults.putStringArray(KEY_EMERGENCY_NUMBER_PREFIX_STRING_ARRAY, new String[0]);
         sDefaults.putBoolean(KEY_USE_USIM_BOOL, false);
         sDefaults.putBoolean(KEY_SHOW_WFC_LOCATION_PRIVACY_POLICY_BOOL, false);
-        sDefaults.putBoolean(KEY_AUTO_CANCEL_CS_REJECT_NOTIFICATION, false);
+        sDefaults.putBoolean(KEY_AUTO_CANCEL_CS_REJECT_NOTIFICATION, true);
         sDefaults.putString(KEY_SMART_FORWARDING_CONFIG_COMPONENT_NAME_STRING, "");
         sDefaults.putBoolean(KEY_ALWAYS_SHOW_PRIMARY_SIGNAL_BAR_IN_OPPORTUNISTIC_NETWORK_BOOLEAN,
                 false);
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 36e8123..bb2269f 100644
--- a/telephony/java/android/telephony/SubscriptionInfo.java
+++ b/telephony/java/android/telephony/SubscriptionInfo.java
@@ -551,7 +551,6 @@
      *
      * @param context Context of the application to check.
      * @return whether the app is authorized to manage this subscription per its metadata.
-     * @throws UnsupportedOperationException if this subscription is not embedded.
      * @hide
      * @deprecated - Do not use.
      */
@@ -567,15 +566,11 @@
      * @param context Any context.
      * @param packageName Package name of the app to check.
      * @return whether the app is authorized to manage this subscription per its metadata.
-     * @throws UnsupportedOperationException if this subscription is not embedded.
      * @hide
      * @deprecated - Do not use.
      */
     @Deprecated
     public boolean canManageSubscription(Context context, String packageName) {
-        if (!isEmbedded()) {
-            throw new UnsupportedOperationException("Not an embedded subscription");
-        }
         List<UiccAccessRule> allAccessRules = getAllAccessRules();
         if (allAccessRules == null) {
             return false;
@@ -606,9 +601,6 @@
      */
     @SystemApi
     public @Nullable List<UiccAccessRule> getAccessRules() {
-        if (!isEmbedded()) {
-            throw new UnsupportedOperationException("Not an embedded subscription");
-        }
         if (mNativeAccessRules == null) return null;
         return Arrays.asList(mNativeAccessRules);
     }
@@ -619,9 +611,6 @@
      * @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) {
diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java
index a84c916..26574c4 100644
--- a/telephony/java/android/telephony/SubscriptionManager.java
+++ b/telephony/java/android/telephony/SubscriptionManager.java
@@ -2588,7 +2588,6 @@
      *
      * @param info The subscription to check.
      * @return whether the app is authorized to manage this subscription per its metadata.
-     * @throws IllegalArgumentException if this subscription is not embedded.
      */
     public boolean canManageSubscription(SubscriptionInfo info) {
         return canManageSubscription(info, mContext.getPackageName());
@@ -2604,13 +2603,9 @@
      * @param info The subscription to check.
      * @param packageName Package name of the app to check.
      * @return whether the app is authorized to manage this subscription per its access rules.
-     * @throws IllegalArgumentException if this subscription is not embedded.
      * @hide
      */
     public boolean canManageSubscription(SubscriptionInfo info, String packageName) {
-        if (!info.isEmbedded()) {
-            throw new IllegalArgumentException("Not an embedded subscription");
-        }
         if (info.getAllAccessRules() == null) {
             return false;
         }
@@ -3012,7 +3007,7 @@
         // to the caller.
         boolean hasCarrierPrivilegePermission = TelephonyManager.from(mContext)
                 .hasCarrierPrivileges(info.getSubscriptionId())
-                || (info.isEmbedded() && canManageSubscription(info));
+                || canManageSubscription(info);
         return hasCarrierPrivilegePermission;
     }
 
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 35b435d..553bff2 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -11047,6 +11047,8 @@
      * The {@link #EXTRA_NETWORK_COUNTRY} extra indicates the country code of the current
      * network returned by {@link #getNetworkCountryIso()}.
      *
+     * <p>There may be a delay of several minutes before reporting that no country is detected.
+     *
      * @see #EXTRA_NETWORK_COUNTRY
      * @see #getNetworkCountryIso()
      */
diff --git a/telephony/java/android/telephony/data/DataServiceCallback.java b/telephony/java/android/telephony/data/DataServiceCallback.java
index 89d30c0d..11dc78a 100644
--- a/telephony/java/android/telephony/data/DataServiceCallback.java
+++ b/telephony/java/android/telephony/data/DataServiceCallback.java
@@ -27,7 +27,6 @@
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
-import java.lang.ref.WeakReference;
 import java.util.List;
 
 /**
@@ -64,11 +63,11 @@
     /** Request sent in illegal state */
     public static final int RESULT_ERROR_ILLEGAL_STATE  = 4;
 
-    private final WeakReference<IDataServiceCallback> mCallback;
+    private final IDataServiceCallback mCallback;
 
     /** @hide */
     public DataServiceCallback(IDataServiceCallback callback) {
-        mCallback = new WeakReference<>(callback);
+        mCallback = callback;
     }
 
     /**
@@ -80,14 +79,15 @@
      */
     public void onSetupDataCallComplete(@ResultCode int result,
                                         @Nullable DataCallResponse response) {
-        IDataServiceCallback callback = mCallback.get();
-        if (callback != null) {
+        if (mCallback != null) {
             try {
                 if (DBG) Rlog.d(TAG, "onSetupDataCallComplete");
-                callback.onSetupDataCallComplete(result, response);
+                mCallback.onSetupDataCallComplete(result, response);
             } catch (RemoteException e) {
                 Rlog.e(TAG, "Failed to onSetupDataCallComplete on the remote");
             }
+        } else {
+            Rlog.e(TAG, "onSetupDataCallComplete: callback is null!");
         }
     }
 
@@ -98,14 +98,15 @@
      * @param result The result code. Must be one of the {@link ResultCode}.
      */
     public void onDeactivateDataCallComplete(@ResultCode int result) {
-        IDataServiceCallback callback = mCallback.get();
-        if (callback != null) {
+        if (mCallback != null) {
             try {
                 if (DBG) Rlog.d(TAG, "onDeactivateDataCallComplete");
-                callback.onDeactivateDataCallComplete(result);
+                mCallback.onDeactivateDataCallComplete(result);
             } catch (RemoteException e) {
                 Rlog.e(TAG, "Failed to onDeactivateDataCallComplete on the remote");
             }
+        } else {
+            Rlog.e(TAG, "onDeactivateDataCallComplete: callback is null!");
         }
     }
 
@@ -116,13 +117,14 @@
      * @param result The result code. Must be one of the {@link ResultCode}.
      */
     public void onSetInitialAttachApnComplete(@ResultCode int result) {
-        IDataServiceCallback callback = mCallback.get();
-        if (callback != null) {
+        if (mCallback != null) {
             try {
-                callback.onSetInitialAttachApnComplete(result);
+                mCallback.onSetInitialAttachApnComplete(result);
             } catch (RemoteException e) {
                 Rlog.e(TAG, "Failed to onSetInitialAttachApnComplete on the remote");
             }
+        } else {
+            Rlog.e(TAG, "onSetInitialAttachApnComplete: callback is null!");
         }
     }
 
@@ -133,13 +135,14 @@
      * @param result The result code. Must be one of the {@link ResultCode}.
      */
     public void onSetDataProfileComplete(@ResultCode int result) {
-        IDataServiceCallback callback = mCallback.get();
-        if (callback != null) {
+        if (mCallback != null) {
             try {
-                callback.onSetDataProfileComplete(result);
+                mCallback.onSetDataProfileComplete(result);
             } catch (RemoteException e) {
                 Rlog.e(TAG, "Failed to onSetDataProfileComplete on the remote");
             }
+        } else {
+            Rlog.e(TAG, "onSetDataProfileComplete: callback is null!");
         }
     }
 
@@ -153,13 +156,14 @@
      */
     public void onRequestDataCallListComplete(@ResultCode int result,
                                               @NonNull List<DataCallResponse> dataCallList) {
-        IDataServiceCallback callback = mCallback.get();
-        if (callback != null) {
+        if (mCallback != null) {
             try {
-                callback.onRequestDataCallListComplete(result, dataCallList);
+                mCallback.onRequestDataCallListComplete(result, dataCallList);
             } catch (RemoteException e) {
                 Rlog.e(TAG, "Failed to onRequestDataCallListComplete on the remote");
             }
+        } else {
+            Rlog.e(TAG, "onRequestDataCallListComplete: callback is null!");
         }
     }
 
@@ -170,14 +174,15 @@
      * @param dataCallList List of the current active data connection.
      */
     public void onDataCallListChanged(@NonNull List<DataCallResponse> dataCallList) {
-        IDataServiceCallback callback = mCallback.get();
-        if (callback != null) {
+        if (mCallback != null) {
             try {
                 if (DBG) Rlog.d(TAG, "onDataCallListChanged");
-                callback.onDataCallListChanged(dataCallList);
+                mCallback.onDataCallListChanged(dataCallList);
             } catch (RemoteException e) {
                 Rlog.e(TAG, "Failed to onDataCallListChanged on the remote");
             }
+        } else {
+            Rlog.e(TAG, "onDataCallListChanged: callback is null!");
         }
     }
 }
diff --git a/telephony/java/com/android/internal/telephony/CbGeoUtils.java b/telephony/java/com/android/internal/telephony/CbGeoUtils.java
new file mode 100644
index 0000000..c973b67
--- /dev/null
+++ b/telephony/java/com/android/internal/telephony/CbGeoUtils.java
@@ -0,0 +1,359 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony;
+
+import android.annotation.NonNull;
+import android.telephony.Rlog;
+import android.text.TextUtils;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.stream.Collectors;
+
+
+/**
+ * This utils class is specifically used for geo-targeting of CellBroadcast messages.
+ * The coordinates used by this utils class are latitude and longitude, but some algorithms in this
+ * class only use them as coordinates on plane, so the calculation will be inaccurate. So don't use
+ * this class for anything other then geo-targeting of cellbroadcast messages.
+ */
+public class CbGeoUtils {
+    /** Geometric interface. */
+    public interface Geometry {
+        /**
+         * Determines if the given point {@code p} is inside the geometry.
+         * @param p point in latitude, longitude format.
+         * @return {@code True} if the given point is inside the geometry.
+         */
+        boolean contains(LatLng p);
+    }
+
+    /**
+     * Tolerance for determining if the value is 0. If the absolute value of a value is less than
+     * this tolerance, it will be treated as 0.
+     */
+    public static final double EPS = 1e-7;
+
+    /** The radius of earth. */
+    public static final int EARTH_RADIUS_METER = 6371 * 1000;
+
+    private static final String TAG = "CbGeoUtils";
+
+    /** The identifier of geometry in the encoded string. */
+    private static final String CIRCLE_SYMBOL = "circle";
+    private static final String POLYGON_SYMBOL = "polygon";
+
+    /** Point represent by (latitude, longitude). */
+    public static class LatLng {
+        public final double lat;
+        public final double lng;
+
+        /**
+         * Constructor.
+         * @param lat latitude, range [-90, 90]
+         * @param lng longitude, range [-180, 180]
+         */
+        public LatLng(double lat, double lng) {
+            this.lat = lat;
+            this.lng = lng;
+        }
+
+        /**
+         * @param p the point use to calculate the subtraction result.
+         * @return the result of this point subtract the given point {@code p}.
+         */
+        public LatLng subtract(LatLng p) {
+            return new LatLng(lat - p.lat, lng - p.lng);
+        }
+
+        /**
+         * Calculate the distance in meter between this point and the given point {@code p}.
+         * @param p the point use to calculate the distance.
+         * @return the distance in meter.
+         */
+        public double distance(LatLng p) {
+            double dlat = Math.sin(0.5 * Math.toRadians(lat - p.lat));
+            double dlng = Math.sin(0.5 * Math.toRadians(lng - p.lng));
+            double x = dlat * dlat
+                    + dlng * dlng * Math.cos(Math.toRadians(lat)) * Math.cos(Math.toRadians(p.lat));
+            return 2 * Math.atan2(Math.sqrt(x), Math.sqrt(1 - x)) * EARTH_RADIUS_METER;
+        }
+    }
+
+    /**
+     * The class represents a simple polygon with at least 3 points.
+     */
+    public static class Polygon implements Geometry {
+        /**
+         * In order to reduce the loss of precision in floating point calculations, all vertices
+         * of the polygon are scaled. Set the value of scale to 1000 can take into account the
+         * actual distance accuracy of 1 meter if the EPS is 1e-7 during the calculation.
+         */
+        private static final double SCALE = 1000.0;
+
+        private final List<LatLng> mVertices;
+        private final List<Point> mScaledVertices;
+        private final LatLng mOrigin;
+
+        /**
+         * Constructs a simple polygon from the given vertices. The adjacent two vertices are
+         * connected to form an edge of the polygon. The polygon has at least 3 vertices, and the
+         * last vertices and the first vertices must be adjacent.
+         *
+         * The longitude difference in the vertices should be less than 180 degree.
+         */
+        public Polygon(@NonNull List<LatLng> vertices) {
+            mVertices = vertices;
+
+            // Find the point with smallest longitude as the mOrigin point.
+            int idx = 0;
+            for (int i = 1; i < vertices.size(); i++) {
+                if (vertices.get(i).lng < vertices.get(idx).lng) {
+                    idx = i;
+                }
+            }
+            mOrigin = vertices.get(idx);
+
+            mScaledVertices = vertices.stream()
+                    .map(latLng -> convertAndScaleLatLng(latLng))
+                    .collect(Collectors.toList());
+        }
+
+        public List<LatLng> getVertices() {
+            return mVertices;
+        }
+
+        /**
+         * Check if the given point {@code p} is inside the polygon. This method counts the number
+         * of times the polygon winds around the point P, A.K.A "winding number". The point is
+         * outside only when this "winding number" is 0.
+         *
+         * If a point is on the edge of the polygon, it is also considered to be inside the polygon.
+         */
+        @Override
+        public boolean contains(LatLng latLng) {
+            Point p = convertAndScaleLatLng(latLng);
+
+            int n = mScaledVertices.size();
+            int windingNumber = 0;
+            for (int i = 0; i < n; i++) {
+                Point a = mScaledVertices.get(i);
+                Point b = mScaledVertices.get((i + 1) % n);
+
+                // CCW is counterclockwise
+                // CCW = ab x ap
+                // CCW > 0 -> ap is on the left side of ab
+                // CCW == 0 -> ap is on the same line of ab
+                // CCW < 0 -> ap is on the right side of ab
+                int ccw = sign(crossProduct(b.subtract(a), p.subtract(a)));
+
+                if (ccw == 0) {
+                    if (Math.min(a.x, b.x) <= p.x && p.x <= Math.max(a.x, b.x)
+                            && Math.min(a.y, b.y) <= p.y && p.y <= Math.max(a.y, b.y)) {
+                        return true;
+                    }
+                } else {
+                    if (sign(a.y - p.y) <= 0) {
+                        // upward crossing
+                        if (ccw > 0 && sign(b.y - p.y) > 0) {
+                            ++windingNumber;
+                        }
+                    } else {
+                        // downward crossing
+                        if (ccw < 0 && sign(b.y - p.y) <= 0) {
+                            --windingNumber;
+                        }
+                    }
+                }
+            }
+            return windingNumber != 0;
+        }
+
+        /**
+         * Move the given point {@code latLng} to the coordinate system with {@code mOrigin} as the
+         * origin and scale it. {@code mOrigin} is selected from the vertices of a polygon, it has
+         * the smallest longitude value among all of the polygon vertices.
+         *
+         * @param latLng the point need to be converted and scaled.
+         * @Return a {@link Point} object.
+         */
+        private Point convertAndScaleLatLng(LatLng latLng) {
+            double x = latLng.lat - mOrigin.lat;
+            double y = latLng.lng - mOrigin.lng;
+
+            // If the point is in different hemispheres(western/eastern) than the mOrigin, and the
+            // edge between them cross the 180th meridian, then its relative coordinates will be
+            // extended.
+            // For example, suppose the longitude of the mOrigin is -178, and the longitude of the
+            // point to be converted is 175, then the longitude after the conversion is -8.
+            // calculation: (-178 - 8) - (-178).
+            if (sign(mOrigin.lng) != 0 && sign(mOrigin.lng) != sign(latLng.lng)) {
+                double distCross0thMeridian = Math.abs(mOrigin.lng) + Math.abs(latLng.lng);
+                if (sign(distCross0thMeridian * 2 - 360) > 0) {
+                    y = sign(mOrigin.lng) * (360 - distCross0thMeridian);
+                }
+            }
+            return new Point(x * SCALE, y * SCALE);
+        }
+
+        private static double crossProduct(Point a, Point b) {
+            return a.x * b.y - a.y * b.x;
+        }
+
+        static final class Point {
+            public final double x;
+            public final double y;
+
+            Point(double x, double y) {
+                this.x = x;
+                this.y = y;
+            }
+
+            public Point subtract(Point p) {
+                return new Point(x - p.x, y - p.y);
+            }
+        }
+    }
+
+    /** The class represents a circle. */
+    public static class Circle implements Geometry {
+        private final LatLng mCenter;
+        private final double mRadiusMeter;
+
+        public Circle(LatLng center, double radiusMeter) {
+            this.mCenter = center;
+            this.mRadiusMeter = radiusMeter;
+        }
+
+        public LatLng getCenter() {
+            return mCenter;
+        }
+
+        public double getRadius() {
+            return mRadiusMeter;
+        }
+
+        @Override
+        public boolean contains(LatLng p) {
+            return mCenter.distance(p) <= mRadiusMeter;
+        }
+    }
+
+    /**
+     * Parse the geometries from the encoded string {@code str}. The string must follow the
+     * geometry encoding specified by {@link android.provider.Telephony.CellBroadcasts#GEOMETRIES}.
+     */
+    @NonNull
+    public static List<Geometry> parseGeometriesFromString(@NonNull String str) {
+        List<Geometry> geometries = new ArrayList<>();
+        for (String geometryStr : str.split("\\s*;\\s*")) {
+            String[] geoParameters = geometryStr.split("\\s*\\|\\s*");
+            switch (geoParameters[0]) {
+                case CIRCLE_SYMBOL:
+                    geometries.add(new Circle(parseLatLngFromString(geoParameters[1]),
+                            Double.parseDouble(geoParameters[2])));
+                    break;
+                case POLYGON_SYMBOL:
+                    List<LatLng> vertices = new ArrayList<>(geoParameters.length - 1);
+                    for (int i = 1; i < geoParameters.length; i++) {
+                        vertices.add(parseLatLngFromString(geoParameters[i]));
+                    }
+                    geometries.add(new Polygon(vertices));
+                    break;
+                default:
+                    Rlog.e(TAG, "Invalid geometry format " + geometryStr);
+            }
+        }
+        return geometries;
+    }
+
+    /**
+     * Encode a list of geometry objects to string. The encoding format is specified by
+     * {@link android.provider.Telephony.CellBroadcasts#GEOMETRIES}.
+     *
+     * @param geometries the list of geometry objects need to be encoded.
+     * @return the encoded string.
+     */
+    @NonNull
+    public static String encodeGeometriesToString(@NonNull List<Geometry> geometries) {
+        return geometries.stream()
+                .map(geometry -> encodeGeometryToString(geometry))
+                .filter(encodedStr -> !TextUtils.isEmpty(encodedStr))
+                .collect(Collectors.joining(";"));
+    }
+
+
+    /**
+     * Encode the geometry object to string. The encoding format is specified by
+     * {@link android.provider.Telephony.CellBroadcasts#GEOMETRIES}.
+     * @param geometry the geometry object need to be encoded.
+     * @return the encoded string.
+     */
+    @NonNull
+    private static String encodeGeometryToString(@NonNull Geometry geometry) {
+        StringBuilder sb = new StringBuilder();
+        if (geometry instanceof Polygon) {
+            sb.append(POLYGON_SYMBOL);
+            for (LatLng latLng : ((Polygon) geometry).getVertices()) {
+                sb.append("|");
+                sb.append(latLng.lat);
+                sb.append(",");
+                sb.append(latLng.lng);
+            }
+        } else if (geometry instanceof Circle) {
+            sb.append(CIRCLE_SYMBOL);
+            Circle circle = (Circle) geometry;
+
+            // Center
+            sb.append("|");
+            sb.append(circle.getCenter().lat);
+            sb.append(",");
+            sb.append(circle.getCenter().lng);
+
+            // Radius
+            sb.append("|");
+            sb.append(circle.getRadius());
+        } else {
+            Rlog.e(TAG, "Unsupported geometry object " + geometry);
+            return null;
+        }
+        return sb.toString();
+    }
+
+    /**
+     * Parse {@link LatLng} from {@link String}. Latitude and longitude are separated by ",".
+     * Example: "13.56,-55.447".
+     *
+     * @param str encoded lat/lng string.
+     * @Return {@link LatLng} object.
+     */
+    @NonNull
+    public static LatLng parseLatLngFromString(@NonNull String str) {
+        String[] latLng = str.split("\\s*,\\s*");
+        return new LatLng(Double.parseDouble(latLng[0]), Double.parseDouble(latLng[1]));
+    }
+
+    /**
+     * @Return the sign of the given value {@code value} with the specified tolerance. Return 1
+     * means the sign is positive, -1 means negative, 0 means the value will be treated as 0.
+     */
+    public static int sign(double value) {
+        if (value > EPS) return 1;
+        if (value < -EPS) return -1;
+        return 0;
+    }
+}
diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java
index ca65736..e9c24cd 100644
--- a/wifi/java/android/net/wifi/WifiManager.java
+++ b/wifi/java/android/net/wifi/WifiManager.java
@@ -70,7 +70,6 @@
 import java.net.InetAddress;
 import java.util.ArrayList;
 import java.util.Collections;
-import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
@@ -1170,15 +1169,16 @@
         updateVerboseLoggingEnabledFromService();
     }
 
-    private IWifiManager getIWifiManager() throws RemoteException {
+    private IWifiManager getIWifiManager() {
         if (mService == null) {
             synchronized (this) {
                 mService = IWifiManager.Stub.asInterface(
                         ServiceManager.getService(Context.WIFI_SERVICE));
-                if (mService == null) {
-                    throw new RemoteException("Wifi Service not running");
+                if (mService != null) {
+                    updateVerboseLoggingEnabledFromService();
+                } else {
+                    Log.e(TAG, "Wifi Service not running yet, ignoring WifiManager API call");
                 }
-                updateVerboseLoggingEnabledFromService();
             }
         }
         return mService;
@@ -1223,8 +1223,10 @@
     @RequiresPermission(allOf = {ACCESS_FINE_LOCATION, ACCESS_WIFI_STATE})
     public List<WifiConfiguration> getConfiguredNetworks() {
         try {
+            IWifiManager iWifiManager = getIWifiManager();
+            if (iWifiManager == null) return Collections.emptyList();
             ParceledListSlice<WifiConfiguration> parceledList =
-                    getIWifiManager().getConfiguredNetworks(mContext.getOpPackageName());
+                    iWifiManager.getConfiguredNetworks(mContext.getOpPackageName());
             if (parceledList == null) {
                 return Collections.emptyList();
             }
@@ -1239,8 +1241,10 @@
     @RequiresPermission(allOf = {ACCESS_FINE_LOCATION, ACCESS_WIFI_STATE, READ_WIFI_CREDENTIAL})
     public List<WifiConfiguration> getPrivilegedConfiguredNetworks() {
         try {
+            IWifiManager iWifiManager = getIWifiManager();
+            if (iWifiManager == null) return Collections.emptyList();
             ParceledListSlice<WifiConfiguration> parceledList =
-                    getIWifiManager().getPrivilegedConfiguredNetworks(mContext.getOpPackageName());
+                    iWifiManager.getPrivilegedConfiguredNetworks(mContext.getOpPackageName());
             if (parceledList == null) {
                 return Collections.emptyList();
             }
@@ -1271,14 +1275,16 @@
             @NonNull List<ScanResult> scanResults) {
         List<Pair<WifiConfiguration, Map<Integer, List<ScanResult>>>> configs = new ArrayList<>();
         try {
+            IWifiManager iWifiManager = getIWifiManager();
+            if (iWifiManager == null) return Collections.emptyList();
             Map<String, Map<Integer, List<ScanResult>>> results =
-                    getIWifiManager().getAllMatchingFqdnsForScanResults(
+                    iWifiManager.getAllMatchingFqdnsForScanResults(
                             scanResults);
             if (results.isEmpty()) {
                 return configs;
             }
             List<WifiConfiguration> wifiConfigurations =
-                    getIWifiManager().getWifiConfigsForPasspointProfiles(
+                    iWifiManager.getWifiConfigsForPasspointProfiles(
                             new ArrayList<>(results.keySet()));
             for (WifiConfiguration configuration : wifiConfigurations) {
                 Map<Integer, List<ScanResult>> scanResultsPerNetworkType = results.get(
@@ -1313,10 +1319,12 @@
     public Map<OsuProvider, List<ScanResult>> getMatchingOsuProviders(
             @Nullable List<ScanResult> scanResults) {
         if (scanResults == null) {
-            return new HashMap<>();
+            return Collections.emptyMap();
         }
         try {
-            return getIWifiManager().getMatchingOsuProviders(scanResults);
+            IWifiManager iWifiManager = getIWifiManager();
+            if (iWifiManager == null) return Collections.emptyMap();
+            return iWifiManager.getMatchingOsuProviders(scanResults);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -1343,7 +1351,9 @@
     public Map<OsuProvider, PasspointConfiguration> getMatchingPasspointConfigsForOsuProviders(
             @NonNull Set<OsuProvider> osuProviders) {
         try {
-            return getIWifiManager().getMatchingPasspointConfigsForOsuProviders(
+            IWifiManager iWifiManager = getIWifiManager();
+            if (iWifiManager == null) return Collections.emptyMap();
+            return iWifiManager.getMatchingPasspointConfigsForOsuProviders(
                     new ArrayList<>(osuProviders));
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
@@ -1428,7 +1438,9 @@
      */
     private int addOrUpdateNetwork(WifiConfiguration config) {
         try {
-            return getIWifiManager().addOrUpdateNetwork(config, mContext.getOpPackageName());
+            IWifiManager iWifiManager = getIWifiManager();
+            if (iWifiManager == null) return -1;
+            return iWifiManager.addOrUpdateNetwork(config, mContext.getOpPackageName());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -1654,7 +1666,11 @@
         Looper looper = (handler == null) ? mContext.getMainLooper() : handler.getLooper();
         Binder binder = new Binder();
         try {
-            getIWifiManager().registerNetworkRequestMatchCallback(
+            IWifiManager iWifiManager = getIWifiManager();
+            if (iWifiManager == null) {
+                throw new RemoteException("Wifi service is not running");
+            }
+            iWifiManager.registerNetworkRequestMatchCallback(
                     binder, new NetworkRequestMatchCallbackProxy(looper, callback),
                     callback.hashCode());
         } catch (RemoteException e) {
@@ -1680,7 +1696,11 @@
         Log.v(TAG, "unregisterNetworkRequestMatchCallback: callback=" + callback);
 
         try {
-            getIWifiManager().unregisterNetworkRequestMatchCallback(callback.hashCode());
+            IWifiManager iWifiManager = getIWifiManager();
+            if (iWifiManager == null) {
+                throw new RemoteException("Wifi service is not running");
+            }
+            iWifiManager.unregisterNetworkRequestMatchCallback(callback.hashCode());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -1714,7 +1734,9 @@
     public @NetworkSuggestionsStatusCode int addNetworkSuggestions(
             @NonNull List<WifiNetworkSuggestion> networkSuggestions) {
         try {
-            return getIWifiManager().addNetworkSuggestions(
+            IWifiManager iWifiManager = getIWifiManager();
+            if (iWifiManager == null) return STATUS_NETWORK_SUGGESTIONS_ERROR_INTERNAL;
+            return iWifiManager.addNetworkSuggestions(
                     networkSuggestions, mContext.getOpPackageName());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
@@ -1738,7 +1760,9 @@
     public @NetworkSuggestionsStatusCode int removeNetworkSuggestions(
             @NonNull List<WifiNetworkSuggestion> networkSuggestions) {
         try {
-            return getIWifiManager().removeNetworkSuggestions(
+            IWifiManager iWifiManager = getIWifiManager();
+            if (iWifiManager == null) return STATUS_NETWORK_SUGGESTIONS_ERROR_INTERNAL;
+            return iWifiManager.removeNetworkSuggestions(
                     networkSuggestions, mContext.getOpPackageName());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
@@ -1754,7 +1778,9 @@
     @RequiresPermission(ACCESS_WIFI_STATE)
     public @NonNull List<WifiNetworkSuggestion> getNetworkSuggestions() {
         try {
-            return mService.getNetworkSuggestions(mContext.getOpPackageName());
+            IWifiManager iWifiManager = getIWifiManager();
+            if (iWifiManager == null) return Collections.emptyList();
+            return iWifiManager.getNetworkSuggestions(mContext.getOpPackageName());
         } catch (RemoteException e) {
             throw e.rethrowAsRuntimeException();
         }
@@ -1784,7 +1810,11 @@
      */
     public void addOrUpdatePasspointConfiguration(PasspointConfiguration config) {
         try {
-            if (!getIWifiManager().addOrUpdatePasspointConfiguration(
+            IWifiManager iWifiManager = getIWifiManager();
+            if (iWifiManager == null) {
+                throw new RemoteException("Wifi service is not running");
+            }
+            if (!iWifiManager.addOrUpdatePasspointConfiguration(
                     config, mContext.getOpPackageName())) {
                 throw new IllegalArgumentException();
             }
@@ -1808,7 +1838,11 @@
     })
     public void removePasspointConfiguration(String fqdn) {
         try {
-            if (!getIWifiManager().removePasspointConfiguration(
+            IWifiManager iWifiManager = getIWifiManager();
+            if (iWifiManager == null) {
+                throw new RemoteException("Wifi service is not running");
+            }
+            if (!iWifiManager.removePasspointConfiguration(
                     fqdn, mContext.getOpPackageName())) {
                 throw new IllegalArgumentException();
             }
@@ -1832,7 +1866,9 @@
     })
     public List<PasspointConfiguration> getPasspointConfigurations() {
         try {
-            return getIWifiManager().getPasspointConfigurations(mContext.getOpPackageName());
+            IWifiManager iWifiManager = getIWifiManager();
+            if (iWifiManager == null) Collections.emptyList();
+            return iWifiManager.getPasspointConfigurations(mContext.getOpPackageName());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -1852,7 +1888,11 @@
      */
     public void queryPasspointIcon(long bssid, String fileName) {
         try {
-            getIWifiManager().queryPasspointIcon(bssid, fileName);
+            IWifiManager iWifiManager = getIWifiManager();
+            if (iWifiManager == null) {
+                throw new RemoteException("Wifi service is not running");
+            }
+            iWifiManager.queryPasspointIcon(bssid, fileName);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -1866,7 +1906,9 @@
      */
     public int matchProviderWithCurrentNetwork(String fqdn) {
         try {
-            return getIWifiManager().matchProviderWithCurrentNetwork(fqdn);
+            IWifiManager iWifiManager = getIWifiManager();
+            if (iWifiManager == null) return -1;
+            return iWifiManager.matchProviderWithCurrentNetwork(fqdn);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -1880,7 +1922,11 @@
      */
     public void deauthenticateNetwork(long holdoff, boolean ess) {
         try {
-            getIWifiManager().deauthenticateNetwork(holdoff, ess);
+            IWifiManager iWifiManager = getIWifiManager();
+            if (iWifiManager == null) {
+                throw new RemoteException("Wifi service is not running");
+            }
+            iWifiManager.deauthenticateNetwork(holdoff, ess);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -1910,7 +1956,9 @@
     @Deprecated
     public boolean removeNetwork(int netId) {
         try {
-            return getIWifiManager().removeNetwork(netId, mContext.getOpPackageName());
+            IWifiManager iWifiManager = getIWifiManager();
+            if (iWifiManager == null) return false;
+            return iWifiManager.removeNetwork(netId, mContext.getOpPackageName());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -1965,7 +2013,9 @@
 
         boolean success;
         try {
-            success = getIWifiManager().enableNetwork(
+            IWifiManager iWifiManager = getIWifiManager();
+            if (iWifiManager == null) return false;
+            success = iWifiManager.enableNetwork(
                     netId, attemptConnect, mContext.getOpPackageName());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
@@ -2002,7 +2052,9 @@
     @Deprecated
     public boolean disableNetwork(int netId) {
         try {
-            return getIWifiManager().disableNetwork(netId, mContext.getOpPackageName());
+            IWifiManager iWifiManager = getIWifiManager();
+            if (iWifiManager == null) return false;
+            return iWifiManager.disableNetwork(netId, mContext.getOpPackageName());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -2025,7 +2077,9 @@
     @Deprecated
     public boolean disconnect() {
         try {
-            return getIWifiManager().disconnect(mContext.getOpPackageName());
+            IWifiManager iWifiManager = getIWifiManager();
+            if (iWifiManager == null) return false;
+            return iWifiManager.disconnect(mContext.getOpPackageName());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -2049,7 +2103,9 @@
     @Deprecated
     public boolean reconnect() {
         try {
-            return getIWifiManager().reconnect(mContext.getOpPackageName());
+            IWifiManager iWifiManager = getIWifiManager();
+            if (iWifiManager == null) return false;
+            return iWifiManager.reconnect(mContext.getOpPackageName());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -2073,7 +2129,9 @@
     @Deprecated
     public boolean reassociate() {
         try {
-            return getIWifiManager().reassociate(mContext.getOpPackageName());
+            IWifiManager iWifiManager = getIWifiManager();
+            if (iWifiManager == null) return false;
+            return iWifiManager.reassociate(mContext.getOpPackageName());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -2159,7 +2217,9 @@
 
     private long getSupportedFeatures() {
         try {
-            return getIWifiManager().getSupportedFeatures();
+            IWifiManager iWifiManager = getIWifiManager();
+            if (iWifiManager == null) return 0L;
+            return iWifiManager.getSupportedFeatures();
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -2289,8 +2349,10 @@
      */
     public WifiActivityEnergyInfo getControllerActivityEnergyInfo() {
         try {
+            IWifiManager iWifiManager = getIWifiManager();
+            if (iWifiManager == null) return null;
             synchronized(this) {
-                return getIWifiManager().reportActivityInfo();
+                return iWifiManager.reportActivityInfo();
             }
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
@@ -2329,8 +2391,10 @@
     @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS)
     public boolean startScan(WorkSource workSource) {
         try {
+            IWifiManager iWifiManager = getIWifiManager();
+            if (iWifiManager == null) return false;
             String packageName = mContext.getOpPackageName();
-            return getIWifiManager().startScan(packageName);
+            return iWifiManager.startScan(packageName);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -2359,7 +2423,9 @@
      */
     public WifiInfo getConnectionInfo() {
         try {
-            return getIWifiManager().getConnectionInfo(mContext.getOpPackageName());
+            IWifiManager iWifiManager = getIWifiManager();
+            if (iWifiManager == null) return null;
+            return iWifiManager.getConnectionInfo(mContext.getOpPackageName());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -2373,7 +2439,9 @@
      */
     public List<ScanResult> getScanResults() {
         try {
-            return getIWifiManager().getScanResults(mContext.getOpPackageName());
+            IWifiManager iWifiManager = getIWifiManager();
+            if (iWifiManager == null) return Collections.emptyList();
+            return iWifiManager.getScanResults(mContext.getOpPackageName());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -2392,7 +2460,9 @@
     @Deprecated
     public boolean isScanAlwaysAvailable() {
         try {
-            return getIWifiManager().isScanAlwaysAvailable();
+            IWifiManager iWifiManager = getIWifiManager();
+            if (iWifiManager == null) return false;
+            return iWifiManager.isScanAlwaysAvailable();
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -2423,7 +2493,11 @@
      */
     public void setCountryCode(@NonNull String country) {
         try {
-            getIWifiManager().setCountryCode(country);
+            IWifiManager iWifiManager = getIWifiManager();
+            if (iWifiManager == null) {
+                throw new RemoteException("Wifi service is not running");
+            }
+            iWifiManager.setCountryCode(country);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -2438,7 +2512,9 @@
     @UnsupportedAppUsage
     public String getCountryCode() {
         try {
-            String country = getIWifiManager().getCountryCode();
+            IWifiManager iWifiManager = getIWifiManager();
+            if (iWifiManager == null) return null;
+            String country = iWifiManager.getCountryCode();
             return country;
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
@@ -2453,7 +2529,9 @@
     @UnsupportedAppUsage
     public boolean isDualBandSupported() {
         try {
-            return getIWifiManager().isDualBandSupported();
+            IWifiManager iWifiManager = getIWifiManager();
+            if (iWifiManager == null) return false;
+            return iWifiManager.isDualBandSupported();
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -2466,7 +2544,9 @@
      */
     public boolean isDualModeSupported() {
         try {
-            return getIWifiManager().needs5GHzToAnyApBandConversion();
+            IWifiManager iWifiManager = getIWifiManager();
+            if (iWifiManager == null) return false;
+            return iWifiManager.needs5GHzToAnyApBandConversion();
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -2479,7 +2559,9 @@
      */
     public DhcpInfo getDhcpInfo() {
         try {
-            return getIWifiManager().getDhcpInfo();
+            IWifiManager iWifiManager = getIWifiManager();
+            if (iWifiManager == null) return null;
+            return iWifiManager.getDhcpInfo();
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -2506,7 +2588,9 @@
     @Deprecated
     public boolean setWifiEnabled(boolean enabled) {
         try {
-            return getIWifiManager().setWifiEnabled(mContext.getOpPackageName(), enabled);
+            IWifiManager iWifiManager = getIWifiManager();
+            if (iWifiManager == null) return false;
+            return iWifiManager.setWifiEnabled(mContext.getOpPackageName(), enabled);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -2521,7 +2605,9 @@
      */
     public int getWifiState() {
         try {
-            return getIWifiManager().getWifiEnabledState();
+            IWifiManager iWifiManager = getIWifiManager();
+            if (iWifiManager == null) return WIFI_STATE_UNKNOWN;
+            return iWifiManager.getWifiEnabledState();
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -2595,7 +2681,11 @@
      */
     public void updateInterfaceIpState(String ifaceName, int mode) {
         try {
-            getIWifiManager().updateInterfaceIpState(ifaceName, mode);
+            IWifiManager iWifiManager = getIWifiManager();
+            if (iWifiManager == null) {
+                throw new RemoteException("Wifi service is not running");
+            }
+            iWifiManager.updateInterfaceIpState(ifaceName, mode);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -2613,7 +2703,9 @@
      */
     public boolean startSoftAp(@Nullable WifiConfiguration wifiConfig) {
         try {
-            return getIWifiManager().startSoftAp(wifiConfig);
+            IWifiManager iWifiManager = getIWifiManager();
+            if (iWifiManager == null) return false;
+            return iWifiManager.startSoftAp(wifiConfig);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -2628,7 +2720,9 @@
      */
     public boolean stopSoftAp() {
         try {
-            return getIWifiManager().stopSoftAp();
+            IWifiManager iWifiManager = getIWifiManager();
+            if (iWifiManager == null) return false;
+            return iWifiManager.stopSoftAp();
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -2696,8 +2790,12 @@
             LocalOnlyHotspotCallbackProxy proxy =
                     new LocalOnlyHotspotCallbackProxy(this, looper, callback);
             try {
+                IWifiManager iWifiManager = getIWifiManager();
+                if (iWifiManager == null) {
+                    throw new RemoteException("Wifi service is not running");
+                }
                 String packageName = mContext.getOpPackageName();
-                int returnCode = getIWifiManager().startLocalOnlyHotspot(
+                int returnCode = iWifiManager.startLocalOnlyHotspot(
                         proxy.getMessenger(), new Binder(), packageName);
                 if (returnCode != LocalOnlyHotspotCallback.REQUEST_REGISTERED) {
                     // Send message to the proxy to make sure we call back on the correct thread
@@ -2749,7 +2847,11 @@
             }
             mLOHSCallbackProxy = null;
             try {
-                getIWifiManager().stopLocalOnlyHotspot();
+                IWifiManager iWifiManager = getIWifiManager();
+                if (iWifiManager == null) {
+                    throw new RemoteException("Wifi service is not running");
+                }
+                iWifiManager.stopLocalOnlyHotspot();
             } catch (RemoteException e) {
                 throw e.rethrowFromSystemServer();
             }
@@ -2779,7 +2881,11 @@
             Looper looper = (handler == null) ? mContext.getMainLooper() : handler.getLooper();
             mLOHSObserverProxy = new LocalOnlyHotspotObserverProxy(this, looper, observer);
             try {
-                getIWifiManager().startWatchLocalOnlyHotspot(
+                IWifiManager iWifiManager = getIWifiManager();
+                if (iWifiManager == null) {
+                    throw new RemoteException("Wifi service is not running");
+                }
+                iWifiManager.startWatchLocalOnlyHotspot(
                         mLOHSObserverProxy.getMessenger(), new Binder());
                 mLOHSObserverProxy.registered();
             } catch (RemoteException e) {
@@ -2803,7 +2909,11 @@
             }
             mLOHSObserverProxy = null;
             try {
-                getIWifiManager().stopWatchLocalOnlyHotspot();
+                IWifiManager iWifiManager = getIWifiManager();
+                if (iWifiManager == null) {
+                    throw new RemoteException("Wifi service is not running");
+                }
+                iWifiManager.stopWatchLocalOnlyHotspot();
             } catch (RemoteException e) {
                 throw e.rethrowFromSystemServer();
             }
@@ -2823,7 +2933,9 @@
     @RequiresPermission(android.Manifest.permission.ACCESS_WIFI_STATE)
     public int getWifiApState() {
         try {
-            return getIWifiManager().getWifiApEnabledState();
+            IWifiManager iWifiManager = getIWifiManager();
+            if (iWifiManager == null) return WIFI_AP_STATE_FAILED;
+            return iWifiManager.getWifiApEnabledState();
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -2852,7 +2964,9 @@
     @RequiresPermission(android.Manifest.permission.ACCESS_WIFI_STATE)
     public WifiConfiguration getWifiApConfiguration() {
         try {
-            return getIWifiManager().getWifiApConfiguration();
+            IWifiManager iWifiManager = getIWifiManager();
+            if (iWifiManager == null) return null;
+            return iWifiManager.getWifiApConfiguration();
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -2869,7 +2983,9 @@
     @RequiresPermission(android.Manifest.permission.CHANGE_WIFI_STATE)
     public boolean setWifiApConfiguration(WifiConfiguration wifiConfig) {
         try {
-            return getIWifiManager().setWifiApConfiguration(
+            IWifiManager iWifiManager = getIWifiManager();
+            if (iWifiManager == null) return false;
+            return iWifiManager.setWifiApConfiguration(
                     wifiConfig, mContext.getOpPackageName());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
@@ -2885,7 +3001,11 @@
     public void notifyUserOfApBandConversion() {
         Log.d(TAG, "apBand was converted, notify the user");
         try {
-            getIWifiManager().notifyUserOfApBandConversion(mContext.getOpPackageName());
+            IWifiManager iWifiManager = getIWifiManager();
+            if (iWifiManager == null) {
+                throw new RemoteException("Wifi service is not running");
+            }
+            iWifiManager.notifyUserOfApBandConversion(mContext.getOpPackageName());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -2913,7 +3033,11 @@
      */
     public void setTdlsEnabled(InetAddress remoteIPAddress, boolean enable) {
         try {
-            getIWifiManager().enableTdls(remoteIPAddress.getHostAddress(), enable);
+            IWifiManager iWifiManager = getIWifiManager();
+            if (iWifiManager == null) {
+                throw new RemoteException("Wifi service is not running");
+            }
+            iWifiManager.enableTdls(remoteIPAddress.getHostAddress(), enable);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -2927,7 +3051,11 @@
      */
     public void setTdlsEnabledWithMacAddress(String remoteMacAddress, boolean enable) {
         try {
-            getIWifiManager().enableTdlsWithMacAddress(remoteMacAddress, enable);
+            IWifiManager iWifiManager = getIWifiManager();
+            if (iWifiManager == null) {
+                throw new RemoteException("Wifi service is not running");
+            }
+            iWifiManager.enableTdlsWithMacAddress(remoteMacAddress, enable);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -3216,7 +3344,11 @@
         Looper looper = (handler == null) ? mContext.getMainLooper() : handler.getLooper();
         Binder binder = new Binder();
         try {
-            getIWifiManager().registerSoftApCallback(
+            IWifiManager iWifiManager = getIWifiManager();
+            if (iWifiManager == null) {
+                throw new RemoteException("Wifi service is not running");
+            }
+            iWifiManager.registerSoftApCallback(
                     binder, new SoftApCallbackProxy(looper, callback), callback.hashCode());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
@@ -3237,7 +3369,11 @@
         Log.v(TAG, "unregisterSoftApCallback: callback=" + callback);
 
         try {
-            getIWifiManager().unregisterSoftApCallback(callback.hashCode());
+            IWifiManager iWifiManager = getIWifiManager();
+            if (iWifiManager == null) {
+                throw new RemoteException("Wifi service is not running");
+            }
+            iWifiManager.unregisterSoftApCallback(callback.hashCode());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -3807,7 +3943,11 @@
     public void disableEphemeralNetwork(String SSID) {
         if (SSID == null) throw new IllegalArgumentException("SSID cannot be null");
         try {
-            getIWifiManager().disableEphemeralNetwork(SSID, mContext.getOpPackageName());
+            IWifiManager iWifiManager = getIWifiManager();
+            if (iWifiManager == null) {
+                throw new RemoteException("Wifi service is not running");
+            }
+            iWifiManager.disableEphemeralNetwork(SSID, mContext.getOpPackageName());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -3851,7 +3991,9 @@
     @UnsupportedAppUsage
     private Messenger getWifiServiceMessenger() {
         try {
-            return getIWifiManager().getWifiServiceMessenger(mContext.getOpPackageName());
+            IWifiManager iWifiManager = getIWifiManager();
+            if (iWifiManager == null) return null;
+            return iWifiManager.getWifiServiceMessenger(mContext.getOpPackageName());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -3911,10 +4053,14 @@
             synchronized (mBinder) {
                 if (mRefCounted ? (++mRefCount == 1) : (!mHeld)) {
                     try {
-                        getIWifiManager().acquireWifiLock(mBinder, mLockType, mTag, mWorkSource);
+                        IWifiManager iWifiManager = getIWifiManager();
+                        if (iWifiManager == null) {
+                            throw new RemoteException("Wifi service is not running");
+                        }
+                        iWifiManager.acquireWifiLock(mBinder, mLockType, mTag, mWorkSource);
                         synchronized (WifiManager.this) {
                             if (mActiveLockCount >= MAX_ACTIVE_LOCKS) {
-                                getIWifiManager().releaseWifiLock(mBinder);
+                                iWifiManager.releaseWifiLock(mBinder);
                                 throw new UnsupportedOperationException(
                                             "Exceeded maximum number of wifi locks");
                             }
@@ -3944,7 +4090,11 @@
             synchronized (mBinder) {
                 if (mRefCounted ? (--mRefCount == 0) : (mHeld)) {
                     try {
-                        getIWifiManager().releaseWifiLock(mBinder);
+                        IWifiManager iWifiManager = getIWifiManager();
+                        if (iWifiManager == null) {
+                            throw new RemoteException("Wifi service is not running");
+                        }
+                        iWifiManager.releaseWifiLock(mBinder);
                         synchronized (WifiManager.this) {
                             mActiveLockCount--;
                         }
@@ -4007,7 +4157,11 @@
                 }
                 if (changed && mHeld) {
                     try {
-                        getIWifiManager().updateWifiLockWorkSource(mBinder, mWorkSource);
+                        IWifiManager iWifiManager = getIWifiManager();
+                        if (iWifiManager == null) {
+                            throw new RemoteException("Wifi service is not running");
+                        }
+                        iWifiManager.updateWifiLockWorkSource(mBinder, mWorkSource);
                     } catch (RemoteException e) {
                         throw e.rethrowFromSystemServer();
                     }
@@ -4035,7 +4189,11 @@
             synchronized (mBinder) {
                 if (mHeld) {
                     try {
-                        getIWifiManager().releaseWifiLock(mBinder);
+                        IWifiManager iWifiManager = getIWifiManager();
+                        if (iWifiManager == null) {
+                            throw new RemoteException("Wifi service is not running");
+                        }
+                        iWifiManager.releaseWifiLock(mBinder);
                         synchronized (WifiManager.this) {
                             mActiveLockCount--;
                         }
@@ -4148,10 +4306,14 @@
             synchronized (mBinder) {
                 if (mRefCounted ? (++mRefCount == 1) : (!mHeld)) {
                     try {
-                        getIWifiManager().acquireMulticastLock(mBinder, mTag);
+                        IWifiManager iWifiManager = getIWifiManager();
+                        if (iWifiManager == null) {
+                            throw new RemoteException("Wifi service is not running");
+                        }
+                        iWifiManager.acquireMulticastLock(mBinder, mTag);
                         synchronized (WifiManager.this) {
                             if (mActiveLockCount >= MAX_ACTIVE_LOCKS) {
-                                getIWifiManager().releaseMulticastLock(mTag);
+                                iWifiManager.releaseMulticastLock(mTag);
                                 throw new UnsupportedOperationException(
                                         "Exceeded maximum number of wifi locks");
                             }
@@ -4193,7 +4355,11 @@
             synchronized (mBinder) {
                 if (mRefCounted ? (--mRefCount == 0) : (mHeld)) {
                     try {
-                        getIWifiManager().releaseMulticastLock(mTag);
+                        IWifiManager iWifiManager = getIWifiManager();
+                        if (iWifiManager == null) {
+                            throw new RemoteException("Wifi service is not running");
+                        }
+                        iWifiManager.releaseMulticastLock(mTag);
                         synchronized (WifiManager.this) {
                             mActiveLockCount--;
                         }
@@ -4270,7 +4436,9 @@
      */
     public boolean isMulticastEnabled() {
         try {
-            return getIWifiManager().isMulticastEnabled();
+            IWifiManager iWifiManager = getIWifiManager();
+            if (iWifiManager == null) return false;
+            return iWifiManager.isMulticastEnabled();
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -4283,7 +4451,9 @@
     @UnsupportedAppUsage
     public boolean initializeMulticastFiltering() {
         try {
-            getIWifiManager().initializeMulticastFiltering();
+            IWifiManager iWifiManager = getIWifiManager();
+            if (iWifiManager == null) return false;
+            iWifiManager.initializeMulticastFiltering();
             return true;
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
@@ -4308,7 +4478,11 @@
     @UnsupportedAppUsage
     public void enableVerboseLogging (int verbose) {
         try {
-            getIWifiManager().enableVerboseLogging(verbose);
+            IWifiManager iWifiManager = getIWifiManager();
+            if (iWifiManager == null) {
+                throw new RemoteException("Wifi service is not running");
+            }
+            iWifiManager.enableVerboseLogging(verbose);
         } catch (Exception e) {
             //ignore any failure here
             Log.e(TAG, "enableVerboseLogging " + e.toString());
@@ -4323,7 +4497,9 @@
     @UnsupportedAppUsage
     public int getVerboseLoggingLevel() {
         try {
-            return getIWifiManager().getVerboseLoggingLevel();
+            IWifiManager iWifiManager = getIWifiManager();
+            if (iWifiManager == null) return -1;
+            return iWifiManager.getVerboseLoggingLevel();
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -4336,7 +4512,11 @@
      */
     public void factoryReset() {
         try {
-            getIWifiManager().factoryReset(mContext.getOpPackageName());
+            IWifiManager iWifiManager = getIWifiManager();
+            if (iWifiManager == null) {
+                throw new RemoteException("Wifi service is not running");
+            }
+            iWifiManager.factoryReset(mContext.getOpPackageName());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -4350,7 +4530,9 @@
     @UnsupportedAppUsage
     public Network getCurrentNetwork() {
         try {
-            return getIWifiManager().getCurrentNetwork();
+            IWifiManager iWifiManager = getIWifiManager();
+            if (iWifiManager == null) return null;
+            return iWifiManager.getCurrentNetwork();
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -4382,7 +4564,11 @@
      */
     public void enableWifiConnectivityManager(boolean enabled) {
         try {
-            getIWifiManager().enableWifiConnectivityManager(enabled);
+            IWifiManager iWifiManager = getIWifiManager();
+            if (iWifiManager == null) {
+                throw new RemoteException("Wifi service is not running");
+            }
+            iWifiManager.enableWifiConnectivityManager(enabled);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -4394,7 +4580,9 @@
      */
     public byte[] retrieveBackupData() {
         try {
-            return getIWifiManager().retrieveBackupData();
+            IWifiManager iWifiManager = getIWifiManager();
+            if (iWifiManager == null) return null;
+            return iWifiManager.retrieveBackupData();
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -4406,7 +4594,11 @@
      */
     public void restoreBackupData(byte[] data) {
         try {
-            getIWifiManager().restoreBackupData(data);
+            IWifiManager iWifiManager = getIWifiManager();
+            if (iWifiManager == null) {
+                throw new RemoteException("Wifi service is not running");
+            }
+            iWifiManager.restoreBackupData(data);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -4422,7 +4614,11 @@
     @Deprecated
     public void restoreSupplicantBackupData(byte[] supplicantData, byte[] ipConfigData) {
         try {
-            getIWifiManager().restoreSupplicantBackupData(supplicantData, ipConfigData);
+            IWifiManager iWifiManager = getIWifiManager();
+            if (iWifiManager == null) {
+                throw new RemoteException("Wifi service is not running");
+            }
+            iWifiManager.restoreSupplicantBackupData(supplicantData, ipConfigData);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -4451,7 +4647,11 @@
             throw new IllegalArgumentException("callback must not be null");
         }
         try {
-            getIWifiManager().startSubscriptionProvisioning(provider,
+            IWifiManager iWifiManager = getIWifiManager();
+            if (iWifiManager == null) {
+                throw new RemoteException("Wifi service is not running");
+            }
+            iWifiManager.startSubscriptionProvisioning(provider,
                     new ProvisioningCallbackProxy(executor, callback));
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
@@ -4565,7 +4765,11 @@
         Looper looper = (handler == null) ? mContext.getMainLooper() : handler.getLooper();
         Binder binder = new Binder();
         try {
-            getIWifiManager().registerTrafficStateCallback(
+            IWifiManager iWifiManager = getIWifiManager();
+            if (iWifiManager == null) {
+                throw new RemoteException("Wifi service is not running");
+            }
+            iWifiManager.registerTrafficStateCallback(
                     binder, new TrafficStateCallbackProxy(looper, callback), callback.hashCode());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
@@ -4585,7 +4789,11 @@
         Log.v(TAG, "unregisterTrafficStateCallback: callback=" + callback);
 
         try {
-            getIWifiManager().unregisterTrafficStateCallback(callback.hashCode());
+            IWifiManager iWifiManager = getIWifiManager();
+            if (iWifiManager == null) {
+                throw new RemoteException("Wifi service is not running");
+            }
+            iWifiManager.unregisterTrafficStateCallback(callback.hashCode());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -4641,7 +4849,9 @@
     @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS)
     public String[] getFactoryMacAddresses() {
         try {
-            return getIWifiManager().getFactoryMacAddresses();
+            IWifiManager iWifiManager = getIWifiManager();
+            if (iWifiManager == null) return null;
+            return iWifiManager.getFactoryMacAddresses();
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -4710,7 +4920,11 @@
     @RequiresPermission(android.Manifest.permission.WIFI_SET_DEVICE_MOBILITY_STATE)
     public void setDeviceMobilityState(@DeviceMobilityState int state) {
         try {
-            getIWifiManager().setDeviceMobilityState(state);
+            IWifiManager iWifiManager = getIWifiManager();
+            if (iWifiManager == null) {
+                throw new RemoteException("Wifi service is not running");
+            }
+            iWifiManager.setDeviceMobilityState(state);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -4765,7 +4979,11 @@
             @NonNull EasyConnectStatusCallback callback) {
         Binder binder = new Binder();
         try {
-            getIWifiManager().startDppAsConfiguratorInitiator(
+            IWifiManager iWifiManager = getIWifiManager();
+            if (iWifiManager == null) {
+                throw new RemoteException("Wifi service is not running");
+            }
+            iWifiManager.startDppAsConfiguratorInitiator(
                     binder, enrolleeUri, selectedNetworkId, enrolleeNetworkRole,
                     new EasyConnectCallbackProxy(executor, callback));
         } catch (RemoteException e) {
@@ -4792,7 +5010,11 @@
             @NonNull EasyConnectStatusCallback callback) {
         Binder binder = new Binder();
         try {
-            getIWifiManager().startDppAsEnrolleeInitiator(binder, configuratorUri,
+            IWifiManager iWifiManager = getIWifiManager();
+            if (iWifiManager == null) {
+                throw new RemoteException("Wifi service is not running");
+            }
+            iWifiManager.startDppAsEnrolleeInitiator(binder, configuratorUri,
                     new EasyConnectCallbackProxy(executor, callback));
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
@@ -4813,8 +5035,12 @@
             android.Manifest.permission.NETWORK_SETUP_WIZARD})
     public void stopEasyConnectSession() {
         try {
+            IWifiManager iWifiManager = getIWifiManager();
+            if (iWifiManager == null) {
+                throw new RemoteException("Wifi service is not running");
+            }
             /* Request lower layers to stop/abort and clear resources */
-            getIWifiManager().stopDppSession();
+            iWifiManager.stopDppSession();
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -4913,7 +5139,11 @@
             Log.v(TAG, "addOnWifiUsabilityStatsListener: listener=" + listener);
         }
         try {
-            getIWifiManager().addOnWifiUsabilityStatsListener(new Binder(),
+            IWifiManager iWifiManager = getIWifiManager();
+            if (iWifiManager == null) {
+                throw new RemoteException("Wifi service is not running");
+            }
+            iWifiManager.addOnWifiUsabilityStatsListener(new Binder(),
                     new IOnWifiUsabilityStatsListener.Stub() {
                         @Override
                         public void onWifiUsabilityStats(int seqNum, boolean isSameBssidAndFreq,
@@ -4950,7 +5180,11 @@
             Log.v(TAG, "removeOnWifiUsabilityStatsListener: listener=" + listener);
         }
         try {
-            getIWifiManager().removeOnWifiUsabilityStatsListener(listener.hashCode());
+            IWifiManager iWifiManager = getIWifiManager();
+            if (iWifiManager == null) {
+                throw new RemoteException("Wifi service is not running");
+            }
+            iWifiManager.removeOnWifiUsabilityStatsListener(listener.hashCode());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -4973,7 +5207,11 @@
     @RequiresPermission(android.Manifest.permission.WIFI_UPDATE_USABILITY_STATS_SCORE)
     public void updateWifiUsabilityScore(int seqNum, int score, int predictionHorizonSec) {
         try {
-            getIWifiManager().updateWifiUsabilityScore(seqNum, score, predictionHorizonSec);
+            IWifiManager iWifiManager = getIWifiManager();
+            if (iWifiManager == null) {
+                throw new RemoteException("Wifi service is not running");
+            }
+            iWifiManager.updateWifiUsabilityScore(seqNum, score, predictionHorizonSec);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }